@@ -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
2932pub 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] {
0 commit comments