From 26dc88e096fd523bf4c71ceefc53e860f25d51b1 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 26 Jan 2025 15:38:24 +0100 Subject: [PATCH] Fix issues with @jump on empty `default` or only `default` #1893 #1894 --- releasenotes.md | 2 +- src/compiler/llvm_codegen_stmt.c | 4 +- src/compiler/sema_stmts.c | 6 ++- test/test_suite/switch/jump_bugs.c3t | 70 ++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 test/test_suite/switch/jump_bugs.c3t diff --git a/releasenotes.md b/releasenotes.md index 3489cb9b8..678034bbb 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -29,7 +29,7 @@ - Fix bug where .min/.max would fail on a distinct int #1888. - 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 ### 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 bc0244009..86ee59bbd 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -777,6 +777,7 @@ static LLVMValueRef llvm_emit_switch_jump_stmt(GenContext *c, LLVMBasicBlockRef default_block, BEValue *switch_value) { + ASSERT_SPAN(switch_ast, min_index > -1); unsigned case_count = vec_size(cases); BEValue min_val; llvm_emit_expr(c, &min_val, exprptr(cases[min_index]->case_stmt.expr)); @@ -848,6 +849,7 @@ static void llvm_emit_switch_jump_table(GenContext *c, Ast *case_ast = cases[i]; if (case_ast->ast_kind == AST_DEFAULT_STMT) { + if (!case_ast->case_stmt.body) continue; default_block = case_ast->case_stmt.backend_block; default_index = i; continue; @@ -914,7 +916,7 @@ static void llvm_emit_switch_jump_table(GenContext *c, if (!case_stmt->case_stmt.body) continue; LLVMAddDestination(instr, block); } - + if (!case_stmt->case_stmt.body) continue; llvm_emit_block(c, block); llvm_emit_stmt(c, case_stmt->case_stmt.body); llvm_emit_br(c, exit_block); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 172df67a2..a15dad0cb 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -2533,7 +2533,11 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc RETURN_SEMA_ERROR(statement, "The switch cannot use a jump table size of the table would exceed " "the maximum allowed (%u), please remove '@jump'.", compiler.build.switchjump_max_size); } - + } + // Do not generate a jump table if we only have a default statement. + if (default_case && case_count == 1) + { + statement->flow.jump = false; } } diff --git a/test/test_suite/switch/jump_bugs.c3t b/test/test_suite/switch/jump_bugs.c3t new file mode 100644 index 000000000..8947effc0 --- /dev/null +++ b/test/test_suite/switch/jump_bugs.c3t @@ -0,0 +1,70 @@ +// #target: macos-aarch64 +module test; + +fn void test1() +{ + switch (0) @jump + { + case 0: + break; + default: + } +} + +enum Foo { A, B, } + +fn void main() +{ + test1(); + switch (Foo.A) @jump + { + default: + break; + } +} + +/* #expect: test.ll + +@jumptable = private unnamed_addr constant [1 x ptr] [ptr blockaddress(@test.test1, %switch.case)], align 4 + +define void @test.test1() #0 { +entry: + %switch = alloca i32, align 4 + 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.exit, 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 + br label %switch.exit + +switch.exit: ; preds = %switch.case, %switch.entry + ret void +} + +define void @test.main() #0 { +entry: + %switch = alloca i32, align 4 + call void @test.test1() + store i32 0, ptr %switch, align 4 + br label %switch.entry + +switch.entry: ; preds = %entry + %0 = load i32, ptr %switch, align 4 + switch i32 %0, label %switch.default [ + ] + +switch.default: ; preds = %switch.entry + br label %switch.exit + +switch.exit: ; preds = %switch.default + ret void +}