From c7a619d16e8e7e4facddeace7222fcaba9919d53 Mon Sep 17 00:00:00 2001 From: lemon Date: Sat, 11 Jun 2022 17:50:19 +0900 Subject: [PATCH] native: support `else`, `break`, `continue` (#14738) --- vlib/v/gen/native/amd64.v | 112 ++++++++++++++++++++++----- vlib/v/gen/native/gen.v | 99 +++++++++++++++++++++-- vlib/v/gen/native/macho_test.v | 1 + vlib/v/gen/native/tests/forin.vv | 63 ++++++++++++++- vlib/v/gen/native/tests/forin.vv.out | 17 ++++ vlib/v/gen/native/tests/ifs.vv | 4 +- vlib/v/gen/native/tests/ifs.vv.out | 4 + 7 files changed, 272 insertions(+), 28 deletions(-) diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index ae7c80bfe4..387dcbfdeb 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -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,28 +1693,50 @@ 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 branch.cond is ast.BoolLiteral { - if branch.cond.val { - g.stmts(branch.stmts) + 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) + } + 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) - 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` - // 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" + } + 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() diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index b2bcb4311a..0253aec987 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -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) diff --git a/vlib/v/gen/native/macho_test.v b/vlib/v/gen/native/macho_test.v index 512ab4e4cc..db8ae629b4 100644 --- a/vlib/v/gen/native/macho_test.v +++ b/vlib/v/gen/native/macho_test.v @@ -12,6 +12,7 @@ fn test_macho() { code_gen: native.Amd64{ g: 0 } + labels: 0 } g.generate_macho_header() g.generate_macho_footer() diff --git a/vlib/v/gen/native/tests/forin.vv b/vlib/v/gen/native/tests/forin.vv index db00f54583..7cbb9b7c51 100644 --- a/vlib/v/gen/native/tests/forin.vv +++ b/vlib/v/gen/native/tests/forin.vv @@ -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() } diff --git a/vlib/v/gen/native/tests/forin.vv.out b/vlib/v/gen/native/tests/forin.vv.out index 32e0af2a81..79f22af3c0 100644 --- a/vlib/v/gen/native/tests/forin.vv.out +++ b/vlib/v/gen/native/tests/forin.vv.out @@ -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 diff --git a/vlib/v/gen/native/tests/ifs.vv b/vlib/v/gen/native/tests/ifs.vv index 674d4aa905..9945cdd884 100644 --- a/vlib/v/gen/native/tests/ifs.vv +++ b/vlib/v/gen/native/tests/ifs.vv @@ -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') } diff --git a/vlib/v/gen/native/tests/ifs.vv.out b/vlib/v/gen/native/tests/ifs.vv.out index 1298ea4729..e53daabe65 100644 --- a/vlib/v/gen/native/tests/ifs.vv.out +++ b/vlib/v/gen/native/tests/ifs.vv.out @@ -4,4 +4,8 @@ var(3) > 1 1 < 3 1 == 1 1 != 3 +start else +ok +ok +end else end