diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3e46119ed..e2f697779 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -268,7 +268,7 @@ jobs: python3 src/tester.py ../build/c3c test_suite/ - name: bundle_output - if: matrix.llvm_version == env.LLVM_RELEASE_VERSION + if: matrix.llvm_version == 16 run: | mkdir linux cp -r lib linux @@ -277,7 +277,7 @@ jobs: tar czf c3-linux-${{matrix.build_type}}.tar.gz linux - name: upload artifacts - if: matrix.llvm_version == env.LLVM_RELEASE_VERSION + if: matrix.llvm_version == 16 uses: actions/upload-artifact@v3 with: name: c3-linux-${{matrix.build_type}} diff --git a/releasenotes.md b/releasenotes.md index 624db88e2..28289f2df 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -165,6 +165,7 @@ - Fix bug initializing nested struct/unions. - Fix of bool -> vector cast. - Correctly widen C style varargs for distinct types and optionals. +- Fix of too aggressive codegen in ternary codegen with array indexing. ## 0.4.0 Change List diff --git a/resources/examples/fannkuch-redux2.c3 b/resources/examples/fannkuch-redux2.c3 new file mode 100644 index 000000000..dcd7646c8 --- /dev/null +++ b/resources/examples/fannkuch-redux2.c3 @@ -0,0 +1,71 @@ +module fannkuch; +import std::io; +import std::math; + +fn int fannkuchredux(int n) +{ + int[] perm = malloc(int, n)[:n]; + int[] perm1 = malloc(int, n)[:n]; + int* count = malloc(int, n); + int max_flips_count; + int perm_count; + int checksum; + + foreach (int i, &v : perm1) *v = i; + + int r = n; + + while (true) + { + for (; r != 1; r--) count[r - 1] = r; + + perm[..] = perm1[..]; + + int flips_count = 0; + int k; + + while (!((k = perm[0]) == 0)) + { + int k2 = (k + 1) >> 1; + for (int i = 0; i < k2; i++) + { + @swap(perm[i], perm[k - i]); + } + flips_count++; + } + + max_flips_count = max(max_flips_count, flips_count); + checksum += perm_count % 2 == 0 ? flips_count : -flips_count; + + /* Use incremental change to generate another permutation */ + while (true) + { + if (r == n) + { + io::printf("%d\n", checksum); + return max_flips_count; + } + + int perm0 = perm1[0]; + int i = 0; + while (i < r) + { + int j = i + 1; + perm1[i] = perm1[j]; + i = j; + } + perm1[r] = perm0; + count[r] -= 1; + if (count[r] > 0) break; + r++; + } + perm_count++; + } + return 0; +} + +fn void! main(String[] argv) +{ + int n = argv.len > 1 ? argv[1].to_int()!! : 7; + io::printf("Pfannkuchen(%d) = %d\n", n, fannkuchredux(n)); +} \ No newline at end of file diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index fd824f394..eb2528187 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -17,7 +17,6 @@ static inline void llvm_emit_expression_list_expr(GenContext *c, BEValue *be_val static inline void llvm_emit_bitassign_array(GenContext *c, BEValue *result, BEValue parent, Decl *parent_decl, Decl *member); static inline void llvm_emit_builtin_access(GenContext *c, BEValue *be_value, Expr *expr); static inline void llvm_emit_const_initialize_reference(GenContext *c, BEValue *ref, Expr *expr); -static inline void llvm_emit_elvis_expr(GenContext *c, BEValue *value, Expr *expr); static inline void llvm_emit_expr_block(GenContext *context, BEValue *be_value, Expr *expr); static inline void llvm_emit_optional(GenContext *c, BEValue *be_value, Expr *expr); static inline void llvm_emit_inc_dec_change(GenContext *c, BEValue *addr, BEValue *after, BEValue *before, Expr *expr, @@ -4238,85 +4237,86 @@ static void llvm_emit_binary_expr(GenContext *c, BEValue *be_value, Expr *expr) static inline void llvm_emit_elvis_expr(GenContext *c, BEValue *value, Expr *expr) { + // Set up basic blocks, following Cone LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "cond.phi"); LLVMBasicBlockRef rhs_block = llvm_basic_block_new(c, "cond.rhs"); // Generate condition and conditional branch Expr *cond = exprptr(expr->ternary_expr.cond); llvm_emit_expr(c, value, cond); - - if (value->type == type_void) return; - - // Get the Rvalue version (in case we have an address) llvm_value_rvalue(c, value); - LLVMValueRef lhs = value->value; - Type *cond_type = cond->type; + LLVMValueRef lhs_value = value->value; - // If the cond is not a boolean, we need to do the cast. if (value->kind != BE_BOOLEAN) { - CastKind cast = cast_to_bool_kind(cond_type); - llvm_emit_cast(c, cast, cond, value, type_bool, cond_type); - assert(value->kind == BE_BOOLEAN); + CastKind cast = cast_to_bool_kind(value->type); + llvm_emit_cast(c, cast, cond, value, type_bool, value->type); } Expr *else_expr = exprptr(expr->ternary_expr.else_expr); - if (!IS_OPTIONAL(expr) && expr_is_constant_eval(else_expr, CONSTANT_EVAL_NO_SIDE_EFFECTS)) + if (!IS_OPTIONAL(expr) && expr_is_const(else_expr)) { BEValue right; llvm_emit_expr(c, &right, else_expr); llvm_value_rvalue(c, &right); - LLVMValueRef val = LLVMBuildSelect(c->builder, value->value, lhs, right.value, "elvis"); + LLVMValueRef val = LLVMBuildSelect(c->builder, value->value, lhs_value, right.value, "elvis"); llvm_value_set(value, val, right.type); return; } LLVMBasicBlockRef lhs_exit = llvm_get_current_block_if_in_use(c); - assert(lhs_exit); + if (!lhs_exit) return; + llvm_emit_cond_br(c, value, phi_block, rhs_block); - llvm_emit_block(c, rhs_block); - // Emit right side: - llvm_emit_expr(c, value, else_expr); - // Lower to value. - llvm_value_rvalue(c, value); - - LLVMBasicBlockRef rhs_exit = llvm_get_current_block_if_in_use(c); - - // RHS may be a jump, in that case just emit the "phi" block. - if (!rhs_exit) + BEValue rhs; + llvm_emit_expr(c, &rhs, else_expr); + llvm_value_rvalue(c, &rhs); + LLVMValueRef rhs_value = rhs.value; + if (rhs.type == type_bool && LLVMTypeOf(rhs_value) != c->bool_type) { - llvm_emit_block(c, phi_block); - return; + llvm_emit_trunc_bool(c, rhs_value); } - - llvm_emit_br(c, phi_block); + LLVMBasicBlockRef rhs_exit = llvm_get_current_block_if_in_use(c); + if (rhs_exit) llvm_emit_br(c, phi_block); // Generate phi llvm_emit_block(c, phi_block); + if (!rhs_exit) + { + if (!lhs_value) lhs_value = LLVMGetUndef(llvm_get_type(c, expr->type)); + llvm_value_set(value, lhs_value, expr->type); + return; + } - // If both sides are bool we produce a bool as well. - LLVMTypeRef phi_type = expr->type->canonical->type_kind == TYPE_BOOL ? c->bool_type : llvm_get_type(c, expr->type); - LLVMValueRef phi = LLVMBuildPhi(c->builder, phi_type, "val"); + if (!lhs_exit) + { + if (!rhs_value) rhs_value = LLVMGetUndef(llvm_get_type(c, expr->type)); + llvm_value_set(value, rhs_value, expr->type); + return; + } - LLVMValueRef logic_values[2] = { lhs, value->value }; + Type *expr_type = type_flatten(expr->type); + LLVMTypeRef type = expr_type == type_bool ? c->bool_type : llvm_get_type(c, expr_type); + LLVMValueRef phi = LLVMBuildPhi(c->builder, type, "val"); + LLVMValueRef logic_values[2] = { lhs_value, rhs_value }; LLVMBasicBlockRef blocks[2] = { lhs_exit, rhs_exit }; LLVMAddIncoming(phi, logic_values, blocks, 2); - - // The rest of value should now be set to the right value. - value->value = phi; + llvm_value_set(value, phi, expr_type); } void gencontext_emit_ternary_expr(GenContext *c, BEValue *value, Expr *expr) { - bool is_elvis = !expr->ternary_expr.then_expr; + if (!expr->ternary_expr.then_expr) return llvm_emit_elvis_expr(c, value, expr); // Set up basic blocks, following Cone LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "cond.phi"); LLVMBasicBlockRef lhs_block = llvm_basic_block_new(c, "cond.lhs"); LLVMBasicBlockRef rhs_block = llvm_basic_block_new(c, "cond.rhs"); + bool is_elvis = false; + // Generate condition and conditional branch Expr *cond = exprptr(expr->ternary_expr.cond); llvm_emit_expr(c, value, cond); @@ -4345,8 +4345,8 @@ void gencontext_emit_ternary_expr(GenContext *c, BEValue *value, Expr *expr) } - if (!IS_OPTIONAL(expr) && expr_is_constant_eval(else_expr, CONSTANT_EVAL_NO_SIDE_EFFECTS) - && (is_elvis || expr_is_constant_eval(then_expr, CONSTANT_EVAL_NO_SIDE_EFFECTS))) + if (!IS_OPTIONAL(expr) && expr_is_const(else_expr) + && (is_elvis || expr_is_const(then_expr))) { if (!lhs_value) { diff --git a/test/test_suite/abi/macho_section_attributes.c3 b/test/test_suite/abi/macho_section_attributes.c3 index a889be39e..b6e1f7707 100644 --- a/test/test_suite/abi/macho_section_attributes.c3 +++ b/test/test_suite/abi/macho_section_attributes.c3 @@ -4,5 +4,5 @@ int foo @section("foo, 12345678901234567 "); // #error: Mach-o requires t int bar @section("foo, "); // #error: Mach-o requires 'segment,section' as the format, did you type it correctly? int baz @section("foo"); // #error: Mach-o requires 'segment,section' as the format, did you type it correctly? int abc @section("foo,b,c,d,e,f"); // #error: Too many parts to the Mach-o section description. -int def @section("foo,b,c,d"); +int defg @section("foo,b,c,d"); diff --git a/test/test_suite/bitstruct/bitstruct_cast_const_init.c3t b/test/test_suite/bitstruct/bitstruct_cast_const_init.c3t index 6a1f822cd..42faeb11b 100644 --- a/test/test_suite/bitstruct/bitstruct_cast_const_init.c3t +++ b/test/test_suite/bitstruct/bitstruct_cast_const_init.c3t @@ -3,13 +3,13 @@ module test; bitstruct Foo : int { int abc : 0..4; - int def : 23..26; + int defg : 23..26; } fn void main() { Foo f; - int z = (int) Foo { .abc = 2, .def = 1 }; + int z = (int) Foo { .abc = 2, .defg = 1 }; } /* #expect: test.ll diff --git a/test/test_suite/clang/2002-01_02.c3t b/test/test_suite/clang/2002-01_02.c3t index 527c059fa..c7d368bc4 100644 --- a/test/test_suite/clang/2002-01_02.c3t +++ b/test/test_suite/clang/2002-01_02.c3t @@ -81,7 +81,7 @@ fn int test(int x) { } extern fn void abc(int *x); -fn int def(int y, int z) { +fn int deff(int y, int z) { abc(&z); return y; } @@ -234,7 +234,7 @@ entry: declare void @abc(ptr) #0 -define i32 @test.def(i32 %0, i32 %1) #0 { +define i32 @test.deff(i32 %0, i32 %1) #0 { entry: %z = alloca i32, align 4 store i32 %1, ptr %z, align 4 diff --git a/test/test_suite/compile_time/untyped_conversions.c3t b/test/test_suite/compile_time/untyped_conversions.c3t index c5770bc8c..2d9b96a44 100644 --- a/test/test_suite/compile_time/untyped_conversions.c3t +++ b/test/test_suite/compile_time/untyped_conversions.c3t @@ -13,10 +13,10 @@ fn void main() { var $x = { { 1, 2 } }; Foo[1] abc = (Foo[1])$x; - Foo def = (Foo)$x[0]; + Foo defg = (Foo)$x[0]; int[2][1] y = (int[2][1])$x; double[2][1] y2 = $x; - io::printfn("%s %s {%s, %s}", y, y2, def.a, def.b); + io::printfn("%s %s {%s, %s}", y, y2, defg.a, defg.b); test({ 1, 2 }, { 3, 4}, { 5, 6 }); var $a = { 2, 7 }; test($a, $a, $a); @@ -78,7 +78,7 @@ entry: define void @test.main() #0 { entry: %abc = alloca [1 x %Foo], align 4 - %def = alloca %Foo, align 4 + %defg = alloca %Foo, align 4 %y = alloca [1 x [2 x i32]], align 4 %y2 = alloca [1 x [2 x double]], align 16 %retparam = alloca i64, align 8 @@ -90,7 +90,7 @@ entry: %literal3 = alloca [2 x i32], align 4 %taddr4 = alloca <2 x i32>, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 4 %abc, ptr align 4 @.__const, i32 8, i1 false) - call void @llvm.memcpy.p0.p0.i32(ptr align 4 %def, ptr align 4 @.__const.1, i32 8, i1 false) + call void @llvm.memcpy.p0.p0.i32(ptr align 4 %defg, ptr align 4 @.__const.1, i32 8, i1 false) call void @llvm.memcpy.p0.p0.i32(ptr align 4 %y, ptr align 4 @.__const.2, i32 8, i1 false) call void @llvm.memcpy.p0.p0.i32(ptr align 16 %y2, ptr align 16 @.__const.3, i32 16, i1 false) %0 = insertvalue %any undef, ptr %y, 0 @@ -101,12 +101,12 @@ entry: %4 = insertvalue %any %3, i64 ptrtoint (ptr @"$ct.a1$a2$double" to i64), 1 %5 = getelementptr inbounds [4 x %any], ptr %varargslots, i64 0, i64 1 store %any %4, ptr %5, align 16 - %6 = getelementptr inbounds %Foo, ptr %def, i32 0, i32 0 + %6 = getelementptr inbounds %Foo, ptr %defg, i32 0, i32 0 %7 = insertvalue %any undef, ptr %6, 0 %8 = insertvalue %any %7, i64 ptrtoint (ptr @"$ct.int" to i64), 1 %9 = getelementptr inbounds [4 x %any], ptr %varargslots, i64 0, i64 2 store %any %8, ptr %9, align 16 - %10 = getelementptr inbounds %Foo, ptr %def, i32 0, i32 1 + %10 = getelementptr inbounds %Foo, ptr %defg, i32 0, i32 1 %11 = insertvalue %any undef, ptr %10, 0 %12 = insertvalue %any %11, i64 ptrtoint (ptr @"$ct.int" to i64), 1 %13 = getelementptr inbounds [4 x %any], ptr %varargslots, i64 0, i64 3 diff --git a/test/test_suite/statements/comparison_widening.c3t b/test/test_suite/statements/comparison_widening.c3t index d79f526b1..07d03769d 100644 --- a/test/test_suite/statements/comparison_widening.c3t +++ b/test/test_suite/statements/comparison_widening.c3t @@ -28,7 +28,7 @@ entry: store i8 %ternary, ptr %c, align 1 %2 = load i32, ptr %b, align 4 %intbool = icmp ne i32 %2, 0 - %ternary1 = select i1 %intbool, i32 %2, i32 1 - store i32 %ternary1, ptr %d, align 4 + %elvis = select i1 %intbool, i32 %2, i32 1 + store i32 %elvis, ptr %d, align 4 ret void } \ No newline at end of file