Merge pull request #1807 from odin-lang/simd-dev

Generic #simd type and intrinsics
This commit is contained in:
gingerBill
2022-05-31 11:52:24 +01:00
committed by GitHub
43 changed files with 5446 additions and 378 deletions
+116 -2
View File
@@ -256,7 +256,6 @@ struct BuildContext {
String extra_linker_flags;
String extra_assembler_flags;
String microarch;
String target_features;
BuildModeKind build_mode;
bool generate_docs;
i32 optimization_level;
@@ -320,6 +319,10 @@ struct BuildContext {
PtrMap<char const *, ExactValue> defined_values;
BlockingMutex target_features_mutex;
StringSet target_features_set;
String target_features_string;
};
gb_global BuildContext build_context = {0};
@@ -629,6 +632,15 @@ bool is_arch_wasm(void) {
return false;
}
bool is_arch_x86(void) {
switch (build_context.metrics.arch) {
case TargetArch_i386:
case TargetArch_amd64:
return true;
}
return false;
}
bool allow_check_foreign_filepath(void) {
switch (build_context.metrics.arch) {
case TargetArch_wasm32:
@@ -1188,6 +1200,100 @@ void init_build_context(TargetMetrics *cross_target) {
#include "microsoft_craziness.h"
#endif
Array<String> split_by_comma(String const &list) {
isize n = 1;
for (isize i = 0; i < list.len; i++) {
if (list.text[i] == ',') {
n++;
}
}
auto res = array_make<String>(heap_allocator(), n);
String s = list;
for (isize i = 0; i < n; i++) {
isize m = string_index_byte(s, ',');
if (m < 0) {
res[i] = s;
break;
}
res[i] = substring(s, 0, m);
s = substring(s, m+1, s.len);
}
return res;
}
bool check_target_feature_is_valid(TokenPos pos, String const &feature) {
// TODO(bill): check_target_feature_is_valid
return true;
}
bool check_target_feature_is_enabled(TokenPos pos, String const &target_feature_list) {
BuildContext *bc = &build_context;
mutex_lock(&bc->target_features_mutex);
defer (mutex_unlock(&bc->target_features_mutex));
auto items = split_by_comma(target_feature_list);
array_free(&items);
for_array(i, items) {
String const &item = items.data[i];
if (!check_target_feature_is_valid(pos, item)) {
error(pos, "Target feature '%.*s' is not valid", LIT(item));
return false;
}
if (!string_set_exists(&bc->target_features_set, item)) {
error(pos, "Target feature '%.*s' is not enabled", LIT(item));
return false;
}
}
return true;
}
void enable_target_feature(TokenPos pos, String const &target_feature_list) {
BuildContext *bc = &build_context;
mutex_lock(&bc->target_features_mutex);
defer (mutex_unlock(&bc->target_features_mutex));
auto items = split_by_comma(target_feature_list);
array_free(&items);
for_array(i, items) {
String const &item = items.data[i];
if (!check_target_feature_is_valid(pos, item)) {
error(pos, "Target feature '%.*s' is not valid", LIT(item));
}
}
}
char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes) {
isize len = 0;
for_array(i, build_context.target_features_set.entries) {
if (i != 0) {
len += 1;
}
String feature = build_context.target_features_set.entries[i].value;
len += feature.len;
if (with_quotes) len += 2;
}
char *features = gb_alloc_array(allocator, char, len+1);
len = 0;
for_array(i, build_context.target_features_set.entries) {
if (i != 0) {
features[len++] = ',';
}
if (with_quotes) features[len++] = '"';
String feature = build_context.target_features_set.entries[i].value;
gb_memmove(features, feature.text, feature.len);
len += feature.len;
if (with_quotes) features[len++] = '"';
}
features[len++] = 0;
return features;
}
// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
// We've previously called `parse_build_flags`, so `out_filepath` should be set.
bool init_build_paths(String init_filename) {
@@ -1197,6 +1303,9 @@ bool init_build_paths(String init_filename) {
// NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index.
array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT);
string_set_init(&bc->target_features_set, heap_allocator(), 1024);
mutex_init(&bc->target_features_mutex);
// [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path.
bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename);
@@ -1377,5 +1486,10 @@ bool init_build_paths(String init_filename) {
return false;
}
if (bc->target_features_string.len != 0) {
enable_target_feature({}, bc->target_features_string);
}
return true;
}
}
+822 -55
View File
@@ -246,7 +246,7 @@ bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr
}
bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
String builtin_name = builtin_procs[id].name;
String const &builtin_name = builtin_procs[id].name;
if (build_context.metrics.os != TargetOs_darwin) {
// allow on doc generation (e.g. Metal stuff)
@@ -409,6 +409,667 @@ bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String con
}
bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
ast_node(ce, CallExpr, call);
String const &builtin_name = builtin_procs[id].name;
switch (id) {
// Any numeric
case BuiltinProc_simd_add:
case BuiltinProc_simd_sub:
case BuiltinProc_simd_mul:
case BuiltinProc_simd_div:
case BuiltinProc_simd_min:
case BuiltinProc_simd_max:
{
Operand x = {};
Operand y = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!is_type_simd_vector(y.type)) {
error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!are_types_identical(x.type, y.type)) {
gbString xs = type_to_string(x.type);
gbString ys = type_to_string(y.type);
error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys);
gb_string_free(ys);
gb_string_free(xs);
return false;
}
Type *elem = base_array_type(x.type);
if (!is_type_integer(elem) && !is_type_float(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
operand->mode = Addressing_Value;
operand->type = x.type;
return true;
}
// Integer only
case BuiltinProc_simd_add_sat:
case BuiltinProc_simd_sub_sat:
case BuiltinProc_simd_rem:
case BuiltinProc_simd_and:
case BuiltinProc_simd_or:
case BuiltinProc_simd_xor:
case BuiltinProc_simd_and_not:
{
Operand x = {};
Operand y = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!is_type_simd_vector(y.type)) {
error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!are_types_identical(x.type, y.type)) {
gbString xs = type_to_string(x.type);
gbString ys = type_to_string(y.type);
error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys);
gb_string_free(ys);
gb_string_free(xs);
return false;
}
Type *elem = base_array_type(x.type);
switch (id) {
case BuiltinProc_simd_add_sat:
case BuiltinProc_simd_sub_sat:
case BuiltinProc_simd_rem:
if (!is_type_integer(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
break;
default:
if (!is_type_integer(elem) && !is_type_boolean(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer or boolean element, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
break;
}
operand->mode = Addressing_Value;
operand->type = x.type;
return true;
}
case BuiltinProc_simd_shl: // Odin-like
case BuiltinProc_simd_shr: // Odin-like
case BuiltinProc_simd_shl_masked: // C-like
case BuiltinProc_simd_shr_masked: // C-like
{
Operand x = {};
Operand y = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!is_type_simd_vector(y.type)) {
error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
GB_ASSERT(x.type->kind == Type_SimdVector);
GB_ASSERT(y.type->kind == Type_SimdVector);
Type *xt = x.type;
Type *yt = y.type;
if (xt->SimdVector.count != yt->SimdVector.count) {
error(x.expr, "'%.*s' mismatched simd vector lengths, got '%lld' vs '%lld'",
LIT(builtin_name),
cast(long long)xt->SimdVector.count,
cast(long long)yt->SimdVector.count);
return false;
}
if (!is_type_integer(base_array_type(x.type))) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
if (!is_type_unsigned(base_array_type(y.type))) {
gbString ys = type_to_string(y.type);
error(y.expr, "'%.*s' expected a #simd type with an unsigned integer element as the shifting operand, got '%s'", LIT(builtin_name), ys);
gb_string_free(ys);
return false;
}
operand->mode = Addressing_Value;
operand->type = x.type;
return true;
}
// Unary
case BuiltinProc_simd_neg:
case BuiltinProc_simd_abs:
{
Operand x = {};
check_expr(c, &x, ce->args[0]);
if (x.mode == Addressing_Invalid) {
return false;
}
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
Type *elem = base_array_type(x.type);
if (!is_type_integer(elem) && !is_type_float(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
operand->mode = Addressing_Value;
operand->type = x.type;
return true;
}
// Return integer masks
case BuiltinProc_simd_lanes_eq:
case BuiltinProc_simd_lanes_ne:
case BuiltinProc_simd_lanes_lt:
case BuiltinProc_simd_lanes_le:
case BuiltinProc_simd_lanes_gt:
case BuiltinProc_simd_lanes_ge:
{
// op(#simd[N]T, #simd[N]T) -> #simd[N]V
// where `V` is an integer, `size_of(T) == size_of(V)`
// `V` will all 0s if false and all 1s if true (e.g. 0x00 and 0xff for false and true, respectively)
Operand x = {};
Operand y = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
Type *elem = base_array_type(x.type);
switch (id) {
case BuiltinProc_simd_lanes_eq:
case BuiltinProc_simd_lanes_ne:
if (!is_type_integer(elem) && !is_type_float(elem) && !is_type_boolean(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer, floating point, or boolean element, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
break;
default:
if (!is_type_integer(elem) && !is_type_float(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
break;
}
Type *vt = base_type(x.type);
GB_ASSERT(vt->kind == Type_SimdVector);
i64 count = vt->SimdVector.count;
i64 sz = type_size_of(elem);
Type *new_elem = nullptr;
switch (sz) {
case 1: new_elem = t_u8; break;
case 2: new_elem = t_u16; break;
case 4: new_elem = t_u32; break;
case 8: new_elem = t_u64; break;
case 16:
error(x.expr, "'%.*s' not supported 128-bit integer backed simd vector types", LIT(builtin_name));
return false;
}
operand->mode = Addressing_Value;
operand->type = alloc_type_simd_vector(count, new_elem);
return true;
}
case BuiltinProc_simd_extract:
{
Operand x = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
Type *elem = base_array_type(x.type);
i64 max_count = x.type->SimdVector.count;
i64 value = -1;
if (!check_index_value(c, x.type, false, ce->args[1], max_count, &value)) {
return false;
}
if (max_count < 0) {
error(ce->args[1], "'%.*s' expected a constant integer index, got '%lld'", LIT(builtin_name), cast(long long)value);
return false;
}
operand->mode = Addressing_Value;
operand->type = elem;
return true;
}
break;
case BuiltinProc_simd_replace:
{
Operand x = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
Type *elem = base_array_type(x.type);
i64 max_count = x.type->SimdVector.count;
i64 value = -1;
if (!check_index_value(c, x.type, false, ce->args[1], max_count, &value)) {
return false;
}
if (max_count < 0) {
error(ce->args[1], "'%.*s' expected a constant integer index, got '%lld'", LIT(builtin_name), cast(long long)value);
return false;
}
Operand y = {};
check_expr_with_type_hint(c, &y, ce->args[2], elem); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &y, elem); if (y.mode == Addressing_Invalid) return false;
if (!are_types_identical(y.type, elem)) {
gbString et = type_to_string(elem);
gbString yt = type_to_string(y.type);
error(y.expr, "'%.*s' expected a type of '%s' to insert, got '%s'", LIT(builtin_name), et, yt);
gb_string_free(yt);
gb_string_free(et);
return false;
}
operand->mode = Addressing_Value;
operand->type = x.type;
return true;
}
break;
case BuiltinProc_simd_reduce_add_ordered:
case BuiltinProc_simd_reduce_mul_ordered:
case BuiltinProc_simd_reduce_min:
case BuiltinProc_simd_reduce_max:
{
Operand x = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
Type *elem = base_array_type(x.type);
if (!is_type_integer(elem) && !is_type_float(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
operand->mode = Addressing_Value;
operand->type = base_array_type(x.type);
return true;
}
case BuiltinProc_simd_reduce_and:
case BuiltinProc_simd_reduce_or:
case BuiltinProc_simd_reduce_xor:
{
Operand x = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
Type *elem = base_array_type(x.type);
if (!is_type_integer(elem) && !is_type_boolean(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer or boolean element, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
operand->mode = Addressing_Value;
operand->type = base_array_type(x.type);
return true;
}
case BuiltinProc_simd_shuffle:
{
Operand x = {};
Operand y = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!is_type_simd_vector(y.type)) {
error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!are_types_identical(x.type, y.type)) {
gbString xs = type_to_string(x.type);
gbString ys = type_to_string(y.type);
error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys);
gb_string_free(ys);
gb_string_free(xs);
return false;
}
Type *elem = base_array_type(x.type);
i64 max_count = x.type->SimdVector.count + y.type->SimdVector.count;
i64 arg_count = 0;
for_array(i, ce->args) {
if (i < 2) {
continue;
}
Ast *arg = ce->args[i];
Operand op = {};
check_expr(c, &op, arg);
if (op.mode == Addressing_Invalid) {
return false;
}
Type *arg_type = base_type(op.type);
if (!is_type_integer(arg_type) || op.mode != Addressing_Constant) {
error(op.expr, "Indices to '%.*s' must be constant integers", LIT(builtin_name));
return false;
}
if (big_int_is_neg(&op.value.value_integer)) {
error(op.expr, "Negative '%.*s' index", LIT(builtin_name));
return false;
}
BigInt mc = {};
big_int_from_i64(&mc, max_count);
if (big_int_cmp(&mc, &op.value.value_integer) <= 0) {
error(op.expr, "'%.*s' index exceeds length", LIT(builtin_name));
return false;
}
arg_count++;
}
if (arg_count > max_count) {
error(call, "Too many '%.*s' indices, %td > %td", LIT(builtin_name), arg_count, max_count);
return false;
}
if (!is_power_of_two(arg_count)) {
error(call, "'%.*s' must have a power of two index arguments, got %lld", LIT(builtin_name), cast(long long)arg_count);
return false;
}
operand->mode = Addressing_Value;
operand->type = alloc_type_simd_vector(arg_count, elem);
return true;
}
case BuiltinProc_simd_select:
{
Operand cond = {};
check_expr(c, &cond, ce->args[0]); if (cond.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(cond.type)) {
error(cond.expr, "'%.*s' expected a simd vector boolean type", LIT(builtin_name));
return false;
}
Type *cond_elem = base_array_type(cond.type);
if (!is_type_boolean(cond_elem) && !is_type_integer(cond_elem)) {
gbString cond_str = type_to_string(cond.type);
error(cond.expr, "'%.*s' expected a simd vector boolean or integer type, got '%s'", LIT(builtin_name), cond_str);
gb_string_free(cond_str);
return false;
}
Operand x = {};
Operand y = {};
check_expr(c, &x, ce->args[1]); if (x.mode == Addressing_Invalid) return false;
check_expr_with_type_hint(c, &y, ce->args[2], x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!is_type_simd_vector(y.type)) {
error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!are_types_identical(x.type, y.type)) {
gbString xs = type_to_string(x.type);
gbString ys = type_to_string(y.type);
error(x.expr, "'%.*s' expected 2 results of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys);
gb_string_free(ys);
gb_string_free(xs);
return false;
}
if (cond.type->SimdVector.count != x.type->SimdVector.count) {
error(x.expr, "'%.*s' expected condition vector to match the length of the result lengths, got '%lld' vs '%lld'",
LIT(builtin_name),
cast(long long)cond.type->SimdVector.count,
cast(long long)x.type->SimdVector.count);
return false;
}
operand->mode = Addressing_Value;
operand->type = x.type;
return true;
}
case BuiltinProc_simd_ceil:
case BuiltinProc_simd_floor:
case BuiltinProc_simd_trunc:
case BuiltinProc_simd_nearest:
{
Operand x = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector boolean type", LIT(builtin_name));
return false;
}
Type *elem = base_array_type(x.type);
if (!is_type_float(elem)) {
gbString x_str = type_to_string(x.type);
error(x.expr, "'%.*s' expected a simd vector floating point type, got '%s'", LIT(builtin_name), x_str);
gb_string_free(x_str);
return false;
}
operand->mode = Addressing_Value;
operand->type = x.type;
return true;
}
case BuiltinProc_simd_lanes_reverse:
{
Operand x = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
operand->type = x.type;
operand->mode = Addressing_Value;
return true;
}
case BuiltinProc_simd_lanes_rotate_left:
case BuiltinProc_simd_lanes_rotate_right:
{
Operand x = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
Operand offset = {};
check_expr(c, &offset, ce->args[1]); if (offset.mode == Addressing_Invalid) return false;
convert_to_typed(c, &offset, t_i64);
if (!is_type_integer(offset.type) || offset.mode != Addressing_Constant) {
error(offset.expr, "'%.*s' expected a constant integer offset");
return false;
}
check_assignment(c, &offset, t_i64, builtin_name);
operand->type = x.type;
operand->mode = Addressing_Value;
return true;
}
case BuiltinProc_simd_clamp:
{
Operand x = {};
Operand y = {};
Operand z = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
check_expr_with_type_hint(c, &z, ce->args[2], x.type); if (z.mode == Addressing_Invalid) return false;
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &z, x.type);
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!is_type_simd_vector(y.type)) {
error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!is_type_simd_vector(z.type)) {
error(z.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
if (!are_types_identical(x.type, y.type)) {
gbString xs = type_to_string(x.type);
gbString ys = type_to_string(y.type);
error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys);
gb_string_free(ys);
gb_string_free(xs);
return false;
}
if (!are_types_identical(x.type, z.type)) {
gbString xs = type_to_string(x.type);
gbString zs = type_to_string(z.type);
error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, zs);
gb_string_free(zs);
gb_string_free(xs);
return false;
}
Type *elem = base_array_type(x.type);
if (!is_type_integer(elem) && !is_type_float(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
operand->mode = Addressing_Value;
operand->type = x.type;
return true;
}
case BuiltinProc_simd_to_bits:
{
Operand x = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
return false;
}
Type *elem = base_array_type(x.type);
i64 count = get_array_type_count(x.type);
i64 sz = type_size_of(elem);
Type *bit_elem = nullptr;
switch (sz) {
case 1: bit_elem = t_u8; break;
case 2: bit_elem = t_u16; break;
case 4: bit_elem = t_u32; break;
case 8: bit_elem = t_u64; break;
}
GB_ASSERT(bit_elem != nullptr);
operand->type = alloc_type_simd_vector(count, bit_elem);
operand->mode = Addressing_Value;
return true;
}
case BuiltinProc_simd_x86__MM_SHUFFLE:
{
Operand x[4] = {};
for (unsigned i = 0; i < 4; i++) {
check_expr(c, x+i, ce->args[i]); if (x[i].mode == Addressing_Invalid) return false;
}
u32 offsets[4] = {6, 4, 2, 0};
u32 result = 0;
for (unsigned i = 0; i < 4; i++) {
if (!is_type_integer(x[i].type) || x[i].mode != Addressing_Constant) {
gbString xs = type_to_string(x[i].type);
error(x[i].expr, "'%.*s' expected a constant integer", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
i64 val = exact_value_to_i64(x[i].value);
if (val < 0 || val > 3) {
error(x[i].expr, "'%.*s' expected a constant integer in the range 0..<4, got %lld", LIT(builtin_name), cast(long long)val);
return false;
}
result |= cast(u32)(val) << offsets[i];
}
operand->type = t_untyped_integer;
operand->mode = Addressing_Constant;
operand->value = exact_value_i64(result);
return true;
}
default:
GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name));
}
return false;
}
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
ast_node(ce, CallExpr, call);
if (ce->inlining != ProcInlining_none) {
@@ -479,7 +1140,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
String builtin_name = builtin_procs[id].name;
String const &builtin_name = builtin_procs[id].name;
if (ce->args.count > 0) {
@@ -491,6 +1152,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
}
if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) {
bool ok = check_builtin_simd_operation(c, operand, call, id, type_hint);
if (!ok) {
operand->type = t_invalid;
}
operand->mode = Addressing_Value;
operand->value = {};
operand->expr = call;
return ok;
}
switch (id) {
default:
GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name));
@@ -1031,6 +1703,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
bt->Struct.soa_kind == StructSoa_Dynamic) {
mode = Addressing_Value;
}
} else if (is_type_simd_vector(op_type)) {
Type *bt = base_type(op_type);
mode = Addressing_Constant;
value = exact_value_i64(bt->SimdVector.count);
type = t_untyped_integer;
}
if (operand->mode == Addressing_Type && mode != Addressing_Constant) {
mode = Addressing_Invalid;
@@ -1445,6 +2122,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->mode = Addressing_Value;
}
if (is_type_simd_vector(type) && !is_power_of_two(arg_count)) {
error(call, "'swizzle' with a #simd vector must have a power of two arguments, got %lld", cast(long long)arg_count);
return false;
}
operand->type = determine_swizzle_array_type(original_type, type_hint, arg_count);
break;
}
@@ -2279,7 +2961,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (i == j) continue;
Operand *b = ops[j];
convert_to_typed(c, a, b->type);
if (a->mode == Addressing_Invalid) { return false; }
if (a->mode == Addressing_Invalid) return false;
}
}
@@ -2685,46 +3367,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
case BuiltinProc_simd_vector: {
Operand x = {};
Operand y = {};
x = *operand;
if (!is_type_integer(x.type) || x.mode != Addressing_Constant) {
error(call, "Expected a constant integer for 'intrinsics.simd_vector'");
operand->mode = Addressing_Type;
operand->type = t_invalid;
return false;
}
if (big_int_is_neg(&x.value.value_integer)) {
error(call, "Negative vector element length");
operand->mode = Addressing_Type;
operand->type = t_invalid;
return false;
}
i64 count = big_int_to_i64(&x.value.value_integer);
check_expr_or_type(c, &y, ce->args[1]);
if (y.mode != Addressing_Type) {
error(call, "Expected a type 'intrinsics.simd_vector'");
operand->mode = Addressing_Type;
operand->type = t_invalid;
return false;
}
Type *elem = y.type;
if (!is_type_valid_vector_elem(elem)) {
gbString str = type_to_string(elem);
error(call, "Invalid element type for 'intrinsics.simd_vector', expected an integer or float with no specific endianness, got '%s'", str);
gb_string_free(str);
operand->mode = Addressing_Type;
operand->type = t_invalid;
return false;
}
operand->mode = Addressing_Type;
operand->type = alloc_type_simd_vector(count, elem);
break;
}
case BuiltinProc_is_package_imported: {
bool value = false;
@@ -2944,7 +3586,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
if (!is_type_integer_like(x.type)) {
if (is_type_simd_vector(x.type)) {
Type *elem = base_array_type(x.type);
if (!is_type_integer_like(elem)) {
gbString xts = type_to_string(x.type);
error(x.expr, "#simd values passed to '%.*s' must have an element of an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_name), xts);
gb_string_free(xts);
}
} else if (!is_type_integer_like(x.type)) {
gbString xts = type_to_string(x.type);
error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_name), xts);
gb_string_free(xts);
@@ -3002,7 +3651,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (y.mode == Addressing_Invalid) {
return false;
}
convert_to_typed(c, &y, x.type);
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &x, y.type);
if (is_type_untyped(x.type)) {
gbString xts = type_to_string(x.type);
@@ -3039,14 +3688,23 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (x.mode == Addressing_Invalid) {
return false;
}
if (!is_type_float(x.type)) {
Type *elem = core_array_type(x.type);
if (!is_type_float(x.type) && !(is_type_simd_vector(x.type) && is_type_float(elem))) {
gbString xts = type_to_string(x.type);
error(x.expr, "Expected a floating point value for '%.*s', got %s", LIT(builtin_name), xts);
error(x.expr, "Expected a floating point or #simd vector value for '%.*s', got %s", LIT(builtin_name), xts);
gb_string_free(xts);
return false;
} else if (is_type_different_to_arch_endianness(elem)) {
GB_ASSERT(elem->kind == Type_Basic);
if (elem->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) {
gbString xts = type_to_string(x.type);
error(x.expr, "Expected a float which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts);
gb_string_free(xts);
return false;
}
}
if (x.mode == Addressing_Constant) {
if (is_type_float(x.type) && x.mode == Addressing_Constant) {
f64 v = exact_value_to_f64(x.value);
operand->mode = Addressing_Constant;
@@ -3059,6 +3717,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
break;
case BuiltinProc_fused_mul_add:
{
Operand x = {};
Operand y = {};
Operand z = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
check_expr(c, &y, ce->args[1]); if (y.mode == Addressing_Invalid) return false;
check_expr(c, &z, ce->args[2]); if (z.mode == Addressing_Invalid) return false;
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false;
convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false;
convert_to_typed(c, &x, z.type); if (x.mode == Addressing_Invalid) return false;
if (is_type_untyped(x.type)) {
gbString xts = type_to_string(x.type);
error(x.expr, "Expected a typed floating point value or #simd vector for '%.*s', got %s", LIT(builtin_name), xts);
gb_string_free(xts);
return false;
}
Type *elem = core_array_type(x.type);
if (!is_type_float(x.type) && !(is_type_simd_vector(x.type) && is_type_float(elem))) {
gbString xts = type_to_string(x.type);
error(x.expr, "Expected a floating point or #simd vector value for '%.*s', got %s", LIT(builtin_name), xts);
gb_string_free(xts);
return false;
}
if (is_type_different_to_arch_endianness(elem)) {
GB_ASSERT(elem->kind == Type_Basic);
if (elem->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) {
gbString xts = type_to_string(x.type);
error(x.expr, "Expected a float which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts);
gb_string_free(xts);
return false;
}
}
if (!are_types_identical(x.type, y.type) || !are_types_identical(y.type, z.type)) {
gbString xts = type_to_string(x.type);
gbString yts = type_to_string(y.type);
gbString zts = type_to_string(z.type);
error(x.expr, "Mismatched types for '%.*s', got %s vs %s vs %s", LIT(builtin_name), xts, yts, zts);
gb_string_free(zts);
gb_string_free(yts);
gb_string_free(xts);
return false;
}
operand->mode = Addressing_Value;
operand->type = default_type(x.type);
}
break;
case BuiltinProc_mem_copy:
case BuiltinProc_mem_copy_non_overlapping:
{
@@ -3309,9 +4020,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
case BuiltinProc_volatile_store:
/*fallthrough*/
case BuiltinProc_unaligned_store:
/*fallthrough*/
case BuiltinProc_non_temporal_store:
case BuiltinProc_atomic_store:
{
Type *elem = nullptr;
@@ -3358,9 +4068,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_volatile_load:
/*fallthrough*/
case BuiltinProc_unaligned_load:
/*fallthrough*/
case BuiltinProc_non_temporal_load:
case BuiltinProc_atomic_load:
{
Type *elem = nullptr;
@@ -3618,7 +4327,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (x.mode == Addressing_Invalid) {
return false;
}
convert_to_typed(c, &y, x.type);
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
if (x.mode == Addressing_Invalid) {
return false;
}
@@ -3675,7 +4384,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (y.mode == Addressing_Invalid) {
return false;
}
convert_to_typed(c, &y, x.type);
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &x, y.type);
if (!are_types_identical(x.type, y.type)) {
gbString xts = type_to_string(x.type);
@@ -4566,6 +5275,64 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
break;
case BuiltinProc_x86_cpuid:
{
if (!is_arch_x86()) {
error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name));
return false;
}
Operand ax = {};
Operand cx = {};
check_expr_with_type_hint(c, &ax, ce->args[0], t_u32); if (ax.mode == Addressing_Invalid) return false;
check_expr_with_type_hint(c, &cx, ce->args[1], t_u32); if (cx.mode == Addressing_Invalid) return false;
convert_to_typed(c, &ax, t_u32); if (ax.mode == Addressing_Invalid) return false;
convert_to_typed(c, &cx, t_u32); if (cx.mode == Addressing_Invalid) return false;
if (!are_types_identical(ax.type, t_u32)) {
gbString str = type_to_string(ax.type);
error(ax.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str);
gb_string_free(str);
return false;
}
if (!are_types_identical(cx.type, t_u32)) {
gbString str = type_to_string(cx.type);
error(cx.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str);
gb_string_free(str);
return false;
}
Type *types[4] = {t_u32, t_u32, t_u32, t_u32}; // eax ebc ecx edx
operand->type = alloc_type_tuple_from_field_types(types, gb_count_of(types), false, false);
operand->mode = Addressing_Value;
operand->value = {};
return true;
}
break;
case BuiltinProc_x86_xgetbv:
{
if (!is_arch_x86()) {
error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name));
return false;
}
Operand cx = {};
check_expr_with_type_hint(c, &cx, ce->args[0], t_u32); if (cx.mode == Addressing_Invalid) return false;
convert_to_typed(c, &cx, t_u32); if (cx.mode == Addressing_Invalid) return false;
if (!are_types_identical(cx.type, t_u32)) {
gbString str = type_to_string(cx.type);
error(cx.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str);
gb_string_free(str);
return false;
}
Type *types[2] = {t_u32, t_u32};
operand->type = alloc_type_tuple_from_field_types(types, gb_count_of(types), false, false);
operand->mode = Addressing_Value;
operand->value = {};
return true;
}
break;
}
return true;
+35 -15
View File
@@ -313,13 +313,19 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
}
named->Named.base = base;
if (is_distinct && is_type_typeid(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'typeid'");
is_distinct = false;
}
if (is_distinct && is_type_any(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'any'");
is_distinct = false;
if (is_distinct) {
if (is_type_typeid(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'typeid'");
is_distinct = false;
} else if (is_type_any(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'any'");
is_distinct = false;
} else if (is_type_simd_vector(e->type)) {
gbString str = type_to_string(e->type);
error(init_expr, "'distinct' cannot be applied to '%s'", str);
gb_string_free(str);
is_distinct = false;
}
}
if (!is_distinct) {
e->type = bt;
@@ -893,6 +899,18 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
} else if (ac.require_target_feature.len != 0) {
if (check_target_feature_is_enabled(e->token.pos, ac.require_target_feature)) {
e->Procedure.target_feature = ac.require_target_feature;
} else {
e->Procedure.target_feature_disabled = true;
}
} else if (ac.enable_target_feature.len != 0) {
enable_target_feature(e->token.pos, ac.enable_target_feature);
e->Procedure.target_feature = ac.enable_target_feature;
}
switch (e->Procedure.optimization_mode) {
case ProcedureOptimizationMode_None:
@@ -996,10 +1014,12 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
if (pt->result_count == 0 && ac.require_results) {
error(pl->type, "'require_results' is not needed on a procedure with no results");
} else {
pt->require_results = ac.require_results;
if (ac.require_results) {
if (pt->result_count == 0) {
error(pl->type, "'require_results' is not needed on a procedure with no results");
} else {
pt->require_results = true;
}
}
if (ac.link_name.len > 0) {
@@ -1309,20 +1329,20 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d)
if (!both_have_where_clauses) switch (kind) {
case ProcOverload_Identical:
error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
// case ProcOverload_CallingConvention:
// error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
// error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
// is_invalid = true;
// break;
case ProcOverload_ParamVariadic:
error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
case ProcOverload_ResultCount:
case ProcOverload_ResultTypes:
error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
error(p->token, "Overloaded procedure '%.*s' has the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
case ProcOverload_Polymorphic:
+153 -116
View File
@@ -442,6 +442,14 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
final_proc_type->Proc.is_poly_specialized = true;
final_proc_type->Proc.is_polymorphic = true;
final_proc_type->Proc.variadic = src->Proc.variadic;
final_proc_type->Proc.require_results = src->Proc.require_results;
final_proc_type->Proc.c_vararg = src->Proc.c_vararg;
final_proc_type->Proc.has_named_results = src->Proc.has_named_results;
final_proc_type->Proc.diverging = src->Proc.diverging;
final_proc_type->Proc.return_by_pointer = src->Proc.return_by_pointer;
final_proc_type->Proc.optional_ok = src->Proc.optional_ok;
for (isize i = 0; i < operands.count; i++) {
Operand o = operands[i];
@@ -777,6 +785,14 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
return distance + 6;
}
}
if (is_type_simd_vector(dst)) {
Type *dst_elem = base_array_type(dst);
i64 distance = check_distance_between_types(c, operand, dst_elem);
if (distance >= 0) {
return distance + 6;
}
}
if (is_type_matrix(dst)) {
Type *dst_elem = base_array_type(dst);
@@ -786,6 +802,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
}
}
if (is_type_any(dst)) {
if (!is_type_polymorphic(src)) {
if (operand->mode == Addressing_Context && operand->type == t_context) {
@@ -1328,6 +1345,19 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
}
}
return false;
case Type_SimdVector:
if (source->kind == Type_SimdVector) {
if (poly->SimdVector.generic_count != nullptr) {
if (!polymorphic_assign_index(&poly->SimdVector.generic_count, &poly->SimdVector.count, source->SimdVector.count)) {
return false;
}
}
if (poly->SimdVector.count == source->SimdVector.count) {
return is_polymorphic_type_assignable(c, poly->SimdVector.elem, source->SimdVector.elem, true, modify_type);
}
}
return false;
}
return false;
}
@@ -1567,9 +1597,11 @@ bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
Type *main_type = o->type;
// TODO(bill): Handle errors correctly
Type *type = base_type(core_array_type(main_type));
Type *ct = core_type(type);
switch (op.kind) {
case Token_Sub:
case Token_SubEq:
@@ -1638,14 +1670,6 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string));
return false;
}
if (is_type_simd_vector(o->type)) {
switch (op.kind) {
case Token_ModMod:
case Token_ModModEq:
error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string));
return false;
}
}
break;
case Token_AndNot:
@@ -1654,14 +1678,6 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
error(op, "Operator '%.*s' is only allowed with integers and bit sets", LIT(op.string));
return false;
}
if (is_type_simd_vector(o->type)) {
switch (op.kind) {
case Token_AndNot:
case Token_AndNotEq:
error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string));
return false;
}
}
break;
case Token_CmpAnd:
@@ -2487,6 +2503,8 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
gb_string_free(err_str);
}
// TODO(bill): Should we support shifts for fixed arrays and #simd vectors?
if (!is_type_integer(x->type)) {
gbString err_str = expr_to_string(y->expr);
error(node, "Shift operand '%s' must be an integer", err_str);
@@ -2697,6 +2715,26 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
return true;
}
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
if (src->SimdVector.count != dst->SimdVector.count) {
return false;
}
Type *elem_src = base_array_type(src);
Type *elem_dst = base_array_type(dst);
Operand x = {};
x.type = elem_src;
x.mode = Addressing_Value;
return check_is_castable_to(c, &x, elem_dst);
}
if (is_type_simd_vector(dst)) {
Type *elem = base_array_type(dst);
if (check_is_castable_to(c, operand, elem)) {
return true;
}
}
return false;
}
@@ -4116,7 +4154,11 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize new_count) {
Type *array_type = base_type(type_deref(original_type));
GB_ASSERT(array_type->kind == Type_Array);
GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
if (array_type->kind == Type_SimdVector) {
Type *elem_type = array_type->SimdVector.elem;
return alloc_type_simd_vector(new_count, elem_type);
}
Type *elem_type = array_type->Array.elem;
Type *swizzle_array_type = nullptr;
@@ -7738,111 +7780,106 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
}
if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
if (is_type_simd_vector(t)) {
error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals");
} else {
RangeCache rc = range_cache_make(heap_allocator());
defer (range_cache_destroy(&rc));
RangeCache rc = range_cache_make(heap_allocator());
defer (range_cache_destroy(&rc));
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
if (elem->kind != Ast_FieldValue) {
error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
if (elem->kind != Ast_FieldValue) {
error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
continue;
}
ast_node(fv, FieldValue, elem);
if (is_ast_range(fv->field)) {
Token op = fv->field->BinaryExpr.op;
Operand x = {};
Operand y = {};
bool ok = check_range(c, fv->field, &x, &y, nullptr);
if (!ok) {
continue;
}
ast_node(fv, FieldValue, elem);
if (is_ast_range(fv->field)) {
Token op = fv->field->BinaryExpr.op;
Operand x = {};
Operand y = {};
bool ok = check_range(c, fv->field, &x, &y, nullptr);
if (!ok) {
continue;
}
if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) {
error(x.expr, "Expected a constant integer as an array field");
continue;
}
if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) {
error(y.expr, "Expected a constant integer as an array field");
continue;
}
i64 lo = exact_value_to_i64(x.value);
i64 hi = exact_value_to_i64(y.value);
i64 max_index = hi;
if (op.kind == Token_RangeHalf) { // ..< (exclusive)
hi -= 1;
} else { // .. (inclusive)
max_index += 1;
}
bool new_range = range_cache_add_range(&rc, lo, hi);
if (!new_range) {
error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name));
continue;
}
if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) {
error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name));
continue;
}
if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name));
continue;
}
if (max < hi) {
max = max_index;
}
Operand operand = {};
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name);
is_constant = is_constant && operand.mode == Addressing_Constant;
} else {
Operand op_index = {};
check_expr(c, &op_index, fv->field);
if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) {
error(elem, "Expected a constant integer as an array field");
continue;
}
// add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
i64 index = exact_value_to_i64(op_index.value);
if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) {
error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name));
continue;
}
bool new_index = range_cache_add_index(&rc, index);
if (!new_index) {
error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name));
continue;
}
if (max < index+1) {
max = index+1;
}
Operand operand = {};
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name);
is_constant = is_constant && operand.mode == Addressing_Constant;
if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) {
error(x.expr, "Expected a constant integer as an array field");
continue;
}
}
cl->max_count = max;
if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) {
error(y.expr, "Expected a constant integer as an array field");
continue;
}
i64 lo = exact_value_to_i64(x.value);
i64 hi = exact_value_to_i64(y.value);
i64 max_index = hi;
if (op.kind == Token_RangeHalf) { // ..< (exclusive)
hi -= 1;
} else { // .. (inclusive)
max_index += 1;
}
bool new_range = range_cache_add_range(&rc, lo, hi);
if (!new_range) {
error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name));
continue;
}
if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) {
error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name));
continue;
}
if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name));
continue;
}
if (max < hi) {
max = max_index;
}
Operand operand = {};
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name);
is_constant = is_constant && operand.mode == Addressing_Constant;
} else {
Operand op_index = {};
check_expr(c, &op_index, fv->field);
if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) {
error(elem, "Expected a constant integer as an array field");
continue;
}
// add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
i64 index = exact_value_to_i64(op_index.value);
if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) {
error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name));
continue;
}
bool new_index = range_cache_add_index(&rc, index);
if (!new_index) {
error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name));
continue;
}
if (max < index+1) {
max = index+1;
}
Operand operand = {};
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name);
is_constant = is_constant && operand.mode == Addressing_Constant;
}
}
cl->max_count = max;
} else {
isize index = 0;
for (; index < cl->elems.count; index++) {
@@ -7887,7 +7924,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
if (t->kind == Type_SimdVector) {
if (!is_constant) {
error(node, "Expected all constant elements for a simd vector");
// error(node, "Expected all constant elements for a simd vector");
}
}
+41 -15
View File
@@ -1381,6 +1381,18 @@ bool all_operands_valid(Array<Operand> const &operands) {
return true;
}
bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *id_) {
BuiltinProcId id = BuiltinProc_Invalid;
Entity *e = entity_of_node(expr);
if (e != nullptr && e->kind == Entity_Builtin) {
if (e->Builtin.id && e->Builtin.id != BuiltinProc_DIRECTIVE) {
id = cast(BuiltinProcId)e->Builtin.id;
}
}
if (id_) *id_ = id;
return id != BuiltinProc_Invalid;
}
void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
switch (node->kind) {
@@ -1405,29 +1417,43 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
if (kind == Expr_Stmt) {
return;
}
Ast *expr = strip_or_return_expr(operand.expr);
Ast *expr = strip_or_return_expr(operand.expr);
if (expr->kind == Ast_CallExpr) {
BuiltinProcId builtin_id = BuiltinProc_Invalid;
bool do_require = false;
AstCallExpr *ce = &expr->CallExpr;
Type *t = type_of_expr(ce->proc);
if (is_type_proc(t)) {
if (t->Proc.require_results) {
gbString expr_str = expr_to_string(ce->proc);
error(node, "'%s' requires that its results must be handled", expr_str);
gb_string_free(expr_str);
}
Type *t = base_type(type_of_expr(ce->proc));
if (t->kind == Type_Proc) {
do_require = t->Proc.require_results;
} else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
auto const &bp = builtin_procs[builtin_id];
do_require = bp.kind == Expr_Expr && !bp.ignore_results;
}
if (do_require) {
gbString expr_str = expr_to_string(ce->proc);
error(node, "'%s' requires that its results must be handled", expr_str);
gb_string_free(expr_str);
}
return;
} else if (expr->kind == Ast_SelectorCallExpr) {
BuiltinProcId builtin_id = BuiltinProc_Invalid;
bool do_require = false;
AstSelectorCallExpr *se = &expr->SelectorCallExpr;
ast_node(ce, CallExpr, se->call);
Type *t = type_of_expr(ce->proc);
if (is_type_proc(t)) {
if (t->Proc.require_results) {
gbString expr_str = expr_to_string(ce->proc);
error(node, "'%s' requires that its results must be handled", expr_str);
gb_string_free(expr_str);
}
Type *t = base_type(type_of_expr(ce->proc));
if (t->kind == Type_Proc) {
do_require = t->Proc.require_results;
} else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
auto const &bp = builtin_procs[builtin_id];
do_require = bp.kind == Expr_Expr && !bp.ignore_results;
}
if (do_require) {
gbString expr_str = expr_to_string(ce->proc);
error(node, "'%s' requires that its results must be handled", expr_str);
gb_string_free(expr_str);
}
return;
}
+16 -4
View File
@@ -1234,7 +1234,7 @@ bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Typ
}
Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand operand) {
Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand) {
bool modify_type = !ctx->no_polymorphic_errors;
bool show_error = modify_type && !ctx->hide_polymorphic_errors;
if (!is_operand_value(operand)) {
@@ -2795,15 +2795,27 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
if (name == "soa") {
*type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type);
} else if (name == "simd") {
if (!is_type_valid_vector_elem(elem)) {
if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) {
gbString str = type_to_string(elem);
error(at->elem, "Invalid element type for 'intrinsics.simd_vector', expected an integer or float with no specific endianness, got '%s'", str);
error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str);
gb_string_free(str);
*type = alloc_type_array(elem, count, generic_type);
goto array_end;
}
*type = alloc_type_simd_vector(count, elem);
if (generic_type != nullptr) {
// Ignore
} else if (count < 1 || !is_power_of_two(count)) {
error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count);
*type = alloc_type_array(elem, count, generic_type);
goto array_end;
}
*type = alloc_type_simd_vector(count, elem, generic_type);
if (count > SIMD_ELEMENT_COUNT_MAX) {
error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count);
}
} else {
error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
*type = alloc_type_array(elem, count, generic_type);
+16
View File
@@ -3207,6 +3207,22 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
}
return true;
} else if (name == "require_target_feature") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
ac->require_target_feature = ev.value_string;
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
} else if (name == "enable_target_feature") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
ac->enable_target_feature = ev.value_string;
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
}
return false;
}
+4
View File
@@ -60,6 +60,7 @@ struct BuiltinProc {
ExprKind kind;
BuiltinProcPkg pkg;
bool diverging;
bool ignore_results; // ignores require results handling
};
@@ -124,6 +125,9 @@ struct AttributeContext {
String objc_name;
bool objc_is_class_method;
Type * objc_type;
String require_target_feature; // required by the target micro-architecture
String enable_target_feature; // will be enabled for the procedure only
};
AttributeContext make_attribute_context(String link_prefix) {
+163 -27
View File
@@ -45,7 +45,6 @@ enum BuiltinProcId {
// "Intrinsics"
BuiltinProc_is_package_imported,
BuiltinProc_simd_vector,
BuiltinProc_soa_struct,
BuiltinProc_alloca,
@@ -66,6 +65,7 @@ enum BuiltinProcId {
BuiltinProc_overflow_mul,
BuiltinProc_sqrt,
BuiltinProc_fused_mul_add,
BuiltinProc_mem_copy,
BuiltinProc_mem_copy_non_overlapping,
@@ -80,6 +80,8 @@ enum BuiltinProcId {
BuiltinProc_unaligned_store,
BuiltinProc_unaligned_load,
BuiltinProc_non_temporal_store,
BuiltinProc_non_temporal_load,
BuiltinProc_prefetch_read_instruction,
BuiltinProc_prefetch_read_data,
@@ -118,10 +120,76 @@ enum BuiltinProcId {
BuiltinProc_fixed_point_div_sat,
BuiltinProc_expect,
BuiltinProc__simd_begin,
BuiltinProc_simd_add,
BuiltinProc_simd_sub,
BuiltinProc_simd_mul,
BuiltinProc_simd_div,
BuiltinProc_simd_rem,
BuiltinProc_simd_shl, // Odin logic
BuiltinProc_simd_shr, // Odin logic
BuiltinProc_simd_shl_masked, // C logic
BuiltinProc_simd_shr_masked, // C logic
BuiltinProc_simd_add_sat, // saturation arithmetic
BuiltinProc_simd_sub_sat, // saturation arithmetic
BuiltinProc_simd_and,
BuiltinProc_simd_or,
BuiltinProc_simd_xor,
BuiltinProc_simd_and_not,
BuiltinProc_simd_neg,
BuiltinProc_simd_abs,
BuiltinProc_simd_min,
BuiltinProc_simd_max,
BuiltinProc_simd_clamp,
BuiltinProc_simd_lanes_eq,
BuiltinProc_simd_lanes_ne,
BuiltinProc_simd_lanes_lt,
BuiltinProc_simd_lanes_le,
BuiltinProc_simd_lanes_gt,
BuiltinProc_simd_lanes_ge,
BuiltinProc_simd_extract,
BuiltinProc_simd_replace,
BuiltinProc_simd_reduce_add_ordered,
BuiltinProc_simd_reduce_mul_ordered,
BuiltinProc_simd_reduce_min,
BuiltinProc_simd_reduce_max,
BuiltinProc_simd_reduce_and,
BuiltinProc_simd_reduce_or,
BuiltinProc_simd_reduce_xor,
BuiltinProc_simd_shuffle,
BuiltinProc_simd_select,
BuiltinProc_simd_ceil,
BuiltinProc_simd_floor,
BuiltinProc_simd_trunc,
BuiltinProc_simd_nearest,
BuiltinProc_simd_to_bits,
BuiltinProc_simd_lanes_reverse,
BuiltinProc_simd_lanes_rotate_left,
BuiltinProc_simd_lanes_rotate_right,
// Platform specific SIMD intrinsics
BuiltinProc_simd_x86__MM_SHUFFLE,
BuiltinProc__simd_end,
// Platform specific intrinsics
BuiltinProc_syscall,
BuiltinProc_x86_cpuid,
BuiltinProc_x86_xgetbv,
// Constant type tests
BuiltinProc__type_begin,
@@ -268,7 +336,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
// "Intrinsics"
{STR_LIT("is_package_imported"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
{STR_LIT("soa_struct"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
{STR_LIT("alloca"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -290,6 +357,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fused_mul_add"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("mem_copy"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("mem_copy_non_overlapping"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -304,6 +372,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("unaligned_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("unaligned_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("non_temporal_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("non_temporal_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_read_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_read_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -315,26 +385,26 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store_explicit"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load_explicit"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_exchange"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_exchange_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_compare_exchange_strong"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_load_explicit"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_add_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_sub_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_and_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_nand_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_or_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_xor_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_exchange"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_exchange_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_compare_exchange_strong"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -342,8 +412,74 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("fixed_point_div_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("expect"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_div"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_rem"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_shl"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_shr"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_shl_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_shr_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_add_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_sub_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_and_not"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_neg"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_abs"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_min"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_max"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_clamp"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_eq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_ne"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_lt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_le"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_gt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_ge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_extract"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_replace"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_add_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_mul_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_min"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_max"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_and"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_or"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_xor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_ceil") , 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_floor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_trunc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_nearest"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_to_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_reverse"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_rotate_left"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("x86_cpuid"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("x86_xgetbv"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -429,12 +565,12 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("objc_find_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+7
View File
@@ -47,6 +47,13 @@ void debugf(char const *fmt, ...);
#include "range_cache.cpp"
bool is_power_of_two(i64 x) {
if (x <= 0) {
return false;
}
return !(x & (x-1));
}
int isize_cmp(isize x, isize y) {
if (x < y) {
return -1;
+5 -3
View File
@@ -233,10 +233,12 @@ struct Entity {
String link_name;
String link_prefix;
DeferredProcedure deferred_procedure;
bool is_foreign;
bool is_export;
bool generated_from_polymorphic;
ProcedureOptimizationMode optimization_mode;
bool is_foreign : 1;
bool is_export : 1;
bool generated_from_polymorphic : 1;
bool target_feature_disabled : 1;
String target_feature;
} Procedure;
struct {
Array<Entity *> entities;
+2 -2
View File
@@ -1332,8 +1332,8 @@ void lb_generate_code(lbGenerator *gen) {
}
}
if (build_context.target_features.len != 0) {
llvm_features = alloc_cstring(permanent_allocator(), build_context.target_features);
if (build_context.target_features_set.entries.count != 0) {
llvm_features = target_features_set_to_cstring(permanent_allocator(), false);
}
// GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target));
+91 -20
View File
@@ -495,9 +495,9 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
res.value = data;
return res;
} else if (is_type_array(type) &&
value.kind != ExactValue_Invalid &&
value.kind != ExactValue_String &&
value.kind != ExactValue_Compound) {
value.kind != ExactValue_Invalid &&
value.kind != ExactValue_String &&
value.kind != ExactValue_Compound) {
i64 count = type->Array.count;
Type *elem = type->Array.elem;
@@ -513,8 +513,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
res.value = llvm_const_array(lb_type(m, elem), elems, cast(unsigned)count);
return res;
} else if (is_type_matrix(type) &&
value.kind != ExactValue_Invalid &&
value.kind != ExactValue_Compound) {
value.kind != ExactValue_Invalid &&
value.kind != ExactValue_Compound) {
i64 row = type->Matrix.row_count;
i64 column = type->Matrix.column_count;
GB_ASSERT(row == column);
@@ -537,6 +537,22 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
res.value = LLVMConstArray(lb_type(m, elem), elems, cast(unsigned)total_elem_count);
return res;
} else if (is_type_simd_vector(type) &&
value.kind != ExactValue_Invalid &&
value.kind != ExactValue_Compound) {
i64 count = type->SimdVector.count;
Type *elem = type->SimdVector.elem;
lbValue single_elem = lb_const_value(m, elem, value, allow_local);
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count);
for (i64 i = 0; i < count; i++) {
elems[i] = single_elem.value;
}
res.value = LLVMConstVector(elems, cast(unsigned)count);
return res;
}
switch (value.kind) {
@@ -819,26 +835,81 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
return lb_const_nil(m, original_type);
}
GB_ASSERT(elem_type_can_be_constant(elem_type));
isize total_elem_count = cast(isize)type->SimdVector.count;
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, total_elem_count);
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
}
LLVMTypeRef et = lb_type(m, elem_type);
if (cl->elems[0]->kind == Ast_FieldValue) {
// TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand
isize value_index = 0;
for (i64 i = 0; i < total_elem_count; i++) {
bool found = false;
for (isize i = elem_count; i < type->SimdVector.count; i++) {
values[i] = LLVMConstNull(et);
}
for (isize i = 0; i < total_elem_count; i++) {
values[i] = llvm_const_cast(values[i], et);
}
for (isize j = 0; j < elem_count; j++) {
Ast *elem = cl->elems[j];
ast_node(fv, FieldValue, elem);
if (is_ast_range(fv->field)) {
ast_node(ie, BinaryExpr, fv->field);
TypeAndValue lo_tav = ie->left->tav;
TypeAndValue hi_tav = ie->right->tav;
GB_ASSERT(lo_tav.mode == Addressing_Constant);
GB_ASSERT(hi_tav.mode == Addressing_Constant);
res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
return res;
TokenKind op = ie->op.kind;
i64 lo = exact_value_to_i64(lo_tav.value);
i64 hi = exact_value_to_i64(hi_tav.value);
if (op != Token_RangeHalf) {
hi += 1;
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
found = true;
i += (hi-lo-1);
break;
}
} else {
TypeAndValue index_tav = fv->field->tav;
GB_ASSERT(index_tav.mode == Addressing_Constant);
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
values[value_index++] = val;
found = true;
break;
}
}
}
if (!found) {
values[value_index++] = LLVMConstNull(lb_type(m, elem_type));
}
}
res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
return res;
} else {
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
}
LLVMTypeRef et = lb_type(m, elem_type);
for (isize i = elem_count; i < total_elem_count; i++) {
values[i] = LLVMConstNull(et);
}
for (isize i = 0; i < total_elem_count; i++) {
values[i] = llvm_const_cast(values[i], et);
}
res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
return res;
}
} else if (is_type_struct(type)) {
ast_node(cl, CompoundLit, value.value_compound);
+207 -1
View File
@@ -258,7 +258,13 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type)
LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP(p->builder, addr.addr.value, 2, ""));
LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP(p->builder, addr.addr.value, 3, ""));
return lb_addr_load(p, addr);
} else if (is_type_simd_vector(x.type)) {
Type *elem = base_array_type(x.type);
if (is_type_float(elem)) {
res.value = LLVMBuildFNeg(p->builder, x.value, "");
} else {
res.value = LLVMBuildNeg(p->builder, x.value, "");
}
} else {
GB_PANIC("Unhandled type %s", type_to_string(x.type));
}
@@ -1820,6 +1826,59 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return res;
}
if (is_type_simd_vector(dst)) {
Type *et = base_array_type(dst);
if (is_type_simd_vector(src)) {
Type *src_elem = core_array_type(src);
Type *dst_elem = core_array_type(dst);
GB_ASSERT(src->SimdVector.count == dst->SimdVector.count);
lbValue res = {};
res.type = t;
if (are_types_identical(src_elem, dst_elem)) {
res.value = value.value;
} else if (is_type_float(src_elem) && is_type_integer(dst_elem)) {
if (is_type_unsigned(dst_elem)) {
res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), "");
} else {
res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), "");
}
} else if (is_type_integer(src_elem) && is_type_float(dst_elem)) {
if (is_type_unsigned(src_elem)) {
res.value = LLVMBuildUIToFP(p->builder, value.value, lb_type(m, t), "");
} else {
res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), "");
}
} else if ((is_type_integer(src_elem) || is_type_boolean(src_elem)) && is_type_integer(dst_elem)) {
res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), !is_type_unsigned(src_elem), "");
} else if (is_type_float(src_elem) && is_type_float(dst_elem)) {
res.value = LLVMBuildFPCast(p->builder, value.value, lb_type(m, t), "");
} else if (is_type_integer(src_elem) && is_type_boolean(dst_elem)) {
LLVMValueRef i1vector = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(LLVMTypeOf(value.value)), "");
res.value = LLVMBuildIntCast2(p->builder, i1vector, lb_type(m, t), !is_type_unsigned(src_elem), "");
} else {
GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst));
}
return res;
} else {
i64 count = get_array_type_count(dst);
LLVMTypeRef vt = lb_type(m, t);
LLVMTypeRef llvm_u32 = lb_type(m, t_u32);
LLVMValueRef elem = lb_emit_conv(p, value, et).value;
LLVMValueRef vector = LLVMConstNull(vt);
for (i64 i = 0; i < count; i++) {
LLVMValueRef idx = LLVMConstInt(llvm_u32, i, false);
vector = LLVMBuildInsertElement(p->builder, vector, elem, idx, "");
}
lbValue res = {};
res.type = t;
res.value = vector;
return res;
}
}
// Pointer <-> uintptr
if (is_type_pointer(src) && is_type_uintptr(dst)) {
lbValue res = {};
@@ -2506,6 +2565,57 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
case Token_NotEq: pred = LLVMIntNE; break;
}
res.value = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
} else if (is_type_simd_vector(a)) {
LLVMValueRef mask = nullptr;
Type *elem = base_array_type(a);
if (is_type_float(elem)) {
LLVMRealPredicate pred = {};
switch (op_kind) {
case Token_CmpEq: pred = LLVMRealOEQ; break;
case Token_NotEq: pred = LLVMRealONE; break;
}
mask = LLVMBuildFCmp(p->builder, pred, left.value, right.value, "");
} else {
LLVMIntPredicate pred = {};
switch (op_kind) {
case Token_CmpEq: pred = LLVMIntEQ; break;
case Token_NotEq: pred = LLVMIntNE; break;
}
mask = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
}
GB_ASSERT_MSG(mask != nullptr, "Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
/* NOTE(bill, 2022-05-28):
Thanks to Per Vognsen, sign extending <N x i1> to
a vector of the same width as the input vector, bit casting to an integer,
and then comparing against zero is the better option
See: https://lists.llvm.org/pipermail/llvm-dev/2012-September/053046.html
// Example assuming 128-bit vector
%1 = <4 x float> ...
%2 = <4 x float> ...
%3 = fcmp oeq <4 x float> %1, %2
%4 = sext <4 x i1> %3 to <4 x i32>
%5 = bitcast <4 x i32> %4 to i128
%6 = icmp ne i128 %5, 0
br i1 %6, label %true1, label %false2
This will result in 1 cmpps + 1 ptest + 1 br
(even without SSE4.1, contrary to what the mail list states, because of pmovmskb)
*/
unsigned count = cast(unsigned)get_array_type_count(a);
unsigned elem_sz = cast(unsigned)(type_size_of(elem)*8);
LLVMTypeRef mask_type = LLVMVectorType(LLVMIntTypeInContext(p->module->ctx, elem_sz), count);
mask = LLVMBuildSExtOrBitCast(p->builder, mask, mask_type, "");
LLVMTypeRef mask_int_type = LLVMIntTypeInContext(p->module->ctx, cast(unsigned)(8*type_size_of(a)));
LLVMValueRef mask_int = LLVMBuildBitCast(p->builder, mask, mask_int_type, "");
res.value = LLVMBuildICmp(p->builder, LLVMIntNE, mask_int, LLVMConstNull(LLVMTypeOf(mask_int)), "");
return res;
} else {
GB_PANIC("Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
}
@@ -4609,6 +4719,102 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
break;
}
case Type_SimdVector: {
if (cl->elems.count > 0) {
lbValue vector_value = lb_const_value(p->module, type, exact_value_compound(expr));
defer (lb_addr_store(p, v, vector_value));
auto temp_data = array_make<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
// NOTE(bill): Separate value, store into their own chunks
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
if (elem->kind == Ast_FieldValue) {
ast_node(fv, FieldValue, elem);
if (lb_is_elem_const(fv->value, et)) {
continue;
}
if (is_ast_range(fv->field)) {
ast_node(ie, BinaryExpr, fv->field);
TypeAndValue lo_tav = ie->left->tav;
TypeAndValue hi_tav = ie->right->tav;
GB_ASSERT(lo_tav.mode == Addressing_Constant);
GB_ASSERT(hi_tav.mode == Addressing_Constant);
TokenKind op = ie->op.kind;
i64 lo = exact_value_to_i64(lo_tav.value);
i64 hi = exact_value_to_i64(hi_tav.value);
if (op != Token_RangeHalf) {
hi += 1;
}
lbValue value = lb_build_expr(p, fv->value);
for (i64 k = lo; k < hi; k++) {
lbCompoundLitElemTempData data = {};
data.value = value;
data.elem_index = cast(i32)k;
array_add(&temp_data, data);
}
} else {
auto tav = fv->field->tav;
GB_ASSERT(tav.mode == Addressing_Constant);
i64 index = exact_value_to_i64(tav.value);
lbValue value = lb_build_expr(p, fv->value);
lbCompoundLitElemTempData data = {};
data.value = lb_emit_conv(p, value, et);
data.expr = fv->value;
data.elem_index = cast(i32)index;
array_add(&temp_data, data);
}
} else {
if (lb_is_elem_const(elem, et)) {
continue;
}
lbCompoundLitElemTempData data = {};
data.expr = elem;
data.elem_index = cast(i32)i;
array_add(&temp_data, data);
}
}
for_array(i, temp_data) {
lbValue field_expr = temp_data[i].value;
Ast *expr = temp_data[i].expr;
auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr);
if (field_expr.value == nullptr) {
field_expr = lb_build_expr(p, expr);
}
Type *t = field_expr.type;
GB_ASSERT(t->kind != Type_Tuple);
lbValue ev = lb_emit_conv(p, field_expr, et);
if (!p->copy_elision_hint.used) {
temp_data[i].value = ev;
}
lb_reset_copy_elision_hint(p, prev_hint);
}
// TODO(bill): reduce the need for individual `insertelement` if a `shufflevector`
// might be a better option
for_array(i, temp_data) {
if (temp_data[i].value.value != nullptr) {
LLVMValueRef index = lb_const_int(p->module, t_u32, temp_data[i].elem_index).value;
vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, temp_data[i].value.value, index, "");
}
}
}
break;
}
}
return v;
+552
View File
@@ -169,6 +169,19 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
}
}
if (!entity->Procedure.target_feature_disabled &&
entity->Procedure.target_feature.len != 0) {
auto features = split_by_comma(entity->Procedure.target_feature);
for_array(i, features) {
String feature = features[i];
LLVMAttributeRef ref = LLVMCreateStringAttribute(
m->ctx,
cast(char const *)feature.text, cast(unsigned)feature.len,
"", 0);
LLVMAddAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, ref);
}
}
if (entity->flags & EntityFlag_Cold) {
lb_add_attribute_to_proc(m, p->value, "cold");
}
@@ -981,10 +994,466 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
return result;
}
LLVMValueRef llvm_splat_float(i64 count, LLVMTypeRef type, f64 value) {
LLVMValueRef v = LLVMConstReal(type, value);
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
for (i64 i = 0; i < count; i++) {
values[i] = v;
}
return LLVMConstVector(values, cast(unsigned)count);
}
LLVMValueRef llvm_splat_int(i64 count, LLVMTypeRef type, i64 value, bool is_signed=false) {
LLVMValueRef v = LLVMConstInt(type, value, is_signed);
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
for (i64 i = 0; i < count; i++) {
values[i] = v;
}
return LLVMConstVector(values, cast(unsigned)count);
}
lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId builtin_id) {
ast_node(ce, CallExpr, expr);
lbModule *m = p->module;
lbValue res = {};
res.type = tv.type;
lbValue arg0 = {}; if (ce->args.count > 0) arg0 = lb_build_expr(p, ce->args[0]);
lbValue arg1 = {}; if (ce->args.count > 1) arg1 = lb_build_expr(p, ce->args[1]);
lbValue arg2 = {}; if (ce->args.count > 2) arg2 = lb_build_expr(p, ce->args[2]);
Type *elem = base_array_type(arg0.type);
bool is_float = is_type_float(elem);
bool is_signed = !is_type_unsigned(elem);
LLVMOpcode op_code = cast(LLVMOpcode)0;
switch (builtin_id) {
case BuiltinProc_simd_add:
case BuiltinProc_simd_sub:
case BuiltinProc_simd_mul:
case BuiltinProc_simd_div:
case BuiltinProc_simd_rem:
if (is_float) {
switch (builtin_id) {
case BuiltinProc_simd_add: op_code = LLVMFAdd; break;
case BuiltinProc_simd_sub: op_code = LLVMFSub; break;
case BuiltinProc_simd_mul: op_code = LLVMFMul; break;
case BuiltinProc_simd_div: op_code = LLVMFDiv; break;
}
} else {
switch (builtin_id) {
case BuiltinProc_simd_add: op_code = LLVMAdd; break;
case BuiltinProc_simd_sub: op_code = LLVMSub; break;
case BuiltinProc_simd_mul: op_code = LLVMMul; break;
case BuiltinProc_simd_div:
if (is_signed) {
op_code = LLVMSDiv;
} else {
op_code = LLVMUDiv;
}
break;
case BuiltinProc_simd_rem:
if (is_signed) {
op_code = LLVMSRem;
} else {
op_code = LLVMURem;
}
break;
}
}
if (op_code) {
res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, "");
return res;
}
break;
case BuiltinProc_simd_shl: // Odin logic
case BuiltinProc_simd_shr: // Odin logic
case BuiltinProc_simd_shl_masked: // C logic
case BuiltinProc_simd_shr_masked: // C logic
{
i64 sz = type_size_of(elem);
GB_ASSERT(arg0.type->kind == Type_SimdVector);
i64 count = arg0.type->SimdVector.count;
Type *elem1 = base_array_type(arg1.type);
bool is_masked = false;
switch (builtin_id) {
case BuiltinProc_simd_shl: op_code = LLVMShl; is_masked = false; break;
case BuiltinProc_simd_shr: op_code = is_signed ? LLVMAShr : LLVMLShr; is_masked = false; break;
case BuiltinProc_simd_shl_masked: op_code = LLVMShl; is_masked = true; break;
case BuiltinProc_simd_shr_masked: op_code = is_signed ? LLVMAShr : LLVMLShr; is_masked = true; break;
}
if (op_code) {
LLVMValueRef bits = llvm_splat_int(count, lb_type(m, elem1), sz*8 - 1);
if (is_masked) {
// C logic
LLVMValueRef shift = LLVMBuildAnd(p->builder, arg1.value, bits, "");
res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, shift, "");
} else {
// Odin logic
LLVMValueRef zero = lb_const_nil(m, arg1.type).value;
LLVMValueRef mask = LLVMBuildICmp(p->builder, LLVMIntULE, arg1.value, bits, "");
LLVMValueRef shift = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, mask, shift, zero, "");
}
return res;
}
}
break;
case BuiltinProc_simd_and:
case BuiltinProc_simd_or:
case BuiltinProc_simd_xor:
case BuiltinProc_simd_and_not:
switch (builtin_id) {
case BuiltinProc_simd_and: op_code = LLVMAnd; break;
case BuiltinProc_simd_or: op_code = LLVMOr; break;
case BuiltinProc_simd_xor: op_code = LLVMXor; break;
case BuiltinProc_simd_and_not:
op_code = LLVMAnd;
arg1.value = LLVMBuildNot(p->builder, arg1.value, "");
break;
}
if (op_code) {
res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, "");
return res;
}
break;
case BuiltinProc_simd_neg:
if (is_float) {
res.value = LLVMBuildFNeg(p->builder, arg0.value, "");
} else {
res.value = LLVMBuildNeg(p->builder, arg0.value, "");
}
return res;
case BuiltinProc_simd_abs:
if (is_float) {
LLVMValueRef pos = arg0.value;
LLVMValueRef neg = LLVMBuildFNeg(p->builder, pos, "");
LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, pos, neg, "");
res.value = LLVMBuildSelect(p->builder, cond, pos, neg, "");
} else {
LLVMValueRef pos = arg0.value;
LLVMValueRef neg = LLVMBuildNeg(p->builder, pos, "");
LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, pos, neg, "");
res.value = LLVMBuildSelect(p->builder, cond, pos, neg, "");
}
return res;
case BuiltinProc_simd_min:
if (is_float) {
LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOLT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
} else {
LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSLT : LLVMIntULT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
}
return res;
case BuiltinProc_simd_max:
if (is_float) {
LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
} else {
LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
}
return res;
case BuiltinProc_simd_lanes_eq:
case BuiltinProc_simd_lanes_ne:
case BuiltinProc_simd_lanes_lt:
case BuiltinProc_simd_lanes_le:
case BuiltinProc_simd_lanes_gt:
case BuiltinProc_simd_lanes_ge:
if (is_float) {
LLVMRealPredicate pred = cast(LLVMRealPredicate)0;
switch (builtin_id) {
case BuiltinProc_simd_lanes_eq: pred = LLVMRealOEQ; break;
case BuiltinProc_simd_lanes_ne: pred = LLVMRealONE; break;
case BuiltinProc_simd_lanes_lt: pred = LLVMRealOLT; break;
case BuiltinProc_simd_lanes_le: pred = LLVMRealOLE; break;
case BuiltinProc_simd_lanes_gt: pred = LLVMRealOGT; break;
case BuiltinProc_simd_lanes_ge: pred = LLVMRealOGE; break;
}
if (pred) {
res.value = LLVMBuildFCmp(p->builder, pred, arg0.value, arg1.value, "");
res.value = LLVMBuildSExtOrBitCast(p->builder, res.value, lb_type(m, tv.type), "");
return res;
}
} else {
LLVMIntPredicate pred = cast(LLVMIntPredicate)0;
switch (builtin_id) {
case BuiltinProc_simd_lanes_eq: pred = LLVMIntEQ; break;
case BuiltinProc_simd_lanes_ne: pred = LLVMIntNE; break;
case BuiltinProc_simd_lanes_lt: pred = is_signed ? LLVMIntSLT :LLVMIntULT; break;
case BuiltinProc_simd_lanes_le: pred = is_signed ? LLVMIntSLE :LLVMIntULE; break;
case BuiltinProc_simd_lanes_gt: pred = is_signed ? LLVMIntSGT :LLVMIntUGT; break;
case BuiltinProc_simd_lanes_ge: pred = is_signed ? LLVMIntSGE :LLVMIntUGE; break;
}
if (pred) {
res.value = LLVMBuildICmp(p->builder, pred, arg0.value, arg1.value, "");
res.value = LLVMBuildSExtOrBitCast(p->builder, res.value, lb_type(m, tv.type), "");
return res;
}
}
break;
case BuiltinProc_simd_extract:
res.value = LLVMBuildExtractElement(p->builder, arg0.value, arg1.value, "");
return res;
case BuiltinProc_simd_replace:
res.value = LLVMBuildInsertElement(p->builder, arg0.value, arg2.value, arg1.value, "");
return res;
case BuiltinProc_simd_reduce_add_ordered:
case BuiltinProc_simd_reduce_mul_ordered:
{
LLVMTypeRef llvm_elem = lb_type(m, elem);
LLVMValueRef args[2] = {};
isize args_count = 0;
char const *name = nullptr;
switch (builtin_id) {
case BuiltinProc_simd_reduce_add_ordered:
if (is_float) {
name = "llvm.vector.reduce.fadd";
args[args_count++] = LLVMConstReal(llvm_elem, 0.0);
} else {
name = "llvm.vector.reduce.add";
}
break;
case BuiltinProc_simd_reduce_mul_ordered:
if (is_float) {
name = "llvm.vector.reduce.fmul";
args[args_count++] = LLVMConstReal(llvm_elem, 1.0);
} else {
name = "llvm.vector.reduce.mul";
}
break;
}
args[args_count++] = arg0.value;
LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
res.value = LLVMBuildCall(p->builder, ip, args, cast(unsigned)args_count, "");
return res;
}
case BuiltinProc_simd_reduce_min:
case BuiltinProc_simd_reduce_max:
case BuiltinProc_simd_reduce_and:
case BuiltinProc_simd_reduce_or:
case BuiltinProc_simd_reduce_xor:
{
char const *name = nullptr;
switch (builtin_id) {
case BuiltinProc_simd_reduce_min:
if (is_float) {
name = "llvm.vector.reduce.fmin";
} else if (is_signed) {
name = "llvm.vector.reduce.smin";
} else {
name = "llvm.vector.reduce.umin";
}
break;
case BuiltinProc_simd_reduce_max:
if (is_float) {
name = "llvm.vector.reduce.fmax";
} else if (is_signed) {
name = "llvm.vector.reduce.smax";
} else {
name = "llvm.vector.reduce.umax";
}
break;
case BuiltinProc_simd_reduce_and: name = "llvm.vector.reduce.and"; break;
case BuiltinProc_simd_reduce_or: name = "llvm.vector.reduce.or"; break;
case BuiltinProc_simd_reduce_xor: name = "llvm.vector.reduce.xor"; break;
}
LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
LLVMValueRef args[1] = {};
args[0] = arg0.value;
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
return res;
}
case BuiltinProc_simd_shuffle:
{
Type *vt = arg0.type;
GB_ASSERT(vt->kind == Type_SimdVector);
i64 indices_count = ce->args.count-2;
i64 max_count = vt->SimdVector.count*2;
GB_ASSERT(indices_count <= max_count);
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, indices_count);
for (isize i = 0; i < indices_count; i++) {
lbValue idx = lb_build_expr(p, ce->args[i+2]);
GB_ASSERT(LLVMIsConstant(idx.value));
values[i] = idx.value;
}
LLVMValueRef indices = LLVMConstVector(values, cast(unsigned)indices_count);
res.value = LLVMBuildShuffleVector(p->builder, arg0.value, arg1.value, indices, "");
return res;
}
case BuiltinProc_simd_select:
{
LLVMValueRef cond = arg0.value;
LLVMValueRef x = lb_build_expr(p, ce->args[1]).value;
LLVMValueRef y = lb_build_expr(p, ce->args[2]).value;
cond = LLVMBuildICmp(p->builder, LLVMIntNE, cond, LLVMConstNull(LLVMTypeOf(cond)), "");
res.value = LLVMBuildSelect(p->builder, cond, x, y, "");
return res;
}
case BuiltinProc_simd_ceil:
case BuiltinProc_simd_floor:
case BuiltinProc_simd_trunc:
case BuiltinProc_simd_nearest:
{
char const *name = nullptr;
switch (builtin_id) {
case BuiltinProc_simd_ceil: name = "llvm.ceil"; break;
case BuiltinProc_simd_floor: name = "llvm.floor"; break;
case BuiltinProc_simd_trunc: name = "llvm.trunc"; break;
case BuiltinProc_simd_nearest: name = "llvm.nearbyint"; break;
}
LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
LLVMValueRef args[1] = {};
args[0] = arg0.value;
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
return res;
}
case BuiltinProc_simd_lanes_reverse:
{
i64 count = get_array_type_count(arg0.type);
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
LLVMTypeRef llvm_u32 = lb_type(m, t_u32);
for (i64 i = 0; i < count; i++) {
values[i] = LLVMConstInt(llvm_u32, count-1-i, false);
}
LLVMValueRef mask = LLVMConstVector(values, cast(unsigned)count);
LLVMValueRef v = arg0.value;
res.value = LLVMBuildShuffleVector(p->builder, v, v, mask, "");
return res;
}
case BuiltinProc_simd_lanes_rotate_left:
case BuiltinProc_simd_lanes_rotate_right:
{
i64 count = get_array_type_count(arg0.type);
GB_ASSERT(is_power_of_two(count));
BigInt bi_count = {};
big_int_from_i64(&bi_count, count);
TypeAndValue const &tv = ce->args[1]->tav;
ExactValue val = exact_value_to_integer(tv.value);
GB_ASSERT(val.kind == ExactValue_Integer);
BigInt *bi = &val.value_integer;
if (builtin_id == BuiltinProc_simd_lanes_rotate_right) {
big_int_neg(bi, bi);
}
big_int_rem(bi, bi, &bi_count);
big_int_dealloc(&bi_count);
i64 left = big_int_to_i64(bi);
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
LLVMTypeRef llvm_u32 = lb_type(m, t_u32);
for (i64 i = 0; i < count; i++) {
u64 idx = cast(u64)(i+left) & cast(u64)(count-1);
values[i] = LLVMConstInt(llvm_u32, idx, false);
}
LLVMValueRef mask = LLVMConstVector(values, cast(unsigned)count);
LLVMValueRef v = arg0.value;
res.value = LLVMBuildShuffleVector(p->builder, v, v, mask, "");
return res;
}
case BuiltinProc_simd_add_sat:
case BuiltinProc_simd_sub_sat:
{
char const *name = nullptr;
switch (builtin_id) {
case BuiltinProc_simd_add_sat: name = is_signed ? "llvm.sadd.sat" : "llvm.uadd.sat"; break;
case BuiltinProc_simd_sub_sat: name = is_signed ? "llvm.ssub.sat" : "llvm.usub.sat"; break;
}
LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
LLVMValueRef args[2] = {};
args[0] = arg0.value;
args[1] = arg1.value;
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
return res;
}
case BuiltinProc_simd_clamp:
{
LLVMValueRef v = arg0.value;
LLVMValueRef min = arg1.value;
LLVMValueRef max = arg2.value;
if (is_float) {
v = LLVMBuildSelect(p->builder, LLVMBuildFCmp(p->builder, LLVMRealOLT, v, min, ""), min, v, "");
res.value = LLVMBuildSelect(p->builder, LLVMBuildFCmp(p->builder, LLVMRealOGT, v, max, ""), max, v, "");
} else if (is_signed) {
v = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntSLT, v, min, ""), min, v, "");
res.value = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntSGT, v, max, ""), max, v, "");
} else {
v = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntULT, v, min, ""), min, v, "");
res.value = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntUGT, v, max, ""), max, v, "");
}
return res;
}
case BuiltinProc_simd_to_bits:
{
res.value = LLVMBuildBitCast(p->builder, arg0.value, lb_type(m, tv.type), "");
return res;
}
}
GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name));
return {};
}
lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId id) {
ast_node(ce, CallExpr, expr);
if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) {
return lb_build_builtin_simd_proc(p, expr, tv, id);
}
switch (id) {
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
@@ -1532,6 +2001,31 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
case BuiltinProc_fused_mul_add:
{
Type *type = tv.type;
lbValue x = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), type);
lbValue y = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), type);
lbValue z = lb_emit_conv(p, lb_build_expr(p, ce->args[2]), type);
char const *name = "llvm.fma";
LLVMTypeRef types[1] = {lb_type(p->module, type)};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
LLVMValueRef args[3] = {};
args[0] = x.value;
args[1] = y.value;
args[2] = z.value;
lbValue res = {};
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
res.type = type;
return res;
}
case BuiltinProc_mem_copy:
{
lbValue dst = lb_build_expr(p, ce->args[0]);
@@ -1614,6 +2108,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return {};
case BuiltinProc_volatile_store:
case BuiltinProc_non_temporal_store:
case BuiltinProc_atomic_store:
case BuiltinProc_atomic_store_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
@@ -1622,6 +2117,13 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value);
switch (id) {
case BuiltinProc_non_temporal_store:
{
unsigned kind_id = LLVMGetMDKindIDInContext(p->module->ctx, "nontemporal", 11);
LLVMMetadataRef node = LLVMValueAsMetadata(LLVMConstInt(lb_type(p->module, t_u32), 1, false));
LLVMSetMetadata(instr, kind_id, LLVMMetadataAsValue(p->module->ctx, node));
}
break;
case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
case BuiltinProc_atomic_store_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[2])); break;
@@ -1633,12 +2135,21 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
case BuiltinProc_volatile_load:
case BuiltinProc_non_temporal_load:
case BuiltinProc_atomic_load:
case BuiltinProc_atomic_load_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, "");
switch (id) {
case BuiltinProc_non_temporal_load:
{
unsigned kind_id = LLVMGetMDKindIDInContext(p->module->ctx, "nontemporal", 11);
LLVMMetadataRef node = LLVMValueAsMetadata(LLVMConstInt(lb_type(p->module, t_u32), 1, false));
LLVMSetMetadata(instr, kind_id, LLVMMetadataAsValue(p->module->ctx, node));
}
break;
break;
case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
case BuiltinProc_atomic_load_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[1])); break;
@@ -2232,6 +2743,47 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
case BuiltinProc_x86_cpuid:
{
Type *param_types[2] = {t_u32, t_u32};
Type *type = alloc_type_proc_from_types(param_types, gb_count_of(param_types), tv.type, false, ProcCC_None);
LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type));
LLVMValueRef the_asm = llvm_get_inline_asm(
func_type,
str_lit("cpuid"),
str_lit("={ax},={bx},={cx},={dx},{ax},{cx}"),
true
);
GB_ASSERT(the_asm != nullptr);
LLVMValueRef args[2] = {};
args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32).value;
args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value;
lbValue res = {};
res.type = tv.type;
res.value = LLVMBuildCall2(p->builder, func_type, the_asm, args, gb_count_of(args), "");
return res;
}
case BuiltinProc_x86_xgetbv:
{
Type *type = alloc_type_proc_from_types(&t_u32, 1, tv.type, false, ProcCC_None);
LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type));
LLVMValueRef the_asm = llvm_get_inline_asm(
func_type,
str_lit("xgetbv"),
str_lit("={ax},={dx},{cx}"),
true
);
GB_ASSERT(the_asm != nullptr);
LLVMValueRef args[1] = {};
args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32).value;
lbValue res = {};
res.type = tv.type;
res.value = LLVMBuildCall2(p->builder, func_type, the_asm, args, gb_count_of(args), "");
return res;
}
}
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
+9 -2
View File
@@ -201,6 +201,11 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
return res;
}
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
return res;
}
if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) {
lbValue s = lb_address_from_load_or_generate_local(p, value);
lbValue d = lb_emit_transmute(p, s, alloc_type_pointer(t));
@@ -480,8 +485,10 @@ lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type) {
}
lbValue lb_emit_count_zeros(lbProcedure *p, lbValue x, Type *type) {
i64 sz = 8*type_size_of(type);
lbValue size = lb_const_int(p->module, type, cast(u64)sz);
Type *elem = base_array_type(type);
i64 sz = 8*type_size_of(elem);
lbValue size = lb_const_int(p->module, elem, cast(u64)sz);
size = lb_emit_conv(p, size, type);
lbValue count = lb_emit_count_ones(p, x, type);
return lb_emit_arith(p, Token_Sub, size, count, type);
}
+2 -2
View File
@@ -1376,8 +1376,8 @@ bool parse_build_flags(Array<String> args) {
}
case BuildFlag_TargetFeatures: {
GB_ASSERT(value.kind == ExactValue_String);
build_context.target_features = value.value_string;
string_to_lower(&build_context.target_features);
build_context.target_features_string = value.value_string;
string_to_lower(&build_context.target_features_string);
break;
}
case BuildFlag_RelocMode: {
+13 -1
View File
@@ -360,6 +360,7 @@ Ast *clone_ast(Ast *node) {
case Ast_ArrayType:
n->ArrayType.count = clone_ast(n->ArrayType.count);
n->ArrayType.elem = clone_ast(n->ArrayType.elem);
n->ArrayType.tag = clone_ast(n->ArrayType.tag);
break;
case Ast_DynamicArrayType:
n->DynamicArrayType.elem = clone_ast(n->DynamicArrayType.elem);
@@ -2127,7 +2128,18 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Token name = expect_token(f, Token_Ident);
if (name.string == "type") {
return ast_helper_type(f, token, parse_type(f));
} else if (name.string == "soa" || name.string == "simd") {
} else if ( name.string == "simd") {
Ast *tag = ast_basic_directive(f, token, name);
Ast *original_type = parse_type(f);
Ast *type = unparen_expr(original_type);
switch (type->kind) {
case Ast_ArrayType: type->ArrayType.tag = tag; break;
default:
syntax_error(type, "Expected a fixed array type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind]));
break;
}
return original_type;
} else if (name.string == "soa") {
Ast *tag = ast_basic_directive(f, token, name);
Ast *original_type = parse_type(f);
Ast *type = unparen_expr(original_type);
-1
View File
@@ -411,7 +411,6 @@ AST_KIND(_ExprBegin, "", bool) \
Token ellipsis; \
ProcInlining inlining; \
bool optional_ok_one; \
i32 builtin_id; \
void *sce_temp_data; \
}) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
+9
View File
@@ -157,6 +157,15 @@ int string_compare(String const &x, String const &y) {
return 0;
}
isize string_index_byte(String const &s, u8 x) {
for (isize i = 0; i < s.len; i++) {
if (s.text[i] == x) {
return i;
}
}
return -1;
}
GB_COMPARE_PROC(string_cmp_proc) {
String x = *(String *)a;
String y = *(String *)b;
+21 -3
View File
@@ -261,6 +261,7 @@ struct TypeProc {
TYPE_KIND(SimdVector, struct { \
i64 count; \
Type *elem; \
Type *generic_count; \
}) \
TYPE_KIND(RelativePointer, struct { \
Type *pointer_type; \
@@ -362,6 +363,9 @@ enum : int {
MATRIX_ELEMENT_COUNT_MIN = 1,
MATRIX_ELEMENT_COUNT_MAX = 16,
MATRIX_ELEMENT_MAX_SIZE = MATRIX_ELEMENT_COUNT_MAX * (2 * 8), // complex128
SIMD_ELEMENT_COUNT_MIN = 1,
SIMD_ELEMENT_COUNT_MAX = 64,
};
@@ -1085,10 +1089,11 @@ Type *alloc_type_bit_set() {
Type *alloc_type_simd_vector(i64 count, Type *elem) {
Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_count=nullptr) {
Type *t = alloc_type(Type_SimdVector);
t->SimdVector.count = count;
t->SimdVector.elem = elem;
t->SimdVector.generic_count = generic_count;
return t;
}
@@ -1593,6 +1598,8 @@ i64 get_array_type_count(Type *t) {
return bt->Array.count;
} else if (bt->kind == Type_EnumeratedArray) {
return bt->EnumeratedArray.count;
} else if (bt->kind == Type_SimdVector) {
return bt->SimdVector.count;
}
GB_ASSERT(is_type_array_like(t));
return -1;
@@ -1932,11 +1939,14 @@ bool is_type_valid_vector_elem(Type *t) {
return false;
}
if (is_type_integer(t)) {
return true;
return !is_type_integer_128bit(t);
}
if (is_type_float(t)) {
return true;
}
if (is_type_boolean(t)) {
return true;
}
}
return false;
}
@@ -2078,6 +2088,11 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) {
return true;
}
return is_type_polymorphic(t->Array.elem, or_specialized);
case Type_SimdVector:
if (t->SimdVector.generic_count != nullptr) {
return true;
}
return is_type_polymorphic(t->SimdVector.elem, or_specialized);
case Type_DynamicArray:
return is_type_polymorphic(t->DynamicArray.elem, or_specialized);
case Type_Slice:
@@ -2291,6 +2306,9 @@ bool is_type_comparable(Type *t) {
}
}
return true;
case Type_SimdVector:
return true;
}
return false;
}
@@ -3446,7 +3464,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
case Type_SimdVector: {
// IMPORTANT TODO(bill): Figure out the alignment of vector types
return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align);
return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align*2);
}
case Type_Matrix: