From a88364aaad0088c02682a3a7403a51552930760e Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 27 Jan 2025 11:35:55 +0100 Subject: [PATCH] Fixes miscompilation of nested `@jump` #1896. --- releasenotes.md | 2 + src/compiler/llvm_codegen_stmt.c | 12 +- test/test_suite/switch/jump_bug_nested.c3t | 141 +++++++++++++++++++++ 3 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 test/test_suite/switch/jump_bug_nested.c3t diff --git a/releasenotes.md b/releasenotes.md index 678034bbb..a79014f2c 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -30,6 +30,8 @@ - Fix issue where compile time declarations in expression list would not be handled properly. - Issue where trailing body argument was allowed without type even though the definition specified it #1879. - Fix issues with @jump on empty `default` or only `default` #1893 #1894 +- Fixes miscompilation of nested `@jump` #1896. + ### Stdlib changes - Added '%h' and '%H' for printing out binary data in hexadecimal using the formatter. - Added weakly linked `__powidf2` diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 86ee59bbd..6aaba2adc 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -847,6 +847,7 @@ static void llvm_emit_switch_jump_table(GenContext *c, for (unsigned i = 0; i < case_count; i++) { Ast *case_ast = cases[i]; + printf("Block %p\n", case_ast->case_stmt.backend_block); if (case_ast->ast_kind == AST_DEFAULT_STMT) { if (!case_ast->case_stmt.body) continue; @@ -889,10 +890,16 @@ static void llvm_emit_switch_jump_table(GenContext *c, llvm_set_private_declaration(jmptable); LLVMSetGlobalConstant(jmptable, 1); BEValue array_value; - LLVMValueRef instr = llvm_emit_switch_jump_stmt(c, switch_ast, cases, count, min_index, jmptable, default_block, switch_value); - static LLVMValueRef refs[DEFAULT_SWITCH_JUMP_MAX_SIZE + 1]; +#define REF_STACK 16 + LLVMValueRef refs_stack[REF_STACK]; + LLVMValueRef *refs = refs_stack; + if (count > REF_STACK) + { + refs = MALLOC(sizeof(LLVMValueRef) * count); + } +#undef REF_STACK LLVMValueRef default_block_address = LLVMBlockAddress(c->cur_func.ref, default_block); ASSERT(count < DEFAULT_SWITCH_JUMP_MAX_SIZE + 1); memset(refs, 0, sizeof(LLVMValueRef) * count); @@ -931,7 +938,6 @@ static void llvm_emit_switch_jump_table(GenContext *c, found = true; LLVMAddDestination(instr, default_block); } - LLVMSetInitializer(jmptable, LLVMConstArray(c->ptr_type, refs, count)); llvm_emit_block(c, exit_block); } diff --git a/test/test_suite/switch/jump_bug_nested.c3t b/test/test_suite/switch/jump_bug_nested.c3t new file mode 100644 index 000000000..d02b80913 --- /dev/null +++ b/test/test_suite/switch/jump_bug_nested.c3t @@ -0,0 +1,141 @@ +module test; +import std; +fn void main() +{ + switch (0) @jump + { + case 0: + foo(); + } +} + + +macro void foo() +{ + io::printn("Inside Foo"); + switch (1) @jump + { + case 2: + io::printfn("This should never happen"); + } +} + +/* #expect: test.ll + + +---------------------------------------------------> test.ll + +define void @test.main() #0 { +entry: + %switch = alloca i32, align 4 + %len = alloca i64, align 8 + %error_var = alloca i64, align 8 + %retparam = alloca i64, align 8 + %taddr = alloca %"char[]", align 8 + %error_var2 = alloca i64, align 8 + %error_var8 = alloca i64, align 8 + %switch14 = alloca i32, align 4 + %retparam20 = alloca i64, align 8 + %taddr21 = alloca %"char[]", align 8 + %taddr22 = alloca %"any[]", align 8 + store i32 0, ptr %switch, align 4 + br label %switch.entry + +switch.entry: ; preds = %entry + %0 = load i32, ptr %switch, align 4 + %1 = icmp ugt i32 %0, 0 + br i1 %1, label %switch.exit25, label %jumpblock + +jumpblock: ; preds = %switch.entry + %ptroffset = getelementptr inbounds [8 x i8], ptr @jumptable, i32 %0 + %target = load ptr, ptr %ptroffset, align 8 + indirectbr ptr %target, [label %switch.case] + +switch.case: ; preds = %jumpblock + %2 = call ptr @std.io.stdout() + store %"char[]" { ptr @.str, i64 10 }, ptr %taddr, align 8 + %3 = load [2 x i64], ptr %taddr, align 8 + %4 = call i64 @std.io.File.write(ptr %retparam, ptr %2, [2 x i64] %3) + %not_err = icmp eq i64 %4, 0 + %5 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %5, label %after_check, label %assign_optional + +assign_optional: ; preds = %switch.case + store i64 %4, ptr %error_var, align 8 + br label %guard_block + +after_check: ; preds = %switch.case + br label %noerr_block + +guard_block: ; preds = %assign_optional + br label %voiderr + +noerr_block: ; preds = %after_check + %6 = load i64, ptr %retparam, align 8 + store i64 %6, ptr %len, align 8 + %7 = call i64 @std.io.File.write_byte(ptr %2, i8 10) + %not_err3 = icmp eq i64 %7, 0 + %8 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true) + br i1 %8, label %after_check5, label %assign_optional4 + +assign_optional4: ; preds = %noerr_block + store i64 %7, ptr %error_var2, align 8 + br label %guard_block6 + +after_check5: ; preds = %noerr_block + br label %noerr_block7 + +guard_block6: ; preds = %assign_optional4 + br label %voiderr + +noerr_block7: ; preds = %after_check5 + %9 = call i64 @std.io.File.flush(ptr %2) + %not_err9 = icmp eq i64 %9, 0 + %10 = call i1 @llvm.expect.i1(i1 %not_err9, i1 true) + br i1 %10, label %after_check11, label %assign_optional10 + +assign_optional10: ; preds = %noerr_block7 + store i64 %9, ptr %error_var8, align 8 + br label %guard_block12 + +after_check11: ; preds = %noerr_block7 + br label %noerr_block13 + +guard_block12: ; preds = %assign_optional10 + br label %voiderr + +noerr_block13: ; preds = %after_check11 + %11 = load i64, ptr %len, align 8 + %add = add i64 %11, 1 + br label %voiderr + +voiderr: ; preds = %noerr_block13, %guard_block12, %guard_block6, %guard_block + store i32 1, ptr %switch14, align 4 + br label %switch.entry15 + +switch.entry15: ; preds = %voiderr + %12 = load i32, ptr %switch14, align 4 + %13 = sub i32 %12, 2 + %14 = icmp ugt i32 %13, 0 + br i1 %14, label %switch.exit, label %jumpblock16 + +jumpblock16: ; preds = %switch.entry15 + %ptroffset17 = getelementptr inbounds [8 x i8], ptr @jumptable.1, i32 %13 + %target18 = load ptr, ptr %ptroffset17, align 8 + indirectbr ptr %target18, [label %switch.case19] + +switch.case19: ; preds = %jumpblock16 + store %"char[]" { ptr @.str.2, i64 24 }, ptr %taddr21, align 8 + %15 = load [2 x i64], ptr %taddr21, align 8 + store %"any[]" zeroinitializer, ptr %taddr22, align 8 + %16 = load [2 x i64], ptr %taddr22, align 8 + %17 = call i64 @std.io.printfn(ptr %retparam20, [2 x i64] %15, [2 x i64] %16) + br label %switch.exit + +switch.exit: ; preds = %switch.case19, %switch.entry15 + br label %switch.exit25 + +switch.exit25: ; preds = %switch.exit, %switch.entry + ret void +} +