mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 14:48:47 +00:00
Merge pull request #1807 from odin-lang/simd-dev
Generic #simd type and intrinsics
This commit is contained in:
+116
-2
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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},
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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; }) \
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user