diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ae4f1ec30..a915aa92f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5760,14 +5760,14 @@ 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)); + 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)); + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); } operand->mode = Addressing_Value; operand->type = t_i64; @@ -5776,9 +5776,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_count_ones: case BuiltinProc_trailing_zeros: case BuiltinProc_reverse_bits: - 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)); + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); // continue anyway } { @@ -5788,24 +5787,102 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } - if (id == BuiltinProc_byte_swap && (!is_type_integer_like(x.type) && !is_type_float(x.type))) { + 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) or float, got %s", LIT(builtin_procs[id].name), xts); - gb_string_free(xts); - } else if (id != BuiltinProc_byte_swap && !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); + 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); + 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: @@ -5945,7 +6022,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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)); + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); // continue anyway } @@ -5971,7 +6048,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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); + 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; @@ -5979,7 +6056,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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); + error(x.expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_procs[id].name), xts); gb_string_free(xts); return false; } @@ -5989,17 +6066,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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)); + 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); + 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); + 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; } @@ -6011,7 +6088,7 @@ 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)); + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); // continue anyway } { @@ -6030,7 +6107,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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); + 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 @@ -6039,14 +6116,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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); + 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)); + 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) { diff --git a/src/check_type.cpp b/src/check_type.cpp index 612a11593..e3aac161c 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2619,12 +2619,11 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) { } Type *make_optional_ok_type(Type *value, bool typed) { - // LEAK TODO(bill): probably don't reallocate everything here and reuse the same one for the same type if possible - gbAllocator a = heap_allocator(); + gbAllocator a = permanent_allocator(); Type *t = alloc_type_tuple(); - array_init(&t->Tuple.variables, a, 0, 2); - array_add (&t->Tuple.variables, alloc_entity_field(nullptr, blank_token, value, false, 0)); - array_add (&t->Tuple.variables, alloc_entity_field(nullptr, blank_token, typed ? t_bool : t_untyped_bool, false, 1)); + array_init(&t->Tuple.variables, a, 2); + t->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, value, false, 0); + t->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, typed ? t_bool : t_untyped_bool, false, 1); return t; } diff --git a/src/checker.cpp b/src/checker.cpp index eab704d9c..f386d6da7 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1773,20 +1773,28 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("memory_equal"), str_lit("memory_compare"), str_lit("memory_compare_zero"), - - 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(required_runtime_entities); i++) { 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 diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 7f8fd5323..b9794da8a 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -50,6 +50,10 @@ enum BuiltinProcId { BuiltinProc_reverse_bits, BuiltinProc_byte_swap, + BuiltinProc_overflow_add, + BuiltinProc_overflow_sub, + BuiltinProc_overflow_mul, + BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -264,6 +268,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {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}, + {STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2e77d66f2..a74d5bd3a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9127,34 +9127,57 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, { lbValue x = lb_build_expr(p, ce->args[0]); x = lb_emit_conv(p, x, tv.type); - if (is_type_float(tv.type)) { - i64 sz = type_size_of(tv.type); - Type *integer_type = nullptr; - switch (sz) { - case 2: integer_type = t_u16; break; - case 4: integer_type = t_u32; break; - case 8: integer_type = t_u64; break; - } - GB_ASSERT(integer_type != nullptr); - x = lb_emit_transmute(p, x, integer_type); - } + return lb_emit_byte_swap(p, x, tv.type); + } - char const *name = "llvm.bswap"; - LLVMTypeRef types[1] = {lb_type(p->module, x.type)}; + case BuiltinProc_overflow_add: + case BuiltinProc_overflow_sub: + case BuiltinProc_overflow_mul: + { + Type *tuple = tv.type; + GB_ASSERT(is_type_tuple(tuple)); + Type *type = tuple->Tuple.variables[0]->type; + + lbValue x = lb_build_expr(p, ce->args[0]); + lbValue y = lb_build_expr(p, ce->args[1]); + x = lb_emit_conv(p, x, type); + y = lb_emit_conv(p, y, type); + + char const *name = nullptr; + if (is_type_unsigned(type)) { + switch (id) { + case BuiltinProc_overflow_add: name = "llvm.uadd.with.overflow"; break; + case BuiltinProc_overflow_sub: name = "llvm.usub.with.overflow"; break; + case BuiltinProc_overflow_mul: name = "llvm.umul.with.overflow"; break; + } + } else { + switch (id) { + case BuiltinProc_overflow_add: name = "llvm.sadd.with.overflow"; break; + case BuiltinProc_overflow_sub: name = "llvm.ssub.with.overflow"; break; + case BuiltinProc_overflow_mul: name = "llvm.smul.with.overflow"; break; + } + } + 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[1] = {}; + LLVMValueRef args[2] = {}; args[0] = x.value; + args[1] = y.value; + + Type *res_type = nullptr; + { + gbAllocator a = permanent_allocator(); + res_type = alloc_type_tuple(); + array_init(&res_type->Tuple.variables, a, 2); + res_type->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, type, false, 0); + res_type->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1); + } lbValue res = {}; res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); - res.type = x.type; - - if (is_type_float(tv.type)) { - res = lb_emit_transmute(p, res, tv.type); - } + res.type = res_type; return res; } @@ -9896,43 +9919,43 @@ LLVMValueRef lb_lookup_runtime_procedure(lbModule *m, String const &name) { return found->value; } -lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type) { - Type *vt = core_type(value.type); - GB_ASSERT(type_size_of(vt) == type_size_of(platform_type)); +lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) { + GB_ASSERT(type_size_of(value.type) == type_size_of(end_type)); - // TODO(bill): lb_emit_byte_swap - lbValue res = {}; - res.type = platform_type; - res.value = value.value; - - int sz = cast(int)type_size_of(vt); - if (sz > 1) { - if (is_type_float(platform_type)) { - String name = {}; - switch (sz) { - case 2: name = str_lit("bswap_f16"); break; - case 4: name = str_lit("bswap_f32"); break; - case 8: name = str_lit("bswap_f64"); break; - default: GB_PANIC("unhandled byteswap size"); break; - } - LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name); - res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, ""); - } else { - GB_ASSERT(is_type_integer(platform_type)); - String name = {}; - switch (sz) { - case 2: name = str_lit("bswap_16"); break; - case 4: name = str_lit("bswap_32"); break; - case 8: name = str_lit("bswap_64"); break; - case 16: name = str_lit("bswap_128"); break; - default: GB_PANIC("unhandled byteswap size"); break; - } - LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name); - - res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, ""); - } + if (type_size_of(value.type) < 2) { + return value; } + Type *original_type = value.type; + if (is_type_float(original_type)) { + i64 sz = type_size_of(original_type); + Type *integer_type = nullptr; + switch (sz) { + case 2: integer_type = t_u16; break; + case 4: integer_type = t_u32; break; + case 8: integer_type = t_u64; break; + } + GB_ASSERT(integer_type != nullptr); + value = lb_emit_transmute(p, value, integer_type); + } + + char const *name = "llvm.bswap"; + LLVMTypeRef types[1] = {lb_type(p->module, value.type)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[1] = {}; + args[0] = value.value; + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = value.type; + + if (is_type_float(original_type)) { + res = lb_emit_transmute(p, res, original_type); + } + res.type = end_type; return res; } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 8117271c1..47c58fde3 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -310,7 +310,7 @@ lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel); lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel); lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type); -lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type); +lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type); void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block); lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t); lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right); diff --git a/src/types.cpp b/src/types.cpp index f706032a5..a5c5c2eb2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1479,7 +1479,7 @@ Type *integer_endian_type_to_platform_type(Type *t) { if (t->kind == Type_BitSet) { t = bit_set_to_int(t); } - GB_ASSERT(t->kind == Type_Basic); + GB_ASSERT_MSG(t->kind == Type_Basic, "%s", type_to_string(t)); switch (t->Basic.kind) { // Endian Specific Types