diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index 9e01e16444..78722d4306 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -1903,7 +1903,7 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) { fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) { g.push(.rbp) g.mov_rbp_rsp() - locals_count := node.scope.objects.len + node.params.len + locals_count := node.scope.objects.len + node.params.len + node.defer_stmts.len g.stackframe_size = (locals_count * 8) + 0x10 g.sub8(.rsp, g.stackframe_size) @@ -1917,6 +1917,11 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) { offset += 4 g.mov_reg_to_var(offset, native.fn_arg_registers[i]) } + // define defer vars + for i in 0 .. node.defer_stmts.len { + name := '_defer$i' + g.allocate_var(name, 8, 0) + } // g.stmts(node.stmts) is_main := node.name == 'main.main' @@ -1930,6 +1935,24 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) { // g.leave() g.labels.addrs[0] = g.pos() g.println('; label 0: return') + if g.defer_stmts.len != 0 { + // save return value + g.push(.rax) + for defer_stmt in g.defer_stmts.reverse() { + defer_var := g.get_var_offset('_defer$defer_stmt.idx_in_fn') + g.mov_var_to_reg(.rax, defer_var) + g.cmp_zero(.rax) + label := g.labels.new_label() + jump_addr := g.cjmp(.je) + g.labels.patches << LabelPatch{ + id: label + pos: jump_addr + } + g.stmts(defer_stmt.stmts) + g.labels.addrs[label] = g.pos() + } + g.pop(.rax) + } g.add8(.rsp, g.stackframe_size) g.pop(.rbp) g.ret() diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index 57d8e061e8..c5db1f60f9 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -48,6 +48,7 @@ mut: callpatches []CallPatch strs []String labels &LabelTable + defer_stmts []ast.DeferStmt } enum RelocType { @@ -71,11 +72,10 @@ struct CallPatch { struct LabelTable { mut: - label_id int - return_ids []int = [0] // array is for defer - addrs []i64 = [i64(0)] // register address of label here - patches []LabelPatch // push placeholders - branches []BranchLabel + label_id int + addrs []i64 = [i64(0)] // register address of label here + patches []LabelPatch // push placeholders + branches []BranchLabel } struct LabelPatch { @@ -489,6 +489,7 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) { g.stack_var_pos = 0 g.register_function_address(node.name) g.labels = &LabelTable{} + g.defer_stmts.clear() if g.pref.arch == .arm64 { g.fn_decl_arm64(node) } else { @@ -678,6 +679,12 @@ fn (mut g Gen) stmt(node ast.Stmt) { } } ast.ConstDecl {} + ast.DeferStmt { + defer_var := g.get_var_offset('_defer$g.defer_stmts.len') + g.mov_int_to_var(defer_var, ._8, 1) + g.defer_stmts << node + g.defer_stmts[g.defer_stmts.len - 1].idx_in_fn = g.defer_stmts.len - 1 + } ast.ExprStmt { g.expr(node.expr) } @@ -714,6 +721,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { // dump(node) // dump(node.types) mut s := '?' //${node.exprs[0].val.str()}' + // TODO: void return e0 := node.exprs[0] match e0 { ast.IntegerLiteral { @@ -740,7 +748,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { } // jump to return label - label := g.labels.return_ids.last() + label := 0 pos := g.jmp(0) g.labels.patches << LabelPatch{ id: label diff --git a/vlib/v/gen/native/tests/defer.vv b/vlib/v/gen/native/tests/defer.vv new file mode 100644 index 0000000000..2e7825f375 --- /dev/null +++ b/vlib/v/gen/native/tests/defer.vv @@ -0,0 +1,38 @@ +fn defer_test() { + println('normal1') + defer { + println('defer1') + } + defer { + println('defer2') + } + println('normal2') +} + +fn defer_condition_test(i int) { + if i > 3 { + defer { + println('defer1') + } + } else { + defer { + println('defer2') + } + } +} + +fn defer_return_test() int { + mut i := 1 + defer { + i = 3 + } + return i +} + +fn main() { + defer_test() + defer_condition_test(1) + defer_condition_test(6) + a := defer_return_test() + println(a) +} diff --git a/vlib/v/gen/native/tests/defer.vv.out b/vlib/v/gen/native/tests/defer.vv.out new file mode 100644 index 0000000000..be9161f342 --- /dev/null +++ b/vlib/v/gen/native/tests/defer.vv.out @@ -0,0 +1,7 @@ +normal1 +normal2 +defer2 +defer1 +defer2 +defer1 +1