native: support C constants (#24660)

This commit is contained in:
Eliyaan (Nopana) 2025-06-09 17:53:06 +02:00 committed by GitHub
parent c61dda3425
commit f5bf1b591e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 274 additions and 40 deletions

View File

@ -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}')
}
}
}

View File

@ -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 }
}
}

View File

@ -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])
}

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -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() {

View File

@ -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'

View File

@ -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()
}

View File

@ -4,3 +4,4 @@
98
2
99
abc