Fixes miscompilation of nested @jump #1896.

This commit is contained in:
Christoffer Lerno
2025-01-27 11:35:55 +01:00
parent 9530fe8fcd
commit a88364aaad
3 changed files with 152 additions and 3 deletions

View File

@@ -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`

View File

@@ -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);
}

View File

@@ -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
}