diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b9ff3bf1f..ae4f1ec30 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5773,6 +5773,39 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->type = t_i64; break; + 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)); + // continue anyway + } + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + + if (id == BuiltinProc_byte_swap && (!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 (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); + 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_atomic_fence: case BuiltinProc_atomic_fence_acq: case BuiltinProc_atomic_fence_rel: @@ -5913,7 +5946,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 { if (!build_context.use_llvm_api) { error(ce->args[0], "%.*s is not supported on this backend", LIT(builtin_procs[id].name)); - return false; + // continue anyway } Operand x = {}; @@ -5979,8 +6012,9 @@ 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)); - return false; - } else { + // continue anyway + } + { Operand x = {}; Operand y = {}; check_expr(c, &x, ce->args[0]); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 19a9d841e..7f8fd5323 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -45,6 +45,11 @@ enum BuiltinProcId { BuiltinProc_debug_trap, BuiltinProc_read_cycle_counter, + BuiltinProc_count_ones, + BuiltinProc_trailing_zeros, + BuiltinProc_reverse_bits, + BuiltinProc_byte_swap, + BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -254,6 +259,10 @@ 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("reverse_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("byte_swap"), 1, 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 a67078180..2e77d66f2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9077,6 +9077,88 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } + case BuiltinProc_trailing_zeros: + { + lbValue x = lb_build_expr(p, ce->args[0]); + x = lb_emit_conv(p, x, tv.type); + + char const *name = "llvm.cttz"; + LLVMTypeRef types[1] = {lb_type(p->module, tv.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 = tv.type; + return res; + } + + case BuiltinProc_count_ones: + case BuiltinProc_reverse_bits: + { + lbValue x = lb_build_expr(p, ce->args[0]); + x = lb_emit_conv(p, x, tv.type); + + char const *name = nullptr; + switch (id) { + case BuiltinProc_count_ones: name = "llvm.ctpop"; break; + case BuiltinProc_reverse_bits: name = "llvm.bitreverse"; break; + } + LLVMTypeRef types[1] = {lb_type(p->module, tv.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] = x.value; + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = tv.type; + return res; + } + + case BuiltinProc_byte_swap: + { + 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); + } + + char const *name = "llvm.bswap"; + LLVMTypeRef types[1] = {lb_type(p->module, x.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] = x.value; + + 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); + } + return res; + } + + case BuiltinProc_atomic_fence: LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); return {};