Skip to content

Commit a986865

Browse files
committed
v2: integrate v2.types into ssa; string structs
1 parent 7f93f9a commit a986865

9 files changed

Lines changed: 354 additions & 27 deletions

File tree

‎cmd/v2/test.v‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,5 +2210,29 @@ fn main() {
22102210
// 44.5 Comptime in function call
22112211
print_int(get_comptime_value())
22122212

2213+
// ==================== 45. STRING STRUCT FIELDS ====================
2214+
print_str('--- 45. String Struct Fields ---')
2215+
2216+
// 45.1 String literal .str field
2217+
s45_1 := 'Hello'
2218+
print_str(s45_1) // Hello
2219+
2220+
// 45.2 String literal .len field
2221+
s45_2 := 'World'
2222+
print_int(s45_2.len) // 5
2223+
2224+
// 45.3 Interpolated string .len field
2225+
val45 := 123
2226+
s45_3 := 'Val: ${val45}'
2227+
print_int(s45_3.len) // 8
2228+
2229+
// 45.4 Multiple string operations
2230+
a45 := 'AB'
2231+
b45 := 'CDE'
2232+
print_int(a45.len + b45.len) // 5
2233+
2234+
// 45.5 String in function parameter
2235+
print_str('Passed directly') // Passed directly
2236+
22132237
print_str('=== All tests completed ===')
22142238
}

‎vlib/v2/gen/arm64/arm64.v‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ pub mut:
2424
reg_map map[int]int
2525
used_regs []int
2626
next_blk int
27+
28+
// Track which string literals have been materialized (value_id -> str_data offset)
29+
string_literal_offsets map[int]int
2730
}
2831

2932
pub fn Gen.new(mod &ssa.Module) &Gen {
@@ -84,6 +87,7 @@ fn (mut g Gen) gen_func(func ssa.Function) {
8487
g.pending_labels = map[int][]int{}
8588
g.reg_map = map[int]int{}
8689
g.used_regs = []int{}
90+
g.string_literal_offsets = map[int]int{}
8791
g.allocate_registers(func)
8892

8993
// Stack Frame
@@ -94,6 +98,16 @@ fn (mut g Gen) gen_func(func ssa.Function) {
9498
slot_offset += 8
9599
}
96100

101+
// Pre-pass: allocate stack slots for string_literal values
102+
for val in g.mod.values {
103+
if val.kind == .string_literal {
104+
// String struct needs 24 bytes (str ptr + len + is_lit)
105+
slot_offset = (slot_offset + 15) & ~0xF
106+
slot_offset += 24
107+
g.stack_map[val.id] = -slot_offset
108+
}
109+
}
110+
97111
for i, blk_id in func.blocks {
98112
g.next_blk = if i + 1 < func.blocks.len { func.blocks[i + 1] } else { -1 }
99113
blk := g.mod.blocks[blk_id]
@@ -133,6 +147,14 @@ fn (mut g Gen) gen_func(func ssa.Function) {
133147
slot_offset += 8
134148
}
135149

150+
if instr.op == .inline_string_init {
151+
// String struct needs 24 bytes (str ptr + len + is_lit)
152+
slot_offset = (slot_offset + 15) & ~0xF
153+
slot_offset += 24
154+
g.stack_map[val_id] = -slot_offset
155+
continue // Don't allocate another 8 bytes below
156+
}
157+
136158
if val_id in g.reg_map {
137159
continue
138160
}
@@ -593,6 +615,34 @@ fn (mut g Gen) gen_instr(val_id int) {
593615
// This is used after code that should never be reached (e.g., after exit() in assert)
594616
g.emit(0x00000000)
595617
}
618+
.inline_string_init {
619+
// Create string struct by value: { str, len, is_lit }
620+
// operands: [str_ptr, len, is_lit]
621+
// This instruction creates a string struct on the stack
622+
// The result is a pointer to the struct
623+
str_ptr_id := instr.operands[0]
624+
len_id := instr.operands[1]
625+
is_lit_id := instr.operands[2]
626+
627+
// Get base pointer for this value's stack slot
628+
base_offset := g.stack_map[val_id]
629+
630+
// Store str field (offset 0)
631+
g.load_val_to_reg(8, str_ptr_id)
632+
g.emit_str_reg_offset(8, 29, base_offset)
633+
634+
// Store len field (offset 8)
635+
g.load_val_to_reg(9, len_id)
636+
g.emit_str_reg_offset(9, 29, base_offset + 8)
637+
638+
// Store is_lit field (offset 16)
639+
g.load_val_to_reg(10, is_lit_id)
640+
g.emit_str_reg_offset(10, 29, base_offset + 16)
641+
642+
// Return pointer to struct (base address)
643+
g.emit_add_fp_imm(8, base_offset) // x8 = fp + offset
644+
g.store_reg_to_val(8, val_id)
645+
}
596646
else {
597647
eprintln('arm64: unknown instruction ${instr}')
598648
exit(1)
@@ -659,6 +709,50 @@ fn (mut g Gen) load_val_to_reg(reg int, val_id int) {
659709
g.emit(0x90000000 | u32(reg))
660710
g.macho.add_reloc(g.macho.text_data.len, sym_idx, arm64_reloc_pageoff12, false)
661711
g.emit(0x91000000 | u32(reg) | (u32(reg) << 5))
712+
} else if val.kind == .string_literal {
713+
// String literal: create string struct { str, len, is_lit } on stack
714+
// val.name contains the string content, val.index contains the length
715+
716+
// Get stack slot for this string struct (24 bytes: str ptr + len + is_lit)
717+
base_offset := g.stack_map[val_id]
718+
719+
// Check if we've already materialized this string literal
720+
if _ := g.string_literal_offsets[val_id] {
721+
// Already materialized - just load pointer to the struct
722+
g.emit_add_fp_imm(reg, base_offset)
723+
} else {
724+
// First time - create string data and initialize struct
725+
str_content := val.name
726+
str_len := val.index
727+
728+
// Create the string data in cstring section
729+
str_offset2 := g.macho.str_data.len
730+
g.macho.str_data << str_content.bytes()
731+
g.macho.str_data << 0 // null terminator
732+
733+
// Track that we've materialized this string literal
734+
g.string_literal_offsets[val_id] = str_offset2
735+
736+
// Store str pointer (offset 0): load address of string data
737+
sym_idx := g.macho.add_symbol('L_str_${str_offset2}', u64(str_offset2), false,
738+
2)
739+
g.macho.add_reloc(g.macho.text_data.len, sym_idx, arm64_reloc_page21, true)
740+
g.emit(0x90000000 | u32(reg))
741+
g.macho.add_reloc(g.macho.text_data.len, sym_idx, arm64_reloc_pageoff12, false)
742+
g.emit(0x91000000 | u32(reg) | (u32(reg) << 5))
743+
g.emit_str_reg_offset(reg, 29, base_offset)
744+
745+
// Store len (offset 8)
746+
g.emit_mov_imm64(9, str_len)
747+
g.emit_str_reg_offset(9, 29, base_offset + 8)
748+
749+
// Store is_lit = 1 (offset 16)
750+
g.emit_mov_imm64(10, 1)
751+
g.emit_str_reg_offset(10, 29, base_offset + 16)
752+
753+
// Load pointer to string struct into reg
754+
g.emit_add_fp_imm(reg, base_offset)
755+
}
662756
} else {
663757
// Handles .instruction, .argument, etc.
664758
if reg_idx := g.reg_map[val_id] {

‎vlib/v2/gen/c/c.v‎

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ pub fn (mut g Gen) gen() string {
2626
g.sb.writeln('#include <stdbool.h>')
2727
g.sb.writeln('#include <stddef.h>')
2828
g.sb.writeln('#include <stdio.h>')
29+
g.sb.writeln('#include <stdlib.h>')
30+
g.sb.writeln('#include <string.h>')
31+
g.sb.writeln('')
32+
// Builtin string type
33+
g.sb.writeln('typedef struct { int8_t* str; int64_t len; int64_t is_lit; } string;')
2934
g.sb.writeln('')
3035

3136
g.gen_struct_decls()
@@ -58,24 +63,47 @@ fn (mut g Gen) gen_func_decls() {
5863
fn (mut g Gen) gen_struct_decls() {
5964
for i, t in g.mod.type_store.types {
6065
if t.kind == .struct_t {
66+
// Skip builtin string type (already defined)
67+
if g.is_string_type(i) {
68+
continue
69+
}
6170
g.sb.writeln('typedef struct Struct_${i} Struct_${i};')
6271
}
6372
}
6473
g.sb.writeln('')
6574

6675
for i, t in g.mod.type_store.types {
6776
if t.kind == .struct_t {
77+
// Skip builtin string type (already defined)
78+
if g.is_string_type(i) {
79+
continue
80+
}
6881
g.sb.writeln('struct Struct_${i} {')
6982
for idx, field_id in t.fields {
7083
type_name := g.type_name(field_id)
71-
g.sb.writeln('\t${type_name} field_${idx};')
84+
// Use named fields if available
85+
field_name := if idx < t.field_names.len {
86+
t.field_names[idx]
87+
} else {
88+
'field_${idx}'
89+
}
90+
g.sb.writeln('\t${type_name} ${field_name};')
7291
}
7392
g.sb.writeln('};')
7493
}
7594
}
7695
g.sb.writeln('')
7796
}
7897

98+
fn (g Gen) is_string_type(type_id int) bool {
99+
t := g.mod.type_store.types[type_id]
100+
if t.kind == .struct_t && t.field_names.len == 3 {
101+
return t.field_names[0] == 'str' && t.field_names[1] == 'len'
102+
&& t.field_names[2] == 'is_lit'
103+
}
104+
return false
105+
}
106+
79107
fn (mut g Gen) gen_globals() {
80108
for gvar in g.mod.globals {
81109
tname := g.type_name(gvar.typ)
@@ -265,11 +293,18 @@ fn (mut g Gen) gen_instr(val_id int) {
265293
elem_type := g.mod.type_store.types[elem_type_id]
266294

267295
if elem_type.kind == .struct_t {
296+
// Get field name - use named field if available, otherwise fall back to field_N
297+
idx := idx_val.name.int()
298+
field_name := if idx < elem_type.field_names.len {
299+
elem_type.field_names[idx]
300+
} else {
301+
'field_${idx_val.name}'
302+
}
268303
// For globals, the C declaration is not a pointer, so use '.' instead of '->'
269304
if base_val.kind == .global {
270-
g.sb.writeln('\t${res} = &${base}.field_${idx_val.name};')
305+
g.sb.writeln('\t${res} = &${base}.${field_name};')
271306
} else {
272-
g.sb.writeln('\t${res} = &${base}->field_${idx_val.name};')
307+
g.sb.writeln('\t${res} = &${base}->${field_name};')
273308
}
274309
} else {
275310
idx := g.val_str(instr.operands[1])
@@ -308,6 +343,13 @@ fn (mut g Gen) gen_instr(val_id int) {
308343
// Unreachable code - typically after abort/exit
309344
g.sb.writeln('\t__builtin_unreachable();')
310345
}
346+
.inline_string_init {
347+
// Create string struct by value: (string){str, len, is_lit}
348+
str_ptr := g.val_str(instr.operands[0])
349+
len_val := g.val_str(instr.operands[1])
350+
is_lit := g.val_str(instr.operands[2])
351+
g.sb.writeln('\t${res} = (string){(int8_t*)${str_ptr}, ${len_val}, ${is_lit}};')
352+
}
311353
else {
312354
g.sb.writeln('\t// Unhandled C op: ${instr.op}')
313355
}
@@ -333,12 +375,20 @@ fn (g Gen) type_name(id int) string {
333375
if elem_t.kind == .array_t {
334376
return g.type_name(elem_t.elem_type) + '*'
335377
}
378+
// Check if pointing to string struct
379+
if g.is_string_type(t.elem_type) {
380+
return 'string*'
381+
}
336382
return g.type_name(t.elem_type) + '*'
337383
}
338384
.array_t {
339385
return g.type_name(t.elem_type) + '*'
340386
} // Arrays decay to pointers
341387
.struct_t {
388+
// Check for builtin string type
389+
if g.is_string_type(id) {
390+
return 'string'
391+
}
342392
return 'Struct_${id}'
343393
}
344394
else {
@@ -355,6 +405,10 @@ fn (g Gen) val_str(id int) string {
355405
return val.name
356406
} else if val.kind == .global {
357407
return val.name
408+
} else if val.kind == .string_literal {
409+
// String literal: generate inline struct literal
410+
// val.name contains the string content, val.index contains length
411+
return '(string){"${val.name}", ${val.index}, 1}'
358412
}
359413
return '_v${val.id}'
360414
}

0 commit comments

Comments
 (0)