Skip to content

Commit b653d65

Browse files
authored
native: structs multi_assign and multi_return (#25281)
1 parent a10c597 commit b653d65

2 files changed

Lines changed: 78 additions & 5 deletions

File tree

‎vlib/v/gen/native/amd64.v‎

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,7 @@ fn (mut c Amd64) lea_var_to_reg(r Register, var_offset i32) {
953953

954954
is_far_var := var_offset > 0x80 || var_offset < -0x7f
955955
match reg {
956-
.rax, .rbx, .rsi, .rdi {
956+
.rax, .rbx, .rsi, .rdi, .rdx, .rcx {
957957
c.g.write8(0x48)
958958
}
959959
else {}
@@ -2753,6 +2753,7 @@ fn (mut c Amd64) gen_type_promotion(from ast.Type, to ast.Type, option Amd64Regi
27532753
}
27542754

27552755
fn (mut c Amd64) return_stmt(node ast.Return) {
2756+
c.g.println('; return statement {')
27562757
mut s := '?' //${node.exprs[0].val.str()}'
27572758
if node.exprs.len == 1 {
27582759
match node.exprs[0] {
@@ -2812,6 +2813,8 @@ fn (mut c Amd64) return_stmt(node ast.Return) {
28122813
c.add(Amd64Register.rax, size % 8)
28132814
c.add(Amd64Register.rdx, size % 8)
28142815
c.mov_deref(Amd64Register.rcx, Amd64Register.rax, ast.i64_type_idx)
2816+
// TODO: check if it does not write too far as the size of
2817+
// the remaining data is not 64bits
28152818
c.mov_store(.rdx, .rcx, ._64)
28162819
}
28172820
c.mov_var_to_reg(c.main_reg(), LocalVar{
@@ -2841,7 +2844,14 @@ fn (mut c Amd64) return_stmt(node ast.Return) {
28412844
offset := c.g.structs[typ.idx()].offsets[i]
28422845
c.g.expr(expr)
28432846
// TODO: expr not on rax
2844-
c.mov_reg_to_var(var, Amd64Register.rax, offset: offset, typ: ts.mr_info().types[i])
2847+
e_typ := ts.mr_info().types[i]
2848+
e_ts := c.g.table.sym(e_typ)
2849+
if e_ts.info is ast.Struct {
2850+
c.lea_var_to_reg(Amd64Register.rdx, var.offset - offset)
2851+
c.move_struct(.rdx, .rax, c.g.get_type_size(e_typ))
2852+
} else {
2853+
c.mov_reg_to_var(var, Amd64Register.rax, offset: offset, typ: ts.mr_info().types[i])
2854+
}
28452855
}
28462856
// store the multi return struct value
28472857
c.lea_var_to_reg(Amd64Register.rax, var.offset)
@@ -2897,6 +2907,7 @@ fn (mut c Amd64) return_stmt(node ast.Return) {
28972907
pos: pos
28982908
}
28992909
c.g.println('; jump to label ${label}')
2910+
c.g.println('; return statement }')
29002911
}
29012912

29022913
fn (mut c Amd64) multi_assign_stmt(node ast.AssignStmt) {
@@ -2926,7 +2937,7 @@ fn (mut c Amd64) multi_assign_stmt(node ast.AssignStmt) {
29262937
} else {
29272938
c.g.expr(node.right[0])
29282939
}
2929-
c.mov_reg(Amd64Register.rdx, Amd64Register.rax)
2940+
c.mov_reg(Amd64Register.rdx, Amd64Register.rax) // value of right expr(s)
29302941

29312942
mut current_offset := i32(0)
29322943
for i, offset in multi_return.offsets {
@@ -2943,7 +2954,7 @@ fn (mut c Amd64) multi_assign_stmt(node ast.AssignStmt) {
29432954
c.add(Amd64Register.rdx, offset - current_offset)
29442955
current_offset = offset
29452956
}
2946-
c.g.gen_left_value(node.left[i])
2957+
c.g.gen_left_value(node.left[i]) // in rax
29472958
left_type := node.left_types[i]
29482959
right_type := node.right_types[i]
29492960
if c.g.is_register_type(right_type) {
@@ -3001,11 +3012,36 @@ fn (mut c Amd64) multi_assign_stmt(node ast.AssignStmt) {
30013012
c.g.println('movsd [rax], xmm0')
30023013
}
30033014
} else {
3004-
c.g.n_error('${@LOCATION} multi return for struct is not supported yet')
3015+
c.move_struct(.rax, .rdx, c.g.get_type_size(left_type))
30053016
}
30063017
}
30073018
}
30083019

3020+
// Moves a struct of size `_size` (in bytes) from the address stored in input to the address stored in output
3021+
fn (mut c Amd64) move_struct(output Amd64Register, input Amd64Register, _size i32) {
3022+
mut size := _size
3023+
for size != 0 {
3024+
c.mov_deref(Amd64Register.rcx, input, ast.i64_type_idx)
3025+
// mov_store can only move powers of 2 bytes at once
3026+
// the remainder will then get handled the next iteration for simplicity
3027+
data_size := i32(match true {
3028+
size < 2 { 1 }
3029+
size < 4 { 2 }
3030+
size < 8 { 4 }
3031+
else { 8 }
3032+
})
3033+
c.mov_store(output, .rcx, match data_size {
3034+
1 { ._8 }
3035+
2 { ._16 }
3036+
4 { ._32 }
3037+
else { ._64 }
3038+
})
3039+
size -= data_size
3040+
c.add(output, data_size)
3041+
c.add(input, data_size)
3042+
}
3043+
}
3044+
30093045
fn (mut c Amd64) assign_stmt(node ast.AssignStmt) {
30103046
// `a, b := foo()`
30113047
// `a, b := if cond { 1, 2 } else { 3, 4 }`

‎vlib/v/gen/native/tests/multi_assign.vv‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,44 @@ fn cross_assign_of_struct_test() { // from cross_assign_test.v
6161
assert x.b == 1
6262
}
6363

64+
struct MyStruct {
65+
a int
66+
b u64
67+
c u16
68+
d u8
69+
}
70+
71+
struct MyStruct2 {
72+
a u8
73+
b u8
74+
c u8
75+
}
76+
77+
fn struct_multi_return() (int, MyStruct) {
78+
return 3, MyStruct{4, 5, 6, 7}
79+
}
80+
81+
fn struct_multi_return2() (int, MyStruct2) {
82+
return 3, MyStruct2{4, 5, 6}
83+
}
84+
85+
fn struct_multi_return_test() {
86+
a, b := struct_multi_return()
87+
assert a == 3
88+
assert b.a == 4
89+
assert b.b == 5
90+
assert b.c == 6
91+
assert b.d == 7
92+
93+
c, d := struct_multi_return2()
94+
assert c == 3
95+
assert d.a == 4
96+
assert d.b == 5
97+
assert d.c == 6
98+
}
99+
64100
fn main() {
65101
fn_multi_return_test()
66102
cross_assign_of_struct_test()
103+
struct_multi_return_test()
67104
}

0 commit comments

Comments
 (0)