From c9b82a21e9a52122a226ecc05c46a29dc8f57dac Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 23 Apr 2021 10:01:52 +0100 Subject: Move `check_builtin_procedure` to check_builtin.cpp --- src/check_builtin.cpp | 2802 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2802 insertions(+) create mode 100644 src/check_builtin.cpp (limited to 'src/check_builtin.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp new file mode 100644 index 000000000..5f4411b90 --- /dev/null +++ b/src/check_builtin.cpp @@ -0,0 +1,2802 @@ +typedef bool (BuiltinTypeIsProc)(Type *t); + +BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end - BuiltinProc__type_simple_boolean_begin] = { + nullptr, // BuiltinProc__type_simple_boolean_begin + + is_type_boolean, + is_type_integer, + is_type_rune, + is_type_float, + is_type_complex, + is_type_quaternion, + is_type_string, + is_type_typeid, + is_type_any, + is_type_endian_little, + is_type_endian_big, + is_type_unsigned, + is_type_numeric, + is_type_ordered, + is_type_ordered_numeric, + is_type_indexable, + is_type_sliceable, + is_type_comparable, + is_type_simple_compare, + is_type_dereferenceable, + is_type_valid_for_keys, + + is_type_named, + is_type_pointer, + is_type_array, + is_type_enumerated_array, + is_type_slice, + is_type_dynamic_array, + + is_type_map, + is_type_struct, + is_type_union, + is_type_enum, + is_type_proc, + is_type_bit_set, + is_type_simd_vector, + + is_type_polymorphic_record_specialized, + is_type_polymorphic_record_unspecialized, + + type_has_nil, +}; + + + +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) { + error(call, "Inlining operators are not allowed on built-in procedures"); + } + + BuiltinProc *bp = &builtin_procs[id]; + { + char const *err = nullptr; + if (ce->args.count < bp->arg_count) { + err = "Too few"; + } else if (ce->args.count > bp->arg_count && !bp->variadic) { + err = "Too many"; + } + + if (err != nullptr) { + gbString expr = expr_to_string(ce->proc); + error(ce->close, "%s arguments for '%s', expected %td, got %td", + err, expr, + bp->arg_count, ce->args.count); + gb_string_free(expr); + return false; + } + } + + switch (id) { + case BuiltinProc_size_of: + case BuiltinProc_align_of: + case BuiltinProc_offset_of: + case BuiltinProc_type_info_of: + case BuiltinProc_typeid_of: + case BuiltinProc_len: + case BuiltinProc_min: + case BuiltinProc_max: + // NOTE(bill): The first arg may be a Type, this will be checked case by case + break; + + case BuiltinProc_DIRECTIVE: { + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name; + if (name == "defined") { + break; + } + if (name == "config") { + break; + } + /*fallthrough*/ + } + default: + if (BuiltinProc__type_begin < id && id < BuiltinProc__type_end) { + check_expr_or_type(c, operand, ce->args[0]); + } else if (ce->args.count > 0) { + check_multi_expr(c, operand, ce->args[0]); + } + break; + } + + String builtin_name = builtin_procs[id].name; + + + if (ce->args.count > 0) { + if (ce->args[0]->kind == Ast_FieldValue) { + if (id != BuiltinProc_soa_zip) { + error(call, "'field = value' calling is not allowed on built-in procedures"); + return false; + } + } + } + + switch (id) { + default: + GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name)); + break; + + case BuiltinProc_DIRECTIVE: { + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name; + if (name == "location") { + if (ce->args.count > 1) { + error(ce->args[0], "'#location' expects either 0 or 1 arguments, got %td", ce->args.count); + } + if (ce->args.count > 0) { + Ast *arg = ce->args[0]; + Entity *e = nullptr; + Operand o = {}; + if (arg->kind == Ast_Ident) { + e = check_ident(c, &o, arg, nullptr, nullptr, true); + } else if (arg->kind == Ast_SelectorExpr) { + e = check_selector(c, &o, arg, nullptr); + } + if (e == nullptr) { + error(ce->args[0], "'#location' expected a valid entity name"); + } + } + + operand->type = t_source_code_location; + operand->mode = Addressing_Value; + } else if (name == "load") { + if (ce->args.count != 1) { + error(ce->args[0], "'#load' expects 1 argument, got %td", ce->args.count); + return false; + } + + Ast *arg = ce->args[0]; + Operand o = {}; + check_expr(c, &o, arg); + if (o.mode != Addressing_Constant) { + error(arg, "'#load' expected a constant string argument"); + return false; + } + + if (!is_type_string(o.type)) { + gbString str = type_to_string(o.type); + error(arg, "'#load' expected a constant string, got %s", str); + gb_string_free(str); + return false; + } + + gbAllocator a = heap_allocator(); + + GB_ASSERT(o.value.kind == ExactValue_String); + String base_dir = dir_from_path(get_file_path_string(bd->token.pos.file_id)); + String original_string = o.value.value_string; + + + gbMutex *ignore_mutex = nullptr; + String path = {}; + bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path); + + char *c_str = alloc_cstring(a, path); + defer (gb_free(a, c_str)); + + + gbFile f = {}; + gbFileError file_err = gb_file_open(&f, c_str); + defer (gb_file_close(&f)); + + switch (file_err) { + default: + case gbFileError_Invalid: + error(ce->proc, "Failed to `#load` file: %s; invalid file or cannot be found", c_str); + return false; + case gbFileError_NotExists: + error(ce->proc, "Failed to `#load` file: %s; file cannot be found", c_str); + return false; + case gbFileError_Permission: + error(ce->proc, "Failed to `#load` file: %s; file permissions problem", c_str); + return false; + case gbFileError_None: + // Okay + break; + } + + String result = {}; + isize file_size = cast(isize)gb_file_size(&f); + if (file_size > 0) { + u8 *data = cast(u8 *)gb_alloc(a, file_size+1); + gb_file_read_at(&f, data, file_size, 0); + data[file_size] = '\0'; + result.text = data; + result.len = file_size; + } + + operand->type = t_u8_slice; + operand->mode = Addressing_Constant; + operand->value = exact_value_string(result); + + } else if (name == "assert") { + if (ce->args.count != 1) { + error(call, "'#assert' expects 1 argument, got %td", ce->args.count); + return false; + } + if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) { + gbString str = expr_to_string(ce->args[0]); + error(call, "'%s' is not a constant boolean", str); + gb_string_free(str); + return false; + } + if (!operand->value.value_bool) { + gbString arg = expr_to_string(ce->args[0]); + error(call, "Compile time assertion: %s", arg); + if (c->proc_name != "") { + gbString str = type_to_string(c->curr_proc_sig); + error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); + gb_string_free(str); + } + gb_string_free(arg); + } + + operand->type = t_untyped_bool; + operand->mode = Addressing_Constant; + } else if (name == "panic") { + if (ce->args.count != 1) { + error(call, "'#panic' expects 1 argument, got %td", ce->args.count); + return false; + } + if (!is_type_string(operand->type) && operand->mode != Addressing_Constant) { + gbString str = expr_to_string(ce->args[0]); + error(call, "'%s' is not a constant string", str); + gb_string_free(str); + return false; + } + error(call, "Compile time panic: %.*s", LIT(operand->value.value_string)); + if (c->proc_name != "") { + gbString str = type_to_string(c->curr_proc_sig); + error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); + gb_string_free(str); + } + operand->type = t_invalid; + operand->mode = Addressing_NoValue; + } else if (name == "defined") { + if (ce->args.count != 1) { + error(call, "'#defined' expects 1 argument, got %td", ce->args.count); + return false; + } + Ast *arg = unparen_expr(ce->args[0]); + if (arg == nullptr || (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr)) { + error(call, "'#defined' expects an identifier or selector expression, got %.*s", LIT(ast_strings[arg->kind])); + return false; + } + + if (c->curr_proc_decl == nullptr) { + error(call, "'#defined' is only allowed within a procedure, prefer the replacement '#config(NAME, default_value)'"); + return false; + } + + bool is_defined = check_identifier_exists(c->scope, arg); + operand->type = t_untyped_bool; + operand->mode = Addressing_Constant; + operand->value = exact_value_bool(false); + + } else if (name == "config") { + if (ce->args.count != 2) { + error(call, "'#config' expects 2 argument, got %td", ce->args.count); + return false; + } + Ast *arg = unparen_expr(ce->args[0]); + if (arg == nullptr || arg->kind != Ast_Ident) { + error(call, "'#config' expects an identifier, got %.*s", LIT(ast_strings[arg->kind])); + return false; + } + + Ast *def_arg = unparen_expr(ce->args[1]); + + Operand def = {}; + check_expr(c, &def, def_arg); + if (def.mode != Addressing_Constant) { + error(def_arg, "'#config' default value must be a constant"); + return false; + } + + String name = arg->Ident.token.string; + + + operand->type = def.type; + operand->mode = def.mode; + operand->value = def.value; + + Entity *found = scope_lookup_current(config_pkg->scope, name); + if (found != nullptr) { + if (found->kind != Entity_Constant) { + error(arg, "'#config' entity '%.*s' found but expected a constant", LIT(name)); + } else { + operand->type = found->type; + operand->mode = Addressing_Constant; + operand->value = found->Constant.value; + } + } + } else { + GB_PANIC("Unhandled #%.*s", LIT(name)); + } + + break; + } + + case BuiltinProc_len: + check_expr_or_type(c, operand, ce->args[0]); + if (operand->mode == Addressing_Invalid) { + return false; + } + /* fallthrough */ + + case BuiltinProc_cap: + { + // len :: proc(Type) -> int + // cap :: proc(Type) -> int + + Type *op_type = type_deref(operand->type); + Type *type = t_int; + AddressingMode mode = Addressing_Invalid; + ExactValue value = {}; + if (is_type_string(op_type) && id == BuiltinProc_len) { + if (operand->mode == Addressing_Constant) { + mode = Addressing_Constant; + String str = operand->value.value_string; + value = exact_value_i64(str.len); + type = t_untyped_integer; + } else { + mode = Addressing_Value; + if (is_type_cstring(op_type)) { + add_package_dependency(c, "runtime", "cstring_len"); + } + } + } else if (is_type_array(op_type)) { + Type *at = core_type(op_type); + mode = Addressing_Constant; + value = exact_value_i64(at->Array.count); + type = t_untyped_integer; + } else if (is_type_enumerated_array(op_type) && id == BuiltinProc_len) { + Type *at = core_type(op_type); + mode = Addressing_Constant; + value = exact_value_i64(at->EnumeratedArray.count); + type = t_untyped_integer; + } else if (is_type_slice(op_type) && id == BuiltinProc_len) { + mode = Addressing_Value; + } else if (is_type_dynamic_array(op_type)) { + mode = Addressing_Value; + } else if (is_type_map(op_type)) { + mode = Addressing_Value; + } else if (operand->mode == Addressing_Type && is_type_enum(op_type) && id == BuiltinProc_len) { + Type *bt = base_type(op_type); + mode = Addressing_Constant; + value = exact_value_i64(bt->Enum.fields.count); + type = t_untyped_integer; + } else if (is_type_struct(op_type)) { + Type *bt = base_type(op_type); + if (bt->Struct.soa_kind == StructSoa_Fixed) { + mode = Addressing_Constant; + value = exact_value_i64(bt->Struct.soa_count); + type = t_untyped_integer; + } else if ((bt->Struct.soa_kind == StructSoa_Slice && id == BuiltinProc_len) || + bt->Struct.soa_kind == StructSoa_Dynamic) { + mode = Addressing_Value; + } + } + if (operand->mode == Addressing_Type && mode != Addressing_Constant) { + mode = Addressing_Invalid; + } + + if (mode == Addressing_Invalid) { + gbString t = type_to_string(operand->type); + error(call, "'%.*s' is not supported for '%s'", LIT(builtin_name), t); + return false; + } + + operand->mode = mode; + operand->value = value; + operand->type = type; + + break; + } + + case BuiltinProc_size_of: { + // size_of :: proc(Type or expr) -> untyped int + Operand o = {}; + check_expr_or_type(c, &o, ce->args[0]); + if (o.mode == Addressing_Invalid) { + return false; + } + Type *t = o.type; + if (t == nullptr || t == t_invalid) { + error(ce->args[0], "Invalid argument for 'size_of'"); + return false; + } + t = default_type(t); + + operand->mode = Addressing_Constant; + operand->value = exact_value_i64(type_size_of(t)); + operand->type = t_untyped_integer; + + break; + } + + case BuiltinProc_align_of: { + // align_of :: proc(Type or expr) -> untyped int + Operand o = {}; + check_expr_or_type(c, &o, ce->args[0]); + if (o.mode == Addressing_Invalid) { + return false; + } + Type *t = o.type; + if (t == nullptr || t == t_invalid) { + error(ce->args[0], "Invalid argument for 'align_of'"); + return false; + } + t = default_type(t); + + operand->mode = Addressing_Constant; + operand->value = exact_value_i64(type_align_of(t)); + operand->type = t_untyped_integer; + + break; + } + + + case BuiltinProc_offset_of: { + // offset_of :: proc(Type, field) -> uintptr + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for 'offset_of'"); + return false; + } + + Ast *field_arg = unparen_expr(ce->args[1]); + if (field_arg == nullptr || + field_arg->kind != Ast_Ident) { + error(field_arg, "Expected an identifier for field argument"); + return false; + } + if (is_type_array(type)) { + error(field_arg, "Invalid type for 'offset_of'"); + return false; + } + + + ast_node(arg, Ident, field_arg); + Selection sel = lookup_field(type, arg->token.string, operand->mode == Addressing_Type); + if (sel.entity == nullptr) { + gbString type_str = type_to_string(bt); + error(ce->args[0], + "'%s' has no field named '%.*s'", type_str, LIT(arg->token.string)); + gb_string_free(type_str); + return false; + } + if (sel.indirect) { + gbString type_str = type_to_string(bt); + error(ce->args[0], + "Field '%.*s' is embedded via a pointer in '%s'", LIT(arg->token.string), type_str); + gb_string_free(type_str); + return false; + } + + operand->mode = Addressing_Constant; + operand->value = exact_value_i64(type_offset_of_from_selection(type, sel)); + operand->type = t_uintptr; + + break; + } + + + case BuiltinProc_type_of: { + // type_of :: proc(val: Type) -> type(Type) + Ast *expr = ce->args[0]; + Operand o = {}; + check_expr_or_type(c, &o, expr); + + // check_assignment(c, operand, nullptr, str_lit("argument of 'type_of'")); + if (o.mode == Addressing_Invalid || o.mode == Addressing_Builtin) { + return false; + } + if (o.type == nullptr || o.type == t_invalid || is_type_asm_proc(o.type)) { + error(o.expr, "Invalid argument to 'type_of'"); + return false; + } + // NOTE(bill): Prevent type cycles for procedure declarations + if (c->curr_proc_sig == o.type) { + gbString s = expr_to_string(o.expr); + error(o.expr, "Invalid cyclic type usage from 'type_of', got '%s'", s); + gb_string_free(s); + return false; + } + + if (is_type_polymorphic(o.type)) { + error(o.expr, "'type_of' of polymorphic type cannot be determined"); + return false; + } + operand->mode = Addressing_Type; + operand->type = o.type; + break; + } + + case BuiltinProc_type_info_of: { + // type_info_of :: proc(Type) -> ^Type_Info + if (c->scope->flags&ScopeFlag_Global) { + compiler_error("'type_info_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); + } + + // NOTE(bill): The type information may not be setup yet + init_core_type_info(c->checker); + Ast *expr = ce->args[0]; + Operand o = {}; + check_expr_or_type(c, &o, expr); + if (o.mode == Addressing_Invalid) { + return false; + } + Type *t = o.type; + if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(t)) { + if (is_type_polymorphic(t)) { + error(ce->args[0], "Invalid argument for 'type_info_of', unspecialized polymorphic type"); + } else { + error(ce->args[0], "Invalid argument for 'type_info_of'"); + } + return false; + } + t = default_type(t); + + add_type_info_type(c, t); + + if (is_operand_value(o) && is_type_typeid(t)) { + add_package_dependency(c, "runtime", "__type_info_of"); + } else if (o.mode != Addressing_Type) { + error(expr, "Expected a type or typeid for 'type_info_of'"); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_type_info_ptr; + break; + } + + case BuiltinProc_typeid_of: { + // typeid_of :: proc(Type) -> typeid + if (c->scope->flags&ScopeFlag_Global) { + compiler_error("'typeid_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); + } + + // NOTE(bill): The type information may not be setup yet + init_core_type_info(c->checker); + Ast *expr = ce->args[0]; + Operand o = {}; + check_expr_or_type(c, &o, expr); + if (o.mode == Addressing_Invalid) { + return false; + } + Type *t = o.type; + if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(operand->type)) { + error(ce->args[0], "Invalid argument for 'typeid_of'"); + return false; + } + t = default_type(t); + + add_type_info_type(c, t); + + if (o.mode != Addressing_Type) { + error(expr, "Expected a type for 'typeid_of'"); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_typeid; + operand->value = exact_value_typeid(t); + break; + } + + case BuiltinProc_swizzle: { + // swizzle :: proc(v: [N]T, ..int) -> [M]T + Type *type = base_type(operand->type); + i64 max_count = 0; + Type *elem_type = nullptr; + + if (!is_type_array(type) && !is_type_simd_vector(type)) { + gbString type_str = type_to_string(operand->type); + error(call, + "'swizzle' is only allowed on an array or #simd vector, got '%s'", + type_str); + gb_string_free(type_str); + return false; + } + if (type->kind == Type_Array) { + max_count = type->Array.count; + elem_type = type->Array.elem; + } else if (type->kind == Type_SimdVector) { + max_count = type->SimdVector.count; + elem_type = type->SimdVector.elem; + if (!build_context.use_llvm_api) { + error(call, "'swizzle' with #simd vector is not supported on this backend"); + } + } + + i64 arg_count = 0; + for_array(i, ce->args) { + if (i == 0) { + 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 'swizzle' must be constant integers"); + return false; + } + + if (op.value.value_integer.neg) { + error(op.expr, "Negative 'swizzle' index"); + return false; + } + + BigInt mc = {}; + big_int_from_i64(&mc, max_count); + if (big_int_cmp(&mc, &op.value.value_integer) <= 0) { + error(op.expr, "'swizzle' index exceeds length"); + return false; + } + + arg_count++; + } + + if (arg_count > max_count) { + error(call, "Too many 'swizzle' indices, %td > %td", arg_count, max_count); + return false; + } + + if (arg_count < max_count) { + operand->type = alloc_type_array(elem_type, arg_count); + } + operand->mode = Addressing_Value; + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_complex: { + // complex :: proc(real, imag: float_type) -> complex_type + Operand x = *operand; + Operand y = {}; + + // NOTE(bill): Invalid will be the default till fixed + operand->type = t_invalid; + operand->mode = Addressing_Invalid; + + check_expr(c, &y, ce->args[1]); + 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, &y, x.type); if (y.mode == Addressing_Invalid) return false; + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant) { + if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) { + x.type = t_untyped_float; + } + if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) { + y.type = t_untyped_float; + } + } + + if (!are_types_identical(x.type, y.type)) { + gbString tx = type_to_string(x.type); + gbString ty = type_to_string(y.type); + error(call, "Mismatched types to 'complex', '%s' vs '%s'", tx, ty); + gb_string_free(ty); + gb_string_free(tx); + return false; + } + + if (!is_type_float(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments have type '%s', expected a floating point", s); + gb_string_free(s); + return false; + } + if (is_type_endian_specific(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments with a specified endian are not allow, expected a normal floating point, got '%s'", s); + gb_string_free(s); + return false; + } + + if (x.mode == Addressing_Constant && y.mode == Addressing_Constant) { + f64 r = exact_value_to_float(x.value).value_float; + f64 i = exact_value_to_float(y.value).value_float; + operand->value = exact_value_complex(r, i); + operand->mode = Addressing_Constant; + } else { + operand->mode = Addressing_Value; + } + + BasicKind kind = core_type(x.type)->Basic.kind; + switch (kind) { + case Basic_f16: operand->type = t_complex32; break; + case Basic_f32: operand->type = t_complex64; break; + case Basic_f64: operand->type = t_complex128; break; + case Basic_UntypedFloat: operand->type = t_untyped_complex; break; + default: GB_PANIC("Invalid type"); break; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_quaternion: { + // quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type + Operand x = *operand; + Operand y = {}; + Operand z = {}; + Operand w = {}; + + // NOTE(bill): Invalid will be the default till fixed + operand->type = t_invalid; + operand->mode = Addressing_Invalid; + + check_expr(c, &y, ce->args[1]); + if (y.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &z, ce->args[2]); + if (y.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &w, ce->args[3]); + 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, &y, x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false; + convert_to_typed(c, &w, x.type); if (w.mode == Addressing_Invalid) return false; + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant && + z.mode == Addressing_Constant && + w.mode == Addressing_Constant) { + if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) { + x.type = t_untyped_float; + } + if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) { + y.type = t_untyped_float; + } + if (is_type_numeric(z.type) && exact_value_imag(z.value).value_float == 0) { + z.type = t_untyped_float; + } + if (is_type_numeric(w.type) && exact_value_imag(w.value).value_float == 0) { + w.type = t_untyped_float; + } + } + + if (!(are_types_identical(x.type, y.type) && are_types_identical(x.type, z.type) && are_types_identical(x.type, w.type))) { + gbString tx = type_to_string(x.type); + gbString ty = type_to_string(y.type); + gbString tz = type_to_string(z.type); + gbString tw = type_to_string(w.type); + error(call, "Mismatched types to 'quaternion', '%s' vs '%s' vs '%s' vs '%s'", tx, ty, tz, tw); + gb_string_free(tw); + gb_string_free(tz); + gb_string_free(ty); + gb_string_free(tx); + return false; + } + + if (!is_type_float(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments have type '%s', expected a floating point", s); + gb_string_free(s); + return false; + } + if (is_type_endian_specific(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments with a specified endian are not allow, expected a normal floating point, got '%s'", s); + gb_string_free(s); + return false; + } + + if (x.mode == Addressing_Constant && y.mode == Addressing_Constant && z.mode == Addressing_Constant && w.mode == Addressing_Constant) { + f64 r = exact_value_to_float(x.value).value_float; + f64 i = exact_value_to_float(y.value).value_float; + f64 j = exact_value_to_float(z.value).value_float; + f64 k = exact_value_to_float(w.value).value_float; + operand->value = exact_value_quaternion(r, i, j, k); + operand->mode = Addressing_Constant; + } else { + operand->mode = Addressing_Value; + } + + BasicKind kind = core_type(x.type)->Basic.kind; + switch (kind) { + case Basic_f16: operand->type = t_quaternion64; break; + case Basic_f32: operand->type = t_quaternion128; break; + case Basic_f64: operand->type = t_quaternion256; break; + case Basic_UntypedFloat: operand->type = t_untyped_quaternion; break; + default: GB_PANIC("Invalid type"); break; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_real: + case BuiltinProc_imag: { + // real :: proc(x: type) -> float_type + // imag :: proc(x: type) -> float_type + + Operand *x = operand; + if (is_type_untyped(x->type)) { + if (x->mode == Addressing_Constant) { + if (is_type_numeric(x->type)) { + x->type = t_untyped_complex; + } + } else if (is_type_quaternion(x->type)) { + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } else{ + convert_to_typed(c, x, t_complex128); + if (x->mode == Addressing_Invalid) { + return false; + } + } + } + + if (!is_type_complex(x->type) && !is_type_quaternion(x->type)) { + gbString s = type_to_string(x->type); + error(call, "Argument has type '%s', expected a complex or quaternion type", s); + gb_string_free(s); + return false; + } + + if (x->mode == Addressing_Constant) { + switch (id) { + case BuiltinProc_real: x->value = exact_value_real(x->value); break; + case BuiltinProc_imag: x->value = exact_value_imag(x->value); break; + } + } else { + x->mode = Addressing_Value; + } + + BasicKind kind = core_type(x->type)->Basic.kind; + switch (kind) { + case Basic_complex32: x->type = t_f16; break; + case Basic_complex64: x->type = t_f32; break; + case Basic_complex128: x->type = t_f64; break; + case Basic_quaternion64: x->type = t_f16; break; + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; + case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; + default: GB_PANIC("Invalid type"); break; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_jmag: + case BuiltinProc_kmag: { + // jmag :: proc(x: type) -> float_type + // kmag :: proc(x: type) -> float_type + + Operand *x = operand; + if (is_type_untyped(x->type)) { + if (x->mode == Addressing_Constant) { + if (is_type_numeric(x->type)) { + x->type = t_untyped_complex; + } + } else{ + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } + } + + if (!is_type_quaternion(x->type)) { + gbString s = type_to_string(x->type); + error(call, "Argument has type '%s', expected a quaternion type", s); + gb_string_free(s); + return false; + } + + if (x->mode == Addressing_Constant) { + switch (id) { + case BuiltinProc_jmag: x->value = exact_value_jmag(x->value); break; + case BuiltinProc_kmag: x->value = exact_value_kmag(x->value); break; + } + } else { + x->mode = Addressing_Value; + } + + BasicKind kind = core_type(x->type)->Basic.kind; + switch (kind) { + case Basic_quaternion64: x->type = t_f16; break; + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; + case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; + default: GB_PANIC("Invalid type"); break; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_conj: { + // conj :: proc(x: type) -> type + Operand *x = operand; + if (is_type_complex(x->type)) { + if (x->mode == Addressing_Constant) { + ExactValue v = exact_value_to_complex(x->value); + f64 r = v.value_complex->real; + f64 i = -v.value_complex->imag; + x->value = exact_value_complex(r, i); + x->mode = Addressing_Constant; + } else { + x->mode = Addressing_Value; + } + } else if (is_type_quaternion(x->type)) { + if (x->mode == Addressing_Constant) { + ExactValue v = exact_value_to_quaternion(x->value); + f64 r = +v.value_quaternion->real; + f64 i = -v.value_quaternion->imag; + f64 j = -v.value_quaternion->jmag; + f64 k = -v.value_quaternion->kmag; + x->value = exact_value_quaternion(r, i, j, k); + x->mode = Addressing_Constant; + } else { + x->mode = Addressing_Value; + } + } else { + gbString s = type_to_string(x->type); + error(call, "Expected a complex or quaternion, got '%s'", s); + gb_string_free(s); + return false; + } + + break; + } + + case BuiltinProc_expand_to_tuple: { + Type *type = base_type(operand->type); + if (!is_type_struct(type) && !is_type_array(type)) { + gbString type_str = type_to_string(operand->type); + error(call, "Expected a struct or array type, got '%s'", type_str); + gb_string_free(type_str); + return false; + } + gbAllocator a = permanent_allocator(); + + Type *tuple = alloc_type_tuple(); + + if (is_type_struct(type)) { + isize variable_count = type->Struct.fields.count; + array_init(&tuple->Tuple.variables, a, variable_count); + // TODO(bill): Should I copy each of the entities or is this good enough? + gb_memmove_array(tuple->Tuple.variables.data, type->Struct.fields.data, variable_count); + } else if (is_type_array(type)) { + isize variable_count = type->Array.count; + array_init(&tuple->Tuple.variables, a, variable_count); + for (isize i = 0; i < variable_count; i++) { + tuple->Tuple.variables[i] = alloc_entity_array_elem(nullptr, blank_token, type->Array.elem, cast(i32)i); + } + } + operand->type = tuple; + operand->mode = Addressing_Value; + + if (tuple->Tuple.variables.count == 1) { + operand->type = tuple->Tuple.variables[0]->type; + } + + break; + } + + case BuiltinProc_min: { + // min :: proc($T: typeid) -> ordered + // min :: proc(a: ..ordered) -> ordered + + check_multi_expr_or_type(c, operand, ce->args[0]); + + Type *original_type = operand->type; + Type *type = base_type(operand->type); + if (operand->mode == Addressing_Type && is_type_enumerated_array(type)) { + // Okay + } else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) { + gbString type_str = type_to_string(original_type); + error(call, "Expected a ordered numeric type to 'min', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + if (operand->mode == Addressing_Type) { + if (ce->args.count != 1) { + error(call, "If 'min' gets a type, only 1 arguments is allowed, got %td", ce->args.count); + return false; + } + + if (is_type_boolean(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + operand->value = exact_value_bool(false); + return true; + } else if (is_type_integer(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + if (is_type_unsigned(type)) { + operand->value = exact_value_u64(0); + return true; + } else { + i64 sz = 8*type_size_of(type); + ExactValue a = exact_value_i64(1); + ExactValue b = exact_value_i64(sz-1); + ExactValue v = exact_binary_operator_value(Token_Shl, a, b); + v = exact_unary_operator_value(Token_Sub, v, cast(i32)sz, false); + operand->value = v; + return true; + } + } else if (is_type_float(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + switch (type_size_of(type)) { + case 2: + operand->value = exact_value_float(-65504.0f); + break; + case 4: + operand->value = exact_value_float(-3.402823466e+38f); + break; + case 8: + operand->value = exact_value_float(-1.7976931348623158e+308); + break; + default: + GB_PANIC("Unhandled float type"); + break; + } + return true; + } else if (is_type_enum(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + operand->value = type->Enum.min_value; + return true; + } else if (is_type_enumerated_array(type)) { + Type *bt = base_type(type); + GB_ASSERT(bt->kind == Type_EnumeratedArray); + operand->mode = Addressing_Constant; + operand->type = bt->EnumeratedArray.index; + operand->value = bt->EnumeratedArray.min_value; + return true; + } + gbString type_str = type_to_string(original_type); + error(call, "Invalid type for 'min', got %s", type_str); + gb_string_free(type_str); + return false; + } + + + bool all_constant = operand->mode == Addressing_Constant; + + auto operands = array_make(heap_allocator(), 0, ce->args.count); + defer (array_free(&operands)); + + array_add(&operands, *operand); + + for (isize i = 1; i < ce->args.count; i++) { + Ast *other_arg = ce->args[i]; + Operand b = {}; + check_expr(c, &b, other_arg); + if (b.mode == Addressing_Invalid) { + return false; + } + if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) { + gbString type_str = type_to_string(b.type); + error(call, + "Expected a ordered numeric type to 'min', got '%s'", + type_str); + gb_string_free(type_str); + return false; + } + array_add(&operands, b); + + if (all_constant) { + all_constant = b.mode == Addressing_Constant; + } + } + + if (all_constant) { + ExactValue value = operands[0].value; + Type *type = operands[0].type; + for (isize i = 1; i < operands.count; i++) { + Operand y = operands[i]; + if (compare_exact_values(Token_Lt, value, y.value)) { + // okay + } else { + value = y.value; + type = y.type; + } + } + operand->value = value; + operand->type = type; + } else { + operand->mode = Addressing_Value; + operand->type = original_type; + + for_array(i, operands) { + Operand *a = &operands[i]; + for_array(j, operands) { + if (i == j) { + continue; + } + Operand *b = &operands[j]; + + convert_to_typed(c, a, b->type); + if (a->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, b, a->type); + if (b->mode == Addressing_Invalid) { + return false; + } + } + } + + for (isize i = 0; i < operands.count-1; i++) { + Operand *a = &operands[i]; + Operand *b = &operands[i+1]; + + if (!are_types_identical(a->type, b->type)) { + gbString type_a = type_to_string(a->type); + gbString type_b = type_to_string(b->type); + error(a->expr, + "Mismatched types to 'min', '%s' vs '%s'", + type_a, type_b); + gb_string_free(type_b); + gb_string_free(type_a); + return false; + } + } + + + { + Type *bt = base_type(operands[0].type); + if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "min_f32"); + if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "min_f64"); + + operand->type = operands[0].type; + } + } + break; + } + + case BuiltinProc_max: { + // max :: proc($T: typeid) -> ordered + // max :: proc(a: ..ordered) -> ordered + + check_multi_expr_or_type(c, operand, ce->args[0]); + + Type *original_type = operand->type; + Type *type = base_type(operand->type); + + if (operand->mode == Addressing_Type && is_type_enumerated_array(type)) { + // Okay + } else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) { + gbString type_str = type_to_string(original_type); + error(call, "Expected a ordered numeric type to 'max', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + if (operand->mode == Addressing_Type) { + if (ce->args.count != 1) { + error(call, "If 'max' gets a type, only 1 arguments is allowed, got %td", ce->args.count); + return false; + } + + if (is_type_boolean(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + operand->value = exact_value_bool(true); + return true; + } else if (is_type_integer(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + if (is_type_unsigned(type)) { + i64 sz = 8*type_size_of(type); + ExactValue a = exact_value_i64(1); + ExactValue b = exact_value_i64(sz); + ExactValue v = exact_binary_operator_value(Token_Shl, a, b); + v = exact_binary_operator_value(Token_Sub, v, a); + operand->value = v; + return true; + } else { + i64 sz = 8*type_size_of(type); + ExactValue a = exact_value_i64(1); + ExactValue b = exact_value_i64(sz-1); + ExactValue v = exact_binary_operator_value(Token_Shl, a, b); + v = exact_binary_operator_value(Token_Sub, v, a); + operand->value = v; + return true; + } + } else if (is_type_float(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + switch (type_size_of(type)) { + case 2: + operand->value = exact_value_float(65504.0f); + break; + case 4: + operand->value = exact_value_float(3.402823466e+38f); + break; + case 8: + operand->value = exact_value_float(1.7976931348623158e+308); + break; + default: + GB_PANIC("Unhandled float type"); + break; + } + return true; + } else if (is_type_enum(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + operand->value = type->Enum.max_value; + return true; + } else if (is_type_enumerated_array(type)) { + Type *bt = base_type(type); + GB_ASSERT(bt->kind == Type_EnumeratedArray); + operand->mode = Addressing_Constant; + operand->type = bt->EnumeratedArray.index; + operand->value = bt->EnumeratedArray.max_value; + return true; + } + gbString type_str = type_to_string(original_type); + error(call, "Invalid type for 'max', got %s", type_str); + gb_string_free(type_str); + return false; + } + + bool all_constant = operand->mode == Addressing_Constant; + + auto operands = array_make(heap_allocator(), 0, ce->args.count); + defer (array_free(&operands)); + + array_add(&operands, *operand); + + + for (isize i = 1; i < ce->args.count; i++) { + Ast *arg = ce->args[i]; + Operand b = {}; + check_expr(c, &b, arg); + if (b.mode == Addressing_Invalid) { + return false; + } + if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) { + gbString type_str = type_to_string(b.type); + error(arg, + "Expected a ordered numeric type to 'max', got '%s'", + type_str); + gb_string_free(type_str); + return false; + } + array_add(&operands, b); + + if (all_constant) { + all_constant = b.mode == Addressing_Constant; + } + } + + if (all_constant) { + ExactValue value = operands[0].value; + Type *type = operands[0].type; + for (isize i = 1; i < operands.count; i++) { + Operand y = operands[i]; + if (compare_exact_values(Token_Gt, value, y.value)) { + // okay + } else { + type = y.type; + value = y.value; + } + } + operand->value = value; + operand->type = type; + } else { + operand->mode = Addressing_Value; + operand->type = original_type; + + for_array(i, operands) { + Operand *a = &operands[i]; + for_array(j, operands) { + if (i == j) { + continue; + } + Operand *b = &operands[j]; + + convert_to_typed(c, a, b->type); + if (a->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, b, a->type); + if (b->mode == Addressing_Invalid) { + return false; + } + } + } + + for (isize i = 0; i < operands.count-1; i++) { + Operand *a = &operands[i]; + Operand *b = &operands[i+1]; + + if (!are_types_identical(a->type, b->type)) { + gbString type_a = type_to_string(a->type); + gbString type_b = type_to_string(b->type); + error(a->expr, + "Mismatched types to 'max', '%s' vs '%s'", + type_a, type_b); + gb_string_free(type_b); + gb_string_free(type_a); + return false; + } + } + + { + Type *bt = base_type(operands[0].type); + if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "max_f32"); + if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "max_f64"); + + operand->type = operands[0].type; + } + } + break; + } + + case BuiltinProc_abs: { + // abs :: proc(n: numeric) -> numeric + if (!(is_type_numeric(operand->type) && !is_type_array(operand->type))) { + gbString type_str = type_to_string(operand->type); + error(call, "Expected a numeric type to 'abs', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + if (operand->mode == Addressing_Constant) { + switch (operand->value.kind) { + case ExactValue_Integer: + operand->value.value_integer.neg = false; + break; + case ExactValue_Float: + operand->value.value_float = gb_abs(operand->value.value_float); + break; + case ExactValue_Complex: { + f64 r = operand->value.value_complex->real; + f64 i = operand->value.value_complex->imag; + operand->value = exact_value_float(gb_sqrt(r*r + i*i)); + + break; + } + default: + GB_PANIC("Invalid numeric constant"); + break; + } + } else { + operand->mode = Addressing_Value; + + { + Type *bt = base_type(operand->type); + if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "abs_f32"); + if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "abs_f64"); + if (are_types_identical(bt, t_complex64)) add_package_dependency(c, "runtime", "abs_complex64"); + if (are_types_identical(bt, t_complex128)) add_package_dependency(c, "runtime", "abs_complex128"); + if (are_types_identical(bt, t_quaternion128)) add_package_dependency(c, "runtime", "abs_quaternion128"); + if (are_types_identical(bt, t_quaternion256)) add_package_dependency(c, "runtime", "abs_quaternion256"); + } + } + + if (is_type_complex(operand->type)) { + operand->type = base_complex_elem_type(operand->type); + } + GB_ASSERT(!is_type_complex(operand->type)); + + break; + } + + case BuiltinProc_clamp: { + // clamp :: proc(a, min, max: ordered) -> ordered + Type *type = operand->type; + if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) { + gbString type_str = type_to_string(operand->type); + error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + Ast *min_arg = ce->args[1]; + Ast *max_arg = ce->args[2]; + Operand x = *operand; + Operand y = {}; + Operand z = {}; + + check_expr(c, &y, min_arg); + if (y.mode == Addressing_Invalid) { + return false; + } + if (!is_type_ordered(y.type) || !(is_type_numeric(y.type) || is_type_string(y.type))) { + gbString type_str = type_to_string(y.type); + error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + check_expr(c, &z, max_arg); + if (z.mode == Addressing_Invalid) { + return false; + } + if (!is_type_ordered(z.type) || !(is_type_numeric(z.type) || is_type_string(z.type))) { + gbString type_str = type_to_string(z.type); + error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant && + z.mode == Addressing_Constant) { + ExactValue a = x.value; + ExactValue b = y.value; + ExactValue c = z.value; + + operand->mode = Addressing_Constant; + if (compare_exact_values(Token_Lt, a, b)) { + operand->value = b; + operand->type = y.type; + } else if (compare_exact_values(Token_Gt, a, c)) { + operand->value = c; + operand->type = z.type; + } else { + operand->value = a; + operand->type = x.type; + } + } else { + operand->mode = Addressing_Value; + operand->type = type; + + Operand *ops[3] = {&x, &y, &z}; + for (isize i = 0; i < 3; i++) { + Operand *a = ops[i]; + for (isize j = 0; j < 3; j++) { + if (i == j) continue; + Operand *b = ops[j]; + convert_to_typed(c, a, b->type); + if (a->mode == Addressing_Invalid) { return false; } + } + } + + if (!are_types_identical(x.type, y.type) || !are_types_identical(x.type, z.type)) { + gbString type_x = type_to_string(x.type); + gbString type_y = type_to_string(y.type); + gbString type_z = type_to_string(z.type); + error(call, + "Mismatched types to 'clamp', '%s', '%s', '%s'", + type_x, type_y, type_z); + gb_string_free(type_z); + gb_string_free(type_y); + gb_string_free(type_x); + return false; + } + + { + Type *bt = base_type(x.type); + if (are_types_identical(bt, t_f32)) { + add_package_dependency(c, "runtime", "min_f32"); + add_package_dependency(c, "runtime", "max_f32"); + } + if (are_types_identical(bt, t_f64)) { + add_package_dependency(c, "runtime", "min_f64"); + add_package_dependency(c, "runtime", "max_f64"); + } + + operand->type = ops[0]->type; + } + } + + break; + } + + case BuiltinProc_soa_zip: { + if (!build_context.use_llvm_api) { + error(call, "'soa_zip' is not supported with this backend"); + return false; + } + + auto types = array_make(temporary_allocator(), 0, ce->args.count); + auto names = array_make(temporary_allocator(), 0, ce->args.count); + + bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); + + bool fail = false; + for_array(i, ce->args) { + Ast *arg = ce->args[i]; + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + error(arg, "Mixture of 'field = value' and value elements in the procedure call 'soa_zip' is not allowed"); + fail = true; + break; + } + } + StringSet name_set = {}; + string_set_init(&name_set, temporary_allocator(), 2*ce->args.count); + + for_array(i, ce->args) { + String name = {}; + Ast *arg = ce->args[i]; + if (arg->kind == Ast_FieldValue) { + Ast *ename = arg->FieldValue.field; + if (!fail && ename->kind != Ast_Ident) { + error(ename, "Expected an identifier for field argument"); + } else if (ename->kind == Ast_Ident) { + name = ename->Ident.token.string; + } + arg = arg->FieldValue.value; + } + + Operand op = {}; + check_expr(c, &op, arg); + if (op.mode == Addressing_Invalid) { + return false; + } + Type *arg_type = base_type(op.type); + if (!is_type_slice(arg_type)) { + gbString s = type_to_string(op.type); + error(op.expr, "Indices to 'soa_zip' must be slices, got %s", s); + gb_string_free(s); + return false; + } + GB_ASSERT(arg_type->kind == Type_Slice); + if (name == "_") { + error(op.expr, "Field argument name '%.*s' is not allowed", LIT(name)); + name = {}; + } + if (name.len == 0) { + gbString field_name = gb_string_make(permanent_allocator(), "_"); + field_name = gb_string_append_fmt(field_name, "%td", types.count); + name = make_string_c(field_name); + } + + + if (string_set_exists(&name_set, name)) { + error(op.expr, "Field argument name '%.*s' already exists", LIT(name)); + } else { + array_add(&types, arg_type->Slice.elem); + array_add(&names, name); + + string_set_add(&name_set, name); + } + } + + + + + Ast *dummy_node_struct = alloc_ast_node(nullptr, Ast_Invalid); + Ast *dummy_node_soa = alloc_ast_node(nullptr, Ast_Invalid); + Scope *s = create_scope(builtin_pkg->scope); + + auto fields = array_make(permanent_allocator(), 0, types.count); + for_array(i, types) { + Type *type = types[i]; + String name = names[i]; + GB_ASSERT(name != ""); + Entity *e = alloc_entity_field(s, make_token_ident(name), type, false, cast(i32)i, EntityState_Resolved); + array_add(&fields, e); + scope_insert(s, e); + } + + Type *elem = nullptr; + if (type_hint != nullptr && is_type_struct(type_hint)) { + Type *soa_type = base_type(type_hint); + if (soa_type->Struct.soa_kind != StructSoa_Slice) { + goto soa_zip_end; + } + Type *soa_elem_type = soa_type->Struct.soa_elem; + Type *et = base_type(soa_elem_type); + if (et->kind != Type_Struct) { + goto soa_zip_end; + } + + if (et->Struct.fields.count != fields.count) { + goto soa_zip_end; + } + if (!fail && first_is_field_value) { + for_array(i, names) { + Selection sel = lookup_field(et, names[i], false); + if (sel.entity == nullptr) { + goto soa_zip_end; + } + if (sel.index.count != 1) { + goto soa_zip_end; + } + if (!are_types_identical(sel.entity->type, types[i])) { + goto soa_zip_end; + } + } + } else { + for_array(i, et->Struct.fields) { + if (!are_types_identical(et->Struct.fields[i]->type, types[i])) { + goto soa_zip_end; + } + } + } + + elem = soa_elem_type; + } + + soa_zip_end:; + + if (elem == nullptr) { + elem = alloc_type_struct(); + elem->Struct.scope = s; + elem->Struct.fields = fields; + elem->Struct.tags = array_make(permanent_allocator(), fields.count); + elem->Struct.node = dummy_node_struct; + type_set_offsets(elem); + } + + Type *soa_type = make_soa_struct_slice(c, dummy_node_soa, nullptr, elem); + type_set_offsets(soa_type); + + operand->type = soa_type; + operand->mode = Addressing_Value; + + break; + } + + case BuiltinProc_soa_unzip: { + if (!build_context.use_llvm_api) { + error(call, "'soa_unzip' is not supported with this backend"); + return false; + } + + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (!is_operand_value(x)) { + error(call, "'soa_unzip' expects an #soa slice"); + return false; + } + Type *t = base_type(x.type); + if (!is_type_soa_struct(t) || t->Struct.soa_kind != StructSoa_Slice) { + gbString s = type_to_string(x.type); + error(call, "'soa_unzip' expects an #soa slice, got %s", s); + gb_string_free(s); + return false; + } + auto types = slice_make(permanent_allocator(), t->Struct.fields.count-1); + for_array(i, types) { + Entity *f = t->Struct.fields[i]; + GB_ASSERT(f->type->kind == Type_Pointer); + types[i] = alloc_type_slice(f->type->Pointer.elem); + } + + operand->type = alloc_type_tuple_from_field_types(types.data, types.count, false, false); + operand->mode = Addressing_Value; + 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 (x.value.value_integer.neg) { + 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_soa_struct: { + Operand x = {}; + Operand y = {}; + x = *operand; + if (!is_type_integer(x.type) || x.mode != Addressing_Constant) { + error(call, "Expected a constant integer for 'intrinsics.soa_struct'"); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + if (x.value.value_integer.neg) { + error(call, "Negative array 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.soa_struct'"); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + Type *elem = y.type; + Type *bt_elem = base_type(elem); + if (!is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) { + gbString str = type_to_string(elem); + error(call, "Invalid type for 'intrinsics.soa_struct', expected a struct or array of length 4 or below, got '%s'", str); + gb_string_free(str); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + + operand->mode = Addressing_Type; + Type *soa_struct = nullptr; + Scope *scope = nullptr; + + if (is_type_array(elem)) { + Type *old_array = base_type(elem); + soa_struct = alloc_type_struct(); + soa_struct->Struct.fields = array_make(heap_allocator(), old_array->Array.count); + soa_struct->Struct.tags = array_make(heap_allocator(), old_array->Array.count); + soa_struct->Struct.node = operand->expr; + soa_struct->Struct.soa_kind = StructSoa_Fixed; + soa_struct->Struct.soa_elem = elem; + soa_struct->Struct.soa_count = count; + + scope = create_scope(c->scope); + soa_struct->Struct.scope = scope; + + String params_xyzw[4] = { + str_lit("x"), + str_lit("y"), + str_lit("z"), + str_lit("w") + }; + + for (i64 i = 0; i < old_array->Array.count; i++) { + Type *array_type = alloc_type_array(old_array->Array.elem, count); + Token token = {}; + token.string = params_xyzw[i]; + + Entity *new_field = alloc_entity_field(scope, token, array_type, false, cast(i32)i); + soa_struct->Struct.fields[i] = new_field; + add_entity(c->checker, scope, nullptr, new_field); + add_entity_use(c, nullptr, new_field); + } + + } else { + GB_ASSERT(is_type_struct(elem)); + + Type *old_struct = base_type(elem); + soa_struct = alloc_type_struct(); + soa_struct->Struct.fields = array_make(heap_allocator(), old_struct->Struct.fields.count); + soa_struct->Struct.tags = array_make(heap_allocator(), old_struct->Struct.tags.count); + soa_struct->Struct.node = operand->expr; + soa_struct->Struct.soa_kind = StructSoa_Fixed; + soa_struct->Struct.soa_elem = elem; + soa_struct->Struct.soa_count = count; + + scope = create_scope(old_struct->Struct.scope->parent); + soa_struct->Struct.scope = scope; + + for_array(i, old_struct->Struct.fields) { + Entity *old_field = old_struct->Struct.fields[i]; + if (old_field->kind == Entity_Variable) { + Type *array_type = alloc_type_array(old_field->type, count); + Entity *new_field = alloc_entity_field(scope, old_field->token, array_type, false, old_field->Variable.field_src_index); + soa_struct->Struct.fields[i] = new_field; + add_entity(c->checker, scope, nullptr, new_field); + } else { + soa_struct->Struct.fields[i] = old_field; + } + + soa_struct->Struct.tags[i] = old_struct->Struct.tags[i]; + } + } + + Token token = {}; + token.string = str_lit("Base_Type"); + Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved); + add_entity(c->checker, scope, nullptr, base_type_entity); + + add_type_info_type(c, soa_struct); + + operand->type = soa_struct; + break; + } + + case BuiltinProc_alloca: + { + Operand sz = {}; + Operand al = {}; + + check_expr(c, &sz, ce->args[0]); + if (sz.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &al, ce->args[1]); + if (al.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &sz, t_int); if (sz.mode == Addressing_Invalid) return false; + convert_to_typed(c, &al, t_int); if (al.mode == Addressing_Invalid) return false; + + if (!is_type_integer(sz.type) || !is_type_integer(al.type)) { + error(operand->expr, "Both parameters to '%.*s' must integers", LIT(builtin_name)); + return false; + } + + if (sz.mode == Addressing_Constant) { + i64 i_sz = exact_value_to_i64(sz.value); + if (i_sz < 0) { + error(sz.expr, "Size parameter to '%.*s' must be non-negative, got %lld", LIT(builtin_name), cast(long long)i_sz); + return false; + } + } + if (al.mode == Addressing_Constant) { + i64 i_al = exact_value_to_i64(al.value); + if (i_al < 0) { + error(al.expr, "Alignment parameter to '%.*s' must be non-negative, got %lld", LIT(builtin_name), cast(long long)i_al); + return false; + } + + if (i_al > 1<<29) { + error(al.expr, "Alignment parameter to '%.*s' must not exceed '1<<29', got %lld", LIT(builtin_name), cast(long long)i_al); + return false; + } + + if (!gb_is_power_of_two(cast(isize)i_al) && i_al != 0) { + error(al.expr, "Alignment parameter to '%.*s' must be a power of 2 or 0, got %lld", LIT(builtin_name), cast(long long)i_al); + return false; + } + } else { + error(al.expr, "Alignment parameter to '%.*s' must be constant", LIT(builtin_name)); + } + + operand->type = t_u8_ptr; + operand->mode = Addressing_Value; + break; + } + + + case BuiltinProc_cpu_relax: + operand->mode = Addressing_NoValue; + break; + + case BuiltinProc_trap: + case BuiltinProc_debug_trap: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + } + operand->mode = Addressing_NoValue; + break; + + case BuiltinProc_read_cycle_counter: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + } + operand->mode = Addressing_Value; + operand->type = t_i64; + break; + + case BuiltinProc_count_ones: + case BuiltinProc_trailing_zeros: + case BuiltinProc_reverse_bits: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + + 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_procs[id].name), xts); + gb_string_free(xts); + } else if (x.type == t_llvm_bool) { + gbString xts = type_to_string(x.type); + error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + } + + operand->mode = Addressing_Value; + operand->type = default_type(x.type); + } + break; + + case BuiltinProc_byte_swap: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + + if (!is_type_integer_like(x.type) && !is_type_float(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) or float, got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + } else if (x.type == t_llvm_bool) { + gbString xts = type_to_string(x.type); + error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + } + i64 sz = type_size_of(x.type); + if (sz < 2) { + gbString xts = type_to_string(x.type); + error(x.expr, "Type passed to '%.*s' must be at least 2 bytes, got %s with size of %lld", LIT(builtin_procs[id].name), xts, sz); + gb_string_free(xts); + } + + operand->mode = Addressing_Value; + operand->type = default_type(x.type); + } + break; + + case BuiltinProc_overflow_add: + case BuiltinProc_overflow_sub: + case BuiltinProc_overflow_mul: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[0]); + check_expr(c, &y, ce->args[1]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (y.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &y, x.type); + convert_to_typed(c, &x, y.type); + if (is_type_untyped(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a typed integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + if (!is_type_integer(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected an integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + Type *ct = core_type(x.type); + if (is_type_different_to_arch_endianness(ct)) { + GB_ASSERT(ct->kind == Type_Basic); + if (ct->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected an integer which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + } + + operand->mode = Addressing_Value; + operand->type = make_optional_ok_type(default_type(x.type), false); // Just reusing this procedure, it's not optional + } + break; + + case BuiltinProc_atomic_fence: + case BuiltinProc_atomic_fence_acq: + case BuiltinProc_atomic_fence_rel: + case BuiltinProc_atomic_fence_acqrel: + operand->mode = Addressing_NoValue; + break; + + case BuiltinProc_volatile_store: + /*fallthrough*/ + case BuiltinProc_atomic_store: + case BuiltinProc_atomic_store_rel: + case BuiltinProc_atomic_store_relaxed: + case BuiltinProc_atomic_store_unordered: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_assignment(c, &x, elem, builtin_name); + + operand->type = nullptr; + operand->mode = Addressing_NoValue; + break; + } + + case BuiltinProc_volatile_load: + /*fallthrough*/ + case BuiltinProc_atomic_load: + case BuiltinProc_atomic_load_acq: + case BuiltinProc_atomic_load_relaxed: + case BuiltinProc_atomic_load_unordered: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + operand->type = elem; + operand->mode = Addressing_Value; + break; + } + + case BuiltinProc_atomic_add: + case BuiltinProc_atomic_add_acq: + case BuiltinProc_atomic_add_rel: + case BuiltinProc_atomic_add_acqrel: + case BuiltinProc_atomic_add_relaxed: + case BuiltinProc_atomic_sub: + case BuiltinProc_atomic_sub_acq: + case BuiltinProc_atomic_sub_rel: + case BuiltinProc_atomic_sub_acqrel: + case BuiltinProc_atomic_sub_relaxed: + case BuiltinProc_atomic_and: + case BuiltinProc_atomic_and_acq: + case BuiltinProc_atomic_and_rel: + case BuiltinProc_atomic_and_acqrel: + case BuiltinProc_atomic_and_relaxed: + case BuiltinProc_atomic_nand: + case BuiltinProc_atomic_nand_acq: + case BuiltinProc_atomic_nand_rel: + case BuiltinProc_atomic_nand_acqrel: + case BuiltinProc_atomic_nand_relaxed: + case BuiltinProc_atomic_or: + case BuiltinProc_atomic_or_acq: + case BuiltinProc_atomic_or_rel: + case BuiltinProc_atomic_or_acqrel: + case BuiltinProc_atomic_or_relaxed: + case BuiltinProc_atomic_xor: + case BuiltinProc_atomic_xor_acq: + case BuiltinProc_atomic_xor_rel: + case BuiltinProc_atomic_xor_acqrel: + case BuiltinProc_atomic_xor_relaxed: + case BuiltinProc_atomic_xchg: + case BuiltinProc_atomic_xchg_acq: + case BuiltinProc_atomic_xchg_rel: + case BuiltinProc_atomic_xchg_acqrel: + case BuiltinProc_atomic_xchg_relaxed: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_assignment(c, &x, elem, builtin_name); + + operand->type = elem; + operand->mode = Addressing_Value; + break; + } + + case BuiltinProc_atomic_cxchg: + case BuiltinProc_atomic_cxchg_acq: + case BuiltinProc_atomic_cxchg_rel: + case BuiltinProc_atomic_cxchg_acqrel: + case BuiltinProc_atomic_cxchg_relaxed: + case BuiltinProc_atomic_cxchg_failrelaxed: + case BuiltinProc_atomic_cxchg_failacq: + case BuiltinProc_atomic_cxchg_acq_failrelaxed: + case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: + + case BuiltinProc_atomic_cxchgweak: + case BuiltinProc_atomic_cxchgweak_acq: + case BuiltinProc_atomic_cxchgweak_rel: + case BuiltinProc_atomic_cxchgweak_acqrel: + case BuiltinProc_atomic_cxchgweak_relaxed: + case BuiltinProc_atomic_cxchgweak_failrelaxed: + case BuiltinProc_atomic_cxchgweak_failacq: + case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: + case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + Operand y = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_expr_with_type_hint(c, &y, ce->args[2], elem); + check_assignment(c, &x, elem, builtin_name); + check_assignment(c, &y, elem, builtin_name); + + operand->mode = Addressing_Value; + operand->type = make_optional_ok_type(elem, /*typed*/false); + break; + } + break; + + case BuiltinProc_fixed_point_mul: + case BuiltinProc_fixed_point_div: + case BuiltinProc_fixed_point_mul_sat: + case BuiltinProc_fixed_point_div_sat: + { + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + + 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; + } + convert_to_typed(c, &x, y.type); + if (x.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &y, x.type); + if (x.mode == Addressing_Invalid) { + return false; + } + if (!are_types_identical(x.type, y.type)) { + gbString xts = type_to_string(x.type); + gbString yts = type_to_string(y.type); + error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts); + gb_string_free(yts); + gb_string_free(xts); + return false; + } + + if (!is_type_integer(x.type) || is_type_untyped(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + + check_expr(c, &z, ce->args[2]); + if (z.mode == Addressing_Invalid) { + return false; + } + if (z.mode != Addressing_Constant || !is_type_integer(z.type)) { + error(z.expr, "Expected a constant integer for the scale in '%.*s'", LIT(builtin_procs[id].name)); + return false; + } + i64 n = exact_value_to_i64(z.value); + if (n <= 0) { + error(z.expr, "Scale parameter in '%.*s' must be positive, got %lld", LIT(builtin_procs[id].name), n); + return false; + } + i64 sz = 8*type_size_of(x.type); + if (n > sz) { + error(z.expr, "Scale parameter in '%.*s' is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_procs[id].name), n, sz); + return false; + } + + operand->type = x.type; + operand->mode = Addressing_Value; + } + break; + + + case BuiltinProc_expect: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[0]); + check_expr(c, &y, ce->args[1]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (y.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &y, x.type); + convert_to_typed(c, &x, y.type); + if (!are_types_identical(x.type, y.type)) { + gbString xts = type_to_string(x.type); + gbString yts = type_to_string(y.type); + error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts); + gb_string_free(yts); + gb_string_free(xts); + *operand = x; // minimize error propagation + return true; + } + + 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_procs[id].name), xts); + gb_string_free(xts); + *operand = x; + return true; + } + + if (y.mode != Addressing_Constant) { + error(y.expr, "Second argument to '%.*s' must be constant as it is the expected value", LIT(builtin_procs[id].name)); + } + + if (x.mode == Addressing_Constant) { + // NOTE(bill): just completely ignore this intrinsic entirely + *operand = x; + return true; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + } + break; + + + + + case BuiltinProc_type_base_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + operand->type = base_type(operand->type); + } + operand->mode = Addressing_Type; + break; + case BuiltinProc_type_core_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + operand->type = core_type(operand->type); + } + operand->mode = Addressing_Type; + break; + case BuiltinProc_type_elem_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + switch (bt->kind) { + case Type_Basic: + switch (bt->Basic.kind) { + case Basic_complex64: operand->type = t_f32; break; + case Basic_complex128: operand->type = t_f64; break; + case Basic_quaternion128: operand->type = t_f32; break; + case Basic_quaternion256: operand->type = t_f64; break; + } + break; + case Type_Pointer: operand->type = bt->Pointer.elem; break; + case Type_Array: operand->type = bt->Array.elem; break; + case Type_EnumeratedArray: operand->type = bt->EnumeratedArray.elem; break; + case Type_Slice: operand->type = bt->Slice.elem; break; + case Type_DynamicArray: operand->type = bt->DynamicArray.elem; break; + } + } + operand->mode = Addressing_Type; + break; + + + case BuiltinProc_type_is_boolean: + case BuiltinProc_type_is_integer: + case BuiltinProc_type_is_rune: + case BuiltinProc_type_is_float: + case BuiltinProc_type_is_complex: + case BuiltinProc_type_is_quaternion: + case BuiltinProc_type_is_string: + case BuiltinProc_type_is_typeid: + case BuiltinProc_type_is_any: + case BuiltinProc_type_is_endian_little: + case BuiltinProc_type_is_endian_big: + case BuiltinProc_type_is_unsigned: + case BuiltinProc_type_is_numeric: + case BuiltinProc_type_is_ordered: + case BuiltinProc_type_is_ordered_numeric: + case BuiltinProc_type_is_indexable: + case BuiltinProc_type_is_sliceable: + case BuiltinProc_type_is_comparable: + case BuiltinProc_type_is_simple_compare: + case BuiltinProc_type_is_dereferenceable: + case BuiltinProc_type_is_valid_map_key: + case BuiltinProc_type_is_named: + case BuiltinProc_type_is_pointer: + case BuiltinProc_type_is_array: + case BuiltinProc_type_is_slice: + case BuiltinProc_type_is_dynamic_array: + case BuiltinProc_type_is_map: + case BuiltinProc_type_is_struct: + case BuiltinProc_type_is_union: + case BuiltinProc_type_is_enum: + case BuiltinProc_type_is_proc: + case BuiltinProc_type_is_bit_field: + case BuiltinProc_type_is_bit_field_value: + case BuiltinProc_type_is_bit_set: + case BuiltinProc_type_is_simd_vector: + case BuiltinProc_type_is_specialized_polymorphic_record: + case BuiltinProc_type_is_unspecialized_polymorphic_record: + case BuiltinProc_type_has_nil: + GB_ASSERT(BuiltinProc__type_simple_boolean_begin < id && id < BuiltinProc__type_simple_boolean_end); + + operand->value = exact_value_bool(false); + if (operand->mode != Addressing_Type) { + gbString str = expr_to_string(ce->args[0]); + error(operand->expr, "Expected a type for '%.*s', got '%s'", LIT(builtin_name), str); + gb_string_free(str); + } else { + i32 i = id - cast(i32)BuiltinProc__type_simple_boolean_begin; + auto procedure = builtin_type_is_procs[i]; + GB_ASSERT_MSG(procedure != nullptr, "%.*s", LIT(builtin_name)); + bool ok = procedure(operand->type); + operand->value = exact_value_bool(ok); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + + case BuiltinProc_type_has_field: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + check_expr(c, &x, ce->args[1]); + + if (!is_type_string(x.type) || x.mode != Addressing_Constant || x.value.kind != ExactValue_String) { + error(ce->args[1], "Expected a const string for field argument"); + return false; + } + + String field_name = x.value.value_string; + + Selection sel = lookup_field(type, field_name, false); + operand->mode = Addressing_Constant; + operand->value = exact_value_bool(sel.index.count != 0); + operand->type = t_untyped_bool; + + break; + } + break; + + case BuiltinProc_type_is_specialization_of: + { + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + Type *t = operand->type; + Type *s = nullptr; + + bool prev_ips = c->in_polymorphic_specialization; + c->in_polymorphic_specialization = true; + s = check_type(c, ce->args[1]); + c->in_polymorphic_specialization = prev_ips; + + if (s == t_invalid) { + error(ce->args[1], "Invalid specialization type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + operand->value = exact_value_bool(check_type_specialization_to(c, s, t, false, false)); + + } + break; + + case BuiltinProc_type_struct_field_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a struct type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_struct(operand->type)) { + error(operand->expr, "Expected a struct type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Struct.fields.count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; + + case BuiltinProc_type_proc_parameter_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Proc.param_count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; + case BuiltinProc_type_proc_return_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Proc.result_count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; + + case BuiltinProc_type_proc_parameter_type: + if (operand->mode != Addressing_Type || !is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + return false; + } else { + if (is_type_polymorphic(operand->type)) { + error(operand->expr, "Expected a non-polymorphic procedure type for '%.*s'", LIT(builtin_name)); + return false; + } + + Operand op = {}; + check_expr(c, &op, ce->args[1]); + if (op.mode != Addressing_Constant && !is_type_integer(op.type)) { + error(op.expr, "Expected a constant integer for the index of procedure parameter value"); + return false; + } + + i64 index = exact_value_to_i64(op.value); + if (index < 0) { + error(op.expr, "Expected a non-negative integer for the index of procedure parameter value, got %lld", cast(long long)index); + return false; + } + + Entity *param = nullptr; + i64 count = 0; + + Type *bt = base_type(operand->type); + if (bt->kind == Type_Proc) { + count = bt->Proc.param_count; + if (index < count) { + param = bt->Proc.params->Tuple.variables[index]; + } + } + + if (index >= count) { + error(op.expr, "Index of procedure parameter value out of bounds, expected 0..<%lld, got %lld", cast(long long)count, cast(long long)index); + return false; + } + GB_ASSERT(param != nullptr); + switch (param->kind) { + case Entity_Constant: + operand->mode = Addressing_Constant; + operand->type = param->type; + operand->value = param->Constant.value; + break; + case Entity_TypeName: + case Entity_Variable: + operand->mode = Addressing_Type; + operand->type = param->type; + break; + default: + GB_PANIC("Unhandled procedure entity type %d", param->kind); + break; + } + + } + + break; + + case BuiltinProc_type_proc_return_type: + if (operand->mode != Addressing_Type || !is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + return false; + } else { + if (is_type_polymorphic(operand->type)) { + error(operand->expr, "Expected a non-polymorphic procedure type for '%.*s'", LIT(builtin_name)); + return false; + } + + Operand op = {}; + check_expr(c, &op, ce->args[1]); + if (op.mode != Addressing_Constant && !is_type_integer(op.type)) { + error(op.expr, "Expected a constant integer for the index of procedure parameter value"); + return false; + } + + i64 index = exact_value_to_i64(op.value); + if (index < 0) { + error(op.expr, "Expected a non-negative integer for the index of procedure parameter value, got %lld", cast(long long)index); + return false; + } + + Entity *param = nullptr; + i64 count = 0; + + Type *bt = base_type(operand->type); + if (bt->kind == Type_Proc) { + count = bt->Proc.result_count; + if (index < count) { + param = bt->Proc.results->Tuple.variables[index]; + } + } + + if (index >= count) { + error(op.expr, "Index of procedure parameter value out of bounds, expected 0..<%lld, got %lld", cast(long long)count, cast(long long)index); + return false; + } + GB_ASSERT(param != nullptr); + switch (param->kind) { + case Entity_Constant: + operand->mode = Addressing_Constant; + operand->type = param->type; + operand->value = param->Constant.value; + break; + case Entity_TypeName: + case Entity_Variable: + operand->mode = Addressing_Type; + operand->type = param->type; + break; + default: + GB_PANIC("Unhandled procedure entity type %d", param->kind); + break; + } + + } + + break; + + case BuiltinProc_type_polymorphic_record_parameter_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + if (bt->kind == Type_Struct) { + if (bt->Struct.polymorphic_params != nullptr) { + operand->value = exact_value_i64(bt->Struct.polymorphic_params->Tuple.variables.count); + } + } else if (bt->kind == Type_Union) { + if (bt->Union.polymorphic_params != nullptr) { + operand->value = exact_value_i64(bt->Union.polymorphic_params->Tuple.variables.count); + } + } else { + error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name)); + } + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; + case BuiltinProc_type_polymorphic_record_parameter_value: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name)); + return false; + } else if (!is_type_polymorphic_record_specialized(operand->type)) { + error(operand->expr, "Expected a specialized polymorphic record type for '%.*s'", LIT(builtin_name)); + return false; + } else { + Operand op = {}; + check_expr(c, &op, ce->args[1]); + if (op.mode != Addressing_Constant && !is_type_integer(op.type)) { + error(op.expr, "Expected a constant integer for the index of record parameter value"); + return false; + } + + i64 index = exact_value_to_i64(op.value); + if (index < 0) { + error(op.expr, "Expected a non-negative integer for the index of record parameter value, got %lld", cast(long long)index); + return false; + } + + Entity *param = nullptr; + i64 count = 0; + + Type *bt = base_type(operand->type); + if (bt->kind == Type_Struct) { + if (bt->Struct.polymorphic_params != nullptr) { + count = bt->Struct.polymorphic_params->Tuple.variables.count; + if (index < count) { + param = bt->Struct.polymorphic_params->Tuple.variables[cast(isize)index]; + } + } + } else if (bt->kind == Type_Union) { + if (bt->Union.polymorphic_params != nullptr) { + count = bt->Union.polymorphic_params->Tuple.variables.count; + if (index < count) { + param = bt->Union.polymorphic_params->Tuple.variables[cast(isize)index]; + } + } + } else { + error(operand->expr, "Expected a specialized polymorphic record type for '%.*s'", LIT(builtin_name)); + return false; + } + + if (index >= count) { + error(op.expr, "Index of record parameter value out of bounds, expected 0..<%lld, got %lld", cast(long long)count, cast(long long)index); + return false; + } + GB_ASSERT(param != nullptr); + switch (param->kind) { + case Entity_Constant: + operand->mode = Addressing_Constant; + operand->type = param->type; + operand->value = param->Constant.value; + break; + case Entity_TypeName: + operand->mode = Addressing_Type; + operand->type = param->type; + break; + default: + GB_PANIC("Unhandled polymorphic record type"); + break; + } + + } + + break; + + case BuiltinProc_type_field_index_of: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + check_expr(c, &x, ce->args[1]); + + if (!is_type_string(x.type) || x.mode != Addressing_Constant || x.value.kind != ExactValue_String) { + error(ce->args[1], "Expected a const string for field argument"); + return false; + } + + String field_name = x.value.value_string; + + Selection sel = lookup_field(type, field_name, false); + if (sel.entity == nullptr) { + gbString type_str = type_to_string(bt); + error(ce->args[0], + "'%s' has no field named '%.*s'", type_str, LIT(field_name)); + gb_string_free(type_str); + return false; + } + if (sel.indirect) { + gbString type_str = type_to_string(bt); + error(ce->args[0], + "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str); + gb_string_free(type_str); + return false; + } + + operand->mode = Addressing_Constant; + operand->value = exact_value_u64(sel.index[0]); + operand->type = t_uintptr; + break; + } + break; + + case BuiltinProc_type_equal_proc: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + if (!is_type_comparable(type)) { + gbString t = type_to_string(type); + error(ce->args[0], "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_equal_proc; + break; + } + + case BuiltinProc_type_hasher_proc: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + if (!is_type_valid_for_keys(type)) { + gbString t = type_to_string(type); + error(ce->args[0], "Expected a valid type for map keys for '%.*s', got %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + add_map_key_type_dependencies(c, type); + + operand->mode = Addressing_Value; + operand->type = t_hasher_proc; + break; + } + } + + return true; +} -- cgit v1.2.3 From cb2e6ea31db90ca80314e5ff8ce8f43371fade7c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 25 Apr 2021 20:03:05 +0100 Subject: Remove `use_llvm_api` related checks and other related things --- core/runtime/internal.odin | 40 ++++++++++------------------------------ core/sys/cpu/cpu.odin | 2 -- src/build_settings.cpp | 8 -------- src/check_builtin.cpp | 40 ---------------------------------------- src/check_expr.cpp | 4 ---- src/check_stmt.cpp | 3 --- src/checker.cpp | 24 +++--------------------- src/main.cpp | 1 - 8 files changed, 13 insertions(+), 109 deletions(-) (limited to 'src/check_builtin.cpp') diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 3c05fb6a7..e2f8c7287 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -107,22 +107,12 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { } // NOTE(bill): This _must_ be implemented like C's memmove foreign _ { - when ODIN_USE_LLVM_API { - when size_of(rawptr) == 8 { - @(link_name="llvm.memmove.p0i8.p0i8.i64") - llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; - } else { - @(link_name="llvm.memmove.p0i8.p0i8.i32") - llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; - } + when size_of(rawptr) == 8 { + @(link_name="llvm.memmove.p0i8.p0i8.i64") + llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } else { - when size_of(rawptr) == 8 { - @(link_name="llvm.memmove.p0i8.p0i8.i64") - llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; - } else { - @(link_name="llvm.memmove.p0i8.p0i8.i32") - llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; - } + @(link_name="llvm.memmove.p0i8.p0i8.i32") + llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } } llvm_memmove(dst, src, len); @@ -135,22 +125,12 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r } // NOTE(bill): This _must_ be implemented like C's memcpy foreign _ { - when ODIN_USE_LLVM_API { - when size_of(rawptr) == 8 { - @(link_name="llvm.memcpy.p0i8.p0i8.i64") - llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; - } else { - @(link_name="llvm.memcpy.p0i8.p0i8.i32") - llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; - } + when size_of(rawptr) == 8 { + @(link_name="llvm.memcpy.p0i8.p0i8.i64") + llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } else { - when size_of(rawptr) == 8 { - @(link_name="llvm.memcpy.p0i8.p0i8.i64") - llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; - } else { - @(link_name="llvm.memcpy.p0i8.p0i8.i32") - llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; - } + @(link_name="llvm.memcpy.p0i8.p0i8.i32") + llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } } llvm_memcpy(dst, src, len); diff --git a/core/sys/cpu/cpu.odin b/core/sys/cpu/cpu.odin index b6f770aed..b99fe01d8 100644 --- a/core/sys/cpu/cpu.odin +++ b/core/sys/cpu/cpu.odin @@ -1,7 +1,5 @@ package sys_cpu -#assert(ODIN_USE_LLVM_API); - Cache_Line_Pad :: struct {_: [_cache_line_size]byte}; initialized: bool; diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 0be18a0d4..461b1610c 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -204,8 +204,6 @@ struct BuildContext { bool ignore_warnings; bool warnings_as_errors; - bool use_llvm_api; - bool use_subsystem_windows; bool ignore_microsoft_magic; bool linker_map_file; @@ -782,8 +780,6 @@ void init_build_context(TargetMetrics *cross_target) { bc->link_flags = str_lit(" "); bc->opt_flags = str_lit(" "); - bc->use_llvm_api = true; - gbString llc_flags = gb_string_make_reserve(heap_allocator(), 64); if (bc->ODIN_DEBUG) { @@ -841,10 +837,6 @@ void init_build_context(TargetMetrics *cross_target) { bc->link_flags = str_lit("-arch arm64 "); break; } - if ((bc->command_kind & Command__does_build) != 0 && !bc->use_llvm_api) { - gb_printf_err("The arm64 architecture is only supported with -llvm-api\n");; - gb_exit(1); - } } else if (bc->metrics.arch == TargetArch_wasm32) { bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined "); diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 5f4411b90..94e8dcee6 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -614,9 +614,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } else if (type->kind == Type_SimdVector) { max_count = type->SimdVector.count; elem_type = type->SimdVector.elem; - if (!build_context.use_llvm_api) { - error(call, "'swizzle' with #simd vector is not supported on this backend"); - } } i64 arg_count = 0; @@ -1529,11 +1526,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } case BuiltinProc_soa_zip: { - if (!build_context.use_llvm_api) { - error(call, "'soa_zip' is not supported with this backend"); - return false; - } - auto types = array_make(temporary_allocator(), 0, ce->args.count); auto names = array_make(temporary_allocator(), 0, ce->args.count); @@ -1681,11 +1673,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } case BuiltinProc_soa_unzip: { - if (!build_context.use_llvm_api) { - error(call, "'soa_unzip' is not supported with this backend"); - return false; - } - Operand x = {}; check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { @@ -1927,16 +1914,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_trap: case BuiltinProc_debug_trap: - if (!build_context.use_llvm_api) { - error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); - } operand->mode = Addressing_NoValue; break; case BuiltinProc_read_cycle_counter: - if (!build_context.use_llvm_api) { - error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); - } operand->mode = Addressing_Value; operand->type = t_i64; break; @@ -1944,10 +1925,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_count_ones: case BuiltinProc_trailing_zeros: case BuiltinProc_reverse_bits: - if (!build_context.use_llvm_api) { - error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); - // continue anyway - } { Operand x = {}; check_expr(c, &x, ce->args[0]); @@ -1971,10 +1948,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; case BuiltinProc_byte_swap: - if (!build_context.use_llvm_api) { - error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); - // continue anyway - } { Operand x = {}; check_expr(c, &x, ce->args[0]); @@ -2006,10 +1979,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_overflow_add: case BuiltinProc_overflow_sub: case BuiltinProc_overflow_mul: - if (!build_context.use_llvm_api) { - error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); - // continue anyway - } { Operand x = {}; Operand y = {}; @@ -2189,11 +2158,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_fixed_point_mul_sat: case BuiltinProc_fixed_point_div_sat: { - if (!build_context.use_llvm_api) { - error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); - // continue anyway - } - Operand x = {}; Operand y = {}; Operand z = {}; @@ -2255,10 +2219,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_expect: - if (!build_context.use_llvm_api) { - error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); - // continue anyway - } { Operand x = {}; Operand y = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 61cdf7822..2c83565a4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7903,10 +7903,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type error(node, "Inline asm expressions are only allowed within a procedure body"); } - if (!build_context.use_llvm_api) { - error(node, "Inline asm expressions are only currently allowed with -llvm-api"); - } - auto param_types = array_make(heap_allocator(), ia->param_types.count); Type *return_type = nullptr; for_array(i, ia->param_types) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index cad2be85b..a3c9a529c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1795,9 +1795,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->Struct.soa_elem); array_add(&vals, t_int); - if (!build_context.use_llvm_api) { - error(operand.expr, "#soa structures do not yet support for in loop iteration"); - } } break; } diff --git a/src/checker.cpp b/src/checker.cpp index 5a2203cfb..ecd9b5ea4 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -755,7 +755,6 @@ void init_universal(void) { add_global_constant(str_lit("ODIN_DEBUG"), t_untyped_bool, exact_value_bool(bc->ODIN_DEBUG)); add_global_constant(str_lit("ODIN_DISABLE_ASSERT"), t_untyped_bool, exact_value_bool(bc->ODIN_DISABLE_ASSERT)); add_global_constant(str_lit("ODIN_DEFAULT_TO_NIL_ALLOCATOR"), t_untyped_bool, exact_value_bool(bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR)); - add_global_constant(str_lit("ODIN_USE_LLVM_API"), t_untyped_bool, exact_value_bool(bc->use_llvm_api)); add_global_constant(str_lit("ODIN_NO_DYNAMIC_LITERALS"), t_untyped_bool, exact_value_bool(bc->no_dynamic_literals)); add_global_constant(str_lit("ODIN_TEST"), t_untyped_bool, exact_value_bool(bc->command_kind == Command_test)); @@ -1774,23 +1773,6 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]); } - if (!build_context.use_llvm_api) { - String other_required_runtime_entities[] = { - str_lit("bswap_16"), - str_lit("bswap_32"), - str_lit("bswap_64"), - str_lit("bswap_128"), - - str_lit("bswap_f16"), - str_lit("bswap_f32"), - str_lit("bswap_f64"), - }; - - for (isize i = 0; i < gb_count_of(other_required_runtime_entities); i++) { - force_add_dependency_entity(c, c->info.runtime_package->scope, other_required_runtime_entities[i]); - } - } - if (build_context.no_crt) { String required_no_crt_entities[] = { // NOTE(bill): Only if these exist @@ -2741,7 +2723,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) { } } - if (valid && build_context.use_llvm_api) { + if (valid) { if (ac->atom_op_table == nullptr) { ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); } @@ -2800,7 +2782,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) { } } - if (valid && build_context.use_llvm_api) { + if (valid) { if (ac->atom_op_table == nullptr) { ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); } @@ -2882,7 +2864,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) { } } - if (valid && build_context.use_llvm_api) { + if (valid) { if (ac->atom_op_table == nullptr) { ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); } diff --git a/src/main.cpp b/src/main.cpp index 02a9b50f7..d2f84b29d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1185,7 +1185,6 @@ bool parse_build_flags(Array args) { case BuildFlag_UseLLVMApi: gb_printf_err("-llvm-api flag is not required any more\n"); - build_context.use_llvm_api = true; bad_flags = true; break; -- cgit v1.2.3 From 72aa0e6e3891c034863476751b2aefda781de5b2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 25 Apr 2021 20:22:26 +0100 Subject: Replace many `foreign` llvm calls with intrinsics --- core/math/bits/bits.odin | 160 +++++-------------------------------- core/runtime/core.odin | 17 ++-- core/runtime/core_builtin.odin | 17 ++-- core/runtime/internal.odin | 45 +++-------- core/runtime/internal_linux.odin | 10 +-- core/runtime/internal_windows.odin | 10 +-- core/runtime/udivmod128.odin | 32 +------- core/time/time.odin | 8 +- src/check_builtin.cpp | 1 + src/checker_builtin_procs.hpp | 2 + src/llvm_backend.cpp | 22 +++++ src/llvm_backend.hpp | 1 + 12 files changed, 78 insertions(+), 247 deletions(-) (limited to 'src/check_builtin.cpp') diff --git a/core/math/bits/bits.odin b/core/math/bits/bits.odin index 2ab1de6e7..303a94c1d 100644 --- a/core/math/bits/bits.odin +++ b/core/math/bits/bits.odin @@ -1,5 +1,6 @@ package math_bits +import "intrinsics" import "core:runtime" U8_MIN :: 0; @@ -22,32 +23,10 @@ I16_MAX :: 1 << 15 - 1; I32_MAX :: 1 << 31 - 1; I64_MAX :: 1 << 63 - 1; -@(default_calling_convention="none") -foreign { - @(link_name="llvm.ctpop.i8") count_ones8 :: proc(i: u8) -> u8 --- - @(link_name="llvm.ctpop.i16") count_ones16 :: proc(i: u16) -> u16 --- - @(link_name="llvm.ctpop.i32") count_ones32 :: proc(i: u32) -> u32 --- - @(link_name="llvm.ctpop.i64") count_ones64 :: proc(i: u64) -> u64 --- - @(link_name="llvm.cttz.i8") trailing_zeros8 :: proc(i: u8, is_zero_undef := false) -> u8 --- - @(link_name="llvm.cttz.i16") trailing_zeros16 :: proc(i: u16, is_zero_undef := false) -> u16 --- - @(link_name="llvm.cttz.i32") trailing_zeros32 :: proc(i: u32, is_zero_undef := false) -> u32 --- - @(link_name="llvm.cttz.i64") trailing_zeros64 :: proc(i: u64, is_zero_undef := false) -> u64 --- - - @(link_name="llvm.bitreverse.i8") reverse_bits8 :: proc(i: u8) -> u8 --- - @(link_name="llvm.bitreverse.i16") reverse_bits16 :: proc(i: u16) -> u16 --- - @(link_name="llvm.bitreverse.i32") reverse_bits32 :: proc(i: u32) -> u32 --- - @(link_name="llvm.bitreverse.i64") reverse_bits64 :: proc(i: u64) -> u64 --- -} - - -trailing_zeros_uint :: proc(i: uint) -> uint { - when size_of(uint) == size_of(u64) { - return uint(trailing_zeros64(u64(i))); - } else { - return uint(trailing_zeros32(u32(i))); - } -} +count_ones :: intrinsics.count_ones; +trailing_zeros :: intrinsics.trailing_zeros; +reverse_bits :: intrinsics.reverse_bits; leading_zeros_u8 :: proc(i: u8) -> int { @@ -117,10 +96,17 @@ byte_swap :: proc{ byte_swap_int, }; -count_zeros8 :: proc(i: u8) -> u8 { return 8 - count_ones8(i); } -count_zeros16 :: proc(i: u16) -> u16 { return 16 - count_ones16(i); } -count_zeros32 :: proc(i: u32) -> u32 { return 32 - count_ones32(i); } -count_zeros64 :: proc(i: u64) -> u64 { return 64 - count_ones64(i); } +count_zeros8 :: proc(i: u8) -> u8 { return 8 - count_ones(i); } +count_zeros16 :: proc(i: u16) -> u16 { return 16 - count_ones(i); } +count_zeros32 :: proc(i: u32) -> u32 { return 32 - count_ones(i); } +count_zeros64 :: proc(i: u64) -> u64 { return 64 - count_ones(i); } + +count_zeros :: proc{ + count_zeros8, + count_zeros16, + count_zeros32, + count_zeros64, +}; rotate_left8 :: proc(x: u8, k: int) -> u8 { @@ -176,120 +162,10 @@ to_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } } -@(default_calling_convention="none") -foreign { - @(link_name="llvm.uadd.with.overflow.i8") overflowing_add_u8 :: proc(lhs, rhs: u8) -> (u8, bool) --- - @(link_name="llvm.sadd.with.overflow.i8") overflowing_add_i8 :: proc(lhs, rhs: i8) -> (i8, bool) --- - @(link_name="llvm.uadd.with.overflow.i16") overflowing_add_u16 :: proc(lhs, rhs: u16) -> (u16, bool) --- - @(link_name="llvm.sadd.with.overflow.i16") overflowing_add_i16 :: proc(lhs, rhs: i16) -> (i16, bool) --- - @(link_name="llvm.uadd.with.overflow.i32") overflowing_add_u32 :: proc(lhs, rhs: u32) -> (u32, bool) --- - @(link_name="llvm.sadd.with.overflow.i32") overflowing_add_i32 :: proc(lhs, rhs: i32) -> (i32, bool) --- - @(link_name="llvm.uadd.with.overflow.i64") overflowing_add_u64 :: proc(lhs, rhs: u64) -> (u64, bool) --- - @(link_name="llvm.sadd.with.overflow.i64") overflowing_add_i64 :: proc(lhs, rhs: i64) -> (i64, bool) --- -} - -overflowing_add_uint :: proc(lhs, rhs: uint) -> (uint, bool) { - when size_of(uint) == size_of(u32) { - x, ok := overflowing_add_u32(u32(lhs), u32(rhs)); - return uint(x), ok; - } else { - x, ok := overflowing_add_u64(u64(lhs), u64(rhs)); - return uint(x), ok; - } -} -overflowing_add_int :: proc(lhs, rhs: int) -> (int, bool) { - when size_of(int) == size_of(i32) { - x, ok := overflowing_add_i32(i32(lhs), i32(rhs)); - return int(x), ok; - } else { - x, ok := overflowing_add_i64(i64(lhs), i64(rhs)); - return int(x), ok; - } -} - -overflowing_add :: proc{ - overflowing_add_u8, overflowing_add_i8, - overflowing_add_u16, overflowing_add_i16, - overflowing_add_u32, overflowing_add_i32, - overflowing_add_u64, overflowing_add_i64, - overflowing_add_uint, overflowing_add_int, -}; -@(default_calling_convention="none") -foreign { - @(link_name="llvm.usub.with.overflow.i8") overflowing_sub_u8 :: proc(lhs, rhs: u8) -> (u8, bool) --- - @(link_name="llvm.ssub.with.overflow.i8") overflowing_sub_i8 :: proc(lhs, rhs: i8) -> (i8, bool) --- - @(link_name="llvm.usub.with.overflow.i16") overflowing_sub_u16 :: proc(lhs, rhs: u16) -> (u16, bool) --- - @(link_name="llvm.ssub.with.overflow.i16") overflowing_sub_i16 :: proc(lhs, rhs: i16) -> (i16, bool) --- - @(link_name="llvm.usub.with.overflow.i32") overflowing_sub_u32 :: proc(lhs, rhs: u32) -> (u32, bool) --- - @(link_name="llvm.ssub.with.overflow.i32") overflowing_sub_i32 :: proc(lhs, rhs: i32) -> (i32, bool) --- - @(link_name="llvm.usub.with.overflow.i64") overflowing_sub_u64 :: proc(lhs, rhs: u64) -> (u64, bool) --- - @(link_name="llvm.ssub.with.overflow.i64") overflowing_sub_i64 :: proc(lhs, rhs: i64) -> (i64, bool) --- -} -overflowing_sub_uint :: proc(lhs, rhs: uint) -> (uint, bool) { - when size_of(uint) == size_of(u32) { - x, ok := overflowing_sub_u32(u32(lhs), u32(rhs)); - return uint(x), ok; - } else { - x, ok := overflowing_sub_u64(u64(lhs), u64(rhs)); - return uint(x), ok; - } -} -overflowing_sub_int :: proc(lhs, rhs: int) -> (int, bool) { - when size_of(int) == size_of(i32) { - x, ok := overflowing_sub_i32(i32(lhs), i32(rhs)); - return int(x), ok; - } else { - x, ok := overflowing_sub_i64(i64(lhs), i64(rhs)); - return int(x), ok; - } -} - -overflowing_sub :: proc{ - overflowing_sub_u8, overflowing_sub_i8, - overflowing_sub_u16, overflowing_sub_i16, - overflowing_sub_u32, overflowing_sub_i32, - overflowing_sub_u64, overflowing_sub_i64, - overflowing_sub_uint, overflowing_sub_int, -}; - -@(default_calling_convention="none") -foreign { - @(link_name="llvm.umul.with.overflow.i8") overflowing_mul_u8 :: proc(lhs, rhs: u8) -> (u8, bool) --- - @(link_name="llvm.smul.with.overflow.i8") overflowing_mul_i8 :: proc(lhs, rhs: i8) -> (i8, bool) --- - @(link_name="llvm.umul.with.overflow.i16") overflowing_mul_u16 :: proc(lhs, rhs: u16) -> (u16, bool) --- - @(link_name="llvm.smul.with.overflow.i16") overflowing_mul_i16 :: proc(lhs, rhs: i16) -> (i16, bool) --- - @(link_name="llvm.umul.with.overflow.i32") overflowing_mul_u32 :: proc(lhs, rhs: u32) -> (u32, bool) --- - @(link_name="llvm.smul.with.overflow.i32") overflowing_mul_i32 :: proc(lhs, rhs: i32) -> (i32, bool) --- - @(link_name="llvm.umul.with.overflow.i64") overflowing_mul_u64 :: proc(lhs, rhs: u64) -> (u64, bool) --- - @(link_name="llvm.smul.with.overflow.i64") overflowing_mul_i64 :: proc(lhs, rhs: i64) -> (i64, bool) --- -} -overflowing_mul_uint :: proc(lhs, rhs: uint) -> (uint, bool) { - when size_of(uint) == size_of(u32) { - x, ok := overflowing_mul_u32(u32(lhs), u32(rhs)); - return uint(x), ok; - } else { - x, ok := overflowing_mul_u64(u64(lhs), u64(rhs)); - return uint(x), ok; - } -} -overflowing_mul_int :: proc(lhs, rhs: int) -> (int, bool) { - when size_of(int) == size_of(i32) { - x, ok := overflowing_mul_i32(i32(lhs), i32(rhs)); - return int(x), ok; - } else { - x, ok := overflowing_mul_i64(i64(lhs), i64(rhs)); - return int(x), ok; - } -} - -overflowing_mul :: proc{ - overflowing_mul_u8, overflowing_mul_i8, - overflowing_mul_u16, overflowing_mul_i16, - overflowing_mul_u32, overflowing_mul_i32, - overflowing_mul_u64, overflowing_mul_i64, - overflowing_mul_uint, overflowing_mul_int, -}; +overflowing_add :: intrinsics.overflow_add; +overflowing_sub :: intrinsics.overflow_sub; +overflowing_mul :: intrinsics.overflow_mul; len_u8 :: proc(x: u8) -> int { diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 78d43b65a..0033aad9a 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -20,6 +20,8 @@ // package runtime +import "intrinsics" + // NOTE(bill): This must match the compiler's Calling_Convention :: enum u8 { Invalid = 0, @@ -430,17 +432,9 @@ typeid_base_without_enum :: typeid_core; -@(default_calling_convention = "none") -foreign { - @(link_name="llvm.debugtrap") - debug_trap :: proc() ---; - - @(link_name="llvm.trap") - trap :: proc() -> ! ---; - - @(link_name="llvm.readcyclecounter") - read_cycle_counter :: proc() -> u64 ---; -} +debug_trap :: intrinsics.debug_trap; +trap :: intrinsics.trap; +read_cycle_counter :: intrinsics.read_cycle_counter; @@ -488,7 +482,6 @@ __init_context :: proc "contextless" (c: ^Context) { c.logger.data = nil; } - default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) { print_caller_location(loc); print_string(" "); diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 6656c16a0..237ad0670 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -1,5 +1,7 @@ package runtime +import "intrinsics" + @builtin Maybe :: union(T: typeid) #maybe {T}; @@ -539,20 +541,15 @@ excl_bit_set :: proc(s: ^$S/bit_set[$E; $U], other: S) { @builtin card :: proc(s: $S/bit_set[$E; $U]) -> int { when size_of(S) == 1 { - foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- } - return int(count_ones(transmute(u8)s)); + return int(intrinsics.count_ones(transmute(u8)s)); } else when size_of(S) == 2 { - foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- } - return int(count_ones(transmute(u16)s)); + return int(intrinsics.count_ones(transmute(u16)s)); } else when size_of(S) == 4 { - foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- } - return int(count_ones(transmute(u32)s)); + return int(intrinsics.count_ones(transmute(u32)s)); } else when size_of(S) == 8 { - foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- } - return int(count_ones(transmute(u64)s)); + return int(intrinsics.count_ones(transmute(u64)s)); } else when size_of(S) == 16 { - foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- } - return int(count_ones(transmute(u128)s)); + return int(intrinsics.count_ones(transmute(u128)s)); } else { #panic("Unhandled card bit_set size"); } diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index e2f8c7287..0e128567a 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -415,59 +415,32 @@ foreign { @(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 --- } abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 { - foreign { - @(link_name="llvm.fabs.f16") _abs :: proc "none" (x: f16) -> f16 --- - } - return _abs(x); + return -x if x < 0 else x; } abs_f32 :: #force_inline proc "contextless" (x: f32) -> f32 { - foreign { - @(link_name="llvm.fabs.f32") _abs :: proc "none" (x: f32) -> f32 --- - } - return _abs(x); + return -x if x < 0 else x; } abs_f64 :: #force_inline proc "contextless" (x: f64) -> f64 { - foreign { - @(link_name="llvm.fabs.f64") _abs :: proc "none" (x: f64) -> f64 --- - } - return _abs(x); + return -x if x < 0 else x; } min_f16 :: proc(a, b: f16) -> f16 { - foreign { - @(link_name="llvm.minnum.f16") _min :: proc "none" (a, b: f16) -> f16 --- - } - return _min(a, b); + return a if a < b else b; } min_f32 :: proc(a, b: f32) -> f32 { - foreign { - @(link_name="llvm.minnum.f32") _min :: proc "none" (a, b: f32) -> f32 --- - } - return _min(a, b); + return a if a < b else b; } min_f64 :: proc(a, b: f64) -> f64 { - foreign { - @(link_name="llvm.minnum.f64") _min :: proc "none" (a, b: f64) -> f64 --- - } - return _min(a, b); + return a if a < b else b; } max_f16 :: proc(a, b: f16) -> f16 { - foreign { - @(link_name="llvm.maxnum.f16") _max :: proc "none" (a, b: f16) -> f16 --- - } - return _max(a, b); + return a if a > b else b; } max_f32 :: proc(a, b: f32) -> f32 { - foreign { - @(link_name="llvm.maxnum.f32") _max :: proc "none" (a, b: f32) -> f32 --- - } - return _max(a, b); + return a if a > b else b; } max_f64 :: proc(a, b: f64) -> f64 { - foreign { - @(link_name="llvm.maxnum.f64") _max :: proc "none" (a, b: f64) -> f64 --- - } - return _max(a, b); + return a if a > b else b; } abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 { diff --git a/core/runtime/internal_linux.odin b/core/runtime/internal_linux.odin index aecd7f601..1e1a25bb8 100644 --- a/core/runtime/internal_linux.odin +++ b/core/runtime/internal_linux.odin @@ -1,5 +1,7 @@ package runtime +import "intrinsics" + @(link_name="__umodti3") umodti3 :: proc "c" (a, b: u128) -> u128 { r: u128 = ---; @@ -86,12 +88,6 @@ fixdfti :: proc(a: u64) -> i128 { } -@(default_calling_convention = "none") -foreign { - @(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 --- -} - - @(link_name="__floattidf") floattidf :: proc(a: i128) -> f64 { DBL_MANT_DIG :: 53; @@ -102,7 +98,7 @@ floattidf :: proc(a: i128) -> f64 { N :: size_of(i128) * 8; s := a >> (N-1); a = (a ~ s) - s; - sd: = N - _clz_i128(a); // number of significant digits + sd: = N - intrinsics.leading_zeros(a); // number of significant digits e := u32(sd - 1); // exponent if sd > DBL_MANT_DIG { switch sd { diff --git a/core/runtime/internal_windows.odin b/core/runtime/internal_windows.odin index 79a4bcdcb..8f50baf7f 100644 --- a/core/runtime/internal_windows.odin +++ b/core/runtime/internal_windows.odin @@ -1,5 +1,7 @@ package runtime +import "intrinsics" + @(link_name="__umodti3") umodti3 :: proc "c" (a, b: u128) -> u128 { r: u128 = ---; @@ -86,12 +88,6 @@ fixdfti :: proc(a: u64) -> i128 { } -@(default_calling_convention = "none") -foreign { - @(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 --- -} - - @(link_name="__floattidf") floattidf :: proc(a: i128) -> f64 { DBL_MANT_DIG :: 53; @@ -102,7 +98,7 @@ floattidf :: proc(a: i128) -> f64 { N :: size_of(i128) * 8; s := a >> (N-1); a = (a ~ s) - s; - sd: = N - _clz_i128(a); // number of significant digits + sd: = N - intrinsics.leading_zeros((a); // number of significant digits e := u32(sd - 1); // exponent if sd > DBL_MANT_DIG { switch sd { diff --git a/core/runtime/udivmod128.odin b/core/runtime/udivmod128.odin index 3486dffc2..c0ba6b9a3 100644 --- a/core/runtime/udivmod128.odin +++ b/core/runtime/udivmod128.odin @@ -1,35 +1,11 @@ package runtime -@(default_calling_convention="none") -foreign { - @(link_name="llvm.cttz.i8") _ctz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 --- - @(link_name="llvm.cttz.i16") _ctz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 --- - @(link_name="llvm.cttz.i32") _ctz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 --- - @(link_name="llvm.cttz.i64") _ctz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 --- -} -_ctz :: proc{ - _ctz_u8, - _ctz_u16, - _ctz_u32, - _ctz_u64, -}; - -@(default_calling_convention="none") -foreign { - @(link_name="llvm.ctlz.i8") _clz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 --- - @(link_name="llvm.ctlz.i16") _clz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 --- - @(link_name="llvm.ctlz.i32") _clz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 --- - @(link_name="llvm.ctlz.i64") _clz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 --- -} -_clz :: proc{ - _clz_u8, - _clz_u16, - _clz_u32, - _clz_u64, -}; - +import "intrinsics" udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { + _ctz :: intrinsics.trailing_zeros; + _clz :: intrinsics.leading_zeros; + n := transmute([2]u64)a; d := transmute([2]u64)b; q, r: [2]u64 = ---, ---; diff --git a/core/time/time.odin b/core/time/time.odin index eb35ac0d9..00d7e529a 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -1,5 +1,7 @@ package time +import "intrinsics" + Duration :: distinct i64; Nanosecond :: Duration(1); @@ -137,11 +139,7 @@ clock :: proc(t: Time) -> (hour, min, sec: int) { read_cycle_counter :: proc() -> u64 { - foreign _ { - @(link_name="llvm.readcyclecounter") - llvm_readcyclecounter :: proc "none" () -> u64 --- - } - return llvm_readcyclecounter(); + return u64(intrinsics.read_cycle_counter()); } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 94e8dcee6..95e1f78cb 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1924,6 +1924,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_count_ones: case BuiltinProc_trailing_zeros: + case BuiltinProc_leading_zeros: case BuiltinProc_reverse_bits: { Operand x = {}; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index b9794da8a..4079d743b 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -47,6 +47,7 @@ enum BuiltinProcId { BuiltinProc_count_ones, BuiltinProc_trailing_zeros, + BuiltinProc_leading_zeros, BuiltinProc_reverse_bits, BuiltinProc_byte_swap, @@ -265,6 +266,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("count_ones"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("trailing_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("leading_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("reverse_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("byte_swap"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 3e7d858ed..cefe740f0 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9108,6 +9108,8 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_trailing_zeros: return lb_emit_trailing_zeros(p, lb_build_expr(p, ce->args[0]), tv.type); + case BuiltinProc_leading_zeros: + return lb_emit_leading_zeros(p, lb_build_expr(p, ce->args[0]), tv.type); case BuiltinProc_count_ones: return lb_emit_count_ones(p, lb_build_expr(p, ce->args[0]), tv.type); @@ -9989,6 +9991,26 @@ lbValue lb_emit_trailing_zeros(lbProcedure *p, lbValue x, Type *type) { return res; } +lbValue lb_emit_leading_zeros(lbProcedure *p, lbValue x, Type *type) { + x = lb_emit_conv(p, x, type); + + char const *name = "llvm.ctlz"; + 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[2] = {}; + args[0] = x.value; + args[1] = LLVMConstNull(LLVMInt1TypeInContext(p->module->ctx)); + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = type; + return res; +} + + lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type) { x = lb_emit_conv(p, x, type); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index c2202131a..a7faa83b2 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -396,6 +396,7 @@ LLVMMetadataRef lb_debug_type(lbModule *m, Type *type); lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type); lbValue lb_emit_trailing_zeros(lbProcedure *p, lbValue x, Type *type); +lbValue lb_emit_leading_zeros(lbProcedure *p, lbValue x, Type *type); lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type); lbValue lb_emit_bit_set_card(lbProcedure *p, lbValue x); -- cgit v1.2.3 From 7086b49ae6755292232d9175d12bd76d93290b9c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 25 Apr 2021 20:26:11 +0100 Subject: Rename intrinsics to count_trailing_zeros and count_leading_zeros --- core/math/bits/bits.odin | 33 ++++++--------------------------- core/runtime/internal_linux.odin | 2 +- core/runtime/internal_windows.odin | 2 +- core/runtime/udivmod128.odin | 4 ++-- src/check_builtin.cpp | 4 ++-- src/checker_builtin_procs.hpp | 14 +++++++------- src/llvm_backend.cpp | 12 ++++++------ src/llvm_backend.hpp | 4 ++-- 8 files changed, 27 insertions(+), 48 deletions(-) (limited to 'src/check_builtin.cpp') diff --git a/core/math/bits/bits.odin b/core/math/bits/bits.odin index 303a94c1d..653cddacd 100644 --- a/core/math/bits/bits.odin +++ b/core/math/bits/bits.odin @@ -25,23 +25,14 @@ I64_MAX :: 1 << 63 - 1; count_ones :: intrinsics.count_ones; -trailing_zeros :: intrinsics.trailing_zeros; +trailing_zeros :: intrinsics.count_trailing_zeros; +leading_zeros :: intrinsics.count_leading_zeros; +count_trailing_zeros :: intrinsics.count_trailing_zeros; +count_leading_zeros :: intrinsics.count_leading_zeros; reverse_bits :: intrinsics.reverse_bits; +byte_swap :: intrinsics.byte_swap; -leading_zeros_u8 :: proc(i: u8) -> int { - return 8*size_of(i) - len_u8(i); -} -leading_zeros_u16 :: proc(i: u16) -> int { - return 8*size_of(i) - len_u16(i); -} -leading_zeros_u32 :: proc(i: u32) -> int { - return 8*size_of(i) - len_u32(i); -} -leading_zeros_u64 :: proc(i: u64) -> int { - return 8*size_of(i) - len_u64(i); -} - byte_swap_u16 :: proc(x: u16) -> u16 { return runtime.bswap_16(x); @@ -83,18 +74,6 @@ byte_swap_int :: proc(i: int) -> int { } } -byte_swap :: proc{ - byte_swap_u16, - byte_swap_u32, - byte_swap_u64, - byte_swap_u128, - byte_swap_i16, - byte_swap_i32, - byte_swap_i64, - byte_swap_i128, - byte_swap_uint, - byte_swap_int, -}; count_zeros8 :: proc(i: u8) -> u8 { return 8 - count_ones(i); } count_zeros16 :: proc(i: u16) -> u16 { return 16 - count_ones(i); } @@ -324,7 +303,7 @@ div_u64 :: proc(hi, lo, y: u64) -> (quo, rem: u64) { panic("overflow error"); } - s := uint(leading_zeros_u64(y)); + s := uint(count_leading_zeros(y)); y <<= s; yn1 := y >> 32; diff --git a/core/runtime/internal_linux.odin b/core/runtime/internal_linux.odin index 1e1a25bb8..19b52a42a 100644 --- a/core/runtime/internal_linux.odin +++ b/core/runtime/internal_linux.odin @@ -98,7 +98,7 @@ floattidf :: proc(a: i128) -> f64 { N :: size_of(i128) * 8; s := a >> (N-1); a = (a ~ s) - s; - sd: = N - intrinsics.leading_zeros(a); // number of significant digits + sd: = N - intrinsics.count_leading_zeros(a); // number of significant digits e := u32(sd - 1); // exponent if sd > DBL_MANT_DIG { switch sd { diff --git a/core/runtime/internal_windows.odin b/core/runtime/internal_windows.odin index 8f50baf7f..d7f00d155 100644 --- a/core/runtime/internal_windows.odin +++ b/core/runtime/internal_windows.odin @@ -98,7 +98,7 @@ floattidf :: proc(a: i128) -> f64 { N :: size_of(i128) * 8; s := a >> (N-1); a = (a ~ s) - s; - sd: = N - intrinsics.leading_zeros((a); // number of significant digits + sd: = N - intrinsics.count_leading_zeros(a); // number of significant digits e := u32(sd - 1); // exponent if sd > DBL_MANT_DIG { switch sd { diff --git a/core/runtime/udivmod128.odin b/core/runtime/udivmod128.odin index c0ba6b9a3..e4b7380d3 100644 --- a/core/runtime/udivmod128.odin +++ b/core/runtime/udivmod128.odin @@ -3,8 +3,8 @@ package runtime import "intrinsics" udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { - _ctz :: intrinsics.trailing_zeros; - _clz :: intrinsics.leading_zeros; + _ctz :: intrinsics.count_trailing_zeros; + _clz :: intrinsics.count_leading_zeros; n := transmute([2]u64)a; d := transmute([2]u64)b; diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 95e1f78cb..d3dbb3120 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1923,8 +1923,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; case BuiltinProc_count_ones: - case BuiltinProc_trailing_zeros: - case BuiltinProc_leading_zeros: + case BuiltinProc_count_trailing_zeros: + case BuiltinProc_count_leading_zeros: case BuiltinProc_reverse_bits: { Operand x = {}; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 4079d743b..7282e9895 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -46,8 +46,8 @@ enum BuiltinProcId { BuiltinProc_read_cycle_counter, BuiltinProc_count_ones, - BuiltinProc_trailing_zeros, - BuiltinProc_leading_zeros, + BuiltinProc_count_trailing_zeros, + BuiltinProc_count_leading_zeros, BuiltinProc_reverse_bits, BuiltinProc_byte_swap, @@ -264,11 +264,11 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("debug_trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/false}, {STR_LIT("read_cycle_counter"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("count_ones"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("trailing_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("leading_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("reverse_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("byte_swap"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("count_ones"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("count_trailing_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("count_leading_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("reverse_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("byte_swap"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("overflow_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index cefe740f0..c6f1c3693 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9106,10 +9106,10 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } - case BuiltinProc_trailing_zeros: - return lb_emit_trailing_zeros(p, lb_build_expr(p, ce->args[0]), tv.type); - case BuiltinProc_leading_zeros: - return lb_emit_leading_zeros(p, lb_build_expr(p, ce->args[0]), tv.type); + case BuiltinProc_count_trailing_zeros: + return lb_emit_count_trailing_zeros(p, lb_build_expr(p, ce->args[0]), tv.type); + case BuiltinProc_count_leading_zeros: + return lb_emit_count_leading_zeros(p, lb_build_expr(p, ce->args[0]), tv.type); case BuiltinProc_count_ones: return lb_emit_count_ones(p, lb_build_expr(p, ce->args[0]), tv.type); @@ -9972,7 +9972,7 @@ lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type) { } -lbValue lb_emit_trailing_zeros(lbProcedure *p, lbValue x, Type *type) { +lbValue lb_emit_count_trailing_zeros(lbProcedure *p, lbValue x, Type *type) { x = lb_emit_conv(p, x, type); char const *name = "llvm.cttz"; @@ -9991,7 +9991,7 @@ lbValue lb_emit_trailing_zeros(lbProcedure *p, lbValue x, Type *type) { return res; } -lbValue lb_emit_leading_zeros(lbProcedure *p, lbValue x, Type *type) { +lbValue lb_emit_count_leading_zeros(lbProcedure *p, lbValue x, Type *type) { x = lb_emit_conv(p, x, type); char const *name = "llvm.ctlz"; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index a7faa83b2..b25d94abb 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -395,8 +395,8 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t); LLVMMetadataRef lb_debug_type(lbModule *m, Type *type); lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type); -lbValue lb_emit_trailing_zeros(lbProcedure *p, lbValue x, Type *type); -lbValue lb_emit_leading_zeros(lbProcedure *p, lbValue x, Type *type); +lbValue lb_emit_count_trailing_zeros(lbProcedure *p, lbValue x, Type *type); +lbValue lb_emit_count_leading_zeros(lbProcedure *p, lbValue x, Type *type); lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type); lbValue lb_emit_bit_set_card(lbProcedure *p, lbValue x); -- cgit v1.2.3 From 2691c394e071f63a72a0f425e1ed2415e14515f5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 25 Apr 2021 20:50:25 +0100 Subject: Add `intrinsics.count_zeros` --- core/math/bits/bits.odin | 73 ++++++------------------------------------- src/check_builtin.cpp | 1 + src/checker_builtin_procs.hpp | 2 ++ src/llvm_backend.cpp | 10 ++++++ src/llvm_backend.hpp | 1 + 5 files changed, 23 insertions(+), 64 deletions(-) (limited to 'src/check_builtin.cpp') diff --git a/core/math/bits/bits.odin b/core/math/bits/bits.odin index 653cddacd..dbd7cc002 100644 --- a/core/math/bits/bits.odin +++ b/core/math/bits/bits.odin @@ -24,68 +24,18 @@ I32_MAX :: 1 << 31 - 1; I64_MAX :: 1 << 63 - 1; -count_ones :: intrinsics.count_ones; -trailing_zeros :: intrinsics.count_trailing_zeros; -leading_zeros :: intrinsics.count_leading_zeros; +count_ones :: intrinsics.count_ones; +count_zeros :: intrinsics.count_zeros; +trailing_zeros :: intrinsics.count_trailing_zeros; +leading_zeros :: intrinsics.count_leading_zeros; count_trailing_zeros :: intrinsics.count_trailing_zeros; count_leading_zeros :: intrinsics.count_leading_zeros; -reverse_bits :: intrinsics.reverse_bits; -byte_swap :: intrinsics.byte_swap; +reverse_bits :: intrinsics.reverse_bits; +byte_swap :: intrinsics.byte_swap; - - -byte_swap_u16 :: proc(x: u16) -> u16 { - return runtime.bswap_16(x); -} -byte_swap_u32 :: proc(x: u32) -> u32 { - return runtime.bswap_32(x); -} -byte_swap_u64 :: proc(x: u64) -> u64 { - return runtime.bswap_64(x); -} -byte_swap_i16 :: proc(x: i16) -> i16 { - return i16(runtime.bswap_16(u16(x))); -} -byte_swap_i32 :: proc(x: i32) -> i32 { - return i32(runtime.bswap_32(u32(x))); -} -byte_swap_i64 :: proc(x: i64) -> i64 { - return i64(runtime.bswap_64(u64(x))); -} -byte_swap_u128 :: proc(x: u128) -> u128 { - return runtime.bswap_128(x); -} -byte_swap_i128 :: proc(x: i128) -> i128 { - return i128(runtime.bswap_128(u128(x))); -} - -byte_swap_uint :: proc(i: uint) -> uint { - when size_of(uint) == size_of(u32) { - return uint(byte_swap_u32(u32(i))); - } else { - return uint(byte_swap_u64(u64(i))); - } -} -byte_swap_int :: proc(i: int) -> int { - when size_of(int) == size_of(i32) { - return int(byte_swap_i32(i32(i))); - } else { - return int(byte_swap_i64(i64(i))); - } -} - - -count_zeros8 :: proc(i: u8) -> u8 { return 8 - count_ones(i); } -count_zeros16 :: proc(i: u16) -> u16 { return 16 - count_ones(i); } -count_zeros32 :: proc(i: u32) -> u32 { return 32 - count_ones(i); } -count_zeros64 :: proc(i: u64) -> u64 { return 64 - count_ones(i); } - -count_zeros :: proc{ - count_zeros8, - count_zeros16, - count_zeros32, - count_zeros64, -}; +overflowing_add :: intrinsics.overflow_add; +overflowing_sub :: intrinsics.overflow_sub; +overflowing_mul :: intrinsics.overflow_mul; rotate_left8 :: proc(x: u8, k: int) -> u8 { @@ -142,11 +92,6 @@ to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } -overflowing_add :: intrinsics.overflow_add; -overflowing_sub :: intrinsics.overflow_sub; -overflowing_mul :: intrinsics.overflow_mul; - - len_u8 :: proc(x: u8) -> int { return int(len_u8_table[x]); } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index d3dbb3120..00963487f 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1923,6 +1923,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; case BuiltinProc_count_ones: + case BuiltinProc_count_zeros: case BuiltinProc_count_trailing_zeros: case BuiltinProc_count_leading_zeros: case BuiltinProc_reverse_bits: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 7282e9895..98ef5180b 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -46,6 +46,7 @@ enum BuiltinProcId { BuiltinProc_read_cycle_counter, BuiltinProc_count_ones, + BuiltinProc_count_zeros, BuiltinProc_count_trailing_zeros, BuiltinProc_count_leading_zeros, BuiltinProc_reverse_bits, @@ -265,6 +266,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("read_cycle_counter"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("count_ones"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("count_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("count_trailing_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("count_leading_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("reverse_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c6f1c3693..c52c9269e 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9113,6 +9113,8 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_count_ones: return lb_emit_count_ones(p, lb_build_expr(p, ce->args[0]), tv.type); + case BuiltinProc_count_zeros: + return lb_emit_count_zeros(p, lb_build_expr(p, ce->args[0]), tv.type); case BuiltinProc_reverse_bits: return lb_emit_reverse_bits(p, lb_build_expr(p, ce->args[0]), tv.type); @@ -9971,6 +9973,14 @@ lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type) { return res; } +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); + lbValue count = lb_emit_count_ones(p, x, type); + return lb_emit_arith(p, Token_Sub, size, count, type); +} + + lbValue lb_emit_count_trailing_zeros(lbProcedure *p, lbValue x, Type *type) { x = lb_emit_conv(p, x, type); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index b25d94abb..e8811a91e 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -395,6 +395,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t); LLVMMetadataRef lb_debug_type(lbModule *m, Type *type); lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type); +lbValue lb_emit_count_zeros(lbProcedure *p, lbValue x, Type *type); lbValue lb_emit_count_trailing_zeros(lbProcedure *p, lbValue x, Type *type); lbValue lb_emit_count_leading_zeros(lbProcedure *p, lbValue x, Type *type); lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type); -- cgit v1.2.3