1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

native: support fncall with 7↑ arguments (#15599)

This commit is contained in:
lemon 2022-08-30 16:24:37 +09:00 committed by GitHub
parent 0876cf86ed
commit 2221dd7058
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 171 additions and 79 deletions

View File

@ -7,6 +7,7 @@ pub struct Amd64 {
mut: mut:
g &Gen g &Gen
// arm64 specific stuff for code generation // arm64 specific stuff for code generation
is_16bit_aligned bool
} }
// The registers are ordered for faster generation // The registers are ordered for faster generation
@ -210,9 +211,14 @@ fn (mut g Gen) cmp_var_reg(var Var, reg Register, config VarConfig) {
// TODO: size // TODO: size
g.write8(0x48) // 83 for 1 byte? g.write8(0x48) // 83 for 1 byte?
g.write8(0x39) g.write8(0x39)
g.write8(0x45)
offset := var.offset - config.offset offset := var.offset - config.offset
g.write8(0xff - offset + 1) is_far_var := offset > 0x80 || offset < -0x7f
g.write8(if is_far_var { 0x85 } else { 0x45 })
if is_far_var {
g.write32(int((0xffffffff - i64(offset) + 1) % 0x100000000))
} else {
g.write8((0xff - offset + 1) % 0x100)
}
g.println('cmp var `$var.name`, $reg') g.println('cmp var `$var.name`, $reg')
} }
GlobalVar { GlobalVar {
@ -241,9 +247,14 @@ fn (mut g Gen) cmp_var(var Var, val int, config VarConfig) {
LocalVar { LocalVar {
// TODO: size // TODO: size
g.write8(0x81) // 83 for 1 byte? g.write8(0x81) // 83 for 1 byte?
g.write8(0x7d)
offset := var.offset - config.offset offset := var.offset - config.offset
g.write8(0xff - offset + 1) is_far_var := offset > 0x80 || offset < -0x7f
g.write8(if is_far_var { 0xbd } else { 0x7d })
if is_far_var {
g.write32(int((0xffffffff - i64(offset) + 1) % 0x100000000))
} else {
g.write8((0xff - offset + 1) % 0x100)
}
g.write32(val) g.write32(val)
g.println('cmp var `$var.name` $val') g.println('cmp var `$var.name` $val')
} }
@ -273,9 +284,15 @@ fn (mut g Gen) dec_var(var Var, config VarConfig) {
} }
LocalVar { LocalVar {
// TODO: size // TODO: size
g.write16(0x6d81) // 83 for 1 byte g.write8(0x81) // 83 for 1 byte
offset := var.offset - config.offset offset := var.offset - config.offset
g.write8(0xff - offset + 1) is_far_var := offset > 0x80 || offset < -0x7f
g.write8(if is_far_var { 0xad } else { 0x6d })
if is_far_var {
g.write32(int((0xffffffff - i64(offset) + 1) % 0x100000000))
} else {
g.write8((0xff - offset + 1) % 0x100)
}
g.write32(1) g.write32(1)
g.println('dec_var `$var.name`') g.println('dec_var `$var.name`')
} }
@ -305,9 +322,15 @@ fn (mut g Gen) inc_var(var Var, config VarConfig) {
} }
LocalVar { LocalVar {
// TODO: size // TODO: size
g.write16(0x4581) // 83 for 1 byte g.write8(0x81) // 83 for 1 byte
offset := var.offset - config.offset offset := var.offset - config.offset
g.write8(0xff - offset + 1) is_far_var := offset > 0x80 || offset < -0x7f
g.write8(if is_far_var { 0x85 } else { 0x45 })
if is_far_var {
g.write32(int((0xffffffff - i64(offset) + 1) % 0x100000000))
} else {
g.write8((0xff - offset + 1) % 0x100)
}
g.write32(1) g.write32(1)
g.println('inc_var `$var.name`') g.println('inc_var `$var.name`')
} }
@ -429,7 +452,7 @@ fn (mut g Gen) mov64(reg Register, val i64) {
g.write8(0x48) g.write8(0x48)
g.write8(0xc7) g.write8(0xc7)
g.write8(0xc2) g.write8(0xc2)
g.write32(i32(int(val))) g.write32(int(val))
g.println('mov32 $reg, $val') g.println('mov32 $reg, $val')
return return
} }
@ -499,40 +522,58 @@ fn (mut g Gen) mov_reg_to_var(var Var, reg Register, config VarConfig) {
} }
LocalVar { LocalVar {
offset := var.offset - config.offset offset := var.offset - config.offset
is_far_var := offset > 0x80 || offset < -0x7f
typ := if config.typ == 0 { var.typ } else { config.typ } typ := if config.typ == 0 { var.typ } else { config.typ }
size := match typ { mut size := 'UNKNOWN'
is_extended_register := int(reg) >= int(Register.r8) && int(reg) <= int(Register.r15)
match typ {
ast.i64_type_idx, ast.u64_type_idx, ast.isize_type_idx, ast.usize_type_idx, ast.i64_type_idx, ast.u64_type_idx, ast.isize_type_idx, ast.usize_type_idx,
ast.int_literal_type_idx { ast.int_literal_type_idx {
g.write16(0x8948) g.write16(0x8948 + if is_extended_register { 4 } else { 0 })
'QWORD' size = 'QWORD'
} }
ast.int_type_idx, ast.u32_type_idx, ast.rune_type_idx { ast.int_type_idx, ast.u32_type_idx, ast.rune_type_idx {
if is_extended_register {
g.write8(0x44)
}
g.write8(0x89) g.write8(0x89)
'DWORD' size = 'DWORD'
} }
ast.i16_type_idx, ast.u16_type_idx { ast.i16_type_idx, ast.u16_type_idx {
g.write16(0x8966) g.write8(0x66)
'WORD' if is_extended_register {
g.write8(0x44)
}
g.write8(0x89)
size = 'WORD'
} }
ast.i8_type_idx, ast.u8_type_idx, ast.char_type_idx, ast.bool_type_idx { ast.i8_type_idx, ast.u8_type_idx, ast.char_type_idx, ast.bool_type_idx {
if is_extended_register {
g.write8(0x44)
}
g.write8(0x88) g.write8(0x88)
'BYTE' size = 'BYTE'
} }
else { else {
g.n_error('unsupported type for mov_reg_to_var') g.n_error('unsupported type for mov_reg_to_var')
'UNKNOWN' 'UNKNOWN'
} }
} }
far_var_offset := if is_far_var { 0x40 } else { 0 }
match reg { match reg {
.eax, .rax { g.write8(0x45) } .eax, .rax, .r8 { g.write8(0x45 + far_var_offset) }
.edi, .rdi { g.write8(0x7d) } .edi, .rdi { g.write8(0x7d + far_var_offset) }
.rsi { g.write8(0x75) } .rsi { g.write8(0x75 + far_var_offset) }
.rdx { g.write8(0x55) } .rdx { g.write8(0x55 + far_var_offset) }
.rcx { g.write8(0x4d) } .rcx, .r9 { g.write8(0x4d + far_var_offset) }
else { g.n_error('mov_from_reg $reg') } else { g.n_error('mov_from_reg $reg') }
} }
g.write8(0xff - offset + 1) if is_far_var {
g.write32(int((0xffffffff - i64(offset) + 1) % 0x100000000))
} else {
g.write8((0xff - offset + 1) % 0x100)
}
g.println('mov $size PTR [rbp-$offset.hex2()],$reg') g.println('mov $size PTR [rbp-$offset.hex2()],$reg')
} }
GlobalVar { GlobalVar {
@ -560,13 +601,17 @@ fn (mut g Gen) mov_int_to_var(var Var, integer int, config VarConfig) {
LocalVar { LocalVar {
offset := var.offset - config.offset offset := var.offset - config.offset
typ := if config.typ == 0 { var.typ } else { config.typ } typ := if config.typ == 0 { var.typ } else { config.typ }
var_addr := 0xff - offset + 1 is_far_var := offset > 0x80 || offset < -0x7f
match typ { match typ {
ast.i8_type_idx, ast.u8_type_idx, ast.char_type_idx { ast.i8_type_idx, ast.u8_type_idx, ast.char_type_idx {
g.write8(0xc6) g.write8(0xc6)
g.write8(0x45) g.write8(if is_far_var { 0x85 } else { 0x45 })
g.write8(var_addr) if is_far_var {
g.write32(int((0xffffffff - i64(offset) + 1) % 0x100000000))
} else {
g.write8((0xff - offset + 1) % 0x100)
}
g.write8(u8(integer)) g.write8(u8(integer))
g.println('mov BYTE PTR[rbp-$offset.hex2()], $integer') g.println('mov BYTE PTR[rbp-$offset.hex2()], $integer')
} }
@ -574,8 +619,12 @@ fn (mut g Gen) mov_int_to_var(var Var, integer int, config VarConfig) {
ast.int_literal_type_idx { ast.int_literal_type_idx {
g.write8(0x48) g.write8(0x48)
g.write8(0xc7) g.write8(0xc7)
g.write8(0x45) g.write8(if is_far_var { 0x85 } else { 0x45 })
g.write8(var_addr) if is_far_var {
g.write32(int((0xffffffff - i64(offset) + 1) % 0x100000000))
} else {
g.write8((0xff - offset + 1) % 0x100)
}
g.write32(integer) g.write32(integer)
g.println('mov QWORD PTR[rbp-$offset.hex2()], $integer') g.println('mov QWORD PTR[rbp-$offset.hex2()], $integer')
} }
@ -591,6 +640,7 @@ fn (mut g Gen) mov_int_to_var(var Var, integer int, config VarConfig) {
} }
fn (mut g Gen) lea_var_to_reg(reg Register, var_offset int) { fn (mut g Gen) lea_var_to_reg(reg Register, var_offset int) {
is_far_var := var_offset > 0x80 || var_offset < -0x7f
match reg { match reg {
.rax, .rbx, .rsi, .rdi { .rax, .rbx, .rsi, .rdi {
g.write8(0x48) g.write8(0x48)
@ -598,16 +648,21 @@ fn (mut g Gen) lea_var_to_reg(reg Register, var_offset int) {
else {} else {}
} }
g.write8(0x8d) g.write8(0x8d)
far_var_offset := if is_far_var { 0x40 } else { 0 }
match reg { match reg {
.eax, .rax { g.write8(0x45) } .eax, .rax { g.write8(0x45 + far_var_offset) }
.edi, .rdi { g.write8(0x7d) } .edi, .rdi { g.write8(0x7d + far_var_offset) }
.rsi { g.write8(0x75) } .rsi { g.write8(0x75 + far_var_offset) }
.rdx { g.write8(0x55) } .rdx { g.write8(0x55 + far_var_offset) }
.rbx { g.write8(0x5d) } .rbx { g.write8(0x5d + far_var_offset) }
.rcx { g.write8(0x4d) } .rcx { g.write8(0x4d + far_var_offset) }
else { g.n_error('lea_var_to_reg $reg') } else { g.n_error('lea_var_to_reg $reg') }
} }
g.write8(0xff - var_offset + 1) if is_far_var {
g.write32(int((0xffffffff - i64(var_offset) + 1) % 0x100000000))
} else {
g.write8((0xff - var_offset + 1) % 0x100)
}
g.println('lea $reg, [rbp-$var_offset.hex2()]') g.println('lea $reg, [rbp-$var_offset.hex2()]')
} }
@ -629,6 +684,7 @@ fn (mut g Gen) mov_var_to_reg(reg Register, var Var, config VarConfig) {
} }
LocalVar { LocalVar {
offset := var.offset - config.offset offset := var.offset - config.offset
is_far_var := offset > 0x80 || offset < -0x7f
typ := if config.typ == 0 { var.typ } else { config.typ } typ := if config.typ == 0 { var.typ } else { config.typ }
instruction, size := match typ { instruction, size := match typ {
@ -673,16 +729,21 @@ fn (mut g Gen) mov_var_to_reg(reg Register, var Var, config VarConfig) {
'mov', 'UNKNOWN' 'mov', 'UNKNOWN'
} }
} }
far_var_offset := if is_far_var { 0x40 } else { 0 }
match reg { match reg {
.eax, .rax { g.write8(0x45) } .eax, .rax { g.write8(0x45 + far_var_offset) }
.edi, .rdi { g.write8(0x7d) } .edi, .rdi { g.write8(0x7d + far_var_offset) }
.rsi { g.write8(0x75) } .rsi { g.write8(0x75 + far_var_offset) }
.rdx { g.write8(0x55) } .rdx { g.write8(0x55 + far_var_offset) }
.rbx { g.write8(0x5d) } .rbx { g.write8(0x5d + far_var_offset) }
.rcx { g.write8(0x4d) } .rcx { g.write8(0x4d + far_var_offset) }
else { g.n_error('mov_var_to_reg $reg') } else { g.n_error('mov_var_to_reg $reg') }
} }
g.write8(0xff - offset + 1) if is_far_var {
g.write32(int((0xffffffff - i64(offset) + 1) % 0x100000000))
} else {
g.write8((0xff - offset + 1) % 0x100)
}
g.println('$instruction $reg, $size PTR [rbp-$offset.hex2()]') g.println('$instruction $reg, $size PTR [rbp-$offset.hex2()]')
} }
GlobalVar { GlobalVar {
@ -746,18 +807,20 @@ pub fn (mut g Gen) push(reg Register) {
g.write8(0x41) g.write8(0x41)
g.write8(0x50 + int(reg) - 8) g.write8(0x50 + int(reg) - 8)
} }
/* if mut g.code_gen is Amd64 {
match reg { g.code_gen.is_16bit_aligned = !g.code_gen.is_16bit_aligned
.rbp { g.write8(0x55) }
else {}
} }
*/
g.println('push $reg') g.println('push $reg')
} }
pub fn (mut g Gen) pop(reg Register) { pub fn (mut g Gen) pop(reg Register) {
g.write8(0x58 + int(reg)) if int(reg) >= int(Register.r8) && int(reg) <= int(Register.r15) {
// TODO r8... g.write8(0x41)
}
g.write8(0x58 + int(reg) % 8)
if mut g.code_gen is Amd64 {
g.code_gen.is_16bit_aligned = !g.code_gen.is_16bit_aligned
}
g.println('pop $reg') g.println('pop $reg')
} }
@ -1444,31 +1507,21 @@ pub fn (mut g Gen) call_fn_amd64(node ast.CallExpr) {
n = 'main.$n' n = 'main.$n'
} }
addr := g.fn_addr[n] addr := g.fn_addr[n]
// Copy values to registers (calling convention) // not aligned now XOR pushed args will be odd
// g.mov(.eax, 0) is_16bit_aligned := if mut g.code_gen is Amd64 { g.code_gen.is_16bit_aligned } else { true } != (
for i in 0 .. node.args.len { node.args.len > 6 && node.args.len % 2 == 1)
expr := node.args[i].expr if !is_16bit_aligned {
match expr { // dummy data
ast.IntegerLiteral { g.push(.rbp)
// `foo(2)` => `mov edi,0x2`
g.mov(native.fn_arg_registers[i], expr.val.int())
}
ast.Ident {
// `foo(x)` => `mov edi,DWORD PTR [rbp-0x8]`
var_offset := g.get_var_offset(expr.name)
if g.pref.is_verbose {
println('i=$i fn name= $name offset=$var_offset')
println(int(native.fn_arg_registers[i]))
}
g.mov_var_to_reg(native.fn_arg_registers[i], expr as ast.Ident)
}
else {
g.v_error('unhandled call_fn (name=$name) node: $expr.type_name()', node.pos)
}
}
} }
if node.args.len > 6 { for i_ in 0 .. node.args.len {
g.v_error('more than 6 args not allowed for now', node.pos) i := node.args.len - i_ - 1
g.expr(node.args[i].expr)
g.push(.rax)
}
num_on_register := if node.args.len > 6 { 6 } else { node.args.len }
for i in 0 .. num_on_register {
g.pop(native.fn_arg_registers[i])
} }
if addr == 0 { if addr == 0 {
g.delay_fn_call(name) g.delay_fn_call(name)
@ -1477,6 +1530,14 @@ pub fn (mut g Gen) call_fn_amd64(node ast.CallExpr) {
g.call(int(addr)) g.call(int(addr))
} }
g.println('call `${name}()`') g.println('call `${name}()`')
if !is_16bit_aligned {
// dummy data
g.pop(.rdi)
}
for _ in 0 .. node.args.len - 6 {
// args
g.pop(.rdi)
}
} }
fn (mut g Gen) call_builtin_amd64(name string) i64 { fn (mut g Gen) call_builtin_amd64(name string) i64 {
@ -2309,7 +2370,13 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) {
// `mov DWORD PTR [rbp-0x4],edi` // `mov DWORD PTR [rbp-0x4],edi`
offset += 4 offset += 4
// TODO size // TODO size
g.mov_reg_to_var(LocalVar{offset, ast.int_type_idx, name}, native.fn_arg_registers[i]) if i < 6 {
g.mov_reg_to_var(LocalVar{offset, ast.int_type_idx, name}, native.fn_arg_registers[i])
} else {
// &var = rbp + 16, 24, 32, ...
g.var_offset[name] = (4 - i) * 8
g.var_alloc_size[name] = 4
}
} }
// define defer vars // define defer vars
for i in 0 .. node.defer_stmts.len { for i in 0 .. node.defer_stmts.len {
@ -2318,6 +2385,10 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) {
} }
// //
g.stmts(node.stmts) g.stmts(node.stmts)
// 16 bytes align
g.stack_var_pos += 23
g.stack_var_pos /= 16
g.stack_var_pos *= 16
g.println('; stack frame size: $g.stack_var_pos') g.println('; stack frame size: $g.stack_var_pos')
g.write32_at(local_alloc_pos + 3, g.stack_var_pos) g.write32_at(local_alloc_pos + 3, g.stack_var_pos)
is_main := node.name == 'main.main' is_main := node.name == 'main.main'
@ -2339,6 +2410,10 @@ pub fn (mut g Gen) builtin_decl_amd64(builtin BuiltinFn) {
g.sub(.rsp, 0) g.sub(.rsp, 0)
builtin.body(builtin, mut g) builtin.body(builtin, mut g)
// 16 bytes align
g.stack_var_pos += 7
g.stack_var_pos /= 16
g.stack_var_pos *= 16
g.println('; stack frame size: $g.stack_var_pos') g.println('; stack frame size: $g.stack_var_pos')
g.write32_at(local_alloc_pos + 3, g.stack_var_pos) g.write32_at(local_alloc_pos + 3, g.stack_var_pos)
@ -2363,32 +2438,39 @@ pub fn (mut g Gen) allocate_var(name string, size int, initial_val int) int {
// TODO // TODO
return 0 return 0
} }
padding := (size - g.stack_var_pos % size) % size
n := g.stack_var_pos + size + padding
is_far_var := n > 0x80 || n < -0x7f
far_var_offset := if is_far_var { 0x40 } else { 0 }
// `a := 3` => `mov DWORD [rbp-0x4],0x3` // `a := 3` => `mov DWORD [rbp-0x4],0x3`
match size { match size {
1 { 1 {
// BYTE // BYTE
g.write8(0xc6) g.write8(0xc6)
g.write8(0x45) g.write8(0x45 + far_var_offset)
} }
4 { 4 {
// DWORD // DWORD
g.write8(0xc7) g.write8(0xc7)
g.write8(0x45) g.write8(0x45 + far_var_offset)
} }
8 { 8 {
// QWORD // QWORD
g.write8(0x48) g.write8(0x48)
g.write8(0xc7) g.write8(0xc7)
g.write8(0x45) g.write8(0x45 + far_var_offset)
} }
else { else {
g.n_error('allocate_var: bad size $size') g.n_error('allocate_var: bad size $size')
} }
} }
// Generate N in `[rbp-N]` // Generate N in `[rbp-N]`
n := g.stack_var_pos + size if is_far_var {
g.write8(0xff - n + 1) g.write32(int((0xffffffff - i64(n) + 1) % 0x100000000))
g.stack_var_pos += size } else {
g.write8((0xff - n + 1) % 0x100)
}
g.stack_var_pos += size + padding
g.var_offset[name] = g.stack_var_pos g.var_offset[name] = g.stack_var_pos
g.var_alloc_size[name] = size g.var_alloc_size[name] = size

View File

@ -7,6 +7,10 @@ fn sumcall2(a int, b int) int {
return a + b return a + b
} }
fn sumcall7(a int, b int, c int, d int, e int, f int, g int) int {
return a + b + c + d + e + f + g
}
fn main() { fn main() {
r := sumcall (1,2) r := sumcall (1,2)
assert r == 3 assert r == 3
@ -14,5 +18,11 @@ fn main() {
s := sumcall2 (1,2) s := sumcall2 (1,2)
assert s == 3 assert s == 3
a := sumcall7(1,2,s,4,5,6,7)
assert a == 28
b := sumcall7(sumcall7(1, 2, 3, 4, 5, 6, 7), 8, 9, sumcall7(10, 11, 12, 13, 14, 15, 16), 17, 18, sumcall7(19, 20, 21, 22, 23, 24, 25))
assert b == 325
exit(0) exit(0)
} }