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)
}
fn (mut g Gen) jmp(addr int) {
fn (mut g Gen) jmp(addr int) int {
g.write8(0xe9)
pos := g.pos()
g.write32(addr) // 0xffffff
g.println('jmp')
// return the position of jump address for placeholder
return int(pos)
}
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) {
pos := g.buf.len
g.callpatches << CallPatch{name, pos}
@ -1567,10 +1584,17 @@ fn (mut g Gen) gen_assert(assert_node ast.AssertStmt) {
} else {
g.n_error('Unsupported expression in assert')
}
label := g.labels.new_label()
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.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 {
@ -1669,14 +1693,19 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
if node.is_comptime {
g.n_error('ignored comptime')
}
if node.has_else {
g.n_error('else statements not yet supported')
}
if node.branches.len == 0 {
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 {
branch := node.branches[idx]
if idx == node.branches.len - 1 && node.has_else {
g.stmts(branch.stmts)
} else {
if branch.cond is ast.BoolLiteral {
if branch.cond.val {
g.stmts(branch.stmts)
@ -1684,13 +1713,30 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
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)
// Now that we know where we need to jump if the condition is false, update the `jne` call.
// The value is the relative address, difference between current position and the location
// after `jne 00 00 00 00`
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.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')
}
}
if has_endif {
g.labels.addrs[endif_label] = g.pos()
g.println('; label $endif_label')
}
}
@ -1712,14 +1758,29 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
}
// infinite loop
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.labels.branches.pop()
g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1))
g.println('jmp after infinite for')
g.labels.addrs[end_label] = g.pos()
g.println('; label $end_label')
return
}
infix_expr := node.cond as ast.InfixExpr
mut jump_addr := 0 // location of `jne *00 00 00 00*`
start := g.pos()
start_label := g.labels.new_label()
g.labels.addrs[start_label] = start
g.println('; label $start_label')
match infix_expr.left {
ast.Ident {
match infix_expr.right {
@ -1752,12 +1813,25 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
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.labels.branches.pop()
// Go back to `cmp ...`
// Diff between `jmp 00 00 00 00 X` and `cmp`
g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1))
// 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')
}
@ -1789,6 +1863,8 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) {
return
}
// g.leave()
g.labels.addrs[0] = g.pos()
g.println('; label 0: return')
g.add8(.rsp, g.stackframe_size)
g.pop(.rbp)
g.ret()

View File

@ -47,6 +47,7 @@ mut:
nlines int
callpatches []CallPatch
strs []String
labels &LabelTable
}
enum RelocType {
@ -68,6 +69,32 @@ struct CallPatch {
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 {
_8
_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`.')
exit(1)
}
labels: 0
}
g.code_gen.g = g
g.generate_header()
@ -458,11 +486,13 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) {
}
g.stack_var_pos = 0
g.register_function_address(node.name)
g.labels = &LabelTable{}
if g.pref.arch == .arm64 {
g.fn_decl_arm64(node)
} else {
g.fn_decl_amd64(node)
}
g.patch_labels()
}
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])
}
start := g.pos()
start_label := g.labels.new_label()
mut jump_addr := i64(0)
if node.has_cond {
cond := node.cond
@ -537,12 +568,27 @@ fn (mut g Gen) gen_forc_stmt(node ast.ForCStmt) {
// dump(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.labels.addrs[start_label] = g.pos()
g.println('; label $start_label')
if node.has_inc {
g.stmts([node.inc])
}
g.labels.branches.pop()
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
}
@ -558,14 +604,30 @@ fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
g.expr(node.cond)
g.mov_reg_to_var(i, .rax) // i = node.cond // initial value
start := g.pos() // label-begin:
start_label := g.labels.new_label()
g.mov_var_to_reg(.rbx, i) // rbx = iterator value
g.expr(node.high) // final value
g.cmp_reg(.rbx, .rax) // rbx = iterator, rax = max value
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.labels.addrs[start_label] = g.pos()
g.println('; label $start_label')
g.inc_var(node.val_var)
g.labels.branches.pop()
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_fixed {
@ -593,6 +655,26 @@ fn (mut g Gen) stmt(node ast.Stmt) {
ast.Block {
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.ExprStmt {
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()')
}
}
// intel specific
g.add8(.rsp, g.stackframe_size)
g.pop(.rbp)
g.ret()
// jump to return label
label := g.labels.return_ids.last()
pos := g.jmp(0)
g.labels.patches << LabelPatch{
id: label
pos: pos
}
g.println('; jump to label $label')
}
ast.AsmStmt {
g.gen_asm_stmt(node)

View File

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

View File

@ -1,4 +1,4 @@
fn main() {
fn simple_for_test() {
mut i := 0
for i = 0; i < 3; i++ {
println('loop0')
@ -23,4 +23,65 @@ fn main() {
println('loop5')
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
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() {
println('start else')
if 1 < 2 {
@ -55,11 +54,10 @@ fn test_elses() {
}
println('end else')
}
*/
fn main() {
println('start')
test_add()
// test_elses()
test_elses()
println('end')
}

View File

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