native: support `else`, `break`, `continue` (#14738)

master
lemon 2022-06-11 17:50:19 +09:00 committed by GitHub
parent da7a166708
commit c7a619d16e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 272 additions and 28 deletions

View File

@ -206,10 +206,13 @@ fn (mut g Gen) cjmp(op JumpOp) int {
return int(pos) return int(pos)
} }
fn (mut g Gen) jmp(addr int) { fn (mut g Gen) jmp(addr int) int {
g.write8(0xe9) g.write8(0xe9)
pos := g.pos()
g.write32(addr) // 0xffffff g.write32(addr) // 0xffffff
g.println('jmp') g.println('jmp')
// return the position of jump address for placeholder
return int(pos)
} }
fn abs(a i64) i64 { fn abs(a i64) i64 {
@ -1149,6 +1152,20 @@ fn (mut g Gen) patch_calls() {
} }
} }
fn (mut g Gen) patch_labels() {
for label in g.labels.patches {
addr := g.labels.addrs[label.id]
if addr == 0 {
g.n_error('label addr = 0')
return
}
// Update jmp or cjmp address.
// The value is the relative address, difference between current position and the location
// after `jxx 00 00 00 00`
g.write32_at(label.pos, int(addr - label.pos - 4))
}
}
fn (mut g Gen) delay_fn_call(name string) { fn (mut g Gen) delay_fn_call(name string) {
pos := g.buf.len pos := g.buf.len
g.callpatches << CallPatch{name, pos} g.callpatches << CallPatch{name, pos}
@ -1567,10 +1584,17 @@ fn (mut g Gen) gen_assert(assert_node ast.AssertStmt) {
} else { } else {
g.n_error('Unsupported expression in assert') g.n_error('Unsupported expression in assert')
} }
label := g.labels.new_label()
cjmp_addr = g.condition(ine, true) cjmp_addr = g.condition(ine, true)
g.labels.patches << LabelPatch{
id: label
pos: cjmp_addr
}
g.println('; jump to label $label')
g.expr(assert_node.expr) g.expr(assert_node.expr)
g.trap() g.trap()
g.write32_at(cjmp_addr, int(g.pos() - cjmp_addr - 4)) // 4 is for "00 00 00 00" g.labels.addrs[label] = g.pos()
g.println('; label $label')
} }
fn (mut g Gen) cjmp_notop(op token.Kind) int { fn (mut g Gen) cjmp_notop(op token.Kind) int {
@ -1669,28 +1693,50 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
if node.is_comptime { if node.is_comptime {
g.n_error('ignored comptime') g.n_error('ignored comptime')
} }
if node.has_else {
g.n_error('else statements not yet supported')
}
if node.branches.len == 0 { if node.branches.len == 0 {
return return
} }
mut endif_label := 0
has_endif := node.branches.len > 1
if has_endif {
endif_label = g.labels.new_label()
}
for idx in 0 .. node.branches.len { for idx in 0 .. node.branches.len {
branch := node.branches[idx] branch := node.branches[idx]
if branch.cond is ast.BoolLiteral { if idx == node.branches.len - 1 && node.has_else {
if branch.cond.val { g.stmts(branch.stmts)
g.stmts(branch.stmts) } else {
if branch.cond is ast.BoolLiteral {
if branch.cond.val {
g.stmts(branch.stmts)
}
continue
} }
continue infix_expr := branch.cond as ast.InfixExpr
label := g.labels.new_label()
cjmp_addr := g.condition(infix_expr, false)
g.labels.patches << LabelPatch{
id: label
pos: cjmp_addr
}
g.println('; jump to label $label')
g.stmts(branch.stmts)
if has_endif {
jump_addr := g.jmp(0)
g.labels.patches << LabelPatch{
id: endif_label
pos: jump_addr
}
g.println('; jump to label $endif_label')
}
// println('after if g.pos=$g.pos() jneaddr=$cjmp_addr')
g.labels.addrs[label] = g.pos()
g.println('; label $label')
} }
infix_expr := branch.cond as ast.InfixExpr }
cjmp_addr := g.condition(infix_expr, false) if has_endif {
g.stmts(branch.stmts) g.labels.addrs[endif_label] = g.pos()
// Now that we know where we need to jump if the condition is false, update the `jne` call. g.println('; label $endif_label')
// The value is the relative address, difference between current position and the location
// after `jne 00 00 00 00`
// println('after if g.pos=$g.pos() jneaddr=$cjmp_addr')
g.write32_at(cjmp_addr, int(g.pos() - cjmp_addr - 4)) // 4 is for "00 00 00 00"
} }
} }
@ -1712,14 +1758,29 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
} }
// infinite loop // infinite loop
start := g.pos() start := g.pos()
start_label := g.labels.new_label()
g.labels.addrs[start_label] = start
g.println('; label $start_label')
end_label := g.labels.new_label()
g.labels.branches << BranchLabel{
name: node.label
start: start_label
end: end_label
}
g.stmts(node.stmts) g.stmts(node.stmts)
g.labels.branches.pop()
g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1)) g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1))
g.println('jmp after infinite for') g.println('jmp after infinite for')
g.labels.addrs[end_label] = g.pos()
g.println('; label $end_label')
return return
} }
infix_expr := node.cond as ast.InfixExpr infix_expr := node.cond as ast.InfixExpr
mut jump_addr := 0 // location of `jne *00 00 00 00*` mut jump_addr := 0 // location of `jne *00 00 00 00*`
start := g.pos() start := g.pos()
start_label := g.labels.new_label()
g.labels.addrs[start_label] = start
g.println('; label $start_label')
match infix_expr.left { match infix_expr.left {
ast.Ident { ast.Ident {
match infix_expr.right { match infix_expr.right {
@ -1752,12 +1813,25 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
g.n_error('unhandled infix.left') g.n_error('unhandled infix.left')
} }
} }
end_label := g.labels.new_label()
g.labels.patches << LabelPatch{
id: end_label
pos: jump_addr
}
g.println('; jump to label $end_label')
g.labels.branches << BranchLabel{
name: node.label
start: start_label
end: end_label
}
g.stmts(node.stmts) g.stmts(node.stmts)
g.labels.branches.pop()
// Go back to `cmp ...` // Go back to `cmp ...`
// Diff between `jmp 00 00 00 00 X` and `cmp` // Diff between `jmp 00 00 00 00 X` and `cmp`
g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1)) g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1))
// Update the jump addr to current pos // Update the jump addr to current pos
g.write32_at(jump_addr, int(g.pos() - jump_addr - 4)) // 4 is for "00 00 00 00" g.labels.addrs[end_label] = g.pos()
g.println('; label $end_label')
g.println('jmp after for') g.println('jmp after for')
} }
@ -1789,6 +1863,8 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) {
return return
} }
// g.leave() // g.leave()
g.labels.addrs[0] = g.pos()
g.println('; label 0: return')
g.add8(.rsp, g.stackframe_size) g.add8(.rsp, g.stackframe_size)
g.pop(.rbp) g.pop(.rbp)
g.ret() g.ret()

View File

@ -47,6 +47,7 @@ mut:
nlines int nlines int
callpatches []CallPatch callpatches []CallPatch
strs []String strs []String
labels &LabelTable
} }
enum RelocType { enum RelocType {
@ -68,6 +69,32 @@ struct CallPatch {
pos int pos int
} }
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
}
struct LabelPatch {
id int
pos int
}
struct BranchLabel {
name string
start int
end int
}
fn (mut l LabelTable) new_label() int {
l.label_id++
l.addrs << 0
return l.label_id
}
enum Size { enum Size {
_8 _8
_16 _16
@ -108,6 +135,7 @@ pub fn gen(files []&ast.File, table &ast.Table, out_name string, pref &pref.Pref
eprintln('No available backend for this configuration. Use `-a arm64` or `-a amd64`.') eprintln('No available backend for this configuration. Use `-a arm64` or `-a amd64`.')
exit(1) exit(1)
} }
labels: 0
} }
g.code_gen.g = g g.code_gen.g = g
g.generate_header() g.generate_header()
@ -458,11 +486,13 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) {
} }
g.stack_var_pos = 0 g.stack_var_pos = 0
g.register_function_address(node.name) g.register_function_address(node.name)
g.labels = &LabelTable{}
if g.pref.arch == .arm64 { if g.pref.arch == .arm64 {
g.fn_decl_arm64(node) g.fn_decl_arm64(node)
} else { } else {
g.fn_decl_amd64(node) g.fn_decl_amd64(node)
} }
g.patch_labels()
} }
pub fn (mut g Gen) register_function_address(name string) { pub fn (mut g Gen) register_function_address(name string) {
@ -505,6 +535,7 @@ fn (mut g Gen) gen_forc_stmt(node ast.ForCStmt) {
g.stmts([node.init]) g.stmts([node.init])
} }
start := g.pos() start := g.pos()
start_label := g.labels.new_label()
mut jump_addr := i64(0) mut jump_addr := i64(0)
if node.has_cond { if node.has_cond {
cond := node.cond cond := node.cond
@ -537,12 +568,27 @@ fn (mut g Gen) gen_forc_stmt(node ast.ForCStmt) {
// dump(node.cond) // dump(node.cond)
g.expr(node.cond) g.expr(node.cond)
} }
end_label := g.labels.new_label()
g.labels.patches << LabelPatch{
id: end_label
pos: int(jump_addr)
}
g.println('; jump to label $end_label')
g.labels.branches << BranchLabel{
name: node.label
start: start_label
end: end_label
}
g.stmts(node.stmts) g.stmts(node.stmts)
g.labels.addrs[start_label] = g.pos()
g.println('; label $start_label')
if node.has_inc { if node.has_inc {
g.stmts([node.inc]) g.stmts([node.inc])
} }
g.labels.branches.pop()
g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1)) g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1))
g.write32_at(jump_addr, int(g.pos() - jump_addr - 4)) g.labels.addrs[end_label] = g.pos()
g.println('; jump to label $end_label')
// loop back // loop back
} }
@ -558,14 +604,30 @@ fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
g.expr(node.cond) g.expr(node.cond)
g.mov_reg_to_var(i, .rax) // i = node.cond // initial value g.mov_reg_to_var(i, .rax) // i = node.cond // initial value
start := g.pos() // label-begin: start := g.pos() // label-begin:
start_label := g.labels.new_label()
g.mov_var_to_reg(.rbx, i) // rbx = iterator value g.mov_var_to_reg(.rbx, i) // rbx = iterator value
g.expr(node.high) // final value g.expr(node.high) // final value
g.cmp_reg(.rbx, .rax) // rbx = iterator, rax = max value g.cmp_reg(.rbx, .rax) // rbx = iterator, rax = max value
jump_addr := g.cjmp(.jge) // leave loop if i is beyond end jump_addr := g.cjmp(.jge) // leave loop if i is beyond end
end_label := g.labels.new_label()
g.labels.patches << LabelPatch{
id: end_label
pos: jump_addr
}
g.println('; jump to label $end_label')
g.labels.branches << BranchLabel{
name: node.label
start: start_label
end: end_label
}
g.stmts(node.stmts) g.stmts(node.stmts)
g.labels.addrs[start_label] = g.pos()
g.println('; label $start_label')
g.inc_var(node.val_var) g.inc_var(node.val_var)
g.labels.branches.pop()
g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1)) g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1))
g.write32_at(jump_addr, int(g.pos() - jump_addr - 4)) g.labels.addrs[end_label] = g.pos()
g.println('; label $end_label')
/* /*
} else if node.kind == .array { } else if node.kind == .array {
} else if node.kind == .array_fixed { } else if node.kind == .array_fixed {
@ -593,6 +655,26 @@ fn (mut g Gen) stmt(node ast.Stmt) {
ast.Block { ast.Block {
g.stmts(node.stmts) g.stmts(node.stmts)
} }
ast.BranchStmt {
label_name := node.label
for i := g.labels.branches.len - 1; i >= 0; i-- {
branch := g.labels.branches[i]
if label_name == '' || label_name == branch.name {
label := if node.kind == .key_break {
branch.end
} else { // continue
branch.start
}
jump_addr := g.jmp(0)
g.labels.patches << LabelPatch{
id: label
pos: jump_addr
}
g.println('; jump to $label: $node.kind')
break
}
}
}
ast.ConstDecl {} ast.ConstDecl {}
ast.ExprStmt { ast.ExprStmt {
g.expr(node.expr) g.expr(node.expr)
@ -654,10 +736,15 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.n_error('unknown return type $e0.type_name()') g.n_error('unknown return type $e0.type_name()')
} }
} }
// intel specific
g.add8(.rsp, g.stackframe_size) // jump to return label
g.pop(.rbp) label := g.labels.return_ids.last()
g.ret() pos := g.jmp(0)
g.labels.patches << LabelPatch{
id: label
pos: pos
}
g.println('; jump to label $label')
} }
ast.AsmStmt { ast.AsmStmt {
g.gen_asm_stmt(node) g.gen_asm_stmt(node)

View File

@ -12,6 +12,7 @@ fn test_macho() {
code_gen: native.Amd64{ code_gen: native.Amd64{
g: 0 g: 0
} }
labels: 0
} }
g.generate_macho_header() g.generate_macho_header()
g.generate_macho_footer() g.generate_macho_footer()

View File

@ -1,4 +1,4 @@
fn main() { fn simple_for_test() {
mut i := 0 mut i := 0
for i = 0; i < 3; i++ { for i = 0; i < 3; i++ {
println('loop0') println('loop0')
@ -23,4 +23,65 @@ fn main() {
println('loop5') println('loop5')
i-- i--
} }
}
fn break_continue_test() {
mut i := 0
for i = 0; i < 3; i++ {
if i == 2 {
break
}
println('loop1')
}
i = 0
for i < 3 {
if i == 2 {
break
}
println('loop2')
i++
}
for j in 0 .. 3 {
if j == 2 {
break
}
println('loop3')
}
for i = 0; i < 3; i++ {
if i < 1 {
continue
}
println('loop4')
}
i = 0
for i < 3 {
if i < 1 {
i++
continue
}
println('loop5')
i++
}
for j in 0 .. 3 {
if j < 1 {
continue
}
println('loop6')
}
outer: for j in 0 .. 3 {
for k in 0 .. 3 {
println('loop7')
if j == 1 {
if k == 1 {
break outer
}
}
}
}
}
fn main() {
simple_for_test()
break_continue_test()
} }

View File

@ -16,3 +16,20 @@ loop4
loop5 loop5
loop5 loop5
loop5 loop5
loop1
loop1
loop2
loop2
loop3
loop3
loop4
loop4
loop5
loop5
loop6
loop6
loop7
loop7
loop7
loop7
loop7

View File

@ -40,7 +40,6 @@ fn test_add() {
} }
} }
/*
fn test_elses() { fn test_elses() {
println('start else') println('start else')
if 1 < 2 { if 1 < 2 {
@ -55,11 +54,10 @@ fn test_elses() {
} }
println('end else') println('end else')
} }
*/
fn main() { fn main() {
println('start') println('start')
test_add() test_add()
// test_elses() test_elses()
println('end') println('end')
} }

View File

@ -4,4 +4,8 @@ var(3) > 1
1 < 3 1 < 3
1 == 1 1 == 1
1 != 3 1 != 3
start else
ok
ok
end else
end end