diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index f7d7f5f09..b3764e559 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2441,6 +2441,7 @@ typedef struct CopyStruct_ { CopyFixup fixups[MAX_FIXUPS]; CopyFixup *current_fixup; + bool single_static; } CopyStruct; #define MACRO_COPY_DECL(x) x = copy_decl(c, x) @@ -2460,6 +2461,7 @@ typedef struct CopyStruct_ Expr *expr_macro_copy(Expr *source_expr); Decl **decl_copy_list(Decl **decl_list); Ast *ast_macro_copy(Ast *source_ast); +Ast *ast_defer_copy(Ast *source_ast); Expr **copy_expr_list(CopyStruct *c, Expr **expr_list); Expr *copy_expr(CopyStruct *c, Expr *source_expr); diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 5eb066fdd..c3a54a1f4 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -3,8 +3,6 @@ #define SCOPE_FIXUP_START do { CopyFixup *current = c->current_fixup; #define SCOPE_FIXUP_END c->current_fixup = current; } while (0) - - static inline void copy_reg_ref(CopyStruct *c, void *original, void *result) { c->current_fixup->new_ptr = result; @@ -146,12 +144,21 @@ static CopyStruct copy_struct; Ast *ast_macro_copy(Ast *source_ast) { copy_struct.current_fixup = copy_struct.fixups; + copy_struct.single_static = false; + return ast_copy_deep(©_struct, source_ast); +} + +Ast *ast_defer_copy(Ast *source_ast) +{ + copy_struct.current_fixup = copy_struct.fixups; + copy_struct.single_static = true; return ast_copy_deep(©_struct, source_ast); } Expr *expr_macro_copy(Expr *source_expr) { copy_struct.current_fixup = copy_struct.fixups; + copy_struct.single_static = false; return copy_expr(©_struct, source_expr); } @@ -560,9 +567,19 @@ static Attr **copy_attributes(CopyStruct *c, Attr** attr_list) } return list; } + +static inline bool decl_is_resolved_static_var(Decl *decl) +{ + if (decl->resolve_status != RESOLVE_DONE) return false; + if (decl->decl_kind != DECL_VAR) return false; + if (decl->var.kind != VARDECL_LOCAL) return false; + return decl->var.is_static; +} + Decl *copy_decl(CopyStruct *c, Decl *decl) { if (!decl) return NULL; + if (c->single_static && decl_is_resolved_static_var(decl)) return decl; Decl *copy = decl_copy(decl); copy_reg_ref(c, decl, copy); copy->attributes = copy_attributes(c, copy->attributes); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 0a165ccd5..a9a2b9898 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -34,6 +34,8 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl) // then we essentially treat this as a global. if (decl->var.is_static) { + // In defers we might already have generated this variable. + if (decl->backend_ref) return decl->backend_ref; void *builder = c->builder; c->builder = NULL; decl->backend_ref = LLVMAddGlobal(c->module, alloc_type, "tempglobal"); @@ -48,6 +50,7 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl) c->builder = builder; return decl->backend_ref; } + assert(!decl->backend_ref); llvm_emit_local_var_alloca(c, decl); Expr *init = decl->var.init_expr; if (IS_FAILABLE(decl)) diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index bd495efc8..963e916a4 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -88,7 +88,7 @@ AstId context_get_defers(SemaContext *context, AstId defer_top, AstId defer_bott while (defer_bottom != defer_top) { Ast *defer = astptr(defer_top); - Ast *defer_body = ast_macro_copy(astptr(defer->defer_stmt.body)); + Ast *defer_body = ast_defer_copy(astptr(defer->defer_stmt.body)); *next = astid(defer_body); next = &defer_body->next; defer_top = defer->defer_stmt.prev_defer; @@ -105,7 +105,7 @@ void context_pop_defers(SemaContext *context, AstId *next) while (defer_current != defer_start) { Ast *defer = astptr(defer_current); - Ast *defer_body = ast_macro_copy(astptr(defer->defer_stmt.body)); + Ast *defer_body = ast_defer_copy(astptr(defer->defer_stmt.body)); *next = astid(defer_body); next = &defer_body->next; defer_current = defer->defer_stmt.prev_defer; diff --git a/test/test_suite/defer/defer_static_var.c3t b/test/test_suite/defer/defer_static_var.c3t new file mode 100644 index 000000000..81c74d8b1 --- /dev/null +++ b/test/test_suite/defer/defer_static_var.c3t @@ -0,0 +1,205 @@ +// #target: x64-darwin +module foo; +extern fn void printf(char*,...); + +fn int foo(int x) +{ + defer + { + static int y = 0; + y++; + printf("Here we go %d\n", y); + } + if (x > 0) return 2; + return x; +} + +macro void foo2(int x) +{ + printf("->%d\n", x); + for (int i = 0; i < 100; i++) + { + defer + { + static int y = 0; + y++; + printf(">%d--%d\n", i, y); + } + if (i == x) break; + printf("--"); + } +} + +fn void main() +{ + foo(1); + foo(2); + foo(-2); + @foo2(0); + @foo2(1); + @foo2(2); +} + +/* #expect: foo.ll + +@foo.y = internal unnamed_addr global i32 0, align 4 +@main.y = internal unnamed_addr global i32 0, align 4 +@main.y.7 = internal unnamed_addr global i32 0, align 4 +@main.y.12 = internal unnamed_addr global i32 0, align 4 + + +define i32 @foo.foo(i32 %0) #0 { +entry: + %gt = icmp sgt i32 %0, 0 + br i1 %gt, label %if.then, label %if.exit + +if.then: ; preds = %entry + %1 = load i32, i32* @foo.y, align 4 + %add = add i32 %1, 1 + store i32 %add, i32* @foo.y, align 4 + %2 = load i32, i32* @foo.y, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str, i32 0, i32 0), i32 %2) + ret i32 2 + +if.exit: ; preds = %entry + %3 = load i32, i32* @foo.y, align 4 + %add1 = add i32 %3, 1 + store i32 %add1, i32* @foo.y, align 4 + %4 = load i32, i32* @foo.y, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.1, i32 0, i32 0), i32 %4) + ret i32 %0 +} + +define void @foo.main() #0 { +entry: + %x = alloca i32, align 4 + %i = alloca i32, align 4 + %x3 = alloca i32, align 4 + %i4 = alloca i32, align 4 + %x15 = alloca i32, align 4 + %i16 = alloca i32, align 4 + %0 = call i32 @foo.foo(i32 1) + %1 = call i32 @foo.foo(i32 2) + %2 = call i32 @foo.foo(i32 -2) + store i32 0, i32* %x, align 4 + %3 = load i32, i32* %x, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.2, i32 0, i32 0), i32 %3) + store i32 0, i32* %i, align 4 + br label %loop.cond + +loop.cond: ; preds = %if.exit, %entry + %4 = load i32, i32* %i, align 4 + %lt = icmp slt i32 %4, 100 + br i1 %lt, label %loop.body, label %loop.exit + +loop.body: ; preds = %loop.cond + %5 = load i32, i32* %i, align 4 + %6 = load i32, i32* %x, align 4 + %eq = icmp eq i32 %5, %6 + br i1 %eq, label %if.then, label %if.exit + +if.then: ; preds = %loop.body + %7 = load i32, i32* @main.y, align 4 + %add = add i32 %7, 1 + store i32 %add, i32* @main.y, align 4 + %8 = load i32, i32* %i, align 4 + %9 = load i32, i32* @main.y, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.3, i32 0, i32 0), i32 %8, i32 %9) + br label %loop.exit + +if.exit: ; preds = %loop.body + call void (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.4, i32 0, i32 0)) + %10 = load i32, i32* @main.y, align 4 + %add1 = add i32 %10, 1 + store i32 %add1, i32* @main.y, align 4 + %11 = load i32, i32* %i, align 4 + %12 = load i32, i32* @main.y, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.5, i32 0, i32 0), i32 %11, i32 %12) + %13 = load i32, i32* %i, align 4 + %add2 = add i32 %13, 1 + store i32 %add2, i32* %i, align 4 + br label %loop.cond + +loop.exit: ; preds = %if.then, %loop.cond + store i32 1, i32* %x3, align 4 + %14 = load i32, i32* %x3, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.6, i32 0, i32 0), i32 %14) + store i32 0, i32* %i4, align 4 + br label %loop.cond5 + +loop.cond5: ; preds = %if.exit11, %loop.exit + %15 = load i32, i32* %i4, align 4 + %lt6 = icmp slt i32 %15, 100 + br i1 %lt6, label %loop.body7, label %loop.exit14 + +loop.body7: ; preds = %loop.cond5 + %16 = load i32, i32* %i4, align 4 + %17 = load i32, i32* %x3, align 4 + %eq8 = icmp eq i32 %16, %17 + br i1 %eq8, label %if.then9, label %if.exit11 + +if.then9: ; preds = %loop.body7 + %18 = load i32, i32* @main.y.7, align 4 + %add10 = add i32 %18, 1 + store i32 %add10, i32* @main.y.7, align 4 + %19 = load i32, i32* %i4, align 4 + %20 = load i32, i32* @main.y.7, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.8, i32 0, i32 0), i32 %19, i32 %20) + br label %loop.exit14 + +if.exit11: ; preds = %loop.body7 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.9, i32 0, i32 0)) + %21 = load i32, i32* @main.y.7, align 4 + %add12 = add i32 %21, 1 + store i32 %add12, i32* @main.y.7, align 4 + %22 = load i32, i32* %i4, align 4 + %23 = load i32, i32* @main.y.7, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.10, i32 0, i32 0), i32 %22, i32 %23) + %24 = load i32, i32* %i4, align 4 + %add13 = add i32 %24, 1 + store i32 %add13, i32* %i4, align 4 + br label %loop.cond5 + +loop.exit14: ; preds = %if.then9, %loop.cond5 + store i32 2, i32* %x15, align 4 + %25 = load i32, i32* %x15, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.11, i32 0, i32 0), i32 %25) + store i32 0, i32* %i16, align 4 + br label %loop.cond17 + +loop.cond17: ; preds = %if.exit23, %loop.exit14 + %26 = load i32, i32* %i16, align 4 + %lt18 = icmp slt i32 %26, 100 + br i1 %lt18, label %loop.body19, label %loop.exit26 + +loop.body19: ; preds = %loop.cond17 + %27 = load i32, i32* %i16, align 4 + %28 = load i32, i32* %x15, align 4 + %eq20 = icmp eq i32 %27, %28 + br i1 %eq20, label %if.then21, label %if.exit23 + +if.then21: ; preds = %loop.body19 + %29 = load i32, i32* @main.y.12, align 4 + %add22 = add i32 %29, 1 + store i32 %add22, i32* @main.y.12, align 4 + %30 = load i32, i32* %i16, align 4 + %31 = load i32, i32* @main.y.12, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.13, i32 0, i32 0), i32 %30, i32 %31) + br label %loop.exit26 + +if.exit23: ; preds = %loop.body19 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.14, i32 0, i32 0)) + %32 = load i32, i32* @main.y.12, align 4 + %add24 = add i32 %32, 1 + store i32 %add24, i32* @main.y.12, align 4 + %33 = load i32, i32* %i16, align 4 + %34 = load i32, i32* @main.y.12, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.15, i32 0, i32 0), i32 %33, i32 %34) + %35 = load i32, i32* %i16, align 4 + %add25 = add i32 %35, 1 + store i32 %add25, i32* %i16, align 4 + br label %loop.cond17 + +loop.exit26: ; preds = %if.then21, %loop.cond17 + ret void +} \ No newline at end of file