diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index 29e693b87f..dc939a095d 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -553,20 +553,20 @@ fn (mut g Gen) mov_reg_to_var(var Var, reg Register, config VarConfig) { is_far_var := offset > 0x80 || offset < -0x7f typ := if config.typ == 0 { var.typ } else { config.typ } - mut size := 'UNKNOWN' + mut size_str := '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.int_literal_type_idx { g.write16(0x8948 + if is_extended_register { 4 } else { 0 }) - size = 'QWORD' + size_str = 'QWORD' } ast.int_type_idx, ast.u32_type_idx, ast.rune_type_idx { if is_extended_register { g.write8(0x44) } g.write8(0x89) - size = 'DWORD' + size_str = 'DWORD' } ast.i16_type_idx, ast.u16_type_idx { g.write8(0x66) @@ -574,18 +574,17 @@ fn (mut g Gen) mov_reg_to_var(var Var, reg Register, config VarConfig) { g.write8(0x44) } g.write8(0x89) - size = 'WORD' + size_str = 'WORD' } 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) - size = 'BYTE' + size_str = 'BYTE' } else { g.n_error('unsupported type for mov_reg_to_var') - 'UNKNOWN' } } far_var_offset := if is_far_var { 0x40 } else { 0 } @@ -602,7 +601,7 @@ fn (mut g Gen) mov_reg_to_var(var Var, reg Register, config VarConfig) { } else { g.write8((0xff - offset + 1) % 0x100) } - g.println('mov $size PTR [rbp-$offset.hex2()],$reg') + g.println('mov $size_str PTR [rbp-$offset.hex2()],$reg') } GlobalVar { // TODO @@ -736,47 +735,44 @@ fn (mut g Gen) mov_var_to_reg(reg Register, var Var, config VarConfig) { offset := var.offset - config.offset is_far_var := offset > 0x80 || offset < -0x7f typ := if config.typ == 0 { var.typ } else { config.typ } + size := g.get_type_size(typ) + is_signed := !typ.is_real_pointer() && typ.is_signed() - instruction, size := match typ { - ast.i64_type_idx, ast.u64_type_idx, ast.isize_type_idx, ast.usize_type_idx, - ast.int_literal_type_idx { - // mov rax, QWORD PTR [rbp-0x8] - g.write16(0x8b48) - 'mov', 'QWORD' - } - ast.int_type_idx { + instruction, size_str := match true { + size == 4 && is_signed { // movsxd rax, DWORD PTR [rbp-0x8] g.write16(0x6348) 'movsxd', 'DWORD' } - ast.u32_type_idx, ast.rune_type_idx { + size == 4 && !is_signed { // mov eax, DWORD PTR [rbp-0x8] g.write8(0x8b) 'mov', 'DWORD' } - ast.i16_type_idx { + size == 2 && is_signed { // movsx rax, WORD PTR [rbp-0x8] g.write([u8(0x48), 0x0f, 0xbf]) 'movsx', 'WORD' } - ast.u16_type_idx { + size == 2 && !is_signed { // movzx rax, WORD PTR [rbp-0x8] g.write([u8(0x48), 0x0f, 0xb7]) 'movzx', 'WORD' } - ast.i8_type_idx { + size == 1 && is_signed { // movsx rax, BYTE PTR [rbp-0x8] g.write([u8(0x48), 0x0f, 0xbe]) 'movsx', 'BYTE' } - ast.u8_type_idx, ast.char_type_idx, ast.bool_type_idx { + size == 1 && !is_signed { // movzx rax, BYTE PTR [rbp-0x8] g.write([u8(0x48), 0x0f, 0xb6]) 'movzx', 'BYTE' } else { - g.n_error('unhandled mov var to reg') - 'mov', 'UNKNOWN' + // mov rax, QWORD PTR [rbp-0x8] + g.write16(0x8b48) + 'mov', 'QWORD' } } far_var_offset := if is_far_var { 0x40 } else { 0 } @@ -794,7 +790,7 @@ fn (mut g Gen) mov_var_to_reg(reg Register, var Var, config VarConfig) { } else { g.write8((0xff - offset + 1) % 0x100) } - g.println('$instruction $reg, $size PTR [rbp-$offset.hex2()]') + g.println('$instruction $reg, $size_str PTR [rbp-$offset.hex2()]') } GlobalVar { // TODO @@ -1559,24 +1555,47 @@ pub fn (mut g Gen) call_fn_amd64(node ast.CallExpr) { if !n.contains('.') { n = 'main.$n' } + if node.is_method { + n = '${g.table.get_type_name(node.receiver_type)}.$node.name' + } addr := g.fn_addr[n] mut reg_args := []int{} mut stack_args := []int{} - mut args_size := node.args.map(g.get_type_size(it.typ)) + mut args := []ast.CallArg{cap: node.args.len + 2} ts := g.table.sym(node.return_type) return_size := g.get_type_size(node.return_type) mut return_pos := -1 - struct_arg_idx := node.args.len + mut is_struct_return := false if ts.kind == .struct_ { return_pos = g.allocate_struct('', node.return_type) if return_size > 16 { - reg_args << struct_arg_idx - args_size << 8 + is_struct_return = true + args << ast.CallArg{ + typ: ast.voidptr_type_idx + } } } + if node.is_method { + expr := if !node.left_type.is_ptr() && node.receiver_type.is_ptr() { + ast.Expr(ast.PrefixExpr{ + op: .amp + right: node.left + }) + } else { + node.left + } + args << ast.CallArg{ + expr: expr + typ: node.receiver_type + } + } + + args << node.args + args_size := args.map(g.get_type_size(it.typ)) + mut reg_left := 6 - reg_args.len for i, size in args_size { if reg_left > 0 { @@ -1607,13 +1626,13 @@ pub fn (mut g Gen) call_fn_amd64(node ast.CallExpr) { } reg_args << stack_args for i in reg_args.reverse() { - if i == struct_arg_idx { + if i == 0 && is_struct_return { g.lea_var_to_reg(.rax, return_pos) g.push(.rax) continue } - g.expr(node.args[i].expr) - if g.table.sym(node.args[i].typ).kind == .struct_ { + g.expr(args[i].expr) + if g.table.sym(args[i].typ).kind == .struct_ && !args[i].typ.is_ptr() { match args_size[i] { 1...8 { g.mov_deref(.rax, .rax, ast.i64_type_idx) @@ -1657,12 +1676,12 @@ pub fn (mut g Gen) call_fn_amd64(node ast.CallExpr) { g.pop(native.fn_arg_registers[i]) } if addr == 0 { - g.delay_fn_call(name) + g.delay_fn_call(n) g.call(int(0)) } else { g.call(int(addr)) } - g.println('call `${name}()`') + g.println('call `${n}()`') if ts.kind == .struct_ { match return_size { @@ -2176,6 +2195,24 @@ fn (mut g Gen) cset_op(op token.Kind) { } } +fn (mut g Gen) gen_left_value(node ast.Expr) { + match node { + ast.Ident { + offset := g.get_var_offset(node.name) + g.lea_var_to_reg(.rax, offset) + } + else { + g.n_error('Unsupported left value') + } + } +} + +fn (mut g Gen) prefix_expr(node ast.PrefixExpr) { + if node.op == .amp { + g.gen_left_value(node.right) + } +} + fn (mut g Gen) infix_expr(node ast.InfixExpr) { /* if node.left is ast.Ident && node.right is ast.Ident { @@ -2702,20 +2739,24 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) { // Copy values from registers to local vars (calling convention) mut reg_args := []int{} mut stack_args := []int{} - mut args_size := node.params.map(g.get_type_size(it.typ)) + mut params := []ast.Param{cap: node.params.len + 2} + // The first parameter is an address of returned struct if size > 16 ts := g.table.sym(node.return_type) return_size := g.get_type_size(node.return_type) - struct_arg_idx := node.params.len - mut return_val_offset := -1 if ts.kind == .struct_ { if return_size > 16 { - return_val_offset = g.allocate_var('_return_val_addr', 8, 0) - reg_args << struct_arg_idx - args_size << 8 + params << ast.Param{ + name: '_return_val_addr' + typ: ast.voidptr_type_idx + } } } + params << node.params + + args_size := params.map(g.get_type_size(it.typ)) + mut reg_left := 6 for i, size in args_size { if reg_left > 0 { @@ -2731,24 +2772,13 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) { } stack_args << i } - /* - reg_size := reg_args.map((args_size[it] + 7) / 8).reduce(fn (a int, b int) int { - return a + b - }, 0) - stack_size := stack_args.map((args_size[it] + 7) / 8).reduce(fn (a int, b int) int { - return a + b - }, 0)*/ // define and copy args on register mut reg_idx := 0 for i in reg_args { - name := if i == struct_arg_idx { '_return_val_addr' } else { node.params[i].name } - g.stack_var_pos += args_size[i] % 8 - offset := if i == struct_arg_idx { - return_val_offset - } else { - g.allocate_struct(name, node.params[i].typ) - } + name := params[i].name + g.stack_var_pos += (8 - args_size[i] % 8) % 8 + offset := g.allocate_struct(name, params[i].typ) // copy g.mov_reg_to_var(LocalVar{ offset: offset, typ: ast.i64_type_idx, name: name }, native.fn_arg_registers[reg_idx]) @@ -2764,7 +2794,7 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) { // define args on stack mut offset := -2 for i in stack_args { - name := node.params[i].name + name := params[i].name g.var_offset[name] = offset * 8 g.var_alloc_size[name] = args_size[i] offset -= (args_size[i] + 7) / 8 diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index 2aaeab8b9f..9a3f0e07dc 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -709,17 +709,22 @@ g.expr } fn (mut g Gen) fn_decl(node ast.FnDecl) { + name := if node.is_method { + '${g.table.get_type_name(node.receiver.typ)}.$node.name' + } else { + node.name + } if g.pref.is_verbose { - println(term.green('\n$node.name:')) + println(term.green('\n$name:')) } if node.is_deprecated { - g.warning('fn_decl: $node.name is deprecated', node.pos) + g.warning('fn_decl: $name is deprecated', node.pos) } if node.is_builtin { - g.warning('fn_decl: $node.name is builtin', node.pos) + g.warning('fn_decl: $name is builtin', node.pos) } g.stack_var_pos = 0 - g.register_function_address(node.name) + g.register_function_address(name) g.labels = &LabelTable{} g.defer_stmts.clear() if g.pref.arch == .arm64 { @@ -1162,6 +1167,9 @@ fn (mut g Gen) expr(node ast.Expr) { ast.PostfixExpr { g.postfix_expr(node) } + ast.PrefixExpr { + g.prefix_expr(node) + } ast.StringLiteral { g.allocate_string(node.val, 3, .rel32) } diff --git a/vlib/v/gen/native/tests/method.vv b/vlib/v/gen/native/tests/method.vv new file mode 100644 index 0000000000..aed51b35c8 --- /dev/null +++ b/vlib/v/gen/native/tests/method.vv @@ -0,0 +1,50 @@ +struct Size7 { + a i8 + b u8 + c i8 + d i8 + e u8 + f i8 + g u8 +} + +struct Size28 { + a int + b i16 + c int + d u32 + e i8 + f int + g int +} + +fn (s Size7) get_c() i8 { + return s.c +} + +fn (s &Size7) get_c_ref() i8 { + return s.c +} + +fn (mut s Size7) modify_c(a i8) i8 { + // s.c = a + return s.c +} + +fn struct_receiver_test() { + mut a := Size7{1,2,3,4,5,6,7} + assert a.get_c() == 3 + assert a.get_c_ref() == 3 + // should be 7 after implementing field assign + assert a.modify_c(7) == 3 + b := Size28{c: 6} + assert b.get_c() == 6 +} + +fn (s Size28) get_c() int { + return s.c +} + +fn main() { + struct_receiver_test() +} diff --git a/vlib/v/gen/native/tests/method.vv.out b/vlib/v/gen/native/tests/method.vv.out new file mode 100644 index 0000000000..e69de29bb2