diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 1e0e542bfa..0deba3a374 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -540,6 +540,7 @@ pub mut: warnings []errors.Warning // all the checker warnings in the file notices []errors.Notice // all the checker notices in the file generic_fns []&FnDecl + global_labels []string // from `asm { .globl labelname }` } pub struct IdentFn { @@ -1064,13 +1065,12 @@ pub: clobbered []AsmClobbered pos token.Position pub mut: - templates []AsmTemplate - scope &Scope - output []AsmIO - input []AsmIO - global_labels []string // listed after clobbers, paired with is_goto == true - local_labels []string // local to the assembly block - exported_symbols []string // functions defined in assembly block, exported with `.globl` + templates []AsmTemplate + scope &Scope + output []AsmIO + input []AsmIO + global_labels []string // labels defined in assembly block, exported with `.globl` + local_labels []string // local to the assembly block } pub struct AsmTemplate { @@ -1083,8 +1083,8 @@ pub mut: pos token.Position } -// [eax+5] | j | eax | true | `a` | 0.594 | 123 | 'hi' | label_name -pub type AsmArg = AsmAddressing | AsmAlias | AsmRegister | BoolLiteral | CharLiteral | +// [eax+5] | j | displacement literal (e.g. 123 in [rax + 123] ) | eax | true | `a` | 0.594 | 123 | label_name +pub type AsmArg = AsmAddressing | AsmAlias | AsmDisp | AsmRegister | BoolLiteral | CharLiteral | FloatLiteral | IntegerLiteral | string pub struct AsmRegister { @@ -1095,6 +1095,12 @@ mut: size int } +pub struct AsmDisp { +pub: + val string + pos token.Position +} + pub struct AsmAlias { pub: name string // a @@ -1103,13 +1109,13 @@ pub: pub struct AsmAddressing { pub: - displacement u32 // 8, 16 or 32 bit literal value - scale int = -1 // 1, 2, 4, or 8 literal - mode AddressingMode - pos token.Position + scale int = -1 // 1, 2, 4, or 8 literal + mode AddressingMode + pos token.Position pub mut: - base AsmArg // gpr - index AsmArg // gpr + displacement AsmArg // 8, 16 or 32 bit literal value + base AsmArg // gpr + index AsmArg // gpr } // adressing modes: @@ -1125,9 +1131,8 @@ pub enum AddressingMode { } pub struct AsmClobbered { -pub: - reg AsmRegister pub mut: + reg AsmRegister comments []Comment } @@ -1204,7 +1209,7 @@ pub const ( ) // TODO: saved priviled registers for arm -const ( +pub const ( arm_no_number_register_list = ['fp' /* aka r11 */, /* not instruction pointer: */ 'ip' /* aka r12 */, 'sp' /* aka r13 */, 'lr' /* aka r14 */, /* this is instruction pointer ('program counter'): */ 'pc' /* aka r15 */, @@ -1214,7 +1219,7 @@ const ( } ) -const ( +pub const ( riscv_no_number_register_list = ['zero', 'ra', 'sp', 'gp', 'tp'] riscv_with_number_register_list = map{ 'x#': 32 diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index f5b4bb7606..46ead38a1a 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3768,11 +3768,11 @@ fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) { // } } - for arg in template.args { + for mut arg in template.args { c.asm_arg(arg, stmt, aliases) } } - for clob in stmt.clobbered { + for mut clob in stmt.clobbered { c.asm_arg(clob.reg, stmt, aliases) } } @@ -3781,8 +3781,11 @@ fn (mut c Checker) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt, aliases []string) { match mut arg { ast.AsmAlias { name := arg.name - if name !in aliases && name !in stmt.local_labels && name !in stmt.global_labels { - suggestion := util.new_suggestion(name, aliases) + if name !in aliases && name !in stmt.local_labels && name !in c.file.global_labels { + mut possible := aliases.clone() + possible << stmt.local_labels + possible << c.file.global_labels + suggestion := util.new_suggestion(name, possible) c.error(suggestion.say('alias or label `$arg.name` does not exist'), arg.pos) } } @@ -3790,6 +3793,7 @@ fn (mut c Checker) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt, aliases []string) { if arg.scale !in [-1, 1, 2, 4, 8] { c.error('scale must be one of 1, 2, 4, or 8', arg.pos) } + c.asm_arg(arg.displacement, stmt, aliases) c.asm_arg(arg.base, stmt, aliases) c.asm_arg(arg.index, stmt, aliases) } @@ -3798,6 +3802,7 @@ fn (mut c Checker) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt, aliases []string) { ast.CharLiteral {} ast.IntegerLiteral {} ast.AsmRegister {} // if the register is not found, the parser will register it as an alias + ast.AsmDisp {} string {} } } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index ed8d84fcd1..31578225f9 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -750,31 +750,36 @@ fn (mut f Fmt) asm_arg(arg ast.AsmArg) { f.asm_arg(base) } .displacement { - f.write('$displacement') + f.asm_arg(displacement) } .base_plus_displacement { f.asm_arg(base) - f.write(' + $displacement') + f.write(' + ') + f.asm_arg(displacement) } .index_times_scale_plus_displacement { f.asm_arg(index) - f.write(' * $scale + $displacement') + f.write(' * $scale + ') + f.asm_arg(displacement) } .base_plus_index_plus_displacement { f.asm_arg(base) f.write(' + ') f.asm_arg(index) - f.write(' + $displacement') + f.write(' + ') + f.asm_arg(displacement) } .base_plus_index_times_scale_plus_displacement { f.asm_arg(base) f.write(' + ') f.asm_arg(index) - f.write(' * $scale + $displacement') + f.write(' * $scale + ') + f.asm_arg(displacement) } .rip_plus_displacement { f.asm_arg(base) - f.write(' + $displacement') + f.write(' + ') + f.asm_arg(displacement) } .invalid { panic('fmt: invalid addressing mode') @@ -782,6 +787,9 @@ fn (mut f Fmt) asm_arg(arg ast.AsmArg) { } f.write(']') } + ast.AsmDisp { + f.write(arg.val) + } } } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 13fe401838..9ff83c2314 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -1861,12 +1861,9 @@ fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) { match arg { ast.AsmAlias { name := arg.name - if name in stmt.local_labels || name in stmt.global_labels { - asm_formatted_name := if name in stmt.local_labels { - name - } else { // val in stmt.global_labels - '%l[$name]' - } + if name in stmt.local_labels || name in stmt.global_labels + || name in g.file.global_labels { + asm_formatted_name := if name in stmt.global_labels { '%l[$name]' } else { name } g.write(asm_formatted_name) } else { g.write('%[$name]') @@ -1896,43 +1893,53 @@ fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) { .base { g.write('(') g.asm_arg(base, stmt) + g.write(')') } .displacement { - g.write('${displacement}(') + g.asm_arg(displacement, stmt) + g.write('()') } .base_plus_displacement { - g.write('${displacement}(') + g.asm_arg(displacement, stmt) + g.write('(') g.asm_arg(base, stmt) + g.write(')') } .index_times_scale_plus_displacement { - g.write('${displacement}(,') + g.asm_arg(displacement, stmt) + g.write('(') g.asm_arg(index, stmt) - g.write(',') - g.write(scale.str()) + g.write(',$scale)') } .base_plus_index_plus_displacement { - g.write('${displacement}(') + g.asm_arg(displacement, stmt) + g.write('(') g.asm_arg(base, stmt) g.write(',') g.asm_arg(index, stmt) - g.write(',1') + g.write(',1)') } .base_plus_index_times_scale_plus_displacement { - g.write('${displacement}(') + g.asm_arg(displacement, stmt) + g.write('(') g.asm_arg(base, stmt) g.write(',') g.asm_arg(index, stmt) - g.write(',$scale') + g.write(',$scale)') } .rip_plus_displacement { - g.write('${displacement}(') + g.asm_arg(displacement, stmt) + g.write('(') g.asm_arg(base, stmt) + g.write(')') } .invalid { g.error('invalid addressing mode', arg.pos) } } - g.write(')') + } + ast.AsmDisp { + g.write(arg.val) } string { g.write('$arg') @@ -1945,7 +1952,9 @@ fn (mut g Gen) gen_asm_ios(ios []ast.AsmIO) { if io.alias != '' { g.write('[$io.alias] ') } - g.write('"$io.constraint" ($io.expr)') + g.write('"$io.constraint" (') + g.expr(io.expr) + g.write(')') if i + 1 < ios.len { g.writeln(',') } else { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index e410b78fb3..3982cdcb5c 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -73,6 +73,7 @@ mut: n_asm int // controls assembly labels inside_asm_template bool inside_asm bool + global_labels []string } // for tests @@ -281,6 +282,7 @@ pub fn (mut p Parser) parse() ast.File { global_scope: p.global_scope errors: p.errors warnings: p.warnings + global_labels: p.global_labels } } @@ -876,7 +878,6 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt { } mut local_labels := []string{} - mut exported_symbols := []string{} // riscv: https://github.com/jameslzhu/riscv-card/blob/master/riscv-card.pdf // x86: https://www.felixcloutier.com/x86/ // arm: https://developer.arm.com/documentation/dui0068/b/arm-instruction-reference @@ -921,8 +922,15 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt { } } ast.IntegerLiteral { - args << ast.IntegerLiteral{ - ...number_lit + if is_directive { + args << ast.AsmDisp{ + val: number_lit.val + pos: number_lit.pos + } + } else { + args << ast.IntegerLiteral{ + ...number_lit + } } } else { @@ -967,7 +975,9 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt { comments << p.comment() } if is_directive && name in ['globl', 'global'] { - exported_symbols << args + for arg in args { + p.global_labels << (arg as ast.AsmAlias).name + } } templates << ast.AsmTemplate{ name: name @@ -1045,7 +1055,6 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt { scope: scope global_labels: global_labels local_labels: local_labels - exported_symbols: exported_symbols } } @@ -1058,7 +1067,8 @@ fn (mut p Parser) reg_or_alias() ast.AsmArg { p.check(.name) return b } else { - panic('parser bug: non-register ast.ScopeObject found in scope') + verror('parser bug: non-register ast.ScopeObject found in scope') + return ast.AsmDisp{} // should not be reached } } else { p.check(.name) @@ -1173,8 +1183,15 @@ fn (mut p Parser) asm_addressing() ast.AsmAddressing { pos: pos.extend(p.prev_tok.position()) } } else if p.tok.kind == .number { - displacement := p.tok.lit.u32() - p.check(.name) + displacement := if p.tok.kind == .name { + x := ast.AsmArg(p.tok.lit) + p.check(.name) + x + } else { + x := ast.AsmArg(p.tok.lit) + p.check(.number) + x + } p.check(.rsbr) return ast.AsmAddressing{ mode: .displacement @@ -1187,13 +1204,22 @@ fn (mut p Parser) asm_addressing() ast.AsmAddressing { } if p.peek_tok.kind == .plus && p.tok.kind == .name { // [base + displacement], [base + index ∗ scale + displacement], [base + index + displacement] or [rip + displacement] if p.tok.lit == 'rip' { - p.check(.name) + rip := p.reg_or_alias() p.check(.plus) - displacement := p.tok.lit.u32() - p.check(.number) + + displacement := if p.tok.kind == .name { + x := ast.AsmArg(p.tok.lit) + p.check(.name) + x + } else { + x := ast.AsmArg(p.tok.lit) + p.check(.number) + x + } + p.check(.rsbr) return ast.AsmAddressing{ mode: .rip_plus_displacement - base: 'rip' + base: rip displacement: displacement pos: pos.extend(p.prev_tok.position()) } @@ -1202,8 +1228,15 @@ fn (mut p Parser) asm_addressing() ast.AsmAddressing { p.check(.plus) if p.peek_tok.kind == .rsbr { if p.tok.kind == .number { - displacement := p.tok.lit.u32() - p.check(.number) + displacement := if p.tok.kind == .name { + x := ast.AsmArg(p.tok.lit) + p.check(.name) + x + } else { + x := ast.AsmArg(p.tok.lit) + p.check(.name) + x + } p.check(.rsbr) return ast.AsmAddressing{ mode: .base_plus_displacement @@ -1221,8 +1254,15 @@ fn (mut p Parser) asm_addressing() ast.AsmAddressing { scale := p.tok.lit.int() p.check(.number) p.check(.plus) - displacement := p.tok.lit.u32() - p.check(.number) + displacement := if p.tok.kind == .name { + x := ast.AsmArg(p.tok.lit) + p.check(.name) + x + } else { + x := ast.AsmArg(p.tok.lit) + p.check(.number) + x + } p.check(.rsbr) return ast.AsmAddressing{ mode: .base_plus_index_times_scale_plus_displacement @@ -1234,8 +1274,15 @@ fn (mut p Parser) asm_addressing() ast.AsmAddressing { } } else if p.tok.kind == .plus { p.check(.plus) - displacement := p.tok.lit.u32() - p.check(.number) + displacement := if p.tok.kind == .name { + x := ast.AsmArg(p.tok.lit) + p.check(.name) + x + } else { + x := ast.AsmArg(p.tok.lit) + p.check(.number) + x + } p.check(.rsbr) return ast.AsmAddressing{ mode: .base_plus_index_plus_displacement @@ -1252,8 +1299,15 @@ fn (mut p Parser) asm_addressing() ast.AsmAddressing { scale := p.tok.lit.int() p.check(.number) p.check(.plus) - displacement := p.tok.lit.u32() - p.check(.number) + displacement := if p.tok.kind == .name { + x := ast.AsmArg(p.tok.lit) + p.check(.name) + x + } else { + x := ast.AsmArg(p.tok.lit) + p.check(.number) + x + } p.check(.rsbr) return ast.AsmAddressing{ mode: .index_times_scale_plus_displacement diff --git a/vlib/v/tests/assembly/asm_test.amd64.v b/vlib/v/tests/assembly/asm_test.amd64.v index bd0679ed02..912fc26d48 100644 --- a/vlib/v/tests/assembly/asm_test.amd64.v +++ b/vlib/v/tests/assembly/asm_test.amd64.v @@ -1,7 +1,7 @@ import v.tests.assembly.util fn test_inline_asm() { - a, mut b := 10, 0 + a, mut b := i64(10), i64(0) asm amd64 { mov rax, a mov b, rax @@ -97,3 +97,26 @@ fn test_inline_asm() { assert util.add(8, 9, 34, 7) == 58 // test .amd64.v files } + +// this test does not appear in i386 test since rip relative addressing was introduced in 64-bit mode +fn test_rip_relative_label() { + mut a := i64(4) + asm amd64 { + mov a, [rip + one_two_three] // see below + ; =r (a) + } + assert a == 48321074923 + + mut b := i64(4) + asm amd64 { + mov b, one_two_three // see below + ; =r (b) + } + assert b == 48321074923 +} + +asm amd64 { + .global one_two_three + one_two_three: + .quad 48321074923 +}