v / vlib / v2 / gen / c / c.v
1148 lines · 1081 sloc · 35.16 KB · a0302bd07fa7ec4502ebe2c1951f5fdf3770c7d1
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module c
6
7import v2.ssa
8import strings
9
10pub struct Gen {
11 mod &ssa.Module
12pub mut:
13 link_builtin bool // When true, skip emitting runtime helpers (they come from builtin.o)
14mut:
15 sb strings.Builder
16 indent int
17 current_fn_name string
18 current_fn_idx int
19 current_fn_ret ssa.TypeID
20}
21
22pub fn Gen.new(mod &ssa.Module) &Gen {
23 return &Gen{
24 mod: mod
25 sb: strings.new_builder(4096)
26 }
27}
28
29// new_gen creates a C backend generator without relying on static method lookup.
30pub fn new_gen(mod &ssa.Module) &Gen {
31 return &Gen{
32 mod: mod
33 sb: strings.new_builder(4096)
34 }
35}
36
37pub fn (mut g Gen) gen() string {
38 g.sb.writeln('// Generated by V2 SSA Compiler')
39 g.sb.writeln('#include <stdint.h>')
40 g.sb.writeln('#include <stdbool.h>')
41 g.sb.writeln('#include <stddef.h>')
42 g.sb.writeln('#include <stdio.h>')
43 g.sb.writeln('#include <stdlib.h>')
44 g.sb.writeln('#include <string.h>')
45 g.sb.writeln('#include <pthread.h>')
46 g.sb.writeln('')
47 // Undefine macOS macros that conflict with struct field names
48 g.sb.writeln('#ifdef __APPLE__')
49 g.sb.writeln('#undef sa_handler')
50 g.sb.writeln('#undef sa_sigaction')
51 g.sb.writeln('#endif')
52 g.sb.writeln('')
53
54 // Builtin type aliases
55 g.sb.writeln('typedef int8_t i8;')
56 g.sb.writeln('typedef int16_t i16;')
57 g.sb.writeln('typedef int32_t i32;')
58 g.sb.writeln('typedef int64_t i64;')
59 g.sb.writeln('typedef uint8_t u8;')
60 g.sb.writeln('typedef uint16_t u16;')
61 g.sb.writeln('typedef uint32_t u32;')
62 g.sb.writeln('typedef uint64_t u64;')
63 g.sb.writeln('typedef float f32;')
64 g.sb.writeln('typedef double f64;')
65 g.sb.writeln('typedef int64_t isize;')
66 g.sb.writeln('typedef uint64_t usize;')
67 g.sb.writeln('typedef u8 rune;')
68 g.sb.writeln('')
69
70 // wyhash implementation (used by map)
71 g.gen_wyhash()
72
73 // Runtime helpers (only when not linking against builtin.o)
74 if !g.link_builtin {
75 g.gen_runtime_helpers()
76 g.sb.writeln('')
77 }
78
79 // Struct declarations
80 g.gen_struct_decls()
81
82 // Global declarations
83 g.gen_globals()
84
85 // Function forward declarations for all functions
86 g.gen_fn_forward_decls()
87
88 // Spawn wrapper functions (pre-scanned from all spawn_call instructions)
89 g.gen_spawn_wrappers()
90
91 // Function bodies (skip stub functions - they'll come from builtin.o)
92 for i, func in g.mod.funcs {
93 if func.blocks.len == 0 {
94 continue
95 }
96 if g.is_stub_function(func) {
97 continue
98 }
99 g.current_fn_idx = i
100 g.gen_function(func)
101 }
102
103 return g.sb.str()
104}
105
106// gen_spawn_wrappers pre-scans all functions for spawn_call instructions with
107// arguments and emits wrapper functions that unpack the args struct and call the
108// original function. These must be emitted before function bodies so that the
109// wrappers are visible at each pthread_create call site.
110fn (mut g Gen) gen_spawn_wrappers() {
111 for func in g.mod.funcs {
112 for blk_id in func.blocks {
113 block := g.mod.blocks[blk_id]
114 for instr_id in block.instrs {
115 val := g.mod.values[instr_id]
116 if val.kind != .instruction {
117 continue
118 }
119 instr := g.mod.instrs[val.index]
120 if instr.op !in [.spawn_call, .go_call] || instr.operands.len < 2 {
121 continue
122 }
123 fn_ref := instr.operands[0]
124 fn_val := g.mod.values[fn_ref]
125 fn_name := sanitize_c_ident(fn_val.name)
126 prefix := if instr.op == .spawn_call { '_spawn' } else { '_go' }
127 wrapper_id := '${fn_name}_${val.name}'
128 arg_count := instr.operands.len - 1
129
130 g.sb.write_string('struct ${prefix}_args_${wrapper_id} { ')
131 for ai in 0 .. arg_count {
132 arg_type := g.type_name(g.mod.values[instr.operands[ai + 1]].typ)
133 g.sb.write_string('${arg_type} a${ai}; ')
134 }
135 g.sb.writeln('};')
136 g.sb.writeln('static void* ${prefix}_wrapper_${wrapper_id}(void* _arg) {')
137 g.sb.writeln('\tstruct ${prefix}_args_${wrapper_id}* _args = (struct ${prefix}_args_${wrapper_id}*)_arg;')
138 g.sb.write_string('\t${fn_name}(')
139 for ai in 0 .. arg_count {
140 if ai > 0 {
141 g.sb.write_string(', ')
142 }
143 g.sb.write_string('_args->a${ai}')
144 }
145 g.sb.writeln(');')
146 g.sb.writeln('\tfree(_arg);')
147 g.sb.writeln('\treturn NULL;')
148 g.sb.writeln('}')
149 g.sb.writeln('')
150 }
151 }
152 }
153}
154
155fn (mut g Gen) gen_runtime_helpers() {
156 // println
157 g.sb.writeln('static void println(string s) {')
158 g.sb.writeln('\tif (s.str && s.len > 0) {')
159 g.sb.writeln('\t\tfwrite(s.str, 1, s.len, stdout);')
160 g.sb.writeln('\t}')
161 g.sb.writeln('\tputchar(10);')
162 g.sb.writeln('}')
163 g.sb.writeln('')
164
165 // print
166 g.sb.writeln('static void print(string s) {')
167 g.sb.writeln('\tif (s.str && s.len > 0) {')
168 g.sb.writeln('\t\tfwrite(s.str, 1, s.len, stdout);')
169 g.sb.writeln('\t}')
170 g.sb.writeln('}')
171 g.sb.writeln('')
172
173 // eprintln
174 g.sb.writeln('static void eprintln(string s) {')
175 g.sb.writeln('\tif (s.str && s.len > 0) {')
176 g.sb.writeln('\t\tfwrite(s.str, 1, s.len, stderr);')
177 g.sb.writeln('\t}')
178 g.sb.writeln('\tfputc(10, stderr);')
179 g.sb.writeln('}')
180 g.sb.writeln('')
181
182 // int_str helper (converts int to string)
183 g.sb.writeln('static string int_str(i64 val) {')
184 g.sb.writeln('\tstatic char buf[32];')
185 g.sb.writeln('\tint len = snprintf(buf, sizeof(buf), "%lld", (long long)val);')
186 g.sb.writeln('\treturn (string){.str = (i8*)buf, .len = len, .is_lit = 1};')
187 g.sb.writeln('}')
188 g.sb.writeln('')
189
190 // string__plus helper
191 g.sb.writeln('static string string__plus(string a, string b) {')
192 g.sb.writeln('\ti64 new_len = a.len + b.len;')
193 g.sb.writeln('\ti8* s = (i8*)malloc(new_len + 1);')
194 g.sb.writeln('\tif (a.str && a.len > 0) memcpy(s, a.str, a.len);')
195 g.sb.writeln('\tif (b.str && b.len > 0) memcpy(s + a.len, b.str, b.len);')
196 g.sb.writeln('\ts[new_len] = 0;')
197 g.sb.writeln('\treturn (string){.str = s, .len = new_len, .is_lit = 0};')
198 g.sb.writeln('}')
199}
200
201fn (mut g Gen) gen_module_init_stubs() {
202 // Generate empty stubs for module __v_init_consts functions
203 // that the transformer injects into main()
204 mut emitted := map[string]bool{}
205 for func in g.mod.funcs {
206 if func.blocks.len == 0 {
207 continue
208 }
209 for block_id in func.blocks {
210 block := g.mod.blocks[block_id]
211 for instr_id in block.instrs {
212 val := g.mod.values[instr_id]
213 if val.kind != .instruction {
214 continue
215 }
216 instr := g.mod.instrs[val.index]
217 if instr.op == .call && instr.operands.len > 0 {
218 fn_val := g.mod.values[instr.operands[0]]
219 if fn_val.kind == .func_ref && fn_val.name.contains('__v_init_consts') {
220 c_name := sanitize_c_ident(fn_val.name)
221 if c_name !in emitted {
222 g.sb.writeln('static void ${c_name}(void) {}')
223 emitted[c_name] = true
224 }
225 }
226 }
227 }
228 }
229 }
230}
231
232fn (mut g Gen) gen_struct_decls() {
233 // Forward declarations for all registered structs
234 for _, name in g.mod.c_struct_names {
235 g.sb.writeln('typedef struct ${name} ${name};')
236 }
237 g.sb.writeln('')
238
239 // Full struct definitions
240 for type_id, name in g.mod.c_struct_names {
241 typ := g.mod.type_store.types[type_id]
242 g.sb.writeln('struct ${name} {')
243 for fi, field_type in typ.fields {
244 field_name := if fi < typ.field_names.len {
245 typ.field_names[fi]
246 } else {
247 'field_${fi}'
248 }
249 g.sb.writeln('\t${g.type_name(field_type)} ${field_name};')
250 }
251 g.sb.writeln('};')
252 g.sb.writeln('')
253 }
254}
255
256fn (g &Gen) struct_name(type_id ssa.TypeID) string {
257 // Check if there's a registered struct name in the module
258 if name := g.mod.c_struct_names[type_id] {
259 return name
260 }
261 return 'Struct_${type_id}'
262}
263
264fn (mut g Gen) gen_globals() {
265 for glob in g.mod.globals {
266 if glob.linkage == .external {
267 g.sb.write_string('extern ')
268 }
269 g.sb.writeln('${g.type_name(glob.typ)} ${sanitize_c_ident(glob.name)};')
270 }
271 if g.mod.globals.len > 0 {
272 g.sb.writeln('')
273 }
274}
275
276fn (mut g Gen) gen_fn_forward_decls() {
277 for func in g.mod.funcs {
278 if func.name == 'main' {
279 g.sb.writeln('int main(int argc, char** argv);')
280 continue
281 }
282 // Skip C extern functions — they conflict with system header declarations.
283 // Non-standard C functions (wyhash, etc.) are provided via inline headers.
284 if func.is_c_extern {
285 continue
286 }
287 c_name := g.fn_c_name(func.name)
288 ret_type := g.type_name(func.typ)
289 mut params := []string{}
290 for i, pid in func.params {
291 val := g.mod.values[pid]
292 ptype := g.type_name(val.typ)
293 if val.name.len == 0 {
294 // C extern functions may have unnamed parameters
295 params << '${ptype} _p${i}'
296 } else {
297 pname := sanitize_c_ident(val.name)
298 params << '${ptype} ${pname}'
299 }
300 }
301 param_str := if params.len > 0 { params.join(', ') } else { 'void' }
302 g.sb.writeln('${ret_type} ${c_name}(${param_str});')
303 }
304 g.sb.writeln('')
305}
306
307// is_stub_function returns true for functions that only have a trivial
308// entry block with a return (empty stubs generated for non-main-module functions).
309// These functions will be provided by the linked builtin.o.
310fn (g &Gen) is_stub_function(func ssa.Function) bool {
311 if func.blocks.len != 1 {
312 return false
313 }
314 block := g.mod.blocks[func.blocks[0]]
315 // A stub has 0-2 instructions (just a return, possibly with a zero constant)
316 mut real_instrs := 0
317 for instr_id in block.instrs {
318 val := g.mod.values[instr_id]
319 if val.kind != .instruction {
320 continue
321 }
322 instr := g.mod.instrs[val.index]
323 if instr.op == .ret {
324 continue
325 }
326 if instr.op in [.alloca, .heap_alloc, .load] {
327 // alloca/heap_alloc+load for struct return stubs
328 continue
329 }
330 real_instrs++
331 }
332 return real_instrs == 0
333}
334
335fn (g &Gen) fn_c_name(name string) string {
336 c_name := sanitize_c_ident(name)
337 // Check if this function name conflicts with a struct name
338 for _, sname in g.mod.c_struct_names {
339 if c_name == sname {
340 return '${c_name}_fn'
341 }
342 }
343 return c_name
344}
345
346fn (mut g Gen) gen_function(func ssa.Function) {
347 g.current_fn_name = func.name
348 g.current_fn_ret = func.typ
349
350 if func.blocks.len == 0 {
351 return
352 }
353
354 // Function signature
355 if func.name == 'main' {
356 g.sb.write_string('int main(int argc, char** argv)')
357 } else {
358 c_name := g.fn_c_name(func.name)
359 ret_type := g.type_name(func.typ)
360 mut params := []string{}
361 for pid in func.params {
362 val := g.mod.values[pid]
363 ptype := g.type_name(val.typ)
364 pname := sanitize_c_ident(val.name)
365 params << '${ptype} ${pname}'
366 }
367 param_str := if params.len > 0 { params.join(', ') } else { 'void' }
368 g.sb.write_string('${ret_type} ${c_name}(${param_str})')
369 }
370
371 g.sb.writeln(' {')
372 g.indent = 1
373
374 // Collect all allocas and local variable declarations
375 mut declared_vars := map[int]bool{}
376
377 // Generate blocks
378 for bi, block_id in func.blocks {
379 block := g.mod.blocks[block_id]
380
381 // Emit label for non-entry blocks
382 if bi > 0 {
383 g.sb.writeln('${block.name}:;')
384 }
385
386 for instr_id in block.instrs {
387 val := g.mod.values[instr_id]
388 if val.kind != .instruction {
389 continue
390 }
391 instr := g.mod.instrs[val.index]
392
393 match instr.op {
394 .alloca {
395 // Declare local variable
396 elem_type := g.mod.type_store.types[instr.typ].elem_type
397 if elem_type != 0 {
398 g.write_indent()
399 // Check if this is a multi-element alloca (fixed-size array on stack)
400 mut count := 1
401 if instr.operands.len > 0 {
402 count_val := g.mod.values[instr.operands[0]]
403 count = count_val.name.int()
404 if count < 1 {
405 count = 1
406 }
407 }
408 if count > 1 {
409 g.sb.writeln('${g.type_name(elem_type)} ${val.name}[${count}];')
410 } else {
411 g.sb.writeln('${g.type_name(elem_type)} ${val.name};')
412 }
413 declared_vars[val.id] = true
414 }
415 }
416 .heap_alloc {
417 // Heap-allocate: declare as pointer and call calloc
418 elem_type := g.mod.type_store.types[instr.typ].elem_type
419 if elem_type != 0 {
420 g.write_indent()
421 g.sb.writeln('${g.type_name(elem_type)}* ${val.name} = (${g.type_name(elem_type)}*)calloc(1, sizeof(${g.type_name(elem_type)}));')
422 declared_vars[val.id] = true
423 }
424 }
425 .store {
426 if instr.operands.len >= 2 {
427 src := instr.operands[0]
428 dst := instr.operands[1]
429 dst_val := g.mod.values[dst]
430 src_val := g.mod.values[src]
431 g.write_indent()
432 if dst_val.kind == .global || (dst_val.kind == .instruction
433 && g.mod.instrs[dst_val.index].op in [.alloca, .heap_alloc]) {
434 if dst_val.kind == .instruction
435 && g.mod.instrs[dst_val.index].op == .heap_alloc {
436 g.sb.write_string('*${sanitize_c_ident(dst_val.name)} = ')
437 } else {
438 g.sb.write_string('${sanitize_c_ident(dst_val.name)} = ')
439 }
440 // If src is an alloca and dst's elem type is a pointer,
441 // we need & (e.g., storing &Point{} alloca into a Point* variable)
442 if src_val.kind == .instruction
443 && g.mod.instrs[src_val.index].op == .alloca {
444 mut dst_ptr_typ := ssa.TypeID(0)
445 if dst_val.kind == .global {
446 dst_ptr_typ = dst_val.typ
447 } else {
448 dst_ptr_typ = g.mod.instrs[dst_val.index].typ
449 }
450 if dst_ptr_typ < g.mod.type_store.types.len {
451 dst_elem := g.mod.type_store.types[dst_ptr_typ].elem_type
452 if dst_elem < g.mod.type_store.types.len
453 && g.mod.type_store.types[dst_elem].kind == .ptr_t {
454 g.sb.write_string('&')
455 }
456 }
457 }
458 g.gen_value(src)
459 g.sb.writeln(';')
460 } else {
461 g.sb.write_string('*(')
462 g.gen_value(dst)
463 g.sb.write_string(') = ')
464 g.gen_value(src)
465 g.sb.writeln(';')
466 }
467 }
468 }
469 .load {
470 if instr.operands.len >= 1 {
471 src := instr.operands[0]
472 src_val := g.mod.values[src]
473 // Check if this load result is actually used
474 if val.uses.len > 0 {
475 g.write_indent()
476 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = ')
477 if src_val.kind == .global
478 || (src_val.kind == .instruction
479 && g.mod.instrs[src_val.index].op == .alloca) {
480 g.gen_value(src)
481 } else {
482 g.sb.write_string('*(')
483 g.gen_value(src)
484 g.sb.write_string(')')
485 }
486 g.sb.writeln(';')
487 }
488 }
489 }
490 .ret {
491 g.write_indent()
492 if instr.operands.len > 0 {
493 g.sb.write_string('return ')
494 g.gen_value(instr.operands[0])
495 g.sb.writeln(';')
496 } else {
497 if g.current_fn_name == 'main' {
498 g.sb.writeln('return 0;')
499 } else {
500 g.sb.writeln('return;')
501 }
502 }
503 }
504 .br {
505 // Conditional branch: br cond, then_label, else_label
506 if instr.operands.len >= 3 {
507 cond := instr.operands[0]
508 then_block := g.mod.get_block_from_val(instr.operands[1])
509 else_block := g.mod.get_block_from_val(instr.operands[2])
510 g.write_indent()
511 g.sb.write_string('if (')
512 g.gen_value(cond)
513 g.sb.write_string(') goto ${g.mod.blocks[then_block].name}; ')
514 g.sb.writeln('else goto ${g.mod.blocks[else_block].name};')
515 }
516 }
517 .jmp {
518 if instr.operands.len >= 1 {
519 target_block := g.mod.get_block_from_val(instr.operands[0])
520 g.write_indent()
521 g.sb.writeln('goto ${g.mod.blocks[target_block].name};')
522 }
523 }
524 .call {
525 if instr.operands.len >= 1 {
526 fn_ref := instr.operands[0]
527 fn_val := g.mod.values[fn_ref]
528 fn_name := sanitize_c_ident(fn_val.name)
529
530 // Look up target function to get parameter types
531 mut target_fn_params := []ssa.ValueID{}
532 for f in g.mod.funcs {
533 if sanitize_c_ident(f.name) == fn_name {
534 target_fn_params = f.params.clone()
535 break
536 }
537 }
538
539 g.write_indent()
540 // Only declare result if used and non-void
541 if val.uses.len > 0 && instr.typ != 0 {
542 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = ')
543 }
544
545 g.sb.write_string('${fn_name}(')
546 for ai := 1; ai < instr.operands.len; ai++ {
547 if ai > 1 {
548 g.sb.write_string(', ')
549 }
550 arg_val := g.mod.values[instr.operands[ai]]
551 param_idx := ai - 1
552 // Check if arg is an alloca being passed to a pointer param
553 if arg_val.kind == .instruction
554 && g.mod.instrs[arg_val.index].op == .alloca
555 && param_idx < target_fn_params.len {
556 param_type := g.mod.values[target_fn_params[param_idx]].typ
557 if param_type < g.mod.type_store.types.len
558 && g.mod.type_store.types[param_type].kind == .ptr_t {
559 // Parameter expects a pointer - emit &var
560 g.sb.write_string('&')
561 g.gen_value(instr.operands[ai])
562 continue
563 }
564 }
565 // Check if arg is a pointer type but param expects non-pointer (auto-deref)
566 if param_idx < target_fn_params.len {
567 param_type := g.mod.values[target_fn_params[param_idx]].typ
568 arg_type := arg_val.typ
569 if arg_type < g.mod.type_store.types.len
570 && g.mod.type_store.types[arg_type].kind == .ptr_t
571 && param_type < g.mod.type_store.types.len
572 && g.mod.type_store.types[param_type].kind != .ptr_t {
573 // Arg is pointer but param expects value - emit *var
574 g.sb.write_string('*(')
575 g.gen_value(instr.operands[ai])
576 g.sb.write_string(')')
577 continue
578 }
579 }
580 g.gen_value(instr.operands[ai])
581 }
582 g.sb.writeln(');')
583 }
584 }
585 .add, .sub, .mul, .sdiv, .srem, .fadd, .fsub, .fmul, .fdiv {
586 if instr.operands.len >= 2 {
587 op_str := match instr.op {
588 .add, .fadd { '+' }
589 .sub, .fsub { '-' }
590 .mul, .fmul { '*' }
591 .sdiv, .fdiv { '/' }
592 .srem { '%' }
593 else { '+' }
594 }
595
596 g.write_indent()
597 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = ')
598 g.gen_value(instr.operands[0])
599 g.sb.write_string(' ${op_str} ')
600 g.gen_value(instr.operands[1])
601 g.sb.writeln(';')
602 }
603 }
604 .eq, .ne, .lt, .gt, .le, .ge {
605 if instr.operands.len >= 2 {
606 op_str := match instr.op {
607 .eq { '==' }
608 .ne { '!=' }
609 .lt { '<' }
610 .gt { '>' }
611 .le { '<=' }
612 .ge { '>=' }
613 else { '==' }
614 }
615
616 g.write_indent()
617 g.sb.write_string('bool ${val.name} = ')
618 g.gen_value(instr.operands[0])
619 g.sb.write_string(' ${op_str} ')
620 g.gen_value(instr.operands[1])
621 g.sb.writeln(';')
622 }
623 }
624 .and_ {
625 if instr.operands.len >= 2 {
626 g.write_indent()
627 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = ')
628 g.gen_value(instr.operands[0])
629 // Use logical && for bool, bitwise & for integers
630 is_bool := instr.typ < g.mod.type_store.types.len
631 && g.mod.type_store.types[instr.typ].kind == .int_t
632 && g.mod.type_store.types[instr.typ].width == 1
633 g.sb.write_string(if is_bool { ' && ' } else { ' & ' })
634 g.gen_value(instr.operands[1])
635 g.sb.writeln(';')
636 }
637 }
638 .or_ {
639 if instr.operands.len >= 2 {
640 g.write_indent()
641 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = ')
642 g.gen_value(instr.operands[0])
643 // Use logical || for bool, bitwise | for integers
644 is_bool := instr.typ < g.mod.type_store.types.len
645 && g.mod.type_store.types[instr.typ].kind == .int_t
646 && g.mod.type_store.types[instr.typ].width == 1
647 g.sb.write_string(if is_bool { ' || ' } else { ' | ' })
648 g.gen_value(instr.operands[1])
649 g.sb.writeln(';')
650 }
651 }
652 .xor {
653 if instr.operands.len >= 2 {
654 g.write_indent()
655 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = ')
656 g.gen_value(instr.operands[0])
657 g.sb.write_string(' ^ ')
658 g.gen_value(instr.operands[1])
659 g.sb.writeln(';')
660 }
661 }
662 .shl {
663 if instr.operands.len >= 2 {
664 g.write_indent()
665 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = ')
666 g.gen_value(instr.operands[0])
667 g.sb.write_string(' << ')
668 g.gen_value(instr.operands[1])
669 g.sb.writeln(';')
670 }
671 }
672 .ashr, .lshr {
673 if instr.operands.len >= 2 {
674 g.write_indent()
675 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = ')
676 g.gen_value(instr.operands[0])
677 g.sb.write_string(' >> ')
678 g.gen_value(instr.operands[1])
679 g.sb.writeln(';')
680 }
681 }
682 .sext, .zext, .trunc, .bitcast {
683 if instr.operands.len >= 1 {
684 g.write_indent()
685 target_tn := g.type_name(instr.typ)
686 src_type := g.mod.values[instr.operands[0]].typ
687 src_kind := if src_type < g.mod.type_store.types.len {
688 g.mod.type_store.types[src_type].kind
689 } else {
690 ssa.TypeKind.void_t
691 }
692 dst_kind := if instr.typ < g.mod.type_store.types.len {
693 g.mod.type_store.types[instr.typ].kind
694 } else {
695 ssa.TypeKind.void_t
696 }
697 // Pointer-to-struct cast: dereference through typed pointer
698 if src_kind == .ptr_t && dst_kind == .struct_t {
699 g.sb.write_string('${target_tn} ${val.name} = *(${target_tn}*)')
700 } else {
701 g.sb.write_string('${target_tn} ${val.name} = (${target_tn})')
702 }
703 g.gen_value(instr.operands[0])
704 g.sb.writeln(';')
705 }
706 }
707 .sitofp, .uitofp {
708 if instr.operands.len >= 1 {
709 g.write_indent()
710 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = (${g.type_name(instr.typ)})')
711 g.gen_value(instr.operands[0])
712 g.sb.writeln(';')
713 }
714 }
715 .fptosi, .fptoui {
716 if instr.operands.len >= 1 {
717 g.write_indent()
718 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = (${g.type_name(instr.typ)})')
719 g.gen_value(instr.operands[0])
720 g.sb.writeln(';')
721 }
722 }
723 .get_element_ptr {
724 // GEP -> struct field address: &base.field_name
725 if instr.operands.len >= 2 {
726 base_val := g.mod.values[instr.operands[0]]
727 idx_val := g.mod.values[instr.operands[1]]
728 field_idx := idx_val.name.int()
729 // Check if base is a struct pointer (alloca produces ptr to struct)
730 base_typ := g.mod.type_store.types[base_val.typ]
731 mut emitted := false
732 if base_typ.kind == .ptr_t {
733 elem_typ := g.mod.type_store.types[base_typ.elem_type]
734 if elem_typ.kind == .struct_t && field_idx < elem_typ.field_names.len {
735 g.write_indent()
736 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = &')
737 g.gen_value(instr.operands[0])
738 // If base is an alloca, access as lvalue directly
739 if base_val.kind == .instruction
740 && g.mod.instrs[base_val.index].op == .alloca {
741 g.sb.writeln('.${elem_typ.field_names[field_idx]};')
742 } else {
743 g.sb.writeln('->${elem_typ.field_names[field_idx]};')
744 }
745 emitted = true
746 }
747 }
748 if !emitted {
749 // Pointer arithmetic: base + index
750 g.write_indent()
751 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = ')
752 g.gen_value(instr.operands[0])
753 g.sb.write_string(' + ')
754 g.gen_value(instr.operands[1])
755 g.sb.writeln(';')
756 }
757 }
758 }
759 .extractvalue {
760 if instr.operands.len >= 2 {
761 g.write_indent()
762 g.sb.write_string('${g.type_name(instr.typ)} ${val.name} = ')
763 g.gen_value(instr.operands[0])
764 // Get field name from index constant
765 idx_val := g.mod.values[instr.operands[1]]
766 field_idx := idx_val.name.int()
767 base_val := g.mod.values[instr.operands[0]]
768 base_typ := g.mod.type_store.types[base_val.typ]
769 if base_typ.kind == .struct_t && field_idx < base_typ.field_names.len {
770 g.sb.writeln('.${base_typ.field_names[field_idx]};')
771 } else {
772 g.sb.writeln('; /* extractvalue idx=${field_idx} */')
773 }
774 }
775 }
776 .struct_init {
777 g.write_indent()
778 type_name := g.type_name(instr.typ)
779 g.sb.write_string('${type_name} ${val.name} = (${type_name}){')
780 typ := g.mod.type_store.types[instr.typ]
781 for fi, op in instr.operands {
782 if fi > 0 {
783 g.sb.write_string(', ')
784 }
785 if fi < typ.field_names.len {
786 g.sb.write_string('.${typ.field_names[fi]} = ')
787 }
788 g.gen_value(op)
789 }
790 g.sb.writeln('};')
791 }
792 .inline_string_init {
793 if instr.operands.len >= 3 {
794 g.write_indent()
795 g.sb.write_string('string ${val.name} = (string){.str = (i8*)')
796 g.gen_value(instr.operands[0])
797 g.sb.write_string(', .len = ')
798 g.gen_value(instr.operands[1])
799 g.sb.write_string(', .is_lit = ')
800 g.gen_value(instr.operands[2])
801 g.sb.writeln('};')
802 }
803 }
804 .unreachable {
805 g.write_indent()
806 g.sb.writeln('__builtin_unreachable();')
807 }
808 .go_call {
809 // go fn(args...) -> goroutines__goroutine_create(fn, args)
810 if instr.operands.len >= 1 {
811 fn_ref := instr.operands[0]
812 fn_val := g.mod.values[fn_ref]
813 fn_name := sanitize_c_ident(fn_val.name)
814 arg_count := instr.operands.len - 1
815
816 if arg_count == 0 {
817 // No arguments: pass function directly with NULL arg
818 g.write_indent()
819 g.sb.writeln('goroutines__goroutine_create((void*)(void(*)())${fn_name}, NULL, 0);')
820 } else {
821 wrapper_id := '${fn_name}_${val.name}'
822
823 g.write_indent()
824 g.sb.writeln('{')
825 g.indent++
826
827 // Declare args struct on heap
828 g.write_indent()
829 g.sb.write_string('struct _go_args_${wrapper_id} { ')
830 for ai in 0 .. arg_count {
831 arg_type := g.type_name(g.mod.values[instr.operands[ai + 1]].typ)
832 g.sb.write_string('${arg_type} a${ai}; ')
833 }
834 g.sb.writeln('};')
835
836 g.write_indent()
837 g.sb.writeln('struct _go_args_${wrapper_id}* _args = (struct _go_args_${wrapper_id}*)malloc(sizeof(struct _go_args_${wrapper_id}));')
838
839 // Pack arguments
840 for ai in 0 .. arg_count {
841 g.write_indent()
842 g.sb.write_string('_args->a${ai} = ')
843 g.gen_value(instr.operands[ai + 1])
844 g.sb.writeln(';')
845 }
846
847 // Call goroutine_create with the unpacking wrapper
848 g.write_indent()
849 g.sb.writeln('goroutines__goroutine_create((void*)(void(*)())_go_wrapper_${wrapper_id}, _args, sizeof(struct _go_args_${wrapper_id}));')
850
851 g.indent--
852 g.write_indent()
853 g.sb.writeln('}')
854 }
855 }
856 }
857 .spawn_call {
858 // spawn fn(args...) -> launch OS thread via pthread_create
859 if instr.operands.len >= 1 {
860 fn_ref := instr.operands[0]
861 fn_val := g.mod.values[fn_ref]
862 fn_name := sanitize_c_ident(fn_val.name)
863 arg_count := instr.operands.len - 1
864
865 g.write_indent()
866 g.sb.writeln('{')
867 g.indent++
868
869 g.write_indent()
870 g.sb.writeln('pthread_t _spawn_thread;')
871
872 if arg_count == 0 {
873 g.write_indent()
874 g.sb.writeln('pthread_create(&_spawn_thread, NULL, (void*(*)(void*))${fn_name}, NULL);')
875 } else {
876 wrapper_id := '${fn_name}_${val.name}'
877
878 g.write_indent()
879 g.sb.write_string('struct _spawn_args_${wrapper_id} { ')
880 for ai in 0 .. arg_count {
881 arg_type := g.type_name(g.mod.values[instr.operands[ai + 1]].typ)
882 g.sb.write_string('${arg_type} a${ai}; ')
883 }
884 g.sb.writeln('};')
885
886 g.write_indent()
887 g.sb.writeln('struct _spawn_args_${wrapper_id}* _args = (struct _spawn_args_${wrapper_id}*)malloc(sizeof(struct _spawn_args_${wrapper_id}));')
888
889 for ai in 0 .. arg_count {
890 g.write_indent()
891 g.sb.write_string('_args->a${ai} = ')
892 g.gen_value(instr.operands[ai + 1])
893 g.sb.writeln(';')
894 }
895
896 g.write_indent()
897 g.sb.writeln('pthread_create(&_spawn_thread, NULL, _spawn_wrapper_${wrapper_id}, _args);')
898 }
899
900 g.write_indent()
901 g.sb.writeln('pthread_detach(_spawn_thread);')
902
903 g.indent--
904 g.write_indent()
905 g.sb.writeln('}')
906 }
907 }
908 else {
909 // Other ops: emit as comment
910 g.write_indent()
911 g.sb.writeln('/* TODO: ${instr.op} */')
912 }
913 }
914 }
915 }
916
917 g.sb.writeln('}')
918 g.sb.writeln('')
919}
920
921fn (mut g Gen) gen_value(id ssa.ValueID) {
922 if id == 0 {
923 g.sb.write_string('0')
924 return
925 }
926 val := g.mod.values[id]
927 match val.kind {
928 .constant {
929 g.sb.write_string(val.name)
930 }
931 .argument {
932 g.sb.write_string(sanitize_c_ident(val.name))
933 }
934 .global {
935 g.sb.write_string(sanitize_c_ident(val.name))
936 }
937 .instruction {
938 g.sb.write_string(val.name)
939 }
940 .string_literal {
941 // Emit as V string struct literal
942 escaped := escape_c_string(val.name)
943 g.sb.write_string('(string){.str = (i8*)"${escaped}", .len = ${val.name.len}, .is_lit = 1}')
944 }
945 .c_string_literal {
946 // Emit as raw C string (char*) — no re-escaping needed,
947 // the value already contains C escape sequences from the source
948 g.sb.write_string('"${val.name}"')
949 }
950 .func_ref {
951 g.sb.write_string(g.fn_c_name(val.name))
952 }
953 .basic_block {
954 g.sb.write_string(val.name)
955 }
956 .unknown {
957 g.sb.write_string('0')
958 }
959 }
960}
961
962fn (g &Gen) type_name(id ssa.TypeID) string {
963 if id == 0 {
964 return 'void'
965 }
966 if id >= g.mod.type_store.types.len {
967 return 'void'
968 }
969 typ := g.mod.type_store.types[id]
970 match typ.kind {
971 .void_t {
972 return 'void'
973 }
974 .int_t {
975 return match typ.width {
976 1 { 'bool' }
977 8 { 'i8' }
978 16 { 'i16' }
979 32 { 'i32' }
980 64 { 'i64' }
981 else { 'i64' }
982 }
983 }
984 .float_t {
985 return if typ.width == 32 { 'f32' } else { 'f64' }
986 }
987 .ptr_t {
988 base := g.type_name(typ.elem_type)
989 return '${base}*'
990 }
991 .struct_t {
992 if typ.field_names.len > 0 {
993 // Only use struct name if it was registered
994 if _ := g.mod.c_struct_names[id] {
995 return g.struct_name(id)
996 }
997 }
998 return 'void*'
999 }
1000 .array_t {
1001 return 'void*'
1002 }
1003 .func_t {
1004 return 'void*'
1005 }
1006 .label_t {
1007 return 'void*'
1008 }
1009 .metadata_t {
1010 return 'void*'
1011 }
1012 }
1013}
1014
1015fn (mut g Gen) write_indent() {
1016 for _ in 0 .. g.indent {
1017 g.sb.write_string('\t')
1018 }
1019}
1020
1021fn sanitize_c_ident(name string) string {
1022 if name.len == 0 {
1023 return '_empty'
1024 }
1025 // C interop: strip C__ prefix
1026 if name.starts_with('C__') {
1027 return name[3..]
1028 }
1029 mut s := name.replace('.', '__')
1030 s = s.replace('-', '_')
1031 // Replace operator symbols that can't appear in C identifiers
1032 s = s.replace('==', '_eq')
1033 s = s.replace('!=', '_ne')
1034 s = s.replace('<=', '_le')
1035 s = s.replace('>=', '_ge')
1036 s = s.replace('<<', '_shl')
1037 s = s.replace('>>', '_shr')
1038 s = s.replace('<', '_lt')
1039 s = s.replace('>', '_gt')
1040 s = s.replace('+', '_plus')
1041 s = s.replace('*', '_mul')
1042 s = s.replace('/', '_div')
1043 s = s.replace('%', '_mod')
1044 // Reserved C keywords
1045 if s in ['auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double', 'else',
1046 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long', 'register',
1047 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch', 'typedef',
1048 'union', 'unsigned', 'void', 'volatile', 'while', 'unix', 'linux', 'error'] {
1049 return 'v__${s}'
1050 }
1051 return s
1052}
1053
1054fn escape_c_string(s string) string {
1055 mut sb := strings.new_builder(s.len + 8)
1056 for ch in s {
1057 match ch {
1058 `"` { sb.write_string('\\"') }
1059 `\\` { sb.write_string('\\\\') }
1060 `\n` { sb.write_string('\\n') }
1061 `\r` { sb.write_string('\\r') }
1062 `\t` { sb.write_string('\\t') }
1063 0 { sb.write_string('\\0') }
1064 else { sb.write_u8(ch) }
1065 }
1066 }
1067 return sb.str()
1068}
1069
1070fn (mut g Gen) gen_wyhash() {
1071 g.sb.writeln('#ifndef wyhash_final_version_4_2')
1072 g.sb.writeln('#define wyhash_final_version_4_2')
1073 g.sb.writeln('#define WYHASH_CONDOM 1')
1074 g.sb.writeln('#define WYHASH_32BIT_MUM 0')
1075 g.sb.writeln('#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)')
1076 g.sb.writeln(' #define _likely_(x) __builtin_expect(x,1)')
1077 g.sb.writeln(' #define _unlikely_(x) __builtin_expect(x,0)')
1078 g.sb.writeln('#else')
1079 g.sb.writeln(' #define _likely_(x) (x)')
1080 g.sb.writeln(' #define _unlikely_(x) (x)')
1081 g.sb.writeln('#endif')
1082 g.sb.writeln('static inline uint64_t _wyrot(uint64_t x) { return (x>>32)|(x<<32); }')
1083 g.sb.writeln('static inline void _wymum(uint64_t *A, uint64_t *B){')
1084 g.sb.writeln('#if defined(__SIZEOF_INT128__)')
1085 g.sb.writeln(' __uint128_t r=*A; r*=*B; *A=(uint64_t)r; *B=(uint64_t)(r>>64);')
1086 g.sb.writeln('#elif defined(_MSC_VER) && defined(_M_X64)')
1087 g.sb.writeln(' *A=_umul128(*A,*B,B);')
1088 g.sb.writeln('#else')
1089 g.sb.writeln(' uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo;')
1090 g.sb.writeln(' uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t<rl;')
1091 g.sb.writeln(' lo=t+(rm1<<32); c+=lo<t; hi=rh+(rm0>>32)+(rm1>>32)+c;')
1092 g.sb.writeln(' *A=lo; *B=hi;')
1093 g.sb.writeln('#endif')
1094 g.sb.writeln('}')
1095 g.sb.writeln('static inline uint64_t _wymix(uint64_t A, uint64_t B){ _wymum(&A,&B); return A^B; }')
1096 g.sb.writeln('#ifndef WYHASH_LITTLE_ENDIAN')
1097 g.sb.writeln(' #ifdef TARGET_ORDER_IS_LITTLE')
1098 g.sb.writeln(' #define WYHASH_LITTLE_ENDIAN 1')
1099 g.sb.writeln(' #else')
1100 g.sb.writeln(' #define WYHASH_LITTLE_ENDIAN 0')
1101 g.sb.writeln(' #endif')
1102 g.sb.writeln('#endif')
1103 g.sb.writeln('#if (WYHASH_LITTLE_ENDIAN)')
1104 g.sb.writeln(' static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return v;}')
1105 g.sb.writeln(' static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return v;}')
1106 g.sb.writeln('#elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)')
1107 g.sb.writeln(' static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return __builtin_bswap64(v);}')
1108 g.sb.writeln(' static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return __builtin_bswap32(v);}')
1109 g.sb.writeln('#else')
1110 g.sb.writeln(' static inline uint64_t _wyr8(const uint8_t *p) {')
1111 g.sb.writeln(' uint64_t v; memcpy(&v, p, 8);')
1112 g.sb.writeln(' return (((v >> 56) & 0xff)| ((v >> 40) & 0xff00)| ((v >> 24) & 0xff0000)| ((v >> 8) & 0xff000000)| ((v << 8) & 0xff00000000)| ((v << 24) & 0xff0000000000)| ((v << 40) & 0xff000000000000)| ((v << 56) & 0xff00000000000000));')
1113 g.sb.writeln(' }')
1114 g.sb.writeln(' static inline uint64_t _wyr4(const uint8_t *p) {')
1115 g.sb.writeln(' uint32_t v; memcpy(&v, p, 4);')
1116 g.sb.writeln(' return (((v >> 24) & 0xff)| ((v >> 8) & 0xff00)| ((v << 8) & 0xff0000)| ((v << 24) & 0xff000000));')
1117 g.sb.writeln(' }')
1118 g.sb.writeln('#endif')
1119 g.sb.writeln('static inline uint64_t _wyr3(const uint8_t *p, size_t k) { return (((uint64_t)p[0])<<16)|(((uint64_t)p[k>>1])<<8)|p[k-1];}')
1120 g.sb.writeln('static inline uint64_t wyhash(const void *key, size_t len, uint64_t seed, const uint64_t *secret){')
1121 g.sb.writeln(' const uint8_t *p=(const uint8_t *)key; seed^=_wymix(seed^secret[0]^len,secret[1]); uint64_t a, b;')
1122 g.sb.writeln(' if(_likely_(len<=16)){')
1123 g.sb.writeln(' if(_likely_(len>=4)){ a=(_wyr4(p)<<32)|_wyr4(p+((len>>3)<<2)); b=(_wyr4(p+len-4)<<32)|_wyr4(p+len-4-((len>>3)<<2)); }')
1124 g.sb.writeln(' else if(_likely_(len>0)){ a=_wyr3(p,len); b=0; }')
1125 g.sb.writeln(' else a=b=0;')
1126 g.sb.writeln(' } else {')
1127 g.sb.writeln(' size_t i=len;')
1128 g.sb.writeln(' if(_unlikely_(i>=48)){')
1129 g.sb.writeln(' uint64_t see1=seed, see2=seed;')
1130 g.sb.writeln(' do{')
1131 g.sb.writeln(' seed=_wymix(_wyr8(p)^secret[1],_wyr8(p+8)^seed);')
1132 g.sb.writeln(' see1=_wymix(_wyr8(p+16)^secret[2],_wyr8(p+24)^see1);')
1133 g.sb.writeln(' see2=_wymix(_wyr8(p+32)^secret[3],_wyr8(p+40)^see2);')
1134 g.sb.writeln(' p+=48; i-=48;')
1135 g.sb.writeln(' }while(_likely_(i>=48));')
1136 g.sb.writeln(' seed^=see1^see2;')
1137 g.sb.writeln(' }')
1138 g.sb.writeln(' while(_unlikely_(i>16)){ seed=_wymix(_wyr8(p)^secret[1],_wyr8(p+8)^seed); i-=16; p+=16; }')
1139 g.sb.writeln(' a=_wyr8(p+i-16); b=_wyr8(p+i-8);')
1140 g.sb.writeln(' }')
1141 g.sb.writeln(' a^=secret[1]; b^=seed; _wymum(&a,&b);')
1142 g.sb.writeln(' return _wymix(a^secret[0]^len,b^secret[1]);')
1143 g.sb.writeln('}')
1144 g.sb.writeln('static const uint64_t _wyp[4] = {0x2d358dccaa6c78a5ull, 0x8bb84b93962eacc9ull, 0x4b33a62ed433d4a3ull, 0x4d5a2da51de1aa47ull};')
1145 g.sb.writeln('static inline uint64_t wyhash64(uint64_t A, uint64_t B){ A^=0x2d358dccaa6c78a5ull; B^=0x8bb84b93962eacc9ull; _wymum(&A,&B); return _wymix(A^0x2d358dccaa6c78a5ull,B^0x8bb84b93962eacc9ull);}')
1146 g.sb.writeln('#endif')
1147 g.sb.writeln('')
1148}
1149