native: support `else`, `break`, `continue` (#14738)
parent
da7a166708
commit
c7a619d16e
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -12,6 +12,7 @@ fn test_macho() {
|
|||
code_gen: native.Amd64{
|
||||
g: 0
|
||||
}
|
||||
labels: 0
|
||||
}
|
||||
g.generate_macho_header()
|
||||
g.generate_macho_footer()
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
|
|
|
@ -4,4 +4,8 @@ var(3) > 1
|
|||
1 < 3
|
||||
1 == 1
|
||||
1 != 3
|
||||
start else
|
||||
ok
|
||||
ok
|
||||
end else
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue