v/vlib/v/gen/native/expr.v
Eliyaan (Nopana) 4ea05636fb
Some checks are pending
Graphics CI / gg-regressions (push) Waiting to run
vlib modules CI / build-module-docs (push) Waiting to run
native backend CI / native-backend-ubuntu (push) Waiting to run
native backend CI / native-backend-windows (push) Waiting to run
Shy and PV CI / v-compiles-puzzle-vibes (push) Waiting to run
Sanitized CI / sanitize-undefined-clang (push) Waiting to run
Sanitized CI / sanitize-undefined-gcc (push) Waiting to run
Sanitized CI / tests-sanitize-address-clang (push) Waiting to run
Sanitized CI / sanitize-address-msvc (push) Waiting to run
Sanitized CI / sanitize-address-gcc (push) Waiting to run
Sanitized CI / sanitize-memory-clang (push) Waiting to run
sdl CI / v-compiles-sdl-examples (push) Waiting to run
Time CI / time-linux (push) Waiting to run
Time CI / time-macos (push) Waiting to run
Time CI / time-windows (push) Waiting to run
toml CI / toml-module-pass-external-test-suites (push) Waiting to run
Tools CI / tools-linux (clang) (push) Waiting to run
Tools CI / tools-linux (gcc) (push) Waiting to run
Tools CI / tools-linux (tcc) (push) Waiting to run
Tools CI / tools-macos (clang) (push) Waiting to run
Tools CI / tools-windows (gcc) (push) Waiting to run
Tools CI / tools-windows (msvc) (push) Waiting to run
Tools CI / tools-windows (tcc) (push) Waiting to run
Tools CI / tools-docker-ubuntu-musl (push) Waiting to run
vab CI / vab-compiles-v-examples (push) Waiting to run
vab CI / v-compiles-os-android (push) Waiting to run
wasm backend CI / wasm-backend (ubuntu-22.04) (push) Waiting to run
wasm backend CI / wasm-backend (windows-2022) (push) Waiting to run
native: improve consts, support more assigns (#25277)
2025-09-10 20:22:13 +03:00

572 lines
14 KiB
V

// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module native
import v.ast
import v.util
import v.errors
fn (mut g Gen) expr(node ast.Expr) {
match node {
ast.AtExpr {
g.allocate_string(g.comptime_at(node), 3, .rel32)
}
ast.ParExpr {
g.expr(node.expr)
}
ast.ArrayInit {
pos := g.allocate_array('_anonarray', 8, i32(node.exprs.len))
g.code_gen.init_array(LocalVar{ offset: pos, typ: node.typ }, node)
g.code_gen.lea_var_to_reg(g.code_gen.main_reg(), pos)
}
ast.BoolLiteral {
g.code_gen.mov64(g.code_gen.main_reg(), i64(node.val))
}
ast.CallExpr {
match node.name {
'C.syscall' {
g.code_gen.gen_syscall(node)
}
'println', 'print', 'eprintln', 'eprint' {
expr := node.args[0].expr
typ := node.args[0].typ
g.gen_print_from_expr(expr, typ, node.name)
}
else {
g.code_gen.call_fn(node)
}
}
}
ast.FloatLiteral {
val := g.eval.expr(node, ast.float_literal_type_idx).float_val()
g.code_gen.load_fp(val)
}
ast.Ident {
var := g.get_var_from_ident(node)
match var {
LocalVar {
g.local_var_ident(node, var)
}
ExternVar {
g.extern_var_ident(var)
}
PreprocVar {
g.preproc_var_ident(var)
}
GlobalVar {
g.global_var_ident(node, var)
}
Register {
g.n_error('${@LOCATION} Unsupported variable kind ${var}')
}
ConstVar {
g.const_var_ident(node, var)
}
}
}
ast.IfExpr {
g.if_expr(node)
}
ast.InfixExpr {
g.code_gen.infix_expr(node)
}
ast.IntegerLiteral {
// Integer literal stores both signed and unsigned integers, some unsigned integers are too big for i64 but not for u64
// println(node.val)
if node.val.len > 0 && node.val[0] == `-` { // if the number is negative
g.code_gen.mov64(g.code_gen.main_reg(), node.val.i64())
} else {
g.code_gen.mov64(g.code_gen.main_reg(), node.val.u64())
}
}
ast.Nil {
g.code_gen.mov64(g.code_gen.main_reg(), i64(0))
}
ast.PostfixExpr {
g.postfix_expr(node)
}
ast.PrefixExpr {
g.code_gen.prefix_expr(node)
}
ast.StringLiteral {
str := g.eval_str_lit_escape_codes(node)
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)
.bytes()
mut val := rune(0)
for i, v in bytes {
val |= v << (i * 8)
if i >= sizeof(rune) {
g.n_error('${@LOCATION} runes are only 4 bytes wide')
}
}
g.code_gen.movabs(g.code_gen.main_reg(), i64(val))
}
ast.StructInit {
pos := g.allocate_by_type('_anonstruct', node.typ)
g.code_gen.init_struct(LocalVar{ offset: pos, typ: node.typ }, node)
g.code_gen.lea_var_to_reg(g.code_gen.main_reg(), pos)
}
ast.GoExpr {
g.v_error('native backend doesnt support threads yet', node.pos)
}
ast.MatchExpr {
g.code_gen.gen_match_expr(node)
}
ast.SelectorExpr {
g.gen_selector_expr(node)
}
ast.CastExpr {
g.code_gen.gen_cast_expr(node)
}
ast.EnumVal {
type_name := g.table.get_type_name(node.typ)
val := g.enum_vals[type_name].fields[node.val] or {
g.n_error('${@LOCATION} enum field not found ${node.val}')
}
match val {
Number {
g.code_gen.mov64(g.code_gen.main_reg(), val)
}
ast.Expr {
g.expr(val)
}
}
}
ast.UnsafeExpr {
g.expr(node.expr)
}
ast.ConcatExpr {
g.gen_concat_expr(node)
}
ast.TypeOf {
g.gen_typeof_expr(node, false)
}
ast.SizeOf {
g.gen_sizeof_expr(node)
}
ast.IndexExpr {
if node.left_type.is_string() {
g.expr(node.index)
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')
} else {
g.n_error('${@LOCATION} expr: unhandled node type: Index expr is not applied on string')
}
}
else {
util.show_compiler_message('error', errors.CompilerMessage{
message: 'detail'
file_path: g.current_file.path
pos: node.pos()
})
g.n_error('${@LOCATION} expr: unhandled node type: ${node.type_name()} ${node}')
}
}
}
fn (mut g Gen) local_var_ident(ident ast.Ident, var LocalVar) {
if g.is_register_type(var.typ) {
g.code_gen.mov_var_to_reg(g.code_gen.main_reg(), ident)
} else if g.is_fp_type(var.typ) {
g.code_gen.load_fp_var(ident)
} else {
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(ident.name))
}
ast.Enum {
g.code_gen.mov_var_to_reg(g.code_gen.main_reg(), ident)
}
else {
g.n_error('${@LOCATION} Unsupported variable type')
}
}
}
}
fn (mut g Gen) global_var_ident(ident ast.Ident, var GlobalVar) {
if g.is_register_type(var.typ) {
g.code_gen.mov_var_to_reg(g.code_gen.main_reg(), ident)
} else {
g.n_error('${@LOCATION} Unsupported variable type ${ident} ${var}')
}
}
fn (mut g Gen) const_var_ident(ident ast.Ident, var ConstVar) {
if g.is_register_type(var.typ) {
g.code_gen.mov_var_to_reg(g.code_gen.main_reg(), ident)
} else {
g.n_error('${@LOCATION} Unsupported variable type ${ident} ${var}')
}
}
fn (mut g Gen) extern_var_ident(var ExternVar) {
if g.pref.os == .linux {
main_reg := g.code_gen.main_reg()
g.extern_vars[g.pos() + 2] = var.name // + 2 for the mov64 instruction
g.code_gen.mov64(main_reg, Number(i64(0)))
g.code_gen.mov_deref(main_reg, main_reg, ast.u64_type_idx)
} else if g.pref.os == .macos {
eprintln('## TODO, macos, extern_var_ident, var: ${var}')
} else {
g.n_error('${@LOCATION} unsupported os for ${var}')
}
}
fn (mut g Gen) preproc_var_ident(var PreprocVar) {
main_reg := g.code_gen.main_reg()
g.code_gen.mov64(main_reg, var.val)
}
fn (mut g Gen) condition(expr ast.Expr, neg bool) i32 {
g.println('; condition cjmp if ${neg}:')
g.expr(expr)
g.code_gen.cmp_zero(g.code_gen.main_reg()) // 0 is false
return g.code_gen.cjmp(if neg { .jne } else { .je })
}
fn (mut g Gen) if_expr(node ast.IfExpr) {
if node.is_comptime {
if branch := g.comptime_conditional(node) {
g.stmts(branch.stmts)
}
return
}
if node.branches.len == 0 {
return
}
mut endif_label := i32(0)
has_endif := node.branches.len > 1
if has_endif {
endif_label = g.labels.new_label()
}
for idx in 0 .. node.branches.len {
branch := node.branches[idx]
if idx == node.branches.len - 1 && node.has_else {
g.stmts(branch.stmts)
} else {
if branch.cond is ast.BoolLiteral {
if branch.cond.val {
g.stmts(branch.stmts)
}
continue
}
expr := branch.cond
label := g.labels.new_label()
cjmp_addr := g.condition(expr, false)
g.labels.patches << LabelPatch{
id: label
pos: cjmp_addr
}
g.println('; jump to label ${label}')
g.stmts(branch.stmts)
if has_endif {
jump_addr := g.code_gen.jmp(0)
g.labels.patches << LabelPatch{
id: endif_label
pos: jump_addr
}
g.println('; jump to label ${int(endif_label)}')
}
// println('after if g.pos=$g.pos() jneaddr=$cjmp_addr')
g.labels.addrs[label] = g.pos()
g.println('; label ${label}')
}
}
if has_endif {
g.labels.addrs[int(endif_label)] = g.pos()
g.println('; label ${int(endif_label)}')
}
}
fn (mut g Gen) postfix_expr(node ast.PostfixExpr) {
if node.expr !is ast.Ident {
return
}
ident := node.expr as ast.Ident
match node.op {
.inc {
g.code_gen.inc_var(ident)
}
.dec {
g.code_gen.dec_var(ident)
}
else {}
}
}
fn (mut g Gen) fn_decl_str(info ast.FnType) string {
mut fn_str := 'fn ('
for i, arg in info.func.params {
if arg.is_mut {
fn_str += 'mut '
}
if i > 0 {
fn_str += ', '
}
if arg.typ.has_flag(.option) {
fn_str += '?'
}
fn_str += util.strip_main_name(g.table.get_type_name(arg.typ))
}
fn_str += ')'
if info.func.return_type == ast.ovoid_type {
fn_str += ' ?'
} else if info.func.return_type == ast.rvoid_type {
fn_str += ' !'
} else if info.func.return_type != ast.void_type {
x := util.strip_main_name(g.table.get_type_name(info.func.return_type))
if info.func.return_type.has_flag(.option) {
fn_str += ' ?${x}'
} else if info.func.return_type.has_flag(.result) {
fn_str += ' !${x}'
} else {
fn_str += ' ${x}'
}
}
return fn_str
}
fn (mut g Gen) gen_typeof_expr(node ast.TypeOf, newline bool) {
nl := if newline { '\n' } else { '' }
ts := g.table.sym(node.typ)
mut str := ''
match ts.kind {
.sum_type {
g.n_error('${@LOCATION} `typeof()` is not implemented for sum types yet')
}
.array_fixed {
fixed_info := ts.info as ast.ArrayFixed
typ_name := g.table.get_type_name(fixed_info.elem_type)
str = '[${fixed_info.size}]${util.strip_main_name(typ_name)}'
}
.function {
func_info := ts.info as ast.FnType
if node.typ.is_ptr() {
str = '&'
}
str += g.fn_decl_str(func_info)
}
else {
str = util.strip_main_name(if node.typ.has_flag(.variadic) {
g.table.sym(g.table.value_type(node.typ)).name
} else {
g.table.type_to_str(node.typ)
})
}
}
g.code_gen.learel(g.code_gen.main_reg(), g.allocate_string('${str}${nl}', 3, .rel32))
}
fn (mut g Gen) gen_sizeof_expr(node ast.SizeOf) {
ts := g.table.sym(node.typ)
if ts.language == .v && ts.kind in [.placeholder, .any] {
g.v_error('unknown type `${ts.name}`', node.pos)
}
g.code_gen.mov64(g.code_gen.main_reg(), i64(g.get_type_size(node.typ)))
}
fn (mut g Gen) gen_print_from_expr(expr ast.Expr, typ ast.Type, name string) {
newline := name in ['println', 'eprintln']
fd := if name in ['eprint', 'eprintln'] { i32(2) } else { i32(1) }
match expr {
ast.StringLiteral {
str := g.eval_str_lit_escape_codes(expr)
if newline {
g.code_gen.gen_print(str + '\n', fd)
} else {
g.code_gen.gen_print(str, fd)
}
}
ast.Nil {
str := '0x0'
if newline {
g.code_gen.gen_print(str + '\n', fd)
} else {
g.code_gen.gen_print(str, fd)
}
}
ast.CharLiteral {
str := g.eval_escape_codes(expr.val)
if newline {
g.code_gen.gen_print(str + '\n', fd)
} else {
g.code_gen.gen_print(str, fd)
}
}
ast.Ident {
vo := g.try_var_offset(expr.name)
reg := g.code_gen.main_reg()
if vo != -1 {
g.gen_var_to_string(reg, expr, expr as ast.Ident)
g.code_gen.gen_print_reg(reg, -1, fd)
if newline {
g.code_gen.gen_print('\n', fd)
}
} else {
g.code_gen.gen_print_reg(reg, -1, fd)
}
}
ast.IntegerLiteral {
if newline {
g.code_gen.gen_print('${expr.val}\n', fd)
} else {
g.code_gen.gen_print('${expr.val}', fd)
}
}
ast.BoolLiteral {
// register 'true' and 'false' strings // g.expr(expr)
// XXX mov64 shouldn't be used for addressing
nl := if newline { '\n' } else { '' }
if expr.val {
g.code_gen.gen_print('true' + nl, fd)
} else {
g.code_gen.gen_print('false' + nl, fd)
}
}
ast.SizeOf {
size := g.get_type_size(expr.typ)
if newline {
g.code_gen.gen_print('${size}\n', fd)
} else {
g.code_gen.gen_print('${size}', fd)
}
}
ast.OffsetOf {
styp := g.styp(expr.struct_type)
field_name := expr.field
if styp.kind == .struct {
off := g.get_field_offset(expr.struct_type, field_name)
if newline {
g.code_gen.gen_print('${off}\n', fd)
} else {
g.code_gen.gen_print('${off}', fd)
}
} else {
g.v_error('_offsetof expects a struct Type as first argument', expr.pos)
}
}
ast.None {
if newline {
g.code_gen.gen_print('<none>\n', fd)
} else {
g.code_gen.gen_print('<none>', fd)
}
}
ast.AtExpr {
if newline {
g.code_gen.gen_print(g.comptime_at(expr) + '\n', fd)
} else {
g.code_gen.gen_print(g.comptime_at(expr), fd)
}
}
ast.StringInterLiteral {
printer := if fd == 1 { 'print' } else { 'eprint' }
for i, val in expr.vals {
g.code_gen.gen_print(val, fd)
if i < expr.exprs.len {
g.gen_print_from_expr(expr.exprs[i], expr.expr_types[i], printer)
}
}
if newline {
g.code_gen.gen_print('\n', fd)
}
}
ast.IfExpr {
if expr.is_comptime {
if branch := g.comptime_conditional(expr) {
for i, stmt in branch.stmts {
if i + 1 == branch.stmts.len && stmt is ast.ExprStmt {
g.gen_print_from_expr(stmt.expr, stmt.typ, name)
} else {
g.stmt(stmt)
}
}
} else {
g.n_error('${@LOCATION} nothing to print')
}
} else {
g.n_error('${@LOCATION} non-comptime conditionals are not implemented yet.')
}
}
else {
g.expr(expr)
g.gen_to_string(g.code_gen.main_reg(), typ)
g.code_gen.gen_print_reg(g.code_gen.main_reg(), -1, fd)
if newline {
g.code_gen.gen_print('\n', fd)
}
}
}
}
fn (mut g Gen) gen_selector_expr(expr ast.SelectorExpr) {
g.println('; .${expr.field_name} {')
main_reg := g.code_gen.main_reg()
g.expr(expr.expr)
offset := g.get_field_offset(expr.expr_type, expr.field_name)
if offset != 0 {
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)
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} }')
}
fn (mut g Gen) gen_left_value(node ast.Expr) {
match node {
ast.Ident {
offset := g.get_var_offset(node.name)
g.code_gen.lea_var_to_reg(Amd64Register.rax, offset)
}
ast.SelectorExpr {
g.expr(node.expr)
offset := g.get_field_offset(node.expr_type, node.field_name)
if offset != 0 {
g.code_gen.add(Amd64Register.rax, offset)
}
}
ast.StructInit {
g.expr(node)
}
ast.ArrayInit {
g.expr(node) // TODO: add a test that uses this
}
ast.IndexExpr { // TODO
g.n_error('${@LOCATION} Unsupported IndexExpr left value')
}
ast.PrefixExpr {
if node.op != .mul {
g.n_error('${@LOCATION} Unsupported left value')
}
g.expr(node.right)
}
else {
g.n_error('${@LOCATION} Unsupported left value')
}
}
}