From d78dfabfe03da6cccb250e73a67e59031125ca9b Mon Sep 17 00:00:00 2001 From: lemon Date: Fri, 30 Sep 2022 21:53:17 +0900 Subject: [PATCH] native: support casting integer to/from enum values (#15928) --- vlib/v/gen/native/amd64.v | 43 ++++++++++++++++++++---------- vlib/v/gen/native/gen.v | 8 +++--- vlib/v/gen/native/tests/enum.vv | 27 ++++++++++++++++++- vlib/v/gen/native/tests/general.vv | 2 ++ 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index 1bbaf2692e..0fd17cbbc0 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -807,6 +807,35 @@ fn (mut g Gen) mov_var_to_reg(reg Register, var Var, config VarConfig) { } } +fn (mut g Gen) mov_extend_reg(a Register, b Register, typ ast.Type) { + size := g.get_type_size(typ) + is_signed := !typ.is_real_pointer() && typ.is_signed() + + if size in [1, 2, 4] { + if size == 4 && !is_signed { + g.write8(0x40 + if int(a) >= int(Register.r8) { 1 } else { 0 } + + if int(b) >= int(Register.r8) { 4 } else { 0 }) + g.write8(0x89) + } else { + g.write8(0x48 + if int(a) >= int(Register.r8) { 1 } else { 0 } + + if int(b) >= int(Register.r8) { 4 } else { 0 }) + if size in [1, 2] { + g.write8(0x0f) + } + g.write8(match true { + size == 1 && is_signed { 0xbe } + size == 1 && !is_signed { 0xb6 } + size == 2 && is_signed { 0xbf } + size == 2 && !is_signed { 0xb7 } + else { 0x63 } + }) + } + g.write8(0xc0 + int(a) % 8 * 8 + int(b) % 8) + instruction := if is_signed { 's' } else { 'z' } + g.println('mov${instruction}x $a, $b') + } +} + fn (mut g Gen) call_addr_at(addr int, at i64) i64 { // Need to calculate the difference between current position (position after the e8 call) // and the function to call. @@ -2135,20 +2164,6 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) { ast.GoExpr { g.v_error('threads not implemented for the native backend', node.pos) } - ast.CastExpr { - g.warning('cast expressions are work in progress', right.pos) - match right.typname { - 'u64' { - g.allocate_var(name, 8, right.expr.str().int()) - } - 'int' { - g.allocate_var(name, 4, right.expr.str().int()) - } - else { - g.v_error('unsupported cast type $right.typ', node.pos) - } - } - } ast.FloatLiteral { g.v_error('floating point arithmetic not yet implemented for the native backend', node.pos) diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index 107e881d04..f5b4e22ab6 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -1086,10 +1086,6 @@ fn (mut g Gen) stmt(node ast.Stmt) { mut s := '?' //${node.exprs[0].val.str()}' if e0 := node.exprs[0] { match e0 { - ast.CastExpr { - g.mov64(.rax, e0.expr.str().int()) - // do the job - } ast.StringLiteral { s = g.eval_escape_codes(e0) g.expr(node.exprs[0]) @@ -1326,6 +1322,10 @@ fn (mut g Gen) expr(node ast.Expr) { g.add(.rax, offset) g.mov_deref(.rax, .rax, node.typ) } + ast.CastExpr { + g.expr(node.expr) + g.mov_extend_reg(.rax, .rax, node.typ) + } ast.EnumVal { type_name := g.table.get_type_name(node.typ) g.mov(.rax, g.enum_vals[type_name].fields[node.val]) diff --git a/vlib/v/gen/native/tests/enum.vv b/vlib/v/gen/native/tests/enum.vv index 45de3f7835..9140a7ae4c 100644 --- a/vlib/v/gen/native/tests/enum.vv +++ b/vlib/v/gen/native/tests/enum.vv @@ -47,5 +47,30 @@ fn test_enum_variant_and_method_name_clash() { x.baz() } -fn main() { +enum Value { + a + b + c = 5 * 3 + d + e = 3 +} + +fn value_test() { + assert int(Value.a) == 0 + assert int(Value.b) == 1 + assert int(Value.c) == 15 + assert int(Value.d) == 16 + assert int(Value.e) == 3 + assert Value.a == Value(0) + assert Value.b == Value(1) + assert Value.c == Value(15) + assert Value.d == Value(16) + assert Value.e == Value(3) +} + +fn main() { + enum_test() + match_test() + test_enum_variant_and_method_name_clash() + value_test() } diff --git a/vlib/v/gen/native/tests/general.vv b/vlib/v/gen/native/tests/general.vv index 7e19273d6f..e671bb046a 100644 --- a/vlib/v/gen/native/tests/general.vv +++ b/vlib/v/gen/native/tests/general.vv @@ -3,6 +3,8 @@ fn cast_test() { assert a == 1 b := u64(2) assert b == 2 + c := int(u8(i64(u64(i16(4))))) + assert c == 4 } fn if_test() {