asm: make fixed labels possible as displacement (#9549)

pull/9543/head
crthpl 2021-03-31 23:58:33 -07:00 committed by GitHub
parent 63f835c4ce
commit 8d5e310189
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 172 additions and 68 deletions

View File

@ -540,6 +540,7 @@ pub mut:
warnings []errors.Warning // all the checker warnings in the file warnings []errors.Warning // all the checker warnings in the file
notices []errors.Notice // all the checker notices in the file notices []errors.Notice // all the checker notices in the file
generic_fns []&FnDecl generic_fns []&FnDecl
global_labels []string // from `asm { .globl labelname }`
} }
pub struct IdentFn { pub struct IdentFn {
@ -1064,13 +1065,12 @@ pub:
clobbered []AsmClobbered clobbered []AsmClobbered
pos token.Position pos token.Position
pub mut: pub mut:
templates []AsmTemplate templates []AsmTemplate
scope &Scope scope &Scope
output []AsmIO output []AsmIO
input []AsmIO input []AsmIO
global_labels []string // listed after clobbers, paired with is_goto == true global_labels []string // labels defined in assembly block, exported with `.globl`
local_labels []string // local to the assembly block local_labels []string // local to the assembly block
exported_symbols []string // functions defined in assembly block, exported with `.globl`
} }
pub struct AsmTemplate { pub struct AsmTemplate {
@ -1083,8 +1083,8 @@ pub mut:
pos token.Position pos token.Position
} }
// [eax+5] | j | eax | true | `a` | 0.594 | 123 | 'hi' | label_name // [eax+5] | j | displacement literal (e.g. 123 in [rax + 123] ) | eax | true | `a` | 0.594 | 123 | label_name
pub type AsmArg = AsmAddressing | AsmAlias | AsmRegister | BoolLiteral | CharLiteral | pub type AsmArg = AsmAddressing | AsmAlias | AsmDisp | AsmRegister | BoolLiteral | CharLiteral |
FloatLiteral | IntegerLiteral | string FloatLiteral | IntegerLiteral | string
pub struct AsmRegister { pub struct AsmRegister {
@ -1095,6 +1095,12 @@ mut:
size int size int
} }
pub struct AsmDisp {
pub:
val string
pos token.Position
}
pub struct AsmAlias { pub struct AsmAlias {
pub: pub:
name string // a name string // a
@ -1103,13 +1109,13 @@ pub:
pub struct AsmAddressing { pub struct AsmAddressing {
pub: pub:
displacement u32 // 8, 16 or 32 bit literal value scale int = -1 // 1, 2, 4, or 8 literal
scale int = -1 // 1, 2, 4, or 8 literal mode AddressingMode
mode AddressingMode pos token.Position
pos token.Position
pub mut: pub mut:
base AsmArg // gpr displacement AsmArg // 8, 16 or 32 bit literal value
index AsmArg // gpr base AsmArg // gpr
index AsmArg // gpr
} }
// adressing modes: // adressing modes:
@ -1125,9 +1131,8 @@ pub enum AddressingMode {
} }
pub struct AsmClobbered { pub struct AsmClobbered {
pub:
reg AsmRegister
pub mut: pub mut:
reg AsmRegister
comments []Comment comments []Comment
} }
@ -1204,7 +1209,7 @@ pub const (
) )
// TODO: saved priviled registers for arm // TODO: saved priviled registers for arm
const ( pub const (
arm_no_number_register_list = ['fp' /* aka r11 */, /* not instruction pointer: */ 'ip' /* aka r12 */, 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'): */ 'sp' /* aka r13 */, 'lr' /* aka r14 */, /* this is instruction pointer ('program counter'): */
'pc' /* aka r15 */, 'pc' /* aka r15 */,
@ -1214,7 +1219,7 @@ const (
} }
) )
const ( pub const (
riscv_no_number_register_list = ['zero', 'ra', 'sp', 'gp', 'tp'] riscv_no_number_register_list = ['zero', 'ra', 'sp', 'gp', 'tp']
riscv_with_number_register_list = map{ riscv_with_number_register_list = map{
'x#': 32 'x#': 32

View File

@ -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) c.asm_arg(arg, stmt, aliases)
} }
} }
for clob in stmt.clobbered { for mut clob in stmt.clobbered {
c.asm_arg(clob.reg, stmt, aliases) 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 { match mut arg {
ast.AsmAlias { ast.AsmAlias {
name := arg.name name := arg.name
if name !in aliases && name !in stmt.local_labels && name !in stmt.global_labels { if name !in aliases && name !in stmt.local_labels && name !in c.file.global_labels {
suggestion := util.new_suggestion(name, aliases) 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) 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] { if arg.scale !in [-1, 1, 2, 4, 8] {
c.error('scale must be one of 1, 2, 4, or 8', arg.pos) 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.base, stmt, aliases)
c.asm_arg(arg.index, 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.CharLiteral {}
ast.IntegerLiteral {} ast.IntegerLiteral {}
ast.AsmRegister {} // if the register is not found, the parser will register it as an alias ast.AsmRegister {} // if the register is not found, the parser will register it as an alias
ast.AsmDisp {}
string {} string {}
} }
} }

View File

@ -750,31 +750,36 @@ fn (mut f Fmt) asm_arg(arg ast.AsmArg) {
f.asm_arg(base) f.asm_arg(base)
} }
.displacement { .displacement {
f.write('$displacement') f.asm_arg(displacement)
} }
.base_plus_displacement { .base_plus_displacement {
f.asm_arg(base) f.asm_arg(base)
f.write(' + $displacement') f.write(' + ')
f.asm_arg(displacement)
} }
.index_times_scale_plus_displacement { .index_times_scale_plus_displacement {
f.asm_arg(index) f.asm_arg(index)
f.write(' * $scale + $displacement') f.write(' * $scale + ')
f.asm_arg(displacement)
} }
.base_plus_index_plus_displacement { .base_plus_index_plus_displacement {
f.asm_arg(base) f.asm_arg(base)
f.write(' + ') f.write(' + ')
f.asm_arg(index) f.asm_arg(index)
f.write(' + $displacement') f.write(' + ')
f.asm_arg(displacement)
} }
.base_plus_index_times_scale_plus_displacement { .base_plus_index_times_scale_plus_displacement {
f.asm_arg(base) f.asm_arg(base)
f.write(' + ') f.write(' + ')
f.asm_arg(index) f.asm_arg(index)
f.write(' * $scale + $displacement') f.write(' * $scale + ')
f.asm_arg(displacement)
} }
.rip_plus_displacement { .rip_plus_displacement {
f.asm_arg(base) f.asm_arg(base)
f.write(' + $displacement') f.write(' + ')
f.asm_arg(displacement)
} }
.invalid { .invalid {
panic('fmt: invalid addressing mode') panic('fmt: invalid addressing mode')
@ -782,6 +787,9 @@ fn (mut f Fmt) asm_arg(arg ast.AsmArg) {
} }
f.write(']') f.write(']')
} }
ast.AsmDisp {
f.write(arg.val)
}
} }
} }

View File

@ -1861,12 +1861,9 @@ fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) {
match arg { match arg {
ast.AsmAlias { ast.AsmAlias {
name := arg.name name := arg.name
if name in stmt.local_labels || name in stmt.global_labels { if name in stmt.local_labels || name in stmt.global_labels
asm_formatted_name := if name in stmt.local_labels { || name in g.file.global_labels {
name asm_formatted_name := if name in stmt.global_labels { '%l[$name]' } else { name }
} else { // val in stmt.global_labels
'%l[$name]'
}
g.write(asm_formatted_name) g.write(asm_formatted_name)
} else { } else {
g.write('%[$name]') g.write('%[$name]')
@ -1896,43 +1893,53 @@ fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) {
.base { .base {
g.write('(') g.write('(')
g.asm_arg(base, stmt) g.asm_arg(base, stmt)
g.write(')')
} }
.displacement { .displacement {
g.write('${displacement}(') g.asm_arg(displacement, stmt)
g.write('()')
} }
.base_plus_displacement { .base_plus_displacement {
g.write('${displacement}(') g.asm_arg(displacement, stmt)
g.write('(')
g.asm_arg(base, stmt) g.asm_arg(base, stmt)
g.write(')')
} }
.index_times_scale_plus_displacement { .index_times_scale_plus_displacement {
g.write('${displacement}(,') g.asm_arg(displacement, stmt)
g.write('(')
g.asm_arg(index, stmt) g.asm_arg(index, stmt)
g.write(',') g.write(',$scale)')
g.write(scale.str())
} }
.base_plus_index_plus_displacement { .base_plus_index_plus_displacement {
g.write('${displacement}(') g.asm_arg(displacement, stmt)
g.write('(')
g.asm_arg(base, stmt) g.asm_arg(base, stmt)
g.write(',') g.write(',')
g.asm_arg(index, stmt) g.asm_arg(index, stmt)
g.write(',1') g.write(',1)')
} }
.base_plus_index_times_scale_plus_displacement { .base_plus_index_times_scale_plus_displacement {
g.write('${displacement}(') g.asm_arg(displacement, stmt)
g.write('(')
g.asm_arg(base, stmt) g.asm_arg(base, stmt)
g.write(',') g.write(',')
g.asm_arg(index, stmt) g.asm_arg(index, stmt)
g.write(',$scale') g.write(',$scale)')
} }
.rip_plus_displacement { .rip_plus_displacement {
g.write('${displacement}(') g.asm_arg(displacement, stmt)
g.write('(')
g.asm_arg(base, stmt) g.asm_arg(base, stmt)
g.write(')')
} }
.invalid { .invalid {
g.error('invalid addressing mode', arg.pos) g.error('invalid addressing mode', arg.pos)
} }
} }
g.write(')') }
ast.AsmDisp {
g.write(arg.val)
} }
string { string {
g.write('$arg') g.write('$arg')
@ -1945,7 +1952,9 @@ fn (mut g Gen) gen_asm_ios(ios []ast.AsmIO) {
if io.alias != '' { if io.alias != '' {
g.write('[$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 { if i + 1 < ios.len {
g.writeln(',') g.writeln(',')
} else { } else {

View File

@ -73,6 +73,7 @@ mut:
n_asm int // controls assembly labels n_asm int // controls assembly labels
inside_asm_template bool inside_asm_template bool
inside_asm bool inside_asm bool
global_labels []string
} }
// for tests // for tests
@ -281,6 +282,7 @@ pub fn (mut p Parser) parse() ast.File {
global_scope: p.global_scope global_scope: p.global_scope
errors: p.errors errors: p.errors
warnings: p.warnings 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 local_labels := []string{}
mut exported_symbols := []string{}
// riscv: https://github.com/jameslzhu/riscv-card/blob/master/riscv-card.pdf // riscv: https://github.com/jameslzhu/riscv-card/blob/master/riscv-card.pdf
// x86: https://www.felixcloutier.com/x86/ // x86: https://www.felixcloutier.com/x86/
// arm: https://developer.arm.com/documentation/dui0068/b/arm-instruction-reference // 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 { ast.IntegerLiteral {
args << ast.IntegerLiteral{ if is_directive {
...number_lit args << ast.AsmDisp{
val: number_lit.val
pos: number_lit.pos
}
} else {
args << ast.IntegerLiteral{
...number_lit
}
} }
} }
else { else {
@ -967,7 +975,9 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
comments << p.comment() comments << p.comment()
} }
if is_directive && name in ['globl', 'global'] { 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{ templates << ast.AsmTemplate{
name: name name: name
@ -1045,7 +1055,6 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
scope: scope scope: scope
global_labels: global_labels global_labels: global_labels
local_labels: local_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) p.check(.name)
return b return b
} else { } 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 { } else {
p.check(.name) p.check(.name)
@ -1173,8 +1183,15 @@ fn (mut p Parser) asm_addressing() ast.AsmAddressing {
pos: pos.extend(p.prev_tok.position()) pos: pos.extend(p.prev_tok.position())
} }
} else if p.tok.kind == .number { } else if p.tok.kind == .number {
displacement := p.tok.lit.u32() displacement := if p.tok.kind == .name {
p.check(.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) p.check(.rsbr)
return ast.AsmAddressing{ return ast.AsmAddressing{
mode: .displacement 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.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' { if p.tok.lit == 'rip' {
p.check(.name) rip := p.reg_or_alias()
p.check(.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{ return ast.AsmAddressing{
mode: .rip_plus_displacement mode: .rip_plus_displacement
base: 'rip' base: rip
displacement: displacement displacement: displacement
pos: pos.extend(p.prev_tok.position()) pos: pos.extend(p.prev_tok.position())
} }
@ -1202,8 +1228,15 @@ fn (mut p Parser) asm_addressing() ast.AsmAddressing {
p.check(.plus) p.check(.plus)
if p.peek_tok.kind == .rsbr { if p.peek_tok.kind == .rsbr {
if p.tok.kind == .number { if p.tok.kind == .number {
displacement := p.tok.lit.u32() displacement := if p.tok.kind == .name {
p.check(.number) x := ast.AsmArg(p.tok.lit)
p.check(.name)
x
} else {
x := ast.AsmArg(p.tok.lit)
p.check(.name)
x
}
p.check(.rsbr) p.check(.rsbr)
return ast.AsmAddressing{ return ast.AsmAddressing{
mode: .base_plus_displacement mode: .base_plus_displacement
@ -1221,8 +1254,15 @@ fn (mut p Parser) asm_addressing() ast.AsmAddressing {
scale := p.tok.lit.int() scale := p.tok.lit.int()
p.check(.number) p.check(.number)
p.check(.plus) p.check(.plus)
displacement := p.tok.lit.u32() displacement := if p.tok.kind == .name {
p.check(.number) x := ast.AsmArg(p.tok.lit)
p.check(.name)
x
} else {
x := ast.AsmArg(p.tok.lit)
p.check(.number)
x
}
p.check(.rsbr) p.check(.rsbr)
return ast.AsmAddressing{ return ast.AsmAddressing{
mode: .base_plus_index_times_scale_plus_displacement 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 { } else if p.tok.kind == .plus {
p.check(.plus) p.check(.plus)
displacement := p.tok.lit.u32() displacement := if p.tok.kind == .name {
p.check(.number) x := ast.AsmArg(p.tok.lit)
p.check(.name)
x
} else {
x := ast.AsmArg(p.tok.lit)
p.check(.number)
x
}
p.check(.rsbr) p.check(.rsbr)
return ast.AsmAddressing{ return ast.AsmAddressing{
mode: .base_plus_index_plus_displacement mode: .base_plus_index_plus_displacement
@ -1252,8 +1299,15 @@ fn (mut p Parser) asm_addressing() ast.AsmAddressing {
scale := p.tok.lit.int() scale := p.tok.lit.int()
p.check(.number) p.check(.number)
p.check(.plus) p.check(.plus)
displacement := p.tok.lit.u32() displacement := if p.tok.kind == .name {
p.check(.number) x := ast.AsmArg(p.tok.lit)
p.check(.name)
x
} else {
x := ast.AsmArg(p.tok.lit)
p.check(.number)
x
}
p.check(.rsbr) p.check(.rsbr)
return ast.AsmAddressing{ return ast.AsmAddressing{
mode: .index_times_scale_plus_displacement mode: .index_times_scale_plus_displacement

View File

@ -1,7 +1,7 @@
import v.tests.assembly.util import v.tests.assembly.util
fn test_inline_asm() { fn test_inline_asm() {
a, mut b := 10, 0 a, mut b := i64(10), i64(0)
asm amd64 { asm amd64 {
mov rax, a mov rax, a
mov b, rax mov b, rax
@ -97,3 +97,26 @@ fn test_inline_asm() {
assert util.add(8, 9, 34, 7) == 58 // test .amd64.v files 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
}