diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index 1cc2ac612..4d4e45abb 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -143,7 +143,9 @@ fn void panicf(String fmt, String file, String function, uint line, args...) **/ macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn { + $if env::COMPILER_SAFE_MODE: panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat()); + $endif; $$unreachable(); } diff --git a/releasenotes.md b/releasenotes.md index 77c30a867..038e86960 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -14,10 +14,12 @@ - Fixed crash on certain recursive function definitions #1209. - Return the typekind "FUNC" for a function pointer. - No longer possible to dereference a function pointer. +- Fix bug with @jump miscompile ### Stdlib changes - Added `remove_first_item` `remove_last_item` and `remove_item` as aliases for the `match` functions. - Added @str_hash, @str_upper, @str_lower, @str_find compile time macros. +- Remove "panic" text from unreachable() when safe mode is turned off. ## 0.6.0 Change list diff --git a/resources/grammar/grammar.y b/resources/grammar/grammar.y index 99df79ff0..8b1d5b2ab 100644 --- a/resources/grammar/grammar.y +++ b/resources/grammar/grammar.y @@ -886,8 +886,8 @@ opt_stmt_list switch_stmt : SWITCH optional_label '{' switch_body '}' | SWITCH optional_label '{' '}' - | SWITCH optional_label paren_cond '{' switch_body '}' - | SWITCH optional_label paren_cond '{' '}' + | SWITCH optional_label paren_cond opt_attributes '{' switch_body '}' + | SWITCH optional_label paren_cond opt_attributes '{' '}' ; expression_list diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 46767d9c6..3d8095680 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -752,7 +752,13 @@ static LLVMValueRef llvm_emit_switch_jump_stmt(GenContext *c, BEValue min_val; llvm_emit_expr(c, &min_val, exprptr(cases[min_index]->case_stmt.expr)); assert(llvm_value_is_const(&min_val)); - switch_value->value = LLVMBuildSub(c->builder, switch_value->value, min_val.value, ""); + llvm_value_rvalue(c, switch_value); + llvm_value_rvalue(c, &min_val); + LLVMValueRef min = min_val.value; + if (!LLVMIsConstant(min) || !LLVMIsNull(min)) + { + switch_value->value = LLVMBuildSub(c->builder, switch_value->value, min_val.value, ""); + } LLVMValueRef is_valid = LLVMBuildICmp(c->builder, LLVMIntUGT, switch_value->value, llvm_const_int(c, switch_value->type, count - 1), ""); LLVMBasicBlockRef switch_block = llvm_basic_block_new(c, "jumpblock"); LLVMBuildCondBr(c->builder, is_valid, default_block, switch_block); diff --git a/test/test_suite/assert/unreachable.c3t b/test/test_suite/assert/unreachable.c3t index 7e2485304..c93c7d572 100644 --- a/test/test_suite/assert/unreachable.c3t +++ b/test/test_suite/assert/unreachable.c3t @@ -17,7 +17,6 @@ fn void test() define void @unreachable.test() #0 { entry: %x = alloca i32, align 4 - %indirectarg = alloca %"any[]", align 8 %0 = call i32 @unreachable.foo() store i32 %0, ptr %x, align 4 %1 = load i32, ptr %x, align 4 @@ -28,7 +27,5 @@ if.then: ; preds = %entry ret void if.exit: ; preds = %entry - store %"any[]" zeroinitializer, ptr %indirectarg, align 8 - call void @std.core.builtin.panicf(ptr @.str, i64 30, ptr @.str.1, i64 14, ptr @.str.2, i64 4, i32 10, ptr byval(%"any[]") align 8 %indirectarg) unreachable } \ No newline at end of file diff --git a/test/test_suite/switch/jump_with_inc.c3t b/test/test_suite/switch/jump_with_inc.c3t new file mode 100644 index 000000000..b6931fd28 --- /dev/null +++ b/test/test_suite/switch/jump_with_inc.c3t @@ -0,0 +1,65 @@ +// #target: macos-aarch64 +module test; +extern fn int b(int y); + +fn int foo(int x) @export +{ + switch (x) @jump + { + case 1: + nextcase (b(x)); + case 0: + x++; + nextcase x; + default: + unreachable(); + } +} +/* #expect: test.ll + +define i32 @test_foo(i32 %0) #0 { +entry: + %x = alloca i32, align 4 + %switch = alloca i32, align 4 + store i32 %0, ptr %x, align 4 + %1 = load i32, ptr %x, align 4 + store i32 %1, ptr %switch, align 4 + br label %switch.entry + +switch.entry: ; preds = %entry + %2 = load i32, ptr %switch, align 4 + %3 = icmp ugt i32 %2, 1 + br i1 %3, label %switch.default, label %jumpblock + +jumpblock: ; preds = %switch.entry + %ptroffset = getelementptr inbounds [8 x i8], ptr @jumptable, i32 %2 + %target = load ptr, ptr %ptroffset, align 8 + indirectbr ptr %target, [label %switch.case, label %switch.case4] + +switch.case: ; preds = %jumpblock5, %jumpblock1, %jumpblock + %4 = load i32, ptr %x, align 4 + %5 = call i32 @b(i32 %4) + %6 = icmp ugt i32 %5, 1 + br i1 %6, label %switch.default, label %jumpblock1 + +jumpblock1: ; preds = %switch.case + %ptroffset2 = getelementptr inbounds [8 x i8], ptr @jumptable, i32 %5 + %target3 = load ptr, ptr %ptroffset2, align 8 + indirectbr ptr %target3, [label %switch.case, label %switch.case4, label %switch.default] + +switch.case4: ; preds = %jumpblock5, %jumpblock, %jumpblock1 + %7 = load i32, ptr %x, align 4 + %add = add i32 %7, 1 + store i32 %add, ptr %x, align 4 + %8 = load i32, ptr %x, align 4 + %9 = icmp ugt i32 %8, 1 + br i1 %9, label %switch.default, label %jumpblock5 + +jumpblock5: ; preds = %switch.case4 + %ptroffset6 = getelementptr inbounds [8 x i8], ptr @jumptable, i32 %8 + %target7 = load ptr, ptr %ptroffset6, align 8 + indirectbr ptr %target7, [label %switch.case, label %switch.case4, label %switch.default] + +switch.default: ; preds = %jumpblock5, %switch.case4, %jumpblock1, %switch.case, %switch.entry + unreachable +} diff --git a/test/test_suite/switch/simple_jump.c3t b/test/test_suite/switch/simple_jump.c3t index 6adc8bd1d..9dcbce76d 100644 --- a/test/test_suite/switch/simple_jump.c3t +++ b/test/test_suite/switch/simple_jump.c3t @@ -71,6 +71,8 @@ fn void test6(int a) @jumptable.2 = private constant [3 x ptr] [ptr blockaddress(@simple_jump.test3, %switch.case), ptr blockaddress(@simple_jump.test3, %switch.exit), ptr blockaddress(@simple_jump.test3, %switch.case1)], align 4 @jumptable.3 = private constant [1 x ptr] [ptr blockaddress(@simple_jump.test4, %switch.case)], align 4 @jumptable.4 = private constant [4 x ptr] [ptr blockaddress(@simple_jump.test5, %switch.case), ptr blockaddress(@simple_jump.test5, %switch.default), ptr blockaddress(@simple_jump.test5, %switch.default), ptr blockaddress(@simple_jump.test5, %switch.case1)], align 4 +@jumptable.5 = private constant [5 x ptr] [ptr blockaddress(@simple_jump.test6, %switch.case), ptr blockaddress(@simple_jump.test6, %switch.default), ptr blockaddress(@simple_jump.test6, %switch.default), ptr blockaddress(@simple_jump.test6, %switch.case1), ptr blockaddress(@simple_jump.test6, %switch.case2)], align 4 + after_check12: ; preds = %noerr_block8 br label %noerr_block14 @@ -87,7 +89,6 @@ voiderr: ; preds = %noerr_block14, %gua ret void } -; Function Attrs: nounwind uwtable define void @simple_jump.test1(i32 %0) #0 { entry: %switch = alloca i32, align 4 @@ -96,12 +97,11 @@ entry: switch.entry: ; preds = %entry %1 = load i32, ptr %switch, align 4 - %2 = sub i32 %1, 0 - %3 = icmp ugt i32 %2, 2 - br i1 %3, label %switch.exit, label %jumpblock + %2 = icmp ugt i32 %1, 2 + br i1 %2, label %switch.exit, label %jumpblock jumpblock: ; preds = %switch.entry - %ptroffset = getelementptr inbounds [8 x i8], ptr @jumptable, i32 %2 + %ptroffset = getelementptr inbounds [8 x i8], ptr @jumptable, i32 %1 %target = load ptr, ptr %ptroffset, align 8 indirectbr ptr %target, [label %switch.case, label %switch.case1]