native: initial support for `defer` (#14779)

master
lemon 2022-06-19 05:51:31 +09:00 committed by GitHub
parent 10051e005a
commit e0310964d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 7 deletions

View File

@ -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()

View File

@ -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

View File

@ -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)
}

View File

@ -0,0 +1,7 @@
normal1
normal2
defer2
defer1
defer2
defer1
1