diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 738c85c37..c962853a7 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1450,7 +1450,6 @@ typedef struct Module_ bool no_extprefix : 1; AnalysisStage stage : 6; - Ast **files; // Asts AstId docs; Decl** method_extensions; HTable symbols; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 1bde64389..846d22a4a 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -813,14 +813,21 @@ typedef enum typedef enum { + BUILTIN_ABS, BUILTIN_BITREVERSE, BUILTIN_BSWAP, BUILTIN_CEIL, - BUILTIN_COS, BUILTIN_COPYSIGN, + BUILTIN_COS, BUILTIN_CTLZ, BUILTIN_CTTZ, + BUILTIN_EXACT_ADD, + BUILTIN_EXACT_DIV, + BUILTIN_EXACT_MOD, + BUILTIN_EXACT_MUL, + BUILTIN_EXACT_NEG, + BUILTIN_EXACT_SUB, BUILTIN_EXP, BUILTIN_EXP2, BUILTIN_FLOOR, @@ -829,14 +836,17 @@ typedef enum BUILTIN_FSHL, BUILTIN_FSHR, BUILTIN_LOG, - BUILTIN_LOG2, BUILTIN_LOG10, + BUILTIN_LOG2, BUILTIN_MAX, BUILTIN_MEMCOPY, - BUILTIN_MEMSET, BUILTIN_MEMMOVE, + BUILTIN_MEMSET, BUILTIN_MIN, BUILTIN_NEARBYINT, + BUILTIN_OVERFLOW_ADD, + BUILTIN_OVERFLOW_MUL, + BUILTIN_OVERFLOW_SUB, BUILTIN_POPCOUNT, BUILTIN_POW, BUILTIN_POW_INT, @@ -845,20 +855,20 @@ typedef enum BUILTIN_REDUCE_AND, BUILTIN_REDUCE_FADD, BUILTIN_REDUCE_FMUL, + BUILTIN_REDUCE_MAX, + BUILTIN_REDUCE_MIN, BUILTIN_REDUCE_MUL, BUILTIN_REDUCE_OR, BUILTIN_REDUCE_XOR, - BUILTIN_REDUCE_MAX, - BUILTIN_REDUCE_MIN, BUILTIN_REVERSE, BUILTIN_RINT, BUILTIN_ROUND, BUILTIN_ROUNDEVEN, BUILTIN_SAT_ADD, - BUILTIN_SAT_SUB, BUILTIN_SAT_SHL, - BUILTIN_SIN, + BUILTIN_SAT_SUB, BUILTIN_SHUFFLEVECTOR, + BUILTIN_SIN, BUILTIN_SQRT, BUILTIN_STACKTRACE, BUILTIN_SYSCALL, @@ -868,6 +878,7 @@ typedef enum BUILTIN_UNREACHABLE, BUILTIN_VOLATILE_LOAD, BUILTIN_VOLATILE_STORE, + BUILTIN_NONE, NUMBER_OF_BUILTINS = BUILTIN_NONE, diff --git a/src/compiler/llvm_codegen_builtins.c b/src/compiler/llvm_codegen_builtins.c index 56d6d815c..b4d6a7b08 100644 --- a/src/compiler/llvm_codegen_builtins.c +++ b/src/compiler/llvm_codegen_builtins.c @@ -358,13 +358,84 @@ void llvm_emit_simple_builtin(GenContext *c, BEValue *be_value, Expr *expr, unsi Expr **args = expr->call_expr.arguments; unsigned count = vec_size(args); assert(count <= 3); + assert(count > 0); LLVMValueRef arg_slots[3]; llvm_emit_intrinsic_args(c, args, arg_slots, count); - LLVMTypeRef call_type[1] = { LLVMTypeOf(arg_slots[0]) }; - LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 1, arg_slots, count); + LLVMTypeRef call_type = LLVMTypeOf(arg_slots[0]); + LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, &call_type, 1, arg_slots, count); llvm_value_set(be_value, result, expr->type); } +static void llvm_emit_overflow_builtin(GenContext *c, BEValue *be_value, Expr *expr, unsigned intrinsic_signed, unsigned intrinsic_unsigned) +{ + Expr **args = expr->call_expr.arguments; + LLVMValueRef arg_slots[2]; + llvm_emit_intrinsic_args(c, args, arg_slots, 2); + BEValue ref; + Expr *ret_addr = args[2]; + llvm_emit_expr(c, &ref, ret_addr); + llvm_value_rvalue(c, &ref); + // Note that we can make additional improvements here! + llvm_value_set_address(&ref, ref.value, ref.type->pointer, type_abi_alignment(ref.type->pointer)); + LLVMTypeRef call_type[1] = { LLVMTypeOf(arg_slots[0]) }; + unsigned intrinsic = type_is_signed(type_lowering(args[0]->type)) ? intrinsic_signed : intrinsic_unsigned; + LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 1, arg_slots, 2); + LLVMValueRef failed = llvm_emit_extract_value(c, result, 1); + LLVMValueRef value = llvm_emit_extract_value(c, result, 0); + llvm_store_raw(c, &ref, value); + llvm_value_set_bool(be_value, failed); +} + +static void llvm_emit_wrap_builtin(GenContext *c, BEValue *result_value, Expr *expr, BuiltinFunction func) +{ + Expr **args = expr->call_expr.arguments; + LLVMValueRef arg_slots[2]; + llvm_emit_intrinsic_args(c, args, arg_slots, func == BUILTIN_EXACT_NEG ? 1 : 2); + Type *base_type = type_lowering(args[0]->type); + if (base_type->type_kind == TYPE_VECTOR) base_type = base_type->array.base; + assert(type_is_integer(base_type)); + bool is_signed = type_is_signed(base_type); + LLVMValueRef res; + switch (func) + { + case BUILTIN_EXACT_NEG: + res = LLVMBuildNeg(c->builder, arg_slots[0], "eneg"); + break; + case BUILTIN_EXACT_SUB: + res = LLVMBuildSub(c->builder, arg_slots[0], arg_slots[1], "esub"); + break; + case BUILTIN_EXACT_ADD: + res = LLVMBuildAdd(c->builder, arg_slots[0], arg_slots[1], "eadd"); + break; + case BUILTIN_EXACT_MUL: + res = LLVMBuildMul(c->builder, arg_slots[0], arg_slots[1], "emul"); + break; + case BUILTIN_EXACT_DIV: + if (type_is_signed(base_type)) + { + res = LLVMBuildSDiv(c->builder, arg_slots[0], arg_slots[1], "esdiv"); + } + else + { + res = LLVMBuildUDiv(c->builder, arg_slots[0], arg_slots[1], "eudiv"); + } + break; + case BUILTIN_EXACT_MOD: + if (type_is_signed(base_type)) + { + res = LLVMBuildSRem(c->builder, arg_slots[0], arg_slots[1], "eumod"); + } + else + { + res = LLVMBuildSDiv(c->builder, arg_slots[0], arg_slots[1], "esmod"); + } + break; + default: + UNREACHABLE + } + llvm_value_set(result_value, res, expr->type); +} + void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr) { BuiltinFunction func = exprptr(expr->call_expr.function)->builtin_expr.builtin; @@ -438,6 +509,23 @@ void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr) case BUILTIN_REDUCE_FMUL: llvm_emit_reduce_float_builtin(c, intrinsic_id.vector_reduce_fmul, result_value, expr); return; + case BUILTIN_EXACT_DIV: + case BUILTIN_EXACT_ADD: + case BUILTIN_EXACT_MUL: + case BUILTIN_EXACT_SUB: + case BUILTIN_EXACT_MOD: + case BUILTIN_EXACT_NEG: + llvm_emit_wrap_builtin(c, result_value, expr, func); + return; + case BUILTIN_OVERFLOW_ADD: + llvm_emit_overflow_builtin(c, result_value, expr, intrinsic_id.sadd_overflow, intrinsic_id.uadd_overflow); + return; + case BUILTIN_OVERFLOW_SUB: + llvm_emit_overflow_builtin(c, result_value, expr, intrinsic_id.ssub_overflow, intrinsic_id.usub_overflow); + return; + case BUILTIN_OVERFLOW_MUL: + llvm_emit_overflow_builtin(c, result_value, expr, intrinsic_id.smul_overflow, intrinsic_id.umul_overflow); + return; case BUILTIN_CTTZ: llvm_emit_int_with_bool_builtin(c, intrinsic_id.cttz, result_value, expr, false); return; diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index dbd1b56aa..fb9192c3e 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -303,6 +303,38 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) return false; } break; + case BUILTIN_OVERFLOW_ADD: + case BUILTIN_OVERFLOW_MUL: + case BUILTIN_OVERFLOW_SUB: + { + if (!sema_check_builtin_args(args, + (BuiltinArg[]) { BA_INTEGER, BA_INTEGER, BA_POINTER }, + arg_count)) return false; + if (!sema_check_builtin_args_match(args, 2)) return false; + if (args[0]->type->canonical != args[2]->type->canonical->pointer) + { + SEMA_ERROR(args[2], "Expected %s, not %s.", type_to_error_string(type_get_ptr(args[0]->type)), + type_to_error_string(args[2]->type)); + return false; + } + rtype = type_bool; + break; + } + case BUILTIN_EXACT_ADD: + case BUILTIN_EXACT_DIV: + case BUILTIN_EXACT_MUL: + case BUILTIN_EXACT_SUB: + case BUILTIN_EXACT_MOD: + if (!sema_check_builtin_args(args, + (BuiltinArg[]) { BA_INTEGER, BA_INTEGER }, + arg_count)) return false; + if (!sema_check_builtin_args_match(args, arg_count)) return false; + rtype = type_no_optional(args[0]->type->canonical); + break; + case BUILTIN_EXACT_NEG: + if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_INTLIKE }, arg_count)) return false; + rtype = type_no_optional(args[0]->type->canonical); + break; case BUILTIN_MEMCOPY: case BUILTIN_MEMMOVE: if (!sema_check_builtin_args(args, @@ -517,6 +549,7 @@ static inline unsigned builtin_expected_args(BuiltinFunction func) case BUILTIN_CTLZ: case BUILTIN_POPCOUNT: case BUILTIN_CTTZ: + case BUILTIN_EXACT_NEG: case BUILTIN_EXP: case BUILTIN_EXP2: case BUILTIN_FLOOR: @@ -546,21 +579,29 @@ static inline unsigned builtin_expected_args(BuiltinFunction func) case BUILTIN_REDUCE_MIN: return 1; case BUILTIN_COPYSIGN: + case BUILTIN_EXACT_ADD: + case BUILTIN_EXACT_DIV: + case BUILTIN_EXACT_MOD: + case BUILTIN_EXACT_MUL: + case BUILTIN_EXACT_SUB: case BUILTIN_MAX: case BUILTIN_MIN: case BUILTIN_POW: case BUILTIN_POW_INT: - case BUILTIN_VOLATILE_STORE: - case BUILTIN_SAT_ADD: - case BUILTIN_SAT_SUB: - case BUILTIN_SAT_SHL: - case BUILTIN_REDUCE_FMUL: case BUILTIN_REDUCE_FADD: + case BUILTIN_REDUCE_FMUL: + case BUILTIN_SAT_ADD: + case BUILTIN_SAT_SHL: + case BUILTIN_SAT_SUB: + case BUILTIN_VOLATILE_STORE: return 2; case BUILTIN_FMA: case BUILTIN_FSHL: case BUILTIN_FSHR: case BUILTIN_FMULADD: + case BUILTIN_OVERFLOW_ADD: + case BUILTIN_OVERFLOW_MUL: + case BUILTIN_OVERFLOW_SUB: case BUILTIN_PREFETCH: return 3; case BUILTIN_MEMSET: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 9fddd13f5..1558ac662 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -187,6 +187,12 @@ void symtab_init(uint32_t capacity) builtin_list[BUILTIN_COS] = KW_DEF("cos"); builtin_list[BUILTIN_CTLZ] = KW_DEF("clz"); builtin_list[BUILTIN_CTTZ] = KW_DEF("ctz"); + builtin_list[BUILTIN_EXACT_ADD] = KW_DEF("add"); + builtin_list[BUILTIN_EXACT_DIV] = KW_DEF("div"); + builtin_list[BUILTIN_EXACT_MOD] = KW_DEF("mod"); + builtin_list[BUILTIN_EXACT_MUL] = KW_DEF("mul"); + builtin_list[BUILTIN_EXACT_NEG] = KW_DEF("neg"); + builtin_list[BUILTIN_EXACT_SUB] = KW_DEF("sub"); builtin_list[BUILTIN_EXP] = KW_DEF("exp"); builtin_list[BUILTIN_EXP2] = KW_DEF("exp2"); builtin_list[BUILTIN_FLOOR] = KW_DEF("floor"); @@ -201,6 +207,9 @@ void symtab_init(uint32_t capacity) builtin_list[BUILTIN_MEMSET] = KW_DEF("memset"); builtin_list[BUILTIN_MEMMOVE] = KW_DEF("memmove"); builtin_list[BUILTIN_NEARBYINT] = KW_DEF("nearbyint"); + builtin_list[BUILTIN_OVERFLOW_ADD] = KW_DEF("overflow_add"); + builtin_list[BUILTIN_OVERFLOW_SUB] = KW_DEF("overflow_sub"); + builtin_list[BUILTIN_OVERFLOW_MUL] = KW_DEF("overflow_mul"); builtin_list[BUILTIN_POPCOUNT] = KW_DEF("popcount"); builtin_list[BUILTIN_POW] = KW_DEF("pow"); builtin_list[BUILTIN_POW_INT] = KW_DEF("pow_int"); diff --git a/src/version.h b/src/version.h index 4064bfd1b..a93fec036 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.98" \ No newline at end of file +#define COMPILER_VERSION "0.3.99" \ No newline at end of file diff --git a/test/test_suite/builtins/exacts.c3t b/test/test_suite/builtins/exacts.c3t new file mode 100644 index 000000000..de670fee6 --- /dev/null +++ b/test/test_suite/builtins/exacts.c3t @@ -0,0 +1,24 @@ +// #target: macos-x64 +module test; +import std::io; +fn void main() +{ + ichar x = 23; + ichar y = 121; + int z1 = $$mul(x, y); + ichar z2 = $$div(y, x); + ichar z3 = $$mod(y, x); + int z4 = $$add(x, y); + int z5 = $$sub(x, y); + int z6 = $$neg(x); + io::printfln("%s %s %s %s %s %s", z1, z2, z3, z4, z5, z6); +} + +/* #expect: test.ll + + %emul = mul i8 %0, %1 + %esdiv = sdiv i8 %2, %3 + %eumod = srem i8 %4, %5 + %eadd = add i8 %6, %7 + %esub = sub i8 %8, %9 + %eneg = sub i8 0, %10 \ No newline at end of file diff --git a/test/test_suite/builtins/overflows.c3t b/test/test_suite/builtins/overflows.c3t new file mode 100644 index 000000000..b0004833a --- /dev/null +++ b/test/test_suite/builtins/overflows.c3t @@ -0,0 +1,66 @@ +module test; +import std::io; +fn void main() +{ + ichar x = 23; + ichar y = 121; + ichar z1; + ichar z2; + ichar z3; + ichar z4; + ichar z5; + bool success1 = $$overflow_mul(x, y, &z1); + bool success2 = $$overflow_add(x, y, &z2); + bool success3 = $$overflow_add(x, x, &z3); + bool success4 = $$overflow_sub($$neg(y), y, &z4); + bool success5 = $$overflow_sub(x, y, &z5); + io::printfln("%s %s", success1, z1); + io::printfln("%s %s", success2, z2); + io::printfln("%s %s", success3, z3); + io::printfln("%s %s", success4, z4); + io::printfln("%s %s", success5, z5); +} + +/* #expect: test.ll + + %0 = load i8, i8* %x, align 1 + %1 = load i8, i8* %y, align 1 + %2 = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %0, i8 %1) + %3 = extractvalue { i8, i1 } %2, 1 + %4 = extractvalue { i8, i1 } %2, 0 + store i8 %4, i8* %z1, align 1 + %5 = zext i1 %3 to i8 + store i8 %5, i8* %success1, align 1 + %6 = load i8, i8* %x, align 1 + %7 = load i8, i8* %y, align 1 + %8 = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %6, i8 %7) + %9 = extractvalue { i8, i1 } %8, 1 + %10 = extractvalue { i8, i1 } %8, 0 + store i8 %10, i8* %z2, align 1 + %11 = zext i1 %9 to i8 + store i8 %11, i8* %success2, align 1 + %12 = load i8, i8* %x, align 1 + %13 = load i8, i8* %x, align 1 + %14 = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %12, i8 %13) + %15 = extractvalue { i8, i1 } %14, 1 + %16 = extractvalue { i8, i1 } %14, 0 + store i8 %16, i8* %z3, align 1 + %17 = zext i1 %15 to i8 + store i8 %17, i8* %success3, align 1 + %18 = load i8, i8* %y, align 1 + %eneg = sub i8 0, %18 + %19 = load i8, i8* %y, align 1 + %20 = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %eneg, i8 %19) + %21 = extractvalue { i8, i1 } %20, 1 + %22 = extractvalue { i8, i1 } %20, 0 + store i8 %22, i8* %z4, align 1 + %23 = zext i1 %21 to i8 + store i8 %23, i8* %success4, align 1 + %24 = load i8, i8* %x, align 1 + %25 = load i8, i8* %y, align 1 + %26 = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %24, i8 %25) + %27 = extractvalue { i8, i1 } %26, 1 + %28 = extractvalue { i8, i1 } %26, 0 + store i8 %28, i8* %z5, align 1 + %29 = zext i1 %27 to i8 + store i8 %29, i8* %success5, align 1 diff --git a/test/test_suite2/builtins/exacts.c3t b/test/test_suite2/builtins/exacts.c3t new file mode 100644 index 000000000..de670fee6 --- /dev/null +++ b/test/test_suite2/builtins/exacts.c3t @@ -0,0 +1,24 @@ +// #target: macos-x64 +module test; +import std::io; +fn void main() +{ + ichar x = 23; + ichar y = 121; + int z1 = $$mul(x, y); + ichar z2 = $$div(y, x); + ichar z3 = $$mod(y, x); + int z4 = $$add(x, y); + int z5 = $$sub(x, y); + int z6 = $$neg(x); + io::printfln("%s %s %s %s %s %s", z1, z2, z3, z4, z5, z6); +} + +/* #expect: test.ll + + %emul = mul i8 %0, %1 + %esdiv = sdiv i8 %2, %3 + %eumod = srem i8 %4, %5 + %eadd = add i8 %6, %7 + %esub = sub i8 %8, %9 + %eneg = sub i8 0, %10 \ No newline at end of file diff --git a/test/test_suite2/builtins/overflows.c3t b/test/test_suite2/builtins/overflows.c3t new file mode 100644 index 000000000..811c64a27 --- /dev/null +++ b/test/test_suite2/builtins/overflows.c3t @@ -0,0 +1,66 @@ +module test; +import std::io; +fn void main() +{ + ichar x = 23; + ichar y = 121; + ichar z1; + ichar z2; + ichar z3; + ichar z4; + ichar z5; + bool success1 = $$overflow_mul(x, y, &z1); + bool success2 = $$overflow_add(x, y, &z2); + bool success3 = $$overflow_add(x, x, &z3); + bool success4 = $$overflow_sub($$neg(y), y, &z4); + bool success5 = $$overflow_sub(x, y, &z5); + io::printfln("%s %s", success1, z1); + io::printfln("%s %s", success2, z2); + io::printfln("%s %s", success3, z3); + io::printfln("%s %s", success4, z4); + io::printfln("%s %s", success5, z5); +} + +/* #expect: test.ll + + %0 = load i8, ptr %x, align 1 + %1 = load i8, ptr %y, align 1 + %2 = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %0, i8 %1) + %3 = extractvalue { i8, i1 } %2, 1 + %4 = extractvalue { i8, i1 } %2, 0 + store i8 %4, ptr %z1, align 1 + %5 = zext i1 %3 to i8 + store i8 %5, ptr %success1, align 1 + %6 = load i8, ptr %x, align 1 + %7 = load i8, ptr %y, align 1 + %8 = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %6, i8 %7) + %9 = extractvalue { i8, i1 } %8, 1 + %10 = extractvalue { i8, i1 } %8, 0 + store i8 %10, ptr %z2, align 1 + %11 = zext i1 %9 to i8 + store i8 %11, ptr %success2, align 1 + %12 = load i8, ptr %x, align 1 + %13 = load i8, ptr %x, align 1 + %14 = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %12, i8 %13) + %15 = extractvalue { i8, i1 } %14, 1 + %16 = extractvalue { i8, i1 } %14, 0 + store i8 %16, ptr %z3, align 1 + %17 = zext i1 %15 to i8 + store i8 %17, ptr %success3, align 1 + %18 = load i8, ptr %y, align 1 + %eneg = sub i8 0, %18 + %19 = load i8, ptr %y, align 1 + %20 = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %eneg, i8 %19) + %21 = extractvalue { i8, i1 } %20, 1 + %22 = extractvalue { i8, i1 } %20, 0 + store i8 %22, ptr %z4, align 1 + %23 = zext i1 %21 to i8 + store i8 %23, ptr %success4, align 1 + %24 = load i8, ptr %x, align 1 + %25 = load i8, ptr %y, align 1 + %26 = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %24, i8 %25) + %27 = extractvalue { i8, i1 } %26, 1 + %28 = extractvalue { i8, i1 } %26, 0 + store i8 %28, ptr %z5, align 1 + %29 = zext i1 %27 to i8 + store i8 %29, ptr %success5, align 1