From da8d8fa27cc13e1e2e38e8f42b2ec27d5e2b50ec Mon Sep 17 00:00:00 2001 From: "Eliyaan (Nopana)" <103932369+Eliyaan@users.noreply.github.com> Date: Sat, 31 May 2025 23:46:54 +0200 Subject: [PATCH] native: support nested structs, improve support for right expr of IndexExpr (#24627) --- vlib/v/gen/native/amd64.v | 49 +++++++++++++++++++++++++++---- vlib/v/gen/native/arm64.v | 2 +- vlib/v/gen/native/expr.v | 21 +++++++++---- vlib/v/gen/native/gen.v | 2 +- vlib/v/gen/native/tests/string.vv | 8 +++++ vlib/v/gen/native/tests/struct.vv | 36 +++++++++++++++++++++++ 6 files changed, 104 insertions(+), 14 deletions(-) diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index 3cccbff69b..f7a4d68fe3 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -631,7 +631,7 @@ fn (mut c Amd64) mov_deref(r Register, rptr Register, typ ast.Type) { }) } c.g.write8(i32(reg) % 8 * 8 + i32(regptr) % 8) - c.g.println('mov ${reg}, [${regptr}]') + c.g.println('mov ${reg}, [${regptr}]; size ${size}') } fn (mut c Amd64) mov_store(regptr Amd64Register, reg Amd64Register, size Size) { @@ -1887,7 +1887,7 @@ pub fn (mut c Amd64) call_fn(node ast.CallExpr) { c.bitand_reg(.rdx, .rbx) } } - else {} + else {} // handled below } } if is_floats[i] { @@ -2214,12 +2214,13 @@ fn (mut c Amd64) mov_float_xmm0_var(reg Amd64Register, var_type ast.Type) { } } -fn (mut c Amd64) create_string_struct(typ ast.Type, name string, str string) { +fn (mut c Amd64) create_string_struct(typ ast.Type, name string, str string) i32 { dest := c.allocate_var(name, c.g.get_type_size(typ), i64(0)) c.learel(Amd64Register.rsi, c.g.allocate_string(str, 3, .rel32)) c.mov_reg_to_var(LocalVar{dest, ast.u64_type_idx, name}, Amd64Register.rsi) offset := c.g.get_field_offset(typ, 'len') c.mov_int_to_var(LocalVar{dest, ast.i32_type_idx, name}, i32(str.len), offset: offset) + return dest } fn (mut c Amd64) assign_ident_right_expr(node ast.AssignStmt, i i32, right ast.Expr, name string, ident ast.Ident) { @@ -3698,14 +3699,20 @@ fn (mut c Amd64) init_struct(var Var, init ast.StructInit) { else {} } for f in init.init_fields { + c.g.println('; ${var.name}.${f.name}') field := ts.find_field(f.name) or { c.g.n_error('${@LOCATION} Could not find field `${f.name}` on init (${ts.info})') } - offset := c.g.structs[typ.idx()].offsets[field.i] + f_offset := c.g.structs[typ.idx()].offsets[field.i] + f_ts := c.g.table.sym(field.typ) c.g.expr(f.expr) - // TODO: expr not on rax - c.mov_reg_to_var(var, Amd64Register.rax, offset: offset, typ: field.typ) + if f_ts.info is ast.Struct { + c.copy_struct_to_struct(field, f_offset, 0, var) + } else { + // TODO: expr not on rax -> may be done + c.mov_reg_to_var(var, Amd64Register.rax, offset: f_offset, typ: field.typ) + } } } GlobalVar { @@ -3714,6 +3721,36 @@ fn (mut c Amd64) init_struct(var Var, init ast.StructInit) { } } +// f_offset is the offset of the field in the root struct +// data_offset is the offset to add to rax to find the data +// needs rax to hold the address of the root field data +fn (mut c Amd64) copy_struct_to_struct(field ast.StructField, f_offset i32, data_offset i32, var LocalVar) { + f_typ_idx := field.typ.idx() + f_ts := c.g.table.sym(field.typ) + + for f2 in (f_ts.info as ast.Struct).fields { + c.g.println('; ${var.name}. ... .${f2.name}') + field2 := f_ts.find_field(f2.name) or { + c.g.n_error('${@LOCATION} Could not find field `${f2.name}` on init (${f_ts.info})') + } + f2_offset := c.g.structs[f_typ_idx].offsets[field2.i] + f2_ts := c.g.table.sym(field2.typ) + + if f2_ts.info is ast.Struct { + c.copy_struct_to_struct(field2, f_offset + f2_offset, data_offset + f2_offset, + var) + } else { + c.mov_reg(Amd64Register.rdx, Amd64Register.rax) + c.add(Amd64Register.rdx, data_offset + f2_offset) + c.mov_deref(Amd64Register.rdx, Amd64Register.rdx, field2.typ) + c.mov_reg_to_var(var, Amd64Register.rdx, + offset: f_offset + f2_offset + typ: field2.typ + ) + } + } +} + fn (mut c Amd64) init_array(var Var, node ast.ArrayInit) { match var { ast.Ident { diff --git a/vlib/v/gen/native/arm64.v b/vlib/v/gen/native/arm64.v index 7f77b54fcf..7b4e24bdec 100644 --- a/vlib/v/gen/native/arm64.v +++ b/vlib/v/gen/native/arm64.v @@ -543,7 +543,7 @@ fn (mut c Arm64) cmp_reg2(reg Register, reg2 Register) { panic('Arm64.add_reg2() not implemented') } -fn (mut c Arm64) create_string_struct(typ ast.Type, name string, str string) { +fn (mut c Arm64) create_string_struct(typ ast.Type, name string, str string) i32 { panic('Arm64.add_reg2() not implemented') } diff --git a/vlib/v/gen/native/expr.v b/vlib/v/gen/native/expr.v index 3fd280f9f1..f4bd494e17 100644 --- a/vlib/v/gen/native/expr.v +++ b/vlib/v/gen/native/expr.v @@ -78,7 +78,8 @@ fn (mut g Gen) expr(node ast.Expr) { } ast.StringLiteral { str := g.eval_str_lit_escape_codes(node) - g.code_gen.create_string_struct(ast.string_type_idx, 'string_lit', str) + pos := g.code_gen.create_string_struct(ast.string_type_idx, 'str_lit', str) + g.code_gen.lea_var_to_reg(g.code_gen.main_reg(), pos) } ast.CharLiteral { bytes := g.eval_escape_codes(node.val) @@ -131,9 +132,14 @@ fn (mut g Gen) expr(node ast.Expr) { ast.IndexExpr { if node.left_type.is_string() { g.expr(node.index) - g.code_gen.mov_var_to_reg(Amd64Register.rdx, node.left as ast.Ident) // load address of string - g.code_gen.add_reg2(Amd64Register.rdx, Amd64Register.rax) // add the offset to the address - g.code_gen.mov_deref(Amd64Register.rax, Amd64Register.rdx, ast.u8_type_idx) + g.code_gen.push(Amd64Register.rax) + + g.expr(node.left) // load address of string struct + g.code_gen.mov_deref(Amd64Register.rax, Amd64Register.rax, ast.u64_type_idx) // load value of the str pointer + + g.code_gen.pop2(Amd64Register.rdx) // index + g.code_gen.add_reg2(Amd64Register.rax, Amd64Register.rdx) // add the offset to the address + g.code_gen.mov_deref(Amd64Register.rax, Amd64Register.rax, ast.u8_type_idx) } else if node.left_type.is_pointer() { dump(node) g.n_error('${@LOCATION} expr: unhandled node type: Index expr is not applied on string') @@ -464,9 +470,12 @@ fn (mut g Gen) gen_selector_expr(expr ast.SelectorExpr) { g.code_gen.add(main_reg, offset) } if expr.next_token != .dot { // the deref needs to be on the last selector (that has no . after it) - g.code_gen.mov_deref(main_reg, main_reg, expr.typ) + ts := g.table.sym(expr.typ) + if ts.info !is ast.Struct { + g.code_gen.mov_deref(main_reg, main_reg, expr.typ) + } } - g.println('; .${expr.field_name} {') + g.println('; .${expr.field_name} }') } fn (mut g Gen) gen_left_value(node ast.Expr) { diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index 08a3cd9a85..5dd8d6ec2a 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -100,7 +100,7 @@ mut: convert_bool_to_string(r Register) convert_int_to_string(a Register, b Register) convert_rune_to_string(r Register, buffer i32, var Var, config VarConfig) - create_string_struct(typ ast.Type, name string, str string) + create_string_struct(typ ast.Type, name string, str string) i32 dec_var(var Var, config VarConfig) fn_decl(node ast.FnDecl) gen_asm_stmt(asm_node ast.AsmStmt) diff --git a/vlib/v/gen/native/tests/string.vv b/vlib/v/gen/native/tests/string.vv index fc342b02e6..398494c4e9 100644 --- a/vlib/v/gen/native/tests/string.vv +++ b/vlib/v/gen/native/tests/string.vv @@ -16,6 +16,10 @@ fn test_string_len() { println(d.len) } +struct Idx { + a string +} + fn test_string_index() { a := '1234' assert a[0] == `1` @@ -26,6 +30,10 @@ fn test_string_index() { assert a[1] != `0` assert a[2] != `0` assert a[3] != `0` + + x := Idx{'abc'} + assert x.a[1] == `b` + assert x.a[2] != `b` } fn test_for_i_in_len() { diff --git a/vlib/v/gen/native/tests/struct.vv b/vlib/v/gen/native/tests/struct.vv index 3bbf3ffd21..627fe6c877 100644 --- a/vlib/v/gen/native/tests/struct.vv +++ b/vlib/v/gen/native/tests/struct.vv @@ -196,9 +196,45 @@ fn assign_fields() { assert f.a == 2 } +struct Nest { + a string +} + +struct Nest2 { + a Nest +} + +struct Nest3 { + a Nest2 +} + +struct Int { + a int +} + +struct NestMixed { + s string + b Int +} + +fn nested_test() { + x := Nest{'abc'} + assert x.a[1] == `b` + + x2 := Nest2{Nest{'def'}} + assert x2.a.a[2] == `f` + + x3 := Nest3{Nest2{Nest{'ghi'}}} + assert x3.a.a.a[1] == `h` + + x4 := NestMixed{'abc', Int{3}} + assert x4.b.a == 3 +} + fn main() { struct_test() return_test() alias_test() assign_fields() + nested_test() }