diff --git a/resources/examples/fannkuch-redux.c3 b/resources/examples/fannkuch-redux.c3 new file mode 100644 index 000000000..cde24d3ad --- /dev/null +++ b/resources/examples/fannkuch-redux.c3 @@ -0,0 +1,81 @@ +module fannkuch; +import std::mem; + +macro int max(int a, int b) +{ + return a > b ? a : b; +} + +func int fannkuchredux(int n) +{ + int* perm = mem::alloc($sizeof(int), n); + int* perm1 = mem::alloc($sizeof(int), n); + int* count = mem::alloc($sizeof(int), n); + int maxFlipsCount; + int permCount; + int checksum; + + for (int i = 0; i < n; i++) perm1[i] = i; + + int r = n; + + while (1) + { + for (; r != 1; r--) count[r - 1] = r; + + for (int i = 0; i < n; i++) perm[i] = perm1[i]; + + int flipsCount = 0; + int k; + + while (!((k = perm[0]) == 0)) + { + int k2 = (k + 1) >> 1; + for (int i = 0; i < k2; i++) + { + int temp = perm[i]; + perm[i] = perm[k - i]; + perm[k - i] = temp; + } + flipsCount++; + } + + maxFlipsCount = @max(maxFlipsCount, flipsCount); + checksum += permCount % 2 == 0 ? flipsCount : -flipsCount; + + /* Use incremental change to generate another permutation */ + while (1) + { + if (r == n) + { + printf("%d\n", checksum); + return maxFlipsCount; + } + + 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] = count[r] - 1; + if (count[r] > 0) break; + r++; + } + permCount++; + } + return 0; +} + +extern func int atoi(char *s); +extern func int printf(char *s, ...); + +func int main(int argc, char** argv) +{ + int n = argc > 1 ? atoi(argv[1]) : 7; + printf("Pfannkuchen(%d) = %d\n", n, fannkuchredux(n)); + return 0; +} \ No newline at end of file diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index b7f30afb7..dddfbef80 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -572,17 +572,41 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast) // First, emit all inits. LLVMBasicBlockRef exit_block = llvm_basic_block_new(context, "while.exit"); LLVMBasicBlockRef begin_block = llvm_basic_block_new(context, "while.begin"); - LLVMBasicBlockRef body_block = ast->while_stmt.body->compound_stmt.stmts ? llvm_basic_block_new(context, - "while.body") : NULL; + LLVMBasicBlockRef body_block = ast->while_stmt.body->compound_stmt.stmts ? llvm_basic_block_new(context, "while.body") : NULL; ast->while_stmt.continue_block = begin_block; ast->while_stmt.break_block = exit_block; + Expr *cond = ast->while_stmt.cond; + + bool is_infinite_loop = false; + + // Is this while (false) or while (true) + if (cond->expr_kind == EXPR_COND && vec_size(cond->cond_expr) == 1) + { + Expr *expr = cond->cond_expr[0]; + if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_BOOL) + { + is_infinite_loop = expr->const_expr.b; + // This is a NOP + if (!is_infinite_loop) return; + assert(body_block); + } + } + + DeferList defers = { 0, 0 }; + // Emit cond llvm_emit_br(context, begin_block); + + if (is_infinite_loop) + { + body_block = begin_block; + goto EMIT_BODY; + } + llvm_emit_block(context, begin_block); - DeferList defers = { 0, 0 }; - Expr *cond = ast->while_stmt.cond; + if (cond->expr_kind == EXPR_SCOPED_EXPR) { defers = cond->expr_scope.defers; @@ -606,6 +630,7 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast) llvm_emit_cond_br(context, &be_value, begin_block, exit_block); } +EMIT_BODY: if (body_block) { llvm_emit_block(context, body_block); diff --git a/test/test_suite/statements/foreach_custom.c3t b/test/test_suite/statements/foreach_custom.c3t index 675c3cf36..d0836e7ed 100644 --- a/test/test_suite/statements/foreach_custom.c3t +++ b/test/test_suite/statements/foreach_custom.c3t @@ -132,14 +132,8 @@ for.body: br label %while.begin while.begin: - br i1 true, label %while.body, label %while.exit - -while.body: br label %for.exit -while.exit: - br label %for.cond - for.exit: ret void } diff --git a/test/test_suite/statements/foreach_custom_macro.c3t b/test/test_suite/statements/foreach_custom_macro.c3t index 49261fef4..157bd8883 100644 --- a/test/test_suite/statements/foreach_custom_macro.c3t +++ b/test/test_suite/statements/foreach_custom_macro.c3t @@ -39,7 +39,6 @@ extern func int printf(char *fmt, ...); // #expect: foo.ll define void @main() -entry: %i = alloca [3 x i32], align 4 %x = alloca %Foo, align 8 %f = alloca i32, align 4 @@ -69,13 +68,13 @@ entry: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %8, i8* align 8 %9, i32 16, i1 false) br label %expr_block.exit -expr_block.exit: +expr_block.exit: ; preds = %entry %10 = bitcast %FooIterator* %.iterator to i8* %11 = bitcast %FooIterator* %blockret to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %10, i8* align 8 %11, i32 16, i1 false) br label %for.cond -for.cond: +for.cond: ; preds = %expr_block.exit store %FooIterator* %.iterator, %FooIterator** %it, align 8 store i32* %f, i32** %value, align 8 %12 = load %FooIterator*, %FooIterator** %it, align 8 @@ -90,11 +89,11 @@ for.cond: %eq = icmp eq i64 %14, %19 br i1 %eq, label %if.then, label %if.exit -if.then: +if.then: ; preds = %for.cond store i8 0, i8* %blockret2, align 1 br label %expr_block.exit3 -if.exit: +if.exit: ; preds = %for.cond %20 = load i32*, i32** %value, align 8 %21 = load %FooIterator*, %FooIterator** %it, align 8 %22 = getelementptr inbounds %FooIterator, %FooIterator* %21, i32 0, i32 1 @@ -113,25 +112,19 @@ if.exit: store i8 1, i8* %blockret2, align 1 br label %expr_block.exit3 -expr_block.exit3: +expr_block.exit3: ; preds = %if.exit, %if.then %29 = load i8, i8* %blockret2, align 1 %30 = trunc i8 %29 to i1 br i1 %30, label %for.body, label %for.exit -for.body: +for.body: ; preds = %expr_block.exit3 %31 = load i32, i32* %f, align 4 %32 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %31) br label %while.begin -while.begin: - br i1 true, label %while.body, label %while.exit - -while.body: +while.begin: ; preds = %for.body br label %for.exit -while.exit: - br label %for.cond - -for.exit: +for.exit: ; preds = %while.begin, %expr_block.exit3 ret void }