From c4ba47a131449dc616eca10eb0dcf715657f6416 Mon Sep 17 00:00:00 2001 From: Spydr <58859306+Spydr06@users.noreply.github.com> Date: Thu, 29 Jun 2023 20:30:48 +0200 Subject: [PATCH] native: inital support for aliased types (#18703) --- vlib/v/gen/native/amd64.v | 307 ++++++++++++++++-------------- vlib/v/gen/native/arm64.v | 4 + vlib/v/gen/native/expr.v | 2 +- vlib/v/gen/native/gen.v | 21 +- vlib/v/gen/native/stmt.v | 1 + vlib/v/gen/native/tests/struct.vv | 46 +++++ 6 files changed, 226 insertions(+), 155 deletions(-) diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index 5f38bc1044..5fa4ec95ae 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -94,6 +94,10 @@ fn (mut c Amd64) main_reg() Register { return Amd64Register.rax } +fn (mut c Amd64) address_size() int { + return 8 +} + fn (mut c Amd64) dec(reg Amd64Register) { c.g.write16(0xff48) match reg { @@ -593,44 +597,46 @@ fn (mut c Amd64) mov_reg_to_var(var Var, r Register, config VarConfig) { LocalVar { offset := var.offset - config.offset is_far_var := offset > 0x80 || offset < -0x7f - typ := if config.typ == 0 { var.typ } else { config.typ } + raw_type := if config.typ == 0 { var.typ } else { config.typ } + typ := c.g.unwrap(raw_type) mut size_str := 'UNKNOWN' is_extended_register := int(reg) >= int(Amd64Register.r8) && int(reg) <= int(Amd64Register.r15) - match typ { - ast.i64_type_idx, ast.u64_type_idx, ast.isize_type_idx, ast.usize_type_idx, - ast.int_literal_type_idx { - c.g.write16(0x8948 + if is_extended_register { 4 } else { 0 }) - size_str = 'QWORD' - } - ast.int_type_idx, ast.u32_type_idx, ast.rune_type_idx { - if is_extended_register { - c.g.write8(0x44) - } - c.g.write8(0x89) - size_str = 'DWORD' - } - ast.i16_type_idx, ast.u16_type_idx { - c.g.write8(0x66) - if is_extended_register { - c.g.write8(0x44) - } - c.g.write8(0x89) - size_str = 'WORD' - } - ast.i8_type_idx, ast.u8_type_idx, ast.char_type_idx, ast.bool_type_idx { - if is_extended_register { - c.g.write8(0x44) - } - c.g.write8(0x88) - size_str = 'BYTE' - } - else { - if typ.is_any_kind_of_pointer() { + + if raw_type.is_any_kind_of_pointer() || typ.is_any_kind_of_pointer() { + c.g.write16(0x8948 + if is_extended_register { 4 } else { 0 }) + size_str = 'QWORD' + } else { + match typ { + ast.i64_type_idx, ast.u64_type_idx, ast.isize_type_idx, ast.usize_type_idx, + ast.int_literal_type_idx { c.g.write16(0x8948 + if is_extended_register { 4 } else { 0 }) size_str = 'QWORD' - } else { + } + ast.int_type_idx, ast.u32_type_idx, ast.rune_type_idx { + if is_extended_register { + c.g.write8(0x44) + } + c.g.write8(0x89) + size_str = 'DWORD' + } + ast.i16_type_idx, ast.u16_type_idx { + c.g.write8(0x66) + if is_extended_register { + c.g.write8(0x44) + } + c.g.write8(0x89) + size_str = 'WORD' + } + ast.i8_type_idx, ast.u8_type_idx, ast.char_type_idx, ast.bool_type_idx { + if is_extended_register { + c.g.write8(0x44) + } + c.g.write8(0x88) + size_str = 'BYTE' + } + else { ts := c.g.table.sym(typ.idx()) if ts.info is ast.Enum { if is_extended_register { @@ -639,7 +645,7 @@ fn (mut c Amd64) mov_reg_to_var(var Var, r Register, config VarConfig) { c.g.write8(0x89) size_str = 'DWORD' } else { - c.g.n_error('unsupported type for mov_reg_to_var') + c.g.n_error('unsupported type for mov_reg_to_var ${ts.info}') } } } @@ -685,7 +691,7 @@ fn (mut c Amd64) mov_int_to_var(var Var, integer int, config VarConfig) { } LocalVar { offset := var.offset - config.offset - typ := if config.typ == 0 { var.typ } else { config.typ } + typ := c.g.unwrap(if config.typ == 0 { var.typ } else { config.typ }) is_far_var := offset > 0x80 || offset < -0x7f match typ { @@ -1338,116 +1344,119 @@ fn (mut c Amd64) lea(reg Amd64Register, val int) { c.g.println('lea ${reg}, ${val}') } +fn (mut c Amd64) mov_neg1(reg Amd64Register) { + match reg { + .rax { + c.g.write8(0x48) + c.g.write8(0xc7) + c.g.write8(0xc0) + c.g.write32(-1) + } + .rcx { + c.g.write8(0x48) + c.g.write8(0xc7) + c.g.write8(0xc1) + c.g.write32(-1) + } + else { + c.g.n_error('unhandled mov ${reg}, -1') + } + } + c.g.println('mov ${reg}, -1') +} + +fn (mut c Amd64) clear_reg(reg Amd64Register) { + // Optimise to xor reg, reg when val is 0 + match reg { + .eax, .rax { + c.g.write8(0x31) + c.g.write8(0xc0) + } + .edi, .rdi { + c.g.write8(0x31) + c.g.write8(0xff) + } + .rcx { + c.g.write8(0x48) + c.g.write8(0x31) + c.g.write8(0xc7) + } + .rdx { + c.g.write8(0x48) + c.g.write8(0x31) + c.g.write8(0xd2) + } + .edx { + c.g.write8(0x31) + c.g.write8(0xd2) + } + .rsi { + c.g.write8(0x48) + c.g.write8(0x31) + c.g.write8(0xf6) + } + .r12 { + c.g.write8(0x4d) + c.g.write8(0x31) + c.g.write8(0xe4) + } + else { + c.g.n_error('unhandled xor ${reg}, ${reg}') + } + } + c.g.println('xor ${reg}, ${reg}') +} + fn (mut c Amd64) mov(r Register, val int) { reg := r as Amd64Register - if val == -1 { - match reg { - .rax { - c.g.write8(0x48) - c.g.write8(0xc7) - c.g.write8(0xc0) - c.g.write32(-1) - c.g.println('mov ${reg}, ${val}') - } - .rcx { - if val == -1 { + match val { + -1 { + c.mov_neg1(reg) + } + 0 { + c.clear_reg(reg) + } + else { + match reg { + .eax, .rax { + c.g.write8(0xb8) + } + .edi, .rdi { + c.g.write8(0xbf) + } + .rcx { c.g.write8(0x48) c.g.write8(0xc7) c.g.write8(0xc1) - c.g.write32(-1) - } else { - c.g.write8(0xff) - c.g.write8(0xff) // mov rcx 0xffff5 } - c.g.println('mov ${reg}, ${val}') - } - else { - c.g.n_error('unhandled mov ${reg}, -1') + .r8 { + c.g.write8(0x41) + c.g.write8(0xb8) + } + .r9 { + c.g.write8(0xb9) + } + .rdx, .edx { + c.g.write8(0xba) + } + .rsi { + // c.g.write8(0x48) // its 32bit! + c.g.write8(0xbe) + } + .r12 { + c.g.write8(0x41) + c.g.write8(0xbc) // r11 is 0xbb etc + } + .rbx { + c.g.write8(0xbb) + } + else { + c.g.n_error('unhandled mov ${reg}') + } } + c.g.write32(val) + c.g.println('mov ${reg}, ${val}') } - c.g.println('mov ${reg}, ${val}') - return - } - if val == 0 { - // Optimise to xor reg, reg when val is 0 - match reg { - .eax, .rax { - c.g.write8(0x31) - c.g.write8(0xc0) - } - .edi, .rdi { - c.g.write8(0x31) - c.g.write8(0xff) - } - .rcx { - c.g.write8(0x48) - c.g.write8(0x31) - c.g.write8(0xc7) - } - .rdx { - c.g.write8(0x48) - c.g.write8(0x31) - c.g.write8(0xd2) - } - .edx { - c.g.write8(0x31) - c.g.write8(0xd2) - } - .rsi { - c.g.write8(0x48) - c.g.write8(0x31) - c.g.write8(0xf6) - } - .r12 { - c.g.write8(0x4d) - c.g.write8(0x31) - c.g.write8(0xe4) - } - else { - c.g.n_error('unhandled mov ${reg}, ${reg}') - } - } - c.g.println('xor ${reg}, ${reg}') - } else { - match reg { - .eax, .rax { - c.g.write8(0xb8) - } - .edi, .rdi { - c.g.write8(0xbf) - } - .rcx { - c.g.write8(0x48) - c.g.write8(0xc7) - c.g.write8(0xc1) - } - .r8 { - c.g.write8(0x41) - c.g.write8(0xb8) - } - .r9 { - c.g.write8(0xb9) - } - .rdx, .edx { - c.g.write8(0xba) - } - .rsi { - // c.g.write8(0x48) // its 32bit! - c.g.write8(0xbe) - } - .r12 { - c.g.write8(0x41) - c.g.write8(0xbc) // r11 is 0xbb etc - } - .rbx { - c.g.write8(0xbb) - } - else { - c.g.n_error('unhandled mov ${reg}') - } - } - c.g.write32(val) - c.g.println('mov ${reg}, ${val}') } } @@ -2005,7 +2014,9 @@ fn (mut c Amd64) assign_struct_var(ident_var IdentVar, typ ast.Type, s int) { assert size == 0 } -fn (mut c Amd64) assign_var(var IdentVar, typ ast.Type) { +fn (mut c Amd64) assign_var(var IdentVar, raw_type ast.Type) { + typ := c.g.unwrap(raw_type) + info := c.g.table.sym(typ).info size := c.g.get_type_size(typ) if typ.is_pure_float() { match var { @@ -2014,7 +2025,8 @@ fn (mut c Amd64) assign_var(var IdentVar, typ ast.Type) { // Amd64Register { c.g.mov_ssereg(var as Amd64Register, .xmm0) } else {} } - } else if c.g.table.sym(typ).info is ast.Struct && !typ.is_any_kind_of_pointer() { + } else if info is ast.Struct && !typ.is_any_kind_of_pointer() + && !raw_type.is_any_kind_of_pointer() { c.assign_struct_var(var, typ, size) } else if size in [1, 2, 4, 8] { match var { @@ -2023,7 +2035,7 @@ fn (mut c Amd64) assign_var(var IdentVar, typ ast.Type) { Register { c.mov_reg(var as Amd64Register, Amd64Register.rax) } } } else { - c.g.n_error('error assigning type ${typ} with size ${size}') + c.g.n_error('error assigning type ${typ} with size ${size}: ${info}') } } @@ -2361,7 +2373,7 @@ fn (mut c Amd64) return_stmt(node ast.Return) { } } } else if node.exprs.len > 1 { - typ := c.g.return_type + typ := c.g.unwrap(c.g.return_type) ts := c.g.table.sym(typ) size := c.g.get_type_size(typ) // construct a struct variable contains the return value @@ -3385,7 +3397,8 @@ fn (mut c Amd64) init_struct(var Var, init ast.StructInit) { } } LocalVar { - size := c.g.get_type_size(var.typ) + typ := c.g.unwrap(var.typ) + size := c.g.get_type_size(typ) // zero fill mut left := if size >= 16 { @@ -3414,12 +3427,12 @@ fn (mut c Amd64) init_struct(var Var, init ast.StructInit) { c.mov_int_to_var(var, 0, offset: size - left, typ: ast.i8_type_idx) } - ts := c.g.table.sym(var.typ) + ts := c.g.table.sym(typ) match ts.info { ast.Struct { for i, f in ts.info.fields { if f.has_default_expr && !init.init_fields.map(it.name).contains(f.name) { - offset := c.g.structs[var.typ.idx()].offsets[i] + offset := c.g.structs[typ.idx()].offsets[i] c.g.expr(f.default_expr) // TODO expr not on rax c.mov_reg_to_var(var, Amd64Register.rax, offset: offset, typ: f.typ) @@ -3430,9 +3443,9 @@ fn (mut c Amd64) init_struct(var Var, init ast.StructInit) { } for f in init.init_fields { field := ts.find_field(f.name) or { - c.g.n_error('Could not find field `${f.name}` on init') + c.g.n_error('Could not find field `${f.name}` on init (${ts.info})') } - offset := c.g.structs[var.typ.idx()].offsets[field.i] + offset := c.g.structs[typ.idx()].offsets[field.i] c.g.expr(f.expr) // TODO expr not on rax @@ -3777,7 +3790,7 @@ fn (mut c Amd64) mov_ssereg_to_var(var Var, reg Amd64SSERegister, config VarConf LocalVar { offset := var.offset - config.offset is_far_var := offset > 0x80 || offset < -0x7f - typ := if config.typ == 0 { var.typ } else { config.typ } + typ := c.g.unwrap(if config.typ == 0 { var.typ } else { config.typ }) far_var_offset := if is_far_var { 0x40 } else { 0 } c.g.write8(if typ == ast.f32_type_idx { 0xf3 } else { 0xf2 }) @@ -3821,7 +3834,7 @@ fn (mut c Amd64) mov_var_to_ssereg(reg Amd64SSERegister, var Var, config VarConf LocalVar { offset := var.offset - config.offset is_far_var := offset > 0x80 || offset < -0x7f - typ := if config.typ == 0 { var.typ } else { config.typ } + typ := c.g.unwrap(if config.typ == 0 { var.typ } else { config.typ }) far_var_offset := if is_far_var { 0x40 } else { 0 } c.g.write8(if typ == ast.f32_type_idx { 0xf3 } else { 0xf2 }) diff --git a/vlib/v/gen/native/arm64.v b/vlib/v/gen/native/arm64.v index 2a96fdf251..c0043ca738 100644 --- a/vlib/v/gen/native/arm64.v +++ b/vlib/v/gen/native/arm64.v @@ -320,6 +320,10 @@ pub fn (mut c Arm64) gen_arm64_exit(expr ast.Expr) { c.svc() } +fn (mut c Arm64) address_size() int { + return 8 +} + fn (mut c Arm64) gen_print(s string, fd int) { panic('Arm64.gen_print() is not implemented') } diff --git a/vlib/v/gen/native/expr.v b/vlib/v/gen/native/expr.v index 43ec5c3667..1d38021170 100644 --- a/vlib/v/gen/native/expr.v +++ b/vlib/v/gen/native/expr.v @@ -49,7 +49,7 @@ fn (mut g Gen) expr(node ast.Expr) { } else if var.typ.is_pure_float() { g.code_gen.load_fp_var(node as ast.Ident) } else { - ts := g.table.sym(var.typ) + ts := g.table.sym(g.unwrap(var.typ)) match ts.info { ast.Struct { g.code_gen.lea_var_to_reg(g.code_gen.main_reg(), g.get_var_offset(node.name)) diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index 891573d687..998d750cf5 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -66,6 +66,7 @@ mut: interface CodeGen { mut: g &Gen + address_size() int adr(r Arm64Register, delta int) // Note: Temporary! allocate_var(name string, size int, initial_val int) int apicall(call ApiCall) // winapi calls @@ -237,10 +238,9 @@ fn (mut g Gen) get_var_from_ident(ident ast.Ident) IdentVar { match obj { ast.Var { offset := g.get_var_offset(obj.name) - typ := obj.typ return LocalVar{ offset: offset - typ: typ + typ: obj.typ name: obj.name } } @@ -609,17 +609,24 @@ fn (mut g Gen) get_var_offset(var_name string) int { return r } -fn (mut g Gen) get_field_offset(typ ast.Type, name string) int { +fn (mut g Gen) get_field_offset(in_type ast.Type, name string) int { + typ := g.unwrap(in_type) ts := g.table.sym(typ) field := ts.find_field(name) or { g.n_error('Could not find field `${name}` on init') } return g.structs[typ.idx()].offsets[field.i] } +fn (mut g Gen) unwrap(typ ast.Type) ast.Type { + ts := g.table.sym(typ) + return if ts.info is ast.Alias { g.unwrap(ts.info.parent_type) } else { typ } +} + // get type size, and calculate size and align and store them to the cache when the type is struct -fn (mut g Gen) get_type_size(typ ast.Type) int { +fn (mut g Gen) get_type_size(raw_type ast.Type) int { // TODO type flags - if typ.is_any_kind_of_pointer() { - return 8 + typ := g.unwrap(raw_type) + if raw_type.is_any_kind_of_pointer() || typ.is_any_kind_of_pointer() { + return g.code_gen.address_size() } if typ in ast.number_type_idxs { return match typ { @@ -699,7 +706,7 @@ fn (mut g Gen) get_type_align(typ ast.Type) int { if g.is_register_type(typ) || typ.is_pure_float() { return size } - ts := g.table.sym(typ) + ts := g.table.sym(g.unwrap(typ)) if ts.align != -1 { return ts.align } diff --git a/vlib/v/gen/native/stmt.v b/vlib/v/gen/native/stmt.v index ba3f09cc85..d78db8d25c 100644 --- a/vlib/v/gen/native/stmt.v +++ b/vlib/v/gen/native/stmt.v @@ -93,6 +93,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { ast.Import {} // do nothing here ast.StructDecl {} ast.EnumDecl {} + ast.TypeDecl {} else { eprintln('native.stmt(): bad node: ' + node.type_name()) } diff --git a/vlib/v/gen/native/tests/struct.vv b/vlib/v/gen/native/tests/struct.vv index b6ae543ecf..4df4b74f33 100644 --- a/vlib/v/gen/native/tests/struct.vv +++ b/vlib/v/gen/native/tests/struct.vv @@ -58,6 +58,52 @@ fn struct_test() { assert e.a == 3 } +type AliasedStruct = Mutable +type AliasedPointer = &Mutable + +fn alias_test() { + mut a := AliasedStruct{1} + a.a = 2 + assert a.a == 2 + mut b := &a + b.a = 3 + assert a.a == 3 + + c := AliasedPointer{2} + assert c.a == 2 +} + +fn init_size28() Size28 { + return Size28{1, 2, 3, 4, 5, 6, 7} +} +type AliasedSize28 = Size28 + +fn init_aliased() AliasedSize28 { + return AliasedSize28{1, 2, 3, 4, 5, 6, 7} +} + +fn return_test() { + a := init_size28() + assert a.a == 1 + assert a.b == 2 + assert a.c == 3 + assert a.d == 4 + assert a.e == 5 + assert a.f == 6 + assert a.g == 7 + + b := init_aliased() + assert b.a == 1 + assert b.b == 2 + assert b.c == 3 + assert b.d == 4 + assert b.e == 5 + assert b.f == 6 + assert b.g == 7 +} + fn main() { struct_test() + return_test() + alias_test() }