mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
native: support simple multi return (#16628)
This commit is contained in:
parent
42a9eaac0e
commit
799af2adad
@ -1606,6 +1606,51 @@ fn (mut g Gen) mov_reg(a Register, b Register) {
|
|||||||
g.println('mov ${a}, ${b}')
|
g.println('mov ${a}, ${b}')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) add_store(a Register, b Register, size Size) {
|
||||||
|
if size == ._16 {
|
||||||
|
g.write8(0x66)
|
||||||
|
}
|
||||||
|
if size == ._64 {
|
||||||
|
g.write8(0x48 + int(b) / 8 * 4 + int(a) / 8)
|
||||||
|
}
|
||||||
|
g.write8(if size == ._8 { 0x00 } else { 0x01 })
|
||||||
|
g.write8(int(b) % 8 * 8 + int(a) % 8)
|
||||||
|
g.println('add [${a}], ${b}')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) sub_store(a Register, b Register, size Size) {
|
||||||
|
if size == ._16 {
|
||||||
|
g.write8(0x66)
|
||||||
|
}
|
||||||
|
if size == ._64 {
|
||||||
|
g.write8(0x48 + int(b) / 8 * 4 + int(a) / 8)
|
||||||
|
}
|
||||||
|
g.write8(if size == ._8 { 0x28 } else { 0x29 })
|
||||||
|
g.write8(int(b) % 8 * 8 + int(a) % 8)
|
||||||
|
g.println('sub [${a}], ${b}')
|
||||||
|
}
|
||||||
|
|
||||||
|
[params]
|
||||||
|
struct AvailableRegister {
|
||||||
|
available Register
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mul_store(a Register, b Register, size Size) {
|
||||||
|
if size == ._8 {
|
||||||
|
} else {
|
||||||
|
if size == ._16 {
|
||||||
|
g.write8(0x66)
|
||||||
|
}
|
||||||
|
if size == ._64 {
|
||||||
|
g.write8(0x48 + int(b) / 8 * 4 + int(a) / 8)
|
||||||
|
}
|
||||||
|
g.write16(0xaf0f)
|
||||||
|
g.write8(int(b) % 8 * 8 + int(a) % 8)
|
||||||
|
g.println('imul ${b}, [${a}]')
|
||||||
|
g.mov_store(a, b, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut g Gen) sar8(r Register, val u8) {
|
fn (mut g Gen) sar8(r Register, val u8) {
|
||||||
g.write8(0x48)
|
g.write8(0x48)
|
||||||
g.write8(0xc1)
|
g.write8(0xc1)
|
||||||
@ -1645,8 +1690,8 @@ pub fn (mut g Gen) call_fn_amd64(node ast.CallExpr) {
|
|||||||
return_size := g.get_type_size(node.return_type)
|
return_size := g.get_type_size(node.return_type)
|
||||||
mut return_pos := -1
|
mut return_pos := -1
|
||||||
mut is_struct_return := false
|
mut is_struct_return := false
|
||||||
if ts.kind == .struct_ {
|
if ts.kind in [.struct_, .multi_return] {
|
||||||
return_pos = g.allocate_struct('', node.return_type)
|
return_pos = g.allocate_by_type('', node.return_type)
|
||||||
if return_size > 16 {
|
if return_size > 16 {
|
||||||
is_struct_return = true
|
is_struct_return = true
|
||||||
args << ast.CallArg{
|
args << ast.CallArg{
|
||||||
@ -1784,7 +1829,7 @@ pub fn (mut g Gen) call_fn_amd64(node ast.CallExpr) {
|
|||||||
}
|
}
|
||||||
g.println('call `${n}()`')
|
g.println('call `${n}()`')
|
||||||
|
|
||||||
if ts.kind == .struct_ {
|
if ts.kind in [.struct_, .multi_return] {
|
||||||
match return_size {
|
match return_size {
|
||||||
1...7 {
|
1...7 {
|
||||||
g.mov_var_to_reg(.rdx, LocalVar{ offset: return_pos, typ: ast.i64_type_idx })
|
g.mov_var_to_reg(.rdx, LocalVar{ offset: return_pos, typ: ast.i64_type_idx })
|
||||||
@ -1963,7 +2008,7 @@ fn (mut g Gen) assign_right_expr(node ast.AssignStmt, i int, right ast.Expr, nam
|
|||||||
ts := g.table.sym(typ)
|
ts := g.table.sym(typ)
|
||||||
match ts.info {
|
match ts.info {
|
||||||
ast.Struct {
|
ast.Struct {
|
||||||
g.allocate_struct(name, typ)
|
g.allocate_by_type(name, typ)
|
||||||
}
|
}
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
@ -2141,7 +2186,7 @@ fn (mut g Gen) assign_right_expr(node ast.AssignStmt, i int, right ast.Expr, nam
|
|||||||
ast.StructInit {
|
ast.StructInit {
|
||||||
match node.op {
|
match node.op {
|
||||||
.decl_assign {
|
.decl_assign {
|
||||||
g.allocate_struct(name, right.typ)
|
g.allocate_by_type(name, right.typ)
|
||||||
g.init_struct(ident, right)
|
g.init_struct(ident, right)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -2266,7 +2311,222 @@ fn (mut g Gen) assign_right_expr(node ast.AssignStmt, i int, right ast.Expr, nam
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[params]
|
||||||
|
struct RegisterOption {
|
||||||
|
reg Register = Register.rax
|
||||||
|
ssereg SSERegister = SSERegister.xmm0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) gen_type_promotion(from ast.Type, to ast.Type, option RegisterOption) {
|
||||||
|
if !to.is_pure_float() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if g.is_register_type(from) {
|
||||||
|
// integer -> float
|
||||||
|
if g.get_type_size(from) == 8 && !from.is_signed() {
|
||||||
|
label1 := g.labels.new_label()
|
||||||
|
label2 := g.labels.new_label()
|
||||||
|
g.test_reg(option.reg)
|
||||||
|
addr1 := g.cjmp(.js)
|
||||||
|
g.labels.patches << LabelPatch{
|
||||||
|
id: label1
|
||||||
|
pos: addr1
|
||||||
|
}
|
||||||
|
// if castee is in the range of i64
|
||||||
|
prefix, inst := if g.get_type_size(to) == 4 {
|
||||||
|
0xf3, 's'
|
||||||
|
} else {
|
||||||
|
0xf2, 'd'
|
||||||
|
}
|
||||||
|
g.write8(prefix)
|
||||||
|
g.write8(0x48 + int(option.ssereg) / 8 * 4 + int(option.reg) / 8)
|
||||||
|
g.write16(0x2a0f)
|
||||||
|
g.write8(0xc0 + int(option.ssereg) % 8 * 8 + int(option.reg) % 8)
|
||||||
|
g.println('cvtsi2s${inst} ${option.ssereg}, ${option.reg}')
|
||||||
|
addr2 := g.jmp(0)
|
||||||
|
g.labels.patches << LabelPatch{
|
||||||
|
id: label2
|
||||||
|
pos: addr2
|
||||||
|
}
|
||||||
|
g.labels.addrs[label1] = g.pos()
|
||||||
|
// if castee has the leftmost bit
|
||||||
|
g.mov_reg(.rbx, .rax)
|
||||||
|
g.write([u8(0x48), 0xd1, 0xe8])
|
||||||
|
g.println('shr rax')
|
||||||
|
g.write([u8(0x83), 0xe3, 0x01])
|
||||||
|
g.println('and ebx, 0x1')
|
||||||
|
g.bitor_reg(.rax, .rbx)
|
||||||
|
g.write8(prefix)
|
||||||
|
g.write8(0x48 + int(option.ssereg) / 8 * 4 + int(option.reg) / 8)
|
||||||
|
g.write16(0x2a0f)
|
||||||
|
g.write8(0xc0 + int(option.ssereg) % 8 * 8 + int(option.reg) % 8)
|
||||||
|
g.println('cvtsi2s${inst} ${option.ssereg}, ${option.reg}')
|
||||||
|
g.add_sse(option.ssereg, option.ssereg, to)
|
||||||
|
g.labels.addrs[label2] = g.pos()
|
||||||
|
} else {
|
||||||
|
prefix, inst := if g.get_type_size(to) == 4 {
|
||||||
|
0xf3, 's'
|
||||||
|
} else {
|
||||||
|
0xf2, 'd'
|
||||||
|
}
|
||||||
|
g.write8(prefix)
|
||||||
|
g.write8(0x48 + int(option.ssereg) / 8 * 4 + int(option.reg) / 8)
|
||||||
|
g.write16(0x2a0f)
|
||||||
|
g.write8(0xc0 + int(option.ssereg) % 8 * 8 + int(option.reg) % 8)
|
||||||
|
g.println('cvtsi2s${inst} ${option.ssereg}, ${option.reg}')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if from == ast.f32_type_idx && to != ast.f32_type_idx {
|
||||||
|
// f32 -> f64
|
||||||
|
g.write8(0xf3)
|
||||||
|
if int(option.ssereg) >= int(SSERegister.xmm8) {
|
||||||
|
g.write8(0x45)
|
||||||
|
}
|
||||||
|
g.write16(0x5a0f)
|
||||||
|
g.write8(0xc0 + int(option.ssereg) % 8 * 9)
|
||||||
|
g.println('cvtss2sd ${option.ssereg}, ${option.ssereg}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) multi_assign_stmt(node ast.AssignStmt) {
|
||||||
|
multi_return := g.get_multi_return(node.right_types)
|
||||||
|
if node.has_cross_var {
|
||||||
|
// `a, b = b, a`
|
||||||
|
// construct a struct variable contains the return value
|
||||||
|
size := multi_return.size
|
||||||
|
align := multi_return.align
|
||||||
|
padding := (align - g.stack_var_pos % align) % align
|
||||||
|
g.stack_var_pos += size + padding
|
||||||
|
var := LocalVar{
|
||||||
|
offset: g.stack_var_pos
|
||||||
|
}
|
||||||
|
// zero fill
|
||||||
|
mut left := if size >= 16 {
|
||||||
|
g.mov(.rax, 0)
|
||||||
|
g.mov(.rcx, size / 8)
|
||||||
|
g.lea_var_to_reg(.rdi, var.offset)
|
||||||
|
g.write([u8(0xf3), 0x48, 0xab])
|
||||||
|
g.println('rep stosq')
|
||||||
|
size % 8
|
||||||
|
} else {
|
||||||
|
size
|
||||||
|
}
|
||||||
|
if left >= 8 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.i64_type_idx)
|
||||||
|
left -= 8
|
||||||
|
}
|
||||||
|
if left >= 4 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.int_type_idx)
|
||||||
|
left -= 4
|
||||||
|
}
|
||||||
|
if left >= 2 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.i16_type_idx)
|
||||||
|
left -= 2
|
||||||
|
}
|
||||||
|
if left == 1 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.i8_type_idx)
|
||||||
|
}
|
||||||
|
// store exprs to the variable
|
||||||
|
for i, expr in node.right {
|
||||||
|
offset := multi_return.offsets[i]
|
||||||
|
g.expr(expr)
|
||||||
|
// TODO expr not on rax
|
||||||
|
g.mov_reg_to_var(var, .rax, offset: offset, typ: node.right_types[i])
|
||||||
|
}
|
||||||
|
// store the multi return struct value
|
||||||
|
g.lea_var_to_reg(.rax, var.offset)
|
||||||
|
} else {
|
||||||
|
g.expr(node.right[0])
|
||||||
|
}
|
||||||
|
g.mov_reg(.rdx, .rax)
|
||||||
|
|
||||||
|
mut current_offset := 0
|
||||||
|
for i, offset in multi_return.offsets {
|
||||||
|
if node.left[i] is ast.Ident {
|
||||||
|
name := (node.left[i] as ast.Ident).name
|
||||||
|
if name == '_' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if node.op == .decl_assign {
|
||||||
|
g.allocate_by_type(name, node.left_types[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if offset != current_offset {
|
||||||
|
g.add(.rdx, offset - current_offset)
|
||||||
|
current_offset = offset
|
||||||
|
}
|
||||||
|
g.gen_left_value(node.left[i])
|
||||||
|
left_type := node.left_types[i]
|
||||||
|
right_type := node.right_types[i]
|
||||||
|
if g.is_register_type(right_type) {
|
||||||
|
g.mov_deref(.rcx, .rdx, right_type)
|
||||||
|
} else if node.right_types[i].is_pure_float() {
|
||||||
|
g.mov_deref_sse(.xmm0, .rdx, right_type)
|
||||||
|
}
|
||||||
|
g.gen_type_promotion(right_type, left_type, reg: .rcx)
|
||||||
|
if g.is_register_type(left_type) {
|
||||||
|
match node.op {
|
||||||
|
.assign, .decl_assign {
|
||||||
|
g.mov_store(.rax, .rcx, match g.get_type_size(left_type) {
|
||||||
|
1 { ._8 }
|
||||||
|
2 { ._16 }
|
||||||
|
4 { ._32 }
|
||||||
|
else { ._64 }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
g.n_error('Unsupported assign instruction')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if left_type.is_pure_float() {
|
||||||
|
is_f32 := left_type == ast.f32_type_idx
|
||||||
|
if node.op !in [.assign, .decl_assign] {
|
||||||
|
g.mov_ssereg(.xmm1, .xmm0)
|
||||||
|
if is_f32 {
|
||||||
|
g.write32(0x00100ff3)
|
||||||
|
g.println('movss xmm0, [rax]')
|
||||||
|
} else {
|
||||||
|
g.write32(0x00100ff2)
|
||||||
|
g.println('movsd xmm0, [rax]')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match node.op {
|
||||||
|
.plus_assign {
|
||||||
|
g.add_sse(.xmm0, .xmm1, left_type)
|
||||||
|
}
|
||||||
|
.minus_assign {
|
||||||
|
g.sub_sse(.xmm0, .xmm1, left_type)
|
||||||
|
}
|
||||||
|
.mult_assign {
|
||||||
|
g.mul_sse(.xmm0, .xmm1, left_type)
|
||||||
|
}
|
||||||
|
.div_assign {
|
||||||
|
g.div_sse(.xmm0, .xmm1, left_type)
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
if is_f32 {
|
||||||
|
g.write32(0x00110ff3)
|
||||||
|
g.println('movss [rax], xmm0')
|
||||||
|
} else {
|
||||||
|
g.write32(0x00110ff2)
|
||||||
|
g.println('movsd [rax], xmm0')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g.n_error('multi return for struct is not supported yet')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut g Gen) assign_stmt(node ast.AssignStmt) {
|
fn (mut g Gen) assign_stmt(node ast.AssignStmt) {
|
||||||
|
// `a, b := foo()`
|
||||||
|
// `a, b := if cond { 1, 2 } else { 3, 4 }`
|
||||||
|
// `a, b = b, a`
|
||||||
|
if (node.left.len > 1 && node.right.len == 1) || node.has_cross_var {
|
||||||
|
g.multi_assign_stmt(node)
|
||||||
|
return
|
||||||
|
}
|
||||||
// `a := 1` | `a,b := 1,2`
|
// `a := 1` | `a,b := 1,2`
|
||||||
for i, left in node.left {
|
for i, left in node.left {
|
||||||
right := node.right[i]
|
right := node.right[i]
|
||||||
@ -2278,19 +2538,20 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if left is ast.Ident && node.op == .decl_assign {
|
if left is ast.Ident && node.op == .decl_assign {
|
||||||
g.allocate_var((left as ast.Ident).name, g.get_type_size(typ), 0)
|
g.allocate_by_type((left as ast.Ident).name, typ)
|
||||||
}
|
}
|
||||||
g.gen_left_value(left)
|
g.gen_left_value(left)
|
||||||
g.push(.rax)
|
g.push(.rax)
|
||||||
g.expr(right)
|
g.expr(right)
|
||||||
g.pop(.rdx)
|
g.pop(.rdx)
|
||||||
|
g.gen_type_promotion(node.right_types[0], typ)
|
||||||
if g.is_register_type(typ) {
|
if g.is_register_type(typ) {
|
||||||
match node.op {
|
match node.op {
|
||||||
.assign {
|
.decl_assign, .assign {
|
||||||
g.mov_store(.rdx, .rax, match g.get_type_size(typ) {
|
g.mov_store(.rdx, .rax, match g.get_type_size(typ) {
|
||||||
1 { ._8 }
|
1 { ._8 }
|
||||||
2 { ._16 }
|
2 { ._16 }
|
||||||
3 { ._32 }
|
4 { ._32 }
|
||||||
else { ._64 }
|
else { ._64 }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -2299,7 +2560,6 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if typ.is_pure_float() {
|
} else if typ.is_pure_float() {
|
||||||
// TODO when the right type is integer
|
|
||||||
is_f32 := typ == ast.f32_type_idx
|
is_f32 := typ == ast.f32_type_idx
|
||||||
if node.op !in [.assign, .decl_assign] {
|
if node.op !in [.assign, .decl_assign] {
|
||||||
g.mov_ssereg(.xmm1, .xmm0)
|
g.mov_ssereg(.xmm1, .xmm0)
|
||||||
@ -2334,7 +2594,7 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) {
|
|||||||
g.println('movsd [rdx], xmm0')
|
g.println('movsd [rdx], xmm0')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if node.op != .assign {
|
if node.op !in [.assign, .decl_assign] {
|
||||||
g.n_error('Unsupported assign instruction')
|
g.n_error('Unsupported assign instruction')
|
||||||
}
|
}
|
||||||
ts := g.table.sym(typ)
|
ts := g.table.sym(typ)
|
||||||
@ -2989,7 +3249,7 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) {
|
|||||||
// The first parameter is an address of returned struct if size > 16
|
// The first parameter is an address of returned struct if size > 16
|
||||||
ts := g.table.sym(node.return_type)
|
ts := g.table.sym(node.return_type)
|
||||||
return_size := g.get_type_size(node.return_type)
|
return_size := g.get_type_size(node.return_type)
|
||||||
if ts.kind == .struct_ {
|
if ts.kind in [.struct_, .multi_return] {
|
||||||
if return_size > 16 {
|
if return_size > 16 {
|
||||||
params << ast.Param{
|
params << ast.Param{
|
||||||
name: '_return_val_addr'
|
name: '_return_val_addr'
|
||||||
@ -3030,7 +3290,7 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) {
|
|||||||
for i in reg_args {
|
for i in reg_args {
|
||||||
name := params[i].name
|
name := params[i].name
|
||||||
g.stack_var_pos += (8 - args_size[i] % 8) % 8
|
g.stack_var_pos += (8 - args_size[i] % 8) % 8
|
||||||
offset := g.allocate_struct(name, params[i].typ)
|
offset := g.allocate_by_type(name, params[i].typ)
|
||||||
// copy
|
// copy
|
||||||
g.mov_reg_to_var(LocalVar{ offset: offset, typ: ast.i64_type_idx, name: name },
|
g.mov_reg_to_var(LocalVar{ offset: offset, typ: ast.i64_type_idx, name: name },
|
||||||
native.fn_arg_registers[reg_idx])
|
native.fn_arg_registers[reg_idx])
|
||||||
@ -3046,7 +3306,7 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) {
|
|||||||
// define and copy args on sse register
|
// define and copy args on sse register
|
||||||
for idx, i in ssereg_args {
|
for idx, i in ssereg_args {
|
||||||
name := params[i].name
|
name := params[i].name
|
||||||
offset := g.allocate_struct(name, params[i].typ)
|
offset := g.allocate_by_type(name, params[i].typ)
|
||||||
// copy
|
// copy
|
||||||
g.mov_ssereg_to_var(LocalVar{ offset: offset, typ: params[i].typ }, native.fn_arg_sse_registers[idx])
|
g.mov_ssereg_to_var(LocalVar{ offset: offset, typ: params[i].typ }, native.fn_arg_sse_registers[idx])
|
||||||
}
|
}
|
||||||
@ -3175,7 +3435,7 @@ pub fn (mut g Gen) allocate_var(name string, size int, initial_val int) int {
|
|||||||
return g.stack_var_pos
|
return g.stack_var_pos
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) allocate_struct(name string, typ ast.Type) int {
|
fn (mut g Gen) allocate_by_type(name string, typ ast.Type) int {
|
||||||
if g.pref.arch == .arm64 {
|
if g.pref.arch == .arm64 {
|
||||||
// TODO
|
// TODO
|
||||||
return 0
|
return 0
|
||||||
@ -3649,6 +3909,21 @@ fn (mut g Gen) mov_reg_to_ssereg(a SSERegister, b Register, typ ast.Type) {
|
|||||||
g.println('${inst} ${a}, ${b}')
|
g.println('${inst} ${a}, ${b}')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mov_deref_sse(a SSERegister, b Register, typ ast.Type) {
|
||||||
|
op, inst, len := if typ == ast.f32_type_idx {
|
||||||
|
0xf3, 'movss', 'DWORD'
|
||||||
|
} else {
|
||||||
|
0xf2, 'movsd', 'QWORD'
|
||||||
|
}
|
||||||
|
g.write8(op)
|
||||||
|
if int(a) >= int(SSERegister.xmm8) || int(b) >= int(Register.r8) {
|
||||||
|
g.write8(0x40 + int(a) / 8 * 4 + int(b) / 8)
|
||||||
|
}
|
||||||
|
g.write16(0x100f)
|
||||||
|
g.write8(int(a) % 8 * 8 + int(b) % 8)
|
||||||
|
g.println('${inst} ${a}, ${len} PTR [${b}]')
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut g Gen) add_sse(a SSERegister, b SSERegister, typ ast.Type) {
|
fn (mut g Gen) add_sse(a SSERegister, b SSERegister, typ ast.Type) {
|
||||||
g.write8(if typ == ast.f32_type_idx { 0xf3 } else { 0xf2 })
|
g.write8(if typ == ast.f32_type_idx { 0xf3 } else { 0xf2 })
|
||||||
if int(a) >= int(SSERegister.xmm8) || int(b) >= int(SSERegister.xmm8) {
|
if int(a) >= int(SSERegister.xmm8) || int(b) >= int(SSERegister.xmm8) {
|
||||||
|
@ -124,6 +124,13 @@ mut:
|
|||||||
fields map[string]int
|
fields map[string]int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MultiReturn {
|
||||||
|
mut:
|
||||||
|
offsets []int
|
||||||
|
size int
|
||||||
|
align int
|
||||||
|
}
|
||||||
|
|
||||||
enum Size {
|
enum Size {
|
||||||
_8
|
_8
|
||||||
_16
|
_16
|
||||||
@ -579,6 +586,19 @@ fn (mut g Gen) get_type_size(typ ast.Type) int {
|
|||||||
size = 4
|
size = 4
|
||||||
align = 4
|
align = 4
|
||||||
}
|
}
|
||||||
|
ast.MultiReturn {
|
||||||
|
for t in ts.info.types {
|
||||||
|
t_size := g.get_type_size(t)
|
||||||
|
t_align := g.get_type_align(t)
|
||||||
|
padding := (t_align - size % t_align) % t_align
|
||||||
|
strc.offsets << size + padding
|
||||||
|
size += t_size + padding
|
||||||
|
if t_align > align {
|
||||||
|
align = t_align
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.structs[typ.idx()] = strc
|
||||||
|
}
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
mut ts_ := g.table.sym(typ)
|
mut ts_ := g.table.sym(typ)
|
||||||
@ -602,6 +622,27 @@ fn (mut g Gen) get_type_align(typ ast.Type) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) get_multi_return(types []ast.Type) MultiReturn {
|
||||||
|
mut size := 0
|
||||||
|
mut align := 1
|
||||||
|
mut ret := MultiReturn{
|
||||||
|
offsets: []int{cap: types.len}
|
||||||
|
}
|
||||||
|
for t in types {
|
||||||
|
t_size := g.get_type_size(t)
|
||||||
|
t_align := g.get_type_align(t)
|
||||||
|
padding := (t_align - size % t_align) % t_align
|
||||||
|
ret.offsets << size + padding
|
||||||
|
size += t_size + padding
|
||||||
|
if t_align > align {
|
||||||
|
align = t_align
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret.size = size
|
||||||
|
ret.align = align
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
fn (g Gen) is_register_type(typ ast.Type) bool {
|
fn (g Gen) is_register_type(typ ast.Type) bool {
|
||||||
return typ.is_pure_int() || typ == ast.char_type_idx || typ.is_real_pointer() || typ.is_bool()
|
return typ.is_pure_int() || typ == ast.char_type_idx || typ.is_real_pointer() || typ.is_bool()
|
||||||
}
|
}
|
||||||
@ -1129,18 +1170,18 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
|||||||
ast.Module {}
|
ast.Module {}
|
||||||
ast.Return {
|
ast.Return {
|
||||||
mut s := '?' //${node.exprs[0].val.str()}'
|
mut s := '?' //${node.exprs[0].val.str()}'
|
||||||
if e0 := node.exprs[0] {
|
if node.exprs.len == 1 {
|
||||||
match e0 {
|
match node.exprs[0] {
|
||||||
ast.StringLiteral {
|
ast.StringLiteral {
|
||||||
s = g.eval_escape_codes(e0)
|
s = g.eval_escape_codes(node.exprs[0] as ast.StringLiteral)
|
||||||
g.expr(node.exprs[0])
|
g.expr(node.exprs[0])
|
||||||
g.mov64(.rax, g.allocate_string(s, 2, .abs64))
|
g.mov64(.rax, g.allocate_string(s, 2, .abs64))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
g.expr(e0)
|
g.expr(node.exprs[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
typ := node.types[0]
|
typ := if node.types.len > 1 { g.return_type } else { node.types[0] }
|
||||||
if typ == ast.float_literal_type_idx && g.return_type == ast.f32_type_idx {
|
if typ == ast.float_literal_type_idx && g.return_type == ast.f32_type_idx {
|
||||||
if g.pref.arch == .amd64 {
|
if g.pref.arch == .amd64 {
|
||||||
g.write32(0xc05a0ff2)
|
g.write32(0xc05a0ff2)
|
||||||
@ -1153,7 +1194,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
|||||||
size := g.get_type_size(typ)
|
size := g.get_type_size(typ)
|
||||||
if g.pref.arch == .amd64 {
|
if g.pref.arch == .amd64 {
|
||||||
match ts.kind {
|
match ts.kind {
|
||||||
.struct_ {
|
.struct_, .multi_return {
|
||||||
if size <= 8 {
|
if size <= 8 {
|
||||||
g.mov_deref(.rax, .rax, ast.i64_type_idx)
|
g.mov_deref(.rax, .rax, ast.i64_type_idx)
|
||||||
if size != 8 {
|
if size != 8 {
|
||||||
@ -1199,6 +1240,92 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if node.exprs.len > 1 {
|
||||||
|
typ := g.return_type
|
||||||
|
ts := g.table.sym(typ)
|
||||||
|
size := g.get_type_size(typ)
|
||||||
|
// construct a struct variable contains the return value
|
||||||
|
var := LocalVar{
|
||||||
|
offset: g.allocate_by_type('', typ)
|
||||||
|
typ: typ
|
||||||
|
}
|
||||||
|
// zero fill
|
||||||
|
mut left := if size >= 16 {
|
||||||
|
g.mov(.rax, 0)
|
||||||
|
g.mov(.rcx, size / 8)
|
||||||
|
g.lea_var_to_reg(.rdi, var.offset)
|
||||||
|
g.write([u8(0xf3), 0x48, 0xab])
|
||||||
|
g.println('rep stosq')
|
||||||
|
size % 8
|
||||||
|
} else {
|
||||||
|
size
|
||||||
|
}
|
||||||
|
if left >= 8 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.i64_type_idx)
|
||||||
|
left -= 8
|
||||||
|
}
|
||||||
|
if left >= 4 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.int_type_idx)
|
||||||
|
left -= 4
|
||||||
|
}
|
||||||
|
if left >= 2 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.i16_type_idx)
|
||||||
|
left -= 2
|
||||||
|
}
|
||||||
|
if left == 1 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.i8_type_idx)
|
||||||
|
}
|
||||||
|
// store exprs to the variable
|
||||||
|
for i, expr in node.exprs {
|
||||||
|
offset := g.structs[typ.idx()].offsets[i]
|
||||||
|
g.expr(expr)
|
||||||
|
// TODO expr not on rax
|
||||||
|
g.mov_reg_to_var(var, .rax, offset: offset, typ: ts.mr_info().types[i])
|
||||||
|
}
|
||||||
|
// store the multi return struct value
|
||||||
|
g.lea_var_to_reg(.rax, var.offset)
|
||||||
|
if g.pref.arch == .amd64 {
|
||||||
|
if size <= 8 {
|
||||||
|
g.mov_deref(.rax, .rax, ast.i64_type_idx)
|
||||||
|
if size != 8 {
|
||||||
|
g.movabs(.rbx, i64((u64(1) << (size * 8)) - 1))
|
||||||
|
g.bitand_reg(.rax, .rbx)
|
||||||
|
}
|
||||||
|
} else if size <= 16 {
|
||||||
|
g.add(.rax, 8)
|
||||||
|
g.mov_deref(.rdx, .rax, ast.i64_type_idx)
|
||||||
|
g.sub(.rax, 8)
|
||||||
|
g.mov_deref(.rax, .rax, ast.i64_type_idx)
|
||||||
|
if size != 16 {
|
||||||
|
g.movabs(.rbx, i64((u64(1) << ((size - 8) * 8)) - 1))
|
||||||
|
g.bitand_reg(.rdx, .rbx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
offset := g.get_var_offset('_return_val_addr')
|
||||||
|
g.mov_var_to_reg(.rdx, LocalVar{
|
||||||
|
offset: offset
|
||||||
|
typ: ast.i64_type_idx
|
||||||
|
})
|
||||||
|
for i in 0 .. size / 8 {
|
||||||
|
g.mov_deref(.rcx, .rax, ast.i64_type_idx)
|
||||||
|
g.mov_store(.rdx, .rcx, ._64)
|
||||||
|
if i != size / 8 - 1 {
|
||||||
|
g.add(.rax, 8)
|
||||||
|
g.add(.rdx, 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if size % 8 != 0 {
|
||||||
|
g.add(.rax, size % 8)
|
||||||
|
g.add(.rdx, size % 8)
|
||||||
|
g.mov_deref(.rcx, .rax, ast.i64_type_idx)
|
||||||
|
g.mov_store(.rdx, .rcx, ._64)
|
||||||
|
}
|
||||||
|
g.mov_var_to_reg(.rax, LocalVar{
|
||||||
|
offset: offset
|
||||||
|
typ: ast.i64_type_idx
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// jump to return label
|
// jump to return label
|
||||||
@ -1389,7 +1516,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||||||
g.allocate_string(str, 3, .rel32)
|
g.allocate_string(str, 3, .rel32)
|
||||||
}
|
}
|
||||||
ast.StructInit {
|
ast.StructInit {
|
||||||
pos := g.allocate_struct('_anonstruct', node.typ)
|
pos := g.allocate_by_type('_anonstruct', node.typ)
|
||||||
g.init_struct(LocalVar{ offset: pos, typ: node.typ }, node)
|
g.init_struct(LocalVar{ offset: pos, typ: node.typ }, node)
|
||||||
g.lea_var_to_reg(.rax, pos)
|
g.lea_var_to_reg(.rax, pos)
|
||||||
}
|
}
|
||||||
@ -1419,6 +1546,51 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||||||
ast.UnsafeExpr {
|
ast.UnsafeExpr {
|
||||||
g.expr(node.expr)
|
g.expr(node.expr)
|
||||||
}
|
}
|
||||||
|
ast.ConcatExpr {
|
||||||
|
typ := node.return_type
|
||||||
|
ts := g.table.sym(typ)
|
||||||
|
size := g.get_type_size(typ)
|
||||||
|
// construct a struct variable contains the return value
|
||||||
|
var := LocalVar{
|
||||||
|
offset: g.allocate_by_type('', typ)
|
||||||
|
typ: typ
|
||||||
|
}
|
||||||
|
// zero fill
|
||||||
|
mut left := if size >= 16 {
|
||||||
|
g.mov(.rax, 0)
|
||||||
|
g.mov(.rcx, size / 8)
|
||||||
|
g.lea_var_to_reg(.rdi, var.offset)
|
||||||
|
g.write([u8(0xf3), 0x48, 0xab])
|
||||||
|
g.println('rep stosq')
|
||||||
|
size % 8
|
||||||
|
} else {
|
||||||
|
size
|
||||||
|
}
|
||||||
|
if left >= 8 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.i64_type_idx)
|
||||||
|
left -= 8
|
||||||
|
}
|
||||||
|
if left >= 4 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.int_type_idx)
|
||||||
|
left -= 4
|
||||||
|
}
|
||||||
|
if left >= 2 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.i16_type_idx)
|
||||||
|
left -= 2
|
||||||
|
}
|
||||||
|
if left == 1 {
|
||||||
|
g.mov_int_to_var(var, 0, offset: size - left, typ: ast.i8_type_idx)
|
||||||
|
}
|
||||||
|
// store exprs to the variable
|
||||||
|
for i, expr in node.vals {
|
||||||
|
offset := g.structs[typ.idx()].offsets[i]
|
||||||
|
g.expr(expr)
|
||||||
|
// TODO expr not on rax
|
||||||
|
g.mov_reg_to_var(var, .rax, offset: offset, typ: ts.mr_info().types[i])
|
||||||
|
}
|
||||||
|
// store the multi return struct value
|
||||||
|
g.lea_var_to_reg(.rax, var.offset)
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
g.n_error('expr: unhandled node type: ${node.type_name()}')
|
g.n_error('expr: unhandled node type: ${node.type_name()}')
|
||||||
}
|
}
|
||||||
|
68
vlib/v/gen/native/tests/multi_assign.vv
Normal file
68
vlib/v/gen/native/tests/multi_assign.vv
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
fn multret1(i int, j int) (int, int) {
|
||||||
|
return if i > j { i, 10 } else { 10, j }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multret2(i int, j int) (int, int) {
|
||||||
|
return match i > j {
|
||||||
|
true { i, 10 }
|
||||||
|
false { 10, j }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multret3(i int, j int) (int, int) {
|
||||||
|
if i > j {
|
||||||
|
return i, 10
|
||||||
|
} else {
|
||||||
|
return 10, j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multret4(i int, j int) (int, int) {
|
||||||
|
match i > j {
|
||||||
|
true { return i, 10 }
|
||||||
|
false { return 10, j }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_multi_return_test() { // from fn_multi_return_test.v
|
||||||
|
mut a, mut b := 0, 0
|
||||||
|
|
||||||
|
a, b = multret1(3, 14)
|
||||||
|
assert a == 10
|
||||||
|
assert b == 14
|
||||||
|
|
||||||
|
a, b = multret2(3, 14)
|
||||||
|
assert a == 10
|
||||||
|
assert b == 14
|
||||||
|
|
||||||
|
a, b = multret3(3, 14)
|
||||||
|
assert a == 10
|
||||||
|
assert b == 14
|
||||||
|
|
||||||
|
a, b = multret4(3, 14)
|
||||||
|
assert a == 10
|
||||||
|
assert b == 14
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Zoo {
|
||||||
|
mut:
|
||||||
|
a int
|
||||||
|
b int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cross_assign_of_struct_test() { // from cross_assign_test.v
|
||||||
|
mut x := Zoo{
|
||||||
|
a: 1
|
||||||
|
b: 2
|
||||||
|
}
|
||||||
|
x.a, x.b = x.b, x.a
|
||||||
|
// println(x)
|
||||||
|
assert x.a == 2
|
||||||
|
assert x.b == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
fn_multi_return_test()
|
||||||
|
cross_assign_of_struct_test()
|
||||||
|
}
|
0
vlib/v/gen/native/tests/multi_assign.vv.out
Normal file
0
vlib/v/gen/native/tests/multi_assign.vv.out
Normal file
Loading…
Reference in New Issue
Block a user