From f5bf1b591eefc0c7131e6098880c6e135abb993a Mon Sep 17 00:00:00 2001 From: "Eliyaan (Nopana)" <103932369+Eliyaan@users.noreply.github.com> Date: Mon, 9 Jun 2025 17:53:06 +0200 Subject: [PATCH] native: support C constants (#24660) --- vlib/v/gen/native/amd64.v | 150 ++++++++++++++++++++++++--- vlib/v/gen/native/blacklist.v | 37 ++++++- vlib/v/gen/native/elf.v | 8 +- vlib/v/gen/native/expr.v | 31 +++++- vlib/v/gen/native/gen.v | 43 ++++++-- vlib/v/gen/native/stmt.c.v | 11 +- vlib/v/gen/native/tests/assign.vv | 20 ++++ vlib/v/gen/native/tests/builtin.vv | 6 +- vlib/v/gen/native/tests/linux.vv | 7 ++ vlib/v/gen/native/tests/linux.vv.out | 1 + 10 files changed, 274 insertions(+), 40 deletions(-) diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index f7a4d68fe3..fb341725fa 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -311,6 +311,9 @@ fn (mut c Amd64) cmp_var_reg(var Var, reg Register, config VarConfig) { // TODO // g.cmp() } + ExternVar { + c.cmp_var_reg(var_object as ExternVar, reg, config) + } } } LocalVar { @@ -330,6 +333,9 @@ fn (mut c Amd64) cmp_var_reg(var Var, reg Register, config VarConfig) { GlobalVar { // TODO } + ExternVar { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } @@ -348,6 +354,9 @@ fn (mut c Amd64) cmp_var(var Var, val i32, config VarConfig) { // TODO // g.cmp() } + ExternVar { + c.cmp_var(var_object as ExternVar, val, config) + } } } LocalVar { @@ -367,6 +376,9 @@ fn (mut c Amd64) cmp_var(var Var, val i32, config VarConfig) { GlobalVar { // TODO } + ExternVar { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } @@ -386,6 +398,9 @@ fn (mut c Amd64) dec_var(var Var, config VarConfig) { // TODO // g.dec() } + ExternVar { + c.dec_var(var_object as ExternVar, config) + } } } LocalVar { @@ -405,6 +420,9 @@ fn (mut c Amd64) dec_var(var Var, config VarConfig) { GlobalVar { // TODO } + ExternVar { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } @@ -425,6 +443,9 @@ fn (mut c Amd64) inc_var(var Var, config VarConfig) { // TODO // g.inc() } + ExternVar { + c.inc_var(var_object as ExternVar, config) + } } } LocalVar { @@ -468,6 +489,9 @@ fn (mut c Amd64) inc_var(var Var, config VarConfig) { c.g.n_error('${@LOCATION} Global variables incrementation is not supported yet') // TODO } + ExternVar { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } @@ -662,6 +686,9 @@ fn (mut c Amd64) mov_reg_to_var(var Var, r Register, config VarConfig) { // TODO c.g.n_error('${@LOCATION} unsupported Ident Register') } + ExternVar { + c.mov_reg_to_var(var_object as ExternVar, reg, config) + } } } LocalVar { @@ -743,6 +770,9 @@ fn (mut c Amd64) mov_reg_to_var(var Var, r Register, config VarConfig) { // TODO c.g.n_error('${@LOCATION} Unsupported GlobalVar') } + ExternVar { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } @@ -760,6 +790,9 @@ fn (mut c Amd64) mov_int_to_var(var Var, integer i32, config VarConfig) { Register { // TODO } + ExternVar { + c.mov_int_to_var(var_object as ExternVar, integer, config) + } } } LocalVar { @@ -822,6 +855,9 @@ fn (mut c Amd64) mov_int_to_var(var Var, integer i32, config VarConfig) { GlobalVar { // TODO } + ExternVar { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } @@ -868,6 +904,9 @@ fn (mut c Amd64) mov_var_to_reg(reg Register, var Var, config VarConfig) { Register { // TODO } + ExternVar { + c.mov_var_to_reg(reg, var_object as ExternVar, config) + } } } LocalVar { @@ -933,6 +972,9 @@ fn (mut c Amd64) mov_var_to_reg(reg Register, var Var, config VarConfig) { GlobalVar { c.g.n_error('${@LOCATION} Unsupported GlobalVar') } + ExternVar { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } @@ -2077,19 +2119,34 @@ fn (mut c Amd64) assign_var(var IdentVar, raw_type ast.Type) { size := c.g.get_type_size(typ) if typ.is_pure_float() { match var { - LocalVar { c.mov_ssereg_to_var(var as LocalVar, .xmm0) } - GlobalVar { c.mov_ssereg_to_var(var as GlobalVar, .xmm0) } + LocalVar { + c.mov_ssereg_to_var(var as LocalVar, .xmm0) + } + GlobalVar { + c.mov_ssereg_to_var(var as GlobalVar, .xmm0) + } // Amd64Register { c.g.mov_ssereg(var as Amd64Register, .xmm0) } - else {} + else { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } 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 int(size) in [1, 2, 4, 8] { match var { - LocalVar { c.mov_reg_to_var(var as LocalVar, Amd64Register.rax) } - GlobalVar { c.mov_reg_to_var(var as GlobalVar, Amd64Register.rax) } - Register { c.mov_reg(var as Amd64Register, Amd64Register.rax) } + LocalVar { + c.mov_reg_to_var(var as LocalVar, Amd64Register.rax) + } + GlobalVar { + c.mov_reg_to_var(var as GlobalVar, Amd64Register.rax) + } + Register { + c.mov_reg(var as Amd64Register, Amd64Register.rax) + } + ExternVar { + c.mov_reg_to_var(var as ExternVar, Amd64Register.rax) + } } } else { c.g.n_error('${@LOCATION} error assigning type ${typ} with size ${size}: ${info}') @@ -2121,6 +2178,12 @@ fn (mut c Amd64) assign_ident_int_lit(node ast.AssignStmt, i i32, int_lit ast.In c.div_reg(.rax, .rdx) c.mov_reg_to_var(left, Amd64Register.rax) } + .mod_assign { + c.mov_var_to_reg(Amd64Register.rax, left) + c.mov64(Amd64Register.rdx, i64(int_lit.val.int())) + c.mod_reg(.rax, .rdx) + c.mov_reg_to_var(left, Amd64Register.rax) + } .decl_assign { c.allocate_var(left.name, 8, i64(int_lit.val.int())) } @@ -2241,11 +2304,19 @@ fn (mut c Amd64) assign_ident_right_expr(node ast.AssignStmt, i i32, right ast.E val := enum_info.fields[right.val] or { c.g.n_error('${@LOCATION} enum field not found ${right.val}') } - if node.op == .decl_assign { - c.allocate_var(ident.name, enum_info.size, val) - } else { - c.mov64(Amd64Register.rax, val) - c.mov_reg_to_var(ident, Amd64Register.rax) + match val { + Number { + if node.op == .decl_assign { + c.allocate_var(ident.name, enum_info.size, val) + } else { + c.mov64(Amd64Register.rax, val) + c.mov_reg_to_var(ident, Amd64Register.rax) + } + } + ast.Expr { + c.g.expr(val) + c.mov_reg_to_var(ident, Amd64Register.rax) + } } } ast.FloatLiteral { @@ -2777,14 +2848,35 @@ fn (mut c Amd64) assign_stmt(node ast.AssignStmt) { c.pop(.rdx) // effective address of left expr c.gen_type_promotion(node.right_types[0], var_type) + size := match c.g.get_type_size(var_type) { + 1 { Size._8 } + 2 { Size._16 } + 4 { Size._32 } + else { Size._64 } + } match node.op { .decl_assign, .assign { - c.mov_store(.rdx, .rax, match c.g.get_type_size(var_type) { - 1 { ._8 } - 2 { ._16 } - 4 { ._32 } - else { ._64 } - }) + c.mov_store(.rdx, .rax, size) + } + .plus_assign { + c.mov_deref(Amd64Register.rcx, Amd64Register.rdx, var_type) + c.add_reg(.rax, .rcx) + c.mov_store(.rdx, .rax, size) + } + .minus_assign { + c.mov_deref(Amd64Register.rcx, Amd64Register.rdx, var_type) + c.sub_reg(.rax, .rcx) + c.mov_store(.rdx, .rax, size) + } + .and_assign { + c.mov_deref(Amd64Register.rcx, Amd64Register.rdx, var_type) + c.bitand_reg(.rax, .rcx) + c.mov_store(.rdx, .rax, size) + } + .mod_assign { + c.mov_deref(Amd64Register.rcx, Amd64Register.rdx, var_type) + c.mod_reg(.rax, .rcx) + c.mov_store(.rdx, .rax, size) } else { c.g.n_error('${@LOCATION} Unsupported assign instruction (${node.op})') @@ -3676,6 +3768,9 @@ fn (mut c Amd64) init_struct(var Var, init ast.StructInit) { // TODO // c.g.cmp() } + ExternVar { + c.init_struct(var_object as ExternVar, init) + } } } LocalVar { @@ -3718,6 +3813,9 @@ fn (mut c Amd64) init_struct(var Var, init ast.StructInit) { GlobalVar { c.g.n_error('${@LOCATION} GlobalVar not implemented for ast.StructInit') } + ExternVar { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } @@ -3766,6 +3864,9 @@ fn (mut c Amd64) init_array(var Var, node ast.ArrayInit) { // TODO // c.g.cmp() } + ExternVar { + c.init_array(var_object as ExternVar, node) + } } } LocalVar { @@ -3779,6 +3880,9 @@ fn (mut c Amd64) init_array(var Var, node ast.ArrayInit) { GlobalVar { c.g.n_error('${@LOCATION} GlobalVar not implemented for ast.ArrayInit') } + ExternVar { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } @@ -4075,6 +4179,9 @@ fn (mut c Amd64) mov_ssereg_to_var(var Var, reg Amd64SSERegister, config VarConf c.mov_ssereg_to_var(var_object as GlobalVar, reg, config) } Register {} + ExternVar { + c.mov_ssereg_to_var(var_object as ExternVar, reg, config) + } } } LocalVar { @@ -4100,6 +4207,9 @@ fn (mut c Amd64) mov_ssereg_to_var(var Var, reg Amd64SSERegister, config VarConf GlobalVar { // TODO } + ExternVar { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } @@ -4119,6 +4229,9 @@ fn (mut c Amd64) mov_var_to_ssereg(reg Amd64SSERegister, var Var, config VarConf c.mov_var_to_ssereg(reg, var_object as GlobalVar, config) } Register {} + ExternVar { + c.mov_var_to_ssereg(reg, var_object as ExternVar, config) + } } } LocalVar { @@ -4144,6 +4257,9 @@ fn (mut c Amd64) mov_var_to_ssereg(reg Amd64SSERegister, var Var, config VarConf GlobalVar { // TODO } + ExternVar { + c.g.n_error('${@LOCATION} unsupported var type ${var}') + } } } diff --git a/vlib/v/gen/native/blacklist.v b/vlib/v/gen/native/blacklist.v index c618acd08c..48e2f4e87f 100644 --- a/vlib/v/gen/native/blacklist.v +++ b/vlib/v/gen/native/blacklist.v @@ -28,7 +28,36 @@ already compiling functions: // false: whitelist function // true: blacklist function -const whitelist = { +const blacklist = { + 'main.main': false + 'c_error_number_str': false + 'exit': false + 'gc_is_enabled': false + 'int_max': false + 'int_min': false + 'flush_stdout': false + 'flush_stderr': false + 'print_character': true + 'u8.is_alnum': false + 'u8.is_bin_digit': false + 'u8.is_capital': false + 'u8.is_digit': false + 'u8.is_hex_digit': false + 'u8.is_letter': false + 'u8.is_oct_digit': false + 'u8.is_space': false + 'string.is_capital': false + 'string.is_ascii': false + 'string.is_identifier': false + 'string.is_blank': false + 'string.indent_width': false + 'string.index_u8': false + 'string.last_index': true + 'string.last_index_u8': false + 'string.contains_u8': false +} + +const windows_blacklist = { 'main.main': false 'c_error_number_str': false 'exit': false @@ -55,5 +84,9 @@ const whitelist = { } fn (g &Gen) is_blacklisted(name string, is_builtin bool) bool { - return whitelist[name] or { is_builtin } + if g.pref.os == .windows { + return windows_blacklist[name] or { is_builtin } + } else { + return blacklist[name] or { is_builtin } + } } diff --git a/vlib/v/gen/native/elf.v b/vlib/v/gen/native/elf.v index 4d790820af..d4279fc038 100644 --- a/vlib/v/gen/native/elf.v +++ b/vlib/v/gen/native/elf.v @@ -614,7 +614,7 @@ fn (mut g Gen) gen_section_data(sections []Section) { for rela in data { g.write64(rela.offset) - g.fn_addr[rela.name] = rela.offset + g.fn_addr[rela.name] = rela.offset // that's wierd it's the call offset, not the fn g.fn_names << rela.name g.write64(rela.info) g.write64(rela.addend) @@ -686,7 +686,7 @@ pub fn (mut g Gen) symtab_get_index(symbols []SymbolTableSection, name string) i return i32(i) } } - return 0 + panic('sym not found') } pub fn (mut g Gen) generate_linkable_elf_header() { @@ -847,6 +847,10 @@ pub fn (mut g Gen) gen_rela_section() { relocations << g.create_rela_section(symbol, call_pos - g.code_start_pos + 2, g.symtab_get_index(g.symbol_table, symbol[2..]), elf_r_amd64_gotpcrelx, -4) } + for var_pos, symbol in g.extern_vars { + relocations << g.create_rela_section(symbol, var_pos - g.code_start_pos + 2, g.symtab_get_index(g.symbol_table, + symbol[2..]), elf_r_amd64_64, 0) + } g.elf_rela_section.data = relocations g.gen_section_data([g.elf_rela_section]) } diff --git a/vlib/v/gen/native/expr.v b/vlib/v/gen/native/expr.v index f4bd494e17..1d6a36a89f 100644 --- a/vlib/v/gen/native/expr.v +++ b/vlib/v/gen/native/expr.v @@ -5,6 +5,7 @@ module native import v.ast import v.util +import v.errors fn (mut g Gen) expr(node ast.Expr) { match node { @@ -47,6 +48,9 @@ fn (mut g Gen) expr(node ast.Expr) { LocalVar { g.local_var_ident(node, var) } + ExternVar { + g.extern_var_ident(var) + } else { g.n_error('${@LOCATION} Unsupported variable kind') } @@ -115,7 +119,14 @@ fn (mut g Gen) expr(node ast.Expr) { val := g.enum_vals[type_name].fields[node.val] or { g.n_error('${@LOCATION} enum field not found ${node.val}') } - g.code_gen.mov64(g.code_gen.main_reg(), 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) @@ -148,7 +159,12 @@ fn (mut g Gen) expr(node ast.Expr) { } } else { - g.n_error('${@LOCATION} expr: unhandled node type: ${node.type_name()}') + 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}') } } } @@ -174,6 +190,17 @@ fn (mut g Gen) local_var_ident(ident ast.Ident, var LocalVar) { } } +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()] = var.name + g.code_gen.mov64(main_reg, Number(i64(0))) + g.code_gen.mov_deref(main_reg, main_reg, ast.u64_type_idx) + } else { + g.n_error('${@LOCATION} unsupported os for ${var}') + } +} + fn (mut g Gen) condition(expr ast.Expr, neg bool) i32 { g.println('; condition cjmp if ${neg}:') g.expr(expr) diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index 5dd8d6ec2a..ed1f1a4260 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -35,6 +35,7 @@ mut: extern_symbols []string linker_include_paths []string linker_libs []string + extern_vars map[i64]string extern_fn_calls map[i64]string fn_addr map[string]i64 fn_names []string @@ -211,9 +212,11 @@ type Number = u64 | i64 struct Enum { size i32 // size of the type of the enum in bytes mut: - fields map[string]Number + fields map[string]EnumVal } +type EnumVal = Number | ast.Expr + struct MultiReturn { mut: offsets []i32 @@ -235,6 +238,11 @@ struct LocalVar { name string } +struct ExternVar { + typ ast.Type + name string +} + struct GlobalVar {} @[params] @@ -244,9 +252,9 @@ pub: typ ast.Type // type of the value you want to process e.g. struct fields. } -type Var = GlobalVar | LocalVar | ast.Ident +type Var = GlobalVar | ExternVar | LocalVar | ast.Ident -type IdentVar = GlobalVar | LocalVar | Register +type IdentVar = GlobalVar | ExternVar | LocalVar | Register enum JumpOp { je @@ -270,6 +278,9 @@ fn byt(n i32, s i32) u8 { } fn (mut g Gen) get_var_from_ident(ident ast.Ident) IdentVar { + if ident.name in g.extern_symbols { + return ExternVar{ident.info.typ, ident.name} + } mut obj := ident.obj if obj !in [ast.Var, ast.ConstField, ast.GlobalField, ast.AsmRegister] { obj = ident.scope.find(ident.name) or { @@ -302,6 +313,9 @@ fn (mut g Gen) get_type_from_var(var Var) ast.Type { GlobalVar { g.n_error('${@LOCATION} cannot get type from GlobalVar yet') } + else { + g.n_error('${@LOCATION} unsupported var type ${var}') + } } } @@ -548,22 +562,33 @@ pub fn (mut g Gen) calculate_enum_fields() { size: i32(enum_size) } mut value := Number(if decl.is_flag { i64(1) } else { i64(0) }) + mut has_expr := false for field in decl.fields { if field.has_expr { - str_val := g.eval.expr(field.expr, ast.int_type_idx).string() - if str_val.len >= 0 && str_val[0] == `-` { - value = str_val.i64() + if field.expr.is_literal() { // does not depend on other declarations (C compile time csts) + str_val := g.eval.expr(field.expr, ast.int_type_idx).string() + if str_val.len >= 0 && str_val[0] == `-` { + value = str_val.i64() + } else { + value = str_val.u64() + } } else { - value = str_val.u64() + enum_vals.fields[field.name] = field.expr + has_expr = true + continue + } + } else { + if has_expr { + g.n_error('${@LOCATION} unsupported auto incr after C consts') } } match value { // Dereferences the sumtype (it would get assigned by address and messed up) i64 { - enum_vals.fields[field.name] = value as i64 + enum_vals.fields[field.name] = Number(value as i64) } u64 { - enum_vals.fields[field.name] = value as u64 + enum_vals.fields[field.name] = Number(value as u64) } } if decl.is_flag { diff --git a/vlib/v/gen/native/stmt.c.v b/vlib/v/gen/native/stmt.c.v index 0ef7da19c9..7f41780517 100644 --- a/vlib/v/gen/native/stmt.c.v +++ b/vlib/v/gen/native/stmt.c.v @@ -5,6 +5,7 @@ module native import v.ast import v.util +import v.errors fn C.strtol(str &char, endptr &&char, base i32) i32 @@ -76,9 +77,13 @@ fn (mut g Gen) stmt(node ast.Stmt) { } match node.kind { - 'include', 'preinclude', 'postinclude', 'define', 'insert' { - g.v_error('#${node.kind} is not supported with the native backend', - node.pos) + 'include' {} + 'preinclude', 'postinclude', 'define', 'insert' { + util.show_compiler_message('notice', errors.CompilerMessage{ + message: '#${node.kind} is not supported with the native backend' + file_path: node.source_file + pos: node.pos + }) } 'flag' { // do nothing; flags are already handled when dispatching extern dependencies diff --git a/vlib/v/gen/native/tests/assign.vv b/vlib/v/gen/native/tests/assign.vv index 529c44823e..ea0e0c6b35 100644 --- a/vlib/v/gen/native/tests/assign.vv +++ b/vlib/v/gen/native/tests/assign.vv @@ -3,6 +3,26 @@ fn main() { test_fp() test_unsafe() test_alias(100, 9) + test_plus_assign() + test_minus_assign() +} + +fn test_minus_assign() { + mut a := 3 + a -= 3 + assert a == 0 + n := 2300 + a -= n + assert a == -2300 +} + +fn test_plus_assign() { + mut a := 3 + a += 3 + assert a == 6 + n := 2300 + a += n + assert a == 2306 } fn test_int() { diff --git a/vlib/v/gen/native/tests/builtin.vv b/vlib/v/gen/native/tests/builtin.vv index 4ebf63409e..72d8f66748 100644 --- a/vlib/v/gen/native/tests/builtin.vv +++ b/vlib/v/gen/native/tests/builtin.vv @@ -127,10 +127,6 @@ println(f64_max(-32.32, 32.32)) println(f64_min(32.32, -32.32)) println(f64_min(-32.32, 32.32)) */ -/* uncomment when C variables are working like C.stdout -flush_stderr() -flush_stdout() -*/ gc_is_enabled() // not yet enabled so only calling it @@ -139,7 +135,7 @@ println(int_max(-32, 32)) println(int_min(32, -32)) println(int_min(-32, 32)) -// print_character(`a`) enable when C vars will work +//print_character(`a`) enable when C vars will work // assert u8(`a`).ascii_str() == 'a' when ptr index will work // assert u8(`b`).ascii_str() != 'a' diff --git a/vlib/v/gen/native/tests/linux.vv b/vlib/v/gen/native/tests/linux.vv index fc8ce95945..d24e5b38e2 100644 --- a/vlib/v/gen/native/tests/linux.vv +++ b/vlib/v/gen/native/tests/linux.vv @@ -16,6 +16,13 @@ fn test_for_c_in_string() { } } +fn test_c_extern_vars() { + C.fprintf(C.stdout, 'abc'.str, 3) +} + fn main() { test_for_c_in_string() + test_c_extern_vars() + flush_stderr() + flush_stdout() } diff --git a/vlib/v/gen/native/tests/linux.vv.out b/vlib/v/gen/native/tests/linux.vv.out index e23e1e2d2b..3b4da7b210 100644 --- a/vlib/v/gen/native/tests/linux.vv.out +++ b/vlib/v/gen/native/tests/linux.vv.out @@ -4,3 +4,4 @@ 98 2 99 +abc