parser, cgen: fix basic asm registers, fix numbered labels in addressing, support indirect branching (#10545)

pull/10552/head
crthpl 2021-06-23 04:20:07 -07:00 committed by GitHub
parent 69227b8be7
commit 500b48788e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 41 additions and 62 deletions

View File

@ -1775,7 +1775,7 @@ fn (t Tree) asm_stmt(node ast.AsmStmt) &Node {
mut obj := new_object() mut obj := new_object()
obj.add('ast_type', t.string_node('AsmStmt')) obj.add('ast_type', t.string_node('AsmStmt'))
obj.add('arch', t.enum_node(node.arch)) obj.add('arch', t.enum_node(node.arch))
obj.add('is_top_level', t.bool_node(node.is_top_level)) obj.add('is_basic', t.bool_node(node.is_basic))
obj.add('is_volatile', t.bool_node(node.is_volatile)) obj.add('is_volatile', t.bool_node(node.is_volatile))
obj.add('is_goto', t.bool_node(node.is_goto)) obj.add('is_goto', t.bool_node(node.is_goto))
obj.add('scope', t.scope(node.scope)) obj.add('scope', t.scope(node.scope))

View File

@ -1106,12 +1106,12 @@ pub mut:
pub struct AsmStmt { pub struct AsmStmt {
pub: pub:
arch pref.Arch arch pref.Arch
is_top_level bool is_basic bool
is_volatile bool is_volatile bool
is_goto bool is_goto bool
clobbered []AsmClobbered clobbered []AsmClobbered
pos token.Position pos token.Position
pub mut: pub mut:
templates []AsmTemplate templates []AsmTemplate
scope &Scope scope &Scope

View File

@ -730,7 +730,7 @@ fn (mut f Fmt) asm_stmt(stmt ast.AsmStmt) {
} }
f.indent-- f.indent--
f.writeln('}') f.writeln('}')
if stmt.is_top_level { if f.indent == 0 {
f.writeln('') f.writeln('')
} }
} }
@ -801,7 +801,13 @@ fn (mut f Fmt) asm_arg(arg ast.AsmArg) {
f.write(']') f.write(']')
} }
ast.AsmDisp { ast.AsmDisp {
f.write(arg.val) if arg.val.len >= 2 && arg.val[arg.val.len - 1] in [`b`, `f`]
&& arg.val[..arg.val.len - 1].bytes().all(it.is_digit()) {
f.write(arg.val[arg.val.len - 1].ascii_str())
f.write(arg.val[..arg.val.len - 1])
} else {
f.write(arg.val)
}
} }
} }
} }

View File

@ -1955,6 +1955,11 @@ fn (mut g Gen) gen_asm_stmt(stmt ast.AsmStmt) {
} }
for i, arg in template.args { for i, arg in template.args {
if stmt.arch == .amd64 && (template.name == 'call' || template.name[0] == `j`)
&& arg is ast.AsmRegister {
g.write('*') // indirect branching
}
g.asm_arg(arg, stmt) g.asm_arg(arg, stmt)
if i + 1 < template.args.len { if i + 1 < template.args.len {
g.write(', ') g.write(', ')
@ -1966,6 +1971,7 @@ fn (mut g Gen) gen_asm_stmt(stmt ast.AsmStmt) {
} }
g.writeln('"') g.writeln('"')
} }
if stmt.output.len != 0 || stmt.input.len != 0 || stmt.clobbered.len != 0 || stmt.is_goto { if stmt.output.len != 0 || stmt.input.len != 0 || stmt.clobbered.len != 0 || stmt.is_goto {
g.write(': ') g.write(': ')
} }
@ -2007,7 +2013,7 @@ fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) {
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
|| name in g.file.global_labels || stmt.is_top_level || name in g.file.global_labels || stmt.is_basic
|| (name !in stmt.input.map(it.alias) && name !in stmt.output.map(it.alias)) { || (name !in stmt.input.map(it.alias) && name !in stmt.output.map(it.alias)) {
asm_formatted_name := if name in stmt.global_labels { '%l[$name]' } else { name } asm_formatted_name := if name in stmt.global_labels { '%l[$name]' } else { name }
g.write(asm_formatted_name) g.write(asm_formatted_name)
@ -2025,8 +2031,8 @@ fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) {
g.write('\$$arg.val.str()') g.write('\$$arg.val.str()')
} }
ast.AsmRegister { ast.AsmRegister {
if !stmt.is_top_level { if !stmt.is_basic {
g.write('%') // escape percent in extended assembly g.write('%') // escape percent with percent in extended assembly
} }
g.write('%$arg.name') g.write('%$arg.name')
} }
@ -2093,25 +2099,10 @@ fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) {
} }
} }
ast.AsmDisp { ast.AsmDisp {
if arg.val.len >= 2 && arg.val[0] in [`b`, `f`] { g.write(arg.val)
mut is_digit := true
for c in arg.val[1..] {
if !c.is_digit() {
is_digit = false
break
}
}
if is_digit {
g.write(arg.val[1..] + rune(arg.val[0]).str())
} else {
g.write(arg.val)
}
} else {
g.write(arg.val)
}
} }
string { string {
g.write('$arg') g.write(arg)
} }
} }
} }

View File

@ -931,27 +931,7 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
} }
match p.tok.kind { match p.tok.kind {
.name { .name {
if p.tok.kind == .name && p.tok.lit.len >= 2 args << p.reg_or_alias()
&& (p.tok.lit.starts_with('b') || p.tok.lit.starts_with('f')) {
mut is_digit := true
for c in p.tok.lit[1..] {
if !c.is_digit() {
is_digit = false
break
}
}
if is_digit {
args << ast.AsmDisp{
val: p.tok.lit
pos: p.tok.position()
}
p.check(.name)
} else {
args << p.reg_or_alias()
}
} else {
args << p.reg_or_alias()
}
} }
.number { .number {
number_lit := p.parse_number_literal() number_lit := p.parse_number_literal()
@ -962,9 +942,7 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
} }
} }
ast.IntegerLiteral { ast.IntegerLiteral {
if (is_directive || number_lit.val.ends_with('b') if is_directive {
|| number_lit.val.ends_with('f'))
&& !number_lit.val.starts_with('0x') {
args << ast.AsmDisp{ args << ast.AsmDisp{
val: number_lit.val val: number_lit.val
pos: number_lit.pos pos: number_lit.pos
@ -1099,7 +1077,7 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
input: input input: input
clobbered: clobbered clobbered: clobbered
pos: pos.extend(p.tok.position()) pos: pos.extend(p.tok.position())
is_top_level: is_top_level is_basic: is_top_level || output.len + input.len + clobbered.len == 0
scope: scope scope: scope
global_labels: global_labels global_labels: global_labels
local_labels: local_labels local_labels: local_labels
@ -1107,18 +1085,21 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
} }
fn (mut p Parser) reg_or_alias() ast.AsmArg { fn (mut p Parser) reg_or_alias() ast.AsmArg {
if p.tok.lit in p.scope.objects { p.check(.name)
x := p.scope.objects[p.tok.lit] if p.prev_tok.lit in p.scope.objects {
x := p.scope.objects[p.prev_tok.lit]
if x is ast.AsmRegister { if x is ast.AsmRegister {
b := x return ast.AsmArg(x as ast.AsmRegister)
p.check(.name)
return b
} else { } else {
verror('parser bug: non-register ast.ScopeObject found in scope') verror('non-register ast.ScopeObject found in scope')
return ast.AsmDisp{} // should not be reached return ast.AsmDisp{} // should not be reached
} }
} else if p.prev_tok.len >= 2 && p.prev_tok.lit[0] in [`b`, `f`]
&& p.prev_tok.lit[1..].bytes().all(it.is_digit()) {
return ast.AsmDisp{
val: p.prev_tok.lit[1..] + p.prev_tok.lit[0].ascii_str()
}
} else { } else {
p.check(.name)
return ast.AsmAlias{ return ast.AsmAlias{
name: p.prev_tok.lit name: p.prev_tok.lit
pos: p.prev_tok.position() pos: p.prev_tok.position()
@ -1215,11 +1196,12 @@ fn (mut p Parser) reg_or_alias() ast.AsmArg {
// pos: pos.extend(p.prev_tok.position()) // pos: pos.extend(p.prev_tok.position())
// } // }
// } // }
fn (mut p Parser) asm_addressing() ast.AsmAddressing { fn (mut p Parser) asm_addressing() ast.AsmAddressing {
pos := p.tok.position() pos := p.tok.position()
p.check(.lsbr) p.check(.lsbr)
unknown_addressing_mode := 'unknown addressing mode. supported ones are [displacement], [base], [base + displacement], [index scale + displacement], [base + index scale + displacement], [base + index + displacement], [rip + displacement]' unknown_addressing_mode := 'unknown addressing mode. supported ones are [displacement], [base], [base + displacement], [index scale + displacement], [base + index scale + displacement], [base + index + displacement], [rip + displacement]'
// this mess used to look much cleaner before the removal of peek_tok3, see above // this mess used to look much cleaner before the removal of peek_tok2/3, see above code for cleaner version
if p.peek_tok.kind == .rsbr { // [displacement] or [base] if p.peek_tok.kind == .rsbr { // [displacement] or [base]
if p.tok.kind == .name { if p.tok.kind == .name {
base := p.reg_or_alias() base := p.reg_or_alias()