vxx2 / vlib / v3 / ssa / builder.v
10818 lines · 10056 sloc · 385.99 KB · 6b7ec839d77b718eb63d27db2cbb1792d06d9a79
Raw
1module ssa
2
3import v3.flat
4import v3.types
5
6const arm64_force_external_syms = ['_malloc', '_free', '_calloc', '_realloc', '_exit', '_abort',
7 '_memcpy', '_memmove', '_memset', '_memcmp', '___stdoutp', '___stderrp', '_puts', '_printf',
8 '_write', '_read', '_open', '_close', '_fwrite', '_fflush', '_fopen', '_fclose', '_putchar',
9 '_sprintf', '_snprintf', '_fprintf', '_sscanf', '_mmap', '_munmap', '_getcwd', '_access',
10 '_readlink', '_getenv', '_strlen', '_opendir', '_readdir', '_closedir', '_mkdir', '_rmdir',
11 '_unlink', '_rename', '_remove', '_stat', '_lstat', '_fstat', '_chmod', '_chdir', '_realpath',
12 '_symlink', '_link', '_getpid', '_getuid', '_geteuid', '_fork', '_execve', '_execvp', '_waitpid',
13 '_kill', '_system', '_posix_spawn', '_signal', '_atexit', '_fgets', '_fputs', '_fread', '_fseek',
14 '_ftell', '_rewind', '_fileno', '_popen', '_pclose', '_dup', '_dup2', '_pipe', '_isatty',
15 '_freopen', '_dprintf', '_getc', '_strdup', '_strcmp', '_strncmp', '_strchr', '_strrchr',
16 '_strerror', '_strncasecmp', '_strcasecmp', '_atoi', '_atof', '_qsort', '_time', '_localtime_r',
17 '_gmtime_r', '_mktime', '_gettimeofday', '_clock', '_clock_gettime_nsec_np',
18 '_mach_absolute_time', '_mach_timebase_info', '_nanosleep', '_sleep', '_usleep', '_strftime',
19 '_task_info', '_mach_task_self_', '_rand', '_srand', '_isdigit', '_isspace', '_tolower',
20 '_toupper', '_setenv', '_unsetenv', '_sysconf', '_uname', '_gethostname', '_pthread_mutex_init',
21 '_pthread_mutex_lock', '_pthread_mutex_unlock', '_pthread_mutex_destroy', '_pthread_self',
22 '_pthread_create', '_pthread_join', '_pthread_attr_init', '_pthread_attr_setstacksize',
23 '_pthread_attr_destroy', '_arc4random_buf', '_proc_pidpath', '_backtrace', '_backtrace_symbols',
24 '_backtrace_symbols_fd', '_dispatch_semaphore_create', '_dispatch_semaphore_signal',
25 '_dispatch_semaphore_wait', '_dispatch_time', '_dispatch_release', '_setvbuf', '_setbuf',
26 '_memchr', '_getlogin_r', '_getppid', '_getgid', '_getegid', '_ftruncate', '_mkstemp', '_statvfs',
27 '_chown', '_sigaction', '_sigemptyset', '_sigaddset', '_sigprocmask', '_select', '_kqueue',
28 '_abs', '_tcgetattr', '_tcsetattr', '_ioctl', '_getchar', '_getline', '_fdopen', '_feof',
29 '_ferror', '_setpgid', '_ptrace', '_wait', '_timegm', '_clock_gettime', '_aligned_alloc',
30 '_utime', '_getlogin', '_environ', '___error', '___stdinp', '__dyld_get_image_name',
31 '__dyld_get_image_header', '_cos', '_sin', '_tan', '_acos', '_asin', '_atan', '_atan2', '_cosh',
32 '_sinh', '_tanh', '_acosh', '_asinh', '_atanh', '_exp', '_exp2', '_log', '_log2', '_log10',
33 '_pow', '_sqrt', '_cbrt', '_ceil', '_floor', '_round', '_trunc', '_fmod', '_remainder', '_fabs',
34 '_copysign', '_fmax', '_fmin', '_hypot', '_ldexp', '_frexp', '_modf', '_scalbn', '_ilogb',
35 '_logb', '_erf', '_erfc', '_lgamma', '_tgamma', '_j0', '_j1', '_jn', '_y0', '_y1', '_yn',
36 '_mprotect', '_sys_icache_invalidate', '_objc_msgSend', '_objc_getClass', '_sel_registerName',
37 '_objc_alloc_init', '_objc_autoreleasePoolPush', '_objc_autoreleasePoolPop',
38 '_MTLCreateSystemDefaultDevice', '_dlopen', '_dlsym']
39
40const bench_runtime_stub_names = ['current_rss_kb', 'macos_peak_rss_kb', 'linux_rss_kb',
41 'bench.current_rss_kb', 'bench.macos_peak_rss_kb', 'bench.linux_rss_kb',
42 'v3.bench.current_rss_kb', 'v3.bench.macos_peak_rss_kb', 'v3.bench.linux_rss_kb']
43
44// Builder stores state for SSA construction.
45pub struct Builder {
46mut:
47 m &Module = unsafe { nil }
48 a &flat.FlatAst = unsafe { nil }
49 tc &types.TypeChecker = unsafe { nil }
50 used_fns map[string]bool
51 cur_module string
52 cur_func int
53 cur_func_ret_type string
54 cur_block BlockID
55 vars map[string]ValueID
56 var_type_names map[string]string
57 i64_type TypeID
58 i32_type TypeID
59 i8_type TypeID
60 i1_type TypeID
61 u64_type TypeID
62 u32_type TypeID
63 u16_type TypeID
64 u8_type TypeID
65 f32_type TypeID
66 f64_type TypeID
67 void_type TypeID
68 str_type TypeID
69 array_type TypeID
70 map_type TypeID
71 map_state_type TypeID
72 fn_types map[string]TypeID
73 fn_ids map[string]int
74 const_exprs map[string]flat.NodeId
75 struct_types map[string]TypeID
76 struct_field_types map[string]string
77 option_types map[string]TypeID
78 sum_type_variants map[string][]string
79 sum_type_canonical map[string]string
80 enum_types map[string]bool
81 flag_enum_types map[string]bool
82 enum_values map[string]int
83 enum_member_values map[string]int
84 enum_member_dupes map[string]bool
85 c_fn_ids map[string]int
86 c_fn_types map[string]TypeID
87 label_blocks map[string]BlockID
88 defer_body_ids []flat.NodeId
89 break_targets []BlockID
90 continue_targets []BlockID
91 top_level_main bool
92 // --- Build-control machinery (ported from v2 build_all) ---
93 // When set, build_functions only materializes this one function body (hot reload).
94 hot_fn string
95 // When set, build_functions registers signatures only and marks them prototypes.
96 skip_fn_bodies bool
97 // When non-empty, all functions declared in these modules are skipped (dead
98 // code elimination for unused backends/modules).
99 skip_modules map[string]bool
100 // SSA-level type alias name -> resolved base TypeID. Supplements the checker's
101 // alias map (used when building without a checker).
102 type_aliases map[string]TypeID
103}
104
105// VarBinding represents var binding data used by ssa.
106struct VarBinding {
107 exists bool
108 addr ValueID
109 typ string
110}
111
112// IfGuardState stores if guard state state used by ssa.
113struct IfGuardState {
114 active bool
115 name string
116 old VarBinding
117 slot ValueID
118 value_ptr ValueID
119 typ TypeID
120 typ_name string
121}
122
123// BuildOptions controls which functions a build materializes. The zero value
124// reproduces the default whole-program build.
125pub struct BuildOptions {
126pub:
127 hot_fn string // build only this function's body (hot reload)
128 skip_fn_bodies bool // register signatures only, mark them prototypes
129 skip_modules []string // skip all functions declared in these modules
130}
131
132// build supports build handling for ssa.
133pub fn build(a_ &flat.FlatAst) &Module {
134 return build_with_used(a_, map[string]bool{}, unsafe { nil })
135}
136
137// build_with_used builds with used data for ssa.
138pub fn build_with_used(a_ &flat.FlatAst, used_fns map[string]bool, tc &types.TypeChecker) &Module {
139 return build_with_options(a_, used_fns, tc, BuildOptions{})
140}
141
142// build_with_options builds with options data for ssa.
143pub fn build_with_options(a_ &flat.FlatAst, used_fns map[string]bool, tc &types.TypeChecker, opts BuildOptions) &Module {
144 mut b := Builder{
145 m: Module.new()
146 a: unsafe { a_ }
147 tc: unsafe { tc }
148 used_fns: used_fns
149 vars: map[string]ValueID{}
150 var_type_names: map[string]string{}
151 fn_types: map[string]TypeID{}
152 fn_ids: map[string]int{}
153 const_exprs: map[string]flat.NodeId{}
154 struct_types: map[string]TypeID{}
155 struct_field_types: map[string]string{}
156 option_types: map[string]TypeID{}
157 sum_type_variants: map[string][]string{}
158 sum_type_canonical: map[string]string{}
159 enum_types: map[string]bool{}
160 flag_enum_types: map[string]bool{}
161 enum_values: map[string]int{}
162 enum_member_values: map[string]int{}
163 enum_member_dupes: map[string]bool{}
164 c_fn_ids: map[string]int{}
165 c_fn_types: map[string]TypeID{}
166 label_blocks: map[string]BlockID{}
167 defer_body_ids: []flat.NodeId{}
168 break_targets: []BlockID{}
169 continue_targets: []BlockID{}
170 skip_modules: map[string]bool{}
171 type_aliases: map[string]TypeID{}
172 }
173 b.void_type = TypeID(0)
174 b.i64_type = b.m.type_store.get_int(64)
175 b.i32_type = b.m.type_store.get_int(32)
176 b.i8_type = b.m.type_store.get_int(8)
177 b.i1_type = b.m.type_store.get_int(1)
178 b.u64_type = b.m.type_store.get_uint(64)
179 b.u32_type = b.m.type_store.get_uint(32)
180 b.u16_type = b.m.type_store.get_uint(16)
181 b.u8_type = b.m.type_store.get_uint(8)
182 b.f32_type = b.m.type_store.get_float(32)
183 b.f64_type = b.m.type_store.get_float(64)
184 mut str_fields := []TypeID{}
185 str_fields << b.m.type_store.get_ptr(b.i8_type)
186 str_fields << b.i32_type
187 // `is_lit` occupies the trailing 4-byte slot (offset 12) that the struct is padded
188 // to anyway. It must be a named field so `s.is_lit` (e.g. in `string.free`) resolves
189 // to offset 12 instead of defaulting to offset 0 (which would read `str`).
190 str_fields << b.i32_type
191 mut str_field_names := []string{}
192 str_field_names << 'str'
193 str_field_names << 'len'
194 str_field_names << 'is_lit'
195 b.str_type = b.m.type_store.register(Type{
196 kind: .struct_t
197 fields: str_fields
198 field_names: str_field_names
199 })
200 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
201 mut array_fields := []TypeID{}
202 array_fields << ptr_i8
203 array_fields << b.i32_type
204 array_fields << b.i32_type
205 array_fields << b.i32_type
206 array_fields << b.i32_type
207 array_fields << b.i32_type
208 mut array_field_names := []string{}
209 array_field_names << 'data'
210 array_field_names << 'offset'
211 array_field_names << 'len'
212 array_field_names << 'cap'
213 array_field_names << 'flags'
214 array_field_names << 'element_size'
215 b.array_type = b.m.type_store.register(Type{
216 kind: .struct_t
217 fields: array_fields
218 field_names: array_field_names
219 })
220 mut map_state_fields := []TypeID{}
221 map_state_fields << ptr_i8
222 map_state_fields << ptr_i8
223 map_state_fields << b.i64_type
224 map_state_fields << b.i64_type
225 map_state_fields << b.i64_type
226 map_state_fields << b.i64_type
227 mut map_state_field_names := []string{}
228 map_state_field_names << 'keys'
229 map_state_field_names << 'vals'
230 map_state_field_names << 'cap'
231 map_state_field_names << 'len'
232 map_state_field_names << 'key_size'
233 map_state_field_names << 'val_size'
234 b.map_state_type = b.m.type_store.register(Type{
235 kind: .struct_t
236 fields: map_state_fields
237 field_names: map_state_field_names
238 })
239 mut map_fields := []TypeID{}
240 map_fields << b.m.type_store.get_ptr(b.map_state_type)
241 mut map_field_names := []string{}
242 map_field_names << 'state'
243 b.map_type = b.m.type_store.register(Type{
244 kind: .struct_t
245 fields: map_fields
246 field_names: map_field_names
247 })
248 b.hot_fn = opts.hot_fn
249 b.skip_fn_bodies = opts.skip_fn_bodies
250 for mname in opts.skip_modules {
251 b.skip_modules[mname] = true
252 }
253 b.m.name = b.main_module_name()
254 b.register_type_aliases()
255 b.register_types()
256 b.register_consts()
257 b.register_globals()
258 b.register_functions()
259 b.build_functions()
260 return b.m
261}
262
263// register_types updates register types state for ssa.
264fn (mut b Builder) register_types() {
265 mut cur_module := ''
266 for node in b.a.nodes {
267 if node.kind == .module_decl {
268 cur_module = node.value
269 continue
270 }
271 if node.kind == .struct_decl {
272 typ_id := b.m.type_store.register(Type{
273 kind: .struct_t
274 })
275 b.register_struct_type_name(node.value, cur_module, typ_id)
276 } else if node.kind == .type_decl && node.children_count > 0 {
277 typ_id := b.m.type_store.register(Type{
278 kind: .struct_t
279 })
280 b.register_sum_type_name(node.value, cur_module, typ_id)
281 } else if node.kind == .enum_decl {
282 b.register_enum_values(node, cur_module)
283 }
284 }
285
286 cur_module = ''
287 for node in b.a.nodes {
288 if node.kind == .module_decl {
289 cur_module = node.value
290 continue
291 }
292 if node.kind == .struct_decl {
293 mut field_types := []TypeID{}
294 mut field_names := []string{}
295 for i in 0 .. node.children_count {
296 f := b.a.child_node(&node, i)
297 field_type := b.resolve_type_in_module(f.typ, cur_module)
298 field_type_name := qualify_type_ref_name(f.typ, cur_module)
299 field_types << field_type
300 field_names << f.value
301 b.struct_field_types[node.value + '.' + f.value] = field_type_name
302 short_name := node.value.all_after('.')
303 b.struct_field_types[short_name + '.' + f.value] = field_type_name
304 if cur_module.len > 0 && cur_module != 'main' && cur_module != 'builtin' {
305 b.struct_field_types[cur_module + '.' + short_name + '.' + f.value] = field_type_name
306 }
307 }
308 typ_id := b.struct_type_id_for_decl(node.value, cur_module)
309 b.m.type_store.types[typ_id] = Type{
310 kind: .struct_t
311 fields: field_types
312 field_names: field_names
313 }
314 } else if node.kind == .type_decl && node.children_count > 0 {
315 sum_name := qualify_type_name(node.value, cur_module)
316 variants := b.sum_type_variants[sum_name] or { []string{} }
317 mut field_types := []TypeID{}
318 mut field_names := []string{}
319 field_types << b.i32_type
320 field_names << 'typ'
321 for variant in variants {
322 mut variant_type := b.resolve_type_in_module(variant, cur_module)
323 if variant_type > 0 && variant_type < b.m.type_store.types.len
324 && b.m.type_store.types[variant_type].kind == .struct_t {
325 variant_type = b.m.type_store.get_ptr(variant_type)
326 }
327 field_types << variant_type
328 field_names << sum_variant_field_name(variant)
329 }
330 typ_id := b.struct_type_id_for_decl(node.value, cur_module)
331 b.m.type_store.types[typ_id] = Type{
332 kind: .struct_t
333 fields: field_types
334 field_names: field_names
335 }
336 }
337 }
338
339 cur_module = ''
340 for node in b.a.nodes {
341 if node.kind == .module_decl {
342 cur_module = node.value
343 continue
344 }
345 if node.kind != .type_decl || node.children_count == 0 {
346 continue
347 }
348 sum_typ_id := b.struct_type_id_for_decl(node.value, cur_module)
349 mut field_types := []TypeID{}
350 mut field_names := []string{}
351 field_types << b.i64_type
352 field_names << 'typ'
353 b.register_sum_field_type(node.value, cur_module, 'typ', 'int')
354 variants := b.sum_type_variants_for_decl(node, cur_module)
355 for variant in variants {
356 field_name := sum_variant_field_name(variant)
357 mut field_type := b.resolve_type_in_module(variant, cur_module)
358 if field_type > 0 && field_type < b.m.type_store.types.len
359 && b.m.type_store.types[field_type].kind == .struct_t {
360 field_type = b.m.type_store.get_ptr(field_type)
361 }
362 field_types << field_type
363 field_names << field_name
364 b.register_sum_field_type(node.value, cur_module, field_name, variant)
365 }
366 b.m.type_store.types[sum_typ_id] = Type{
367 kind: .struct_t
368 fields: field_types
369 field_names: field_names
370 }
371 }
372 b.register_multi_return_types()
373}
374
375// register_multi_return_types updates register multi return types state for ssa.
376fn (mut b Builder) register_multi_return_types() {
377 if b.tc == unsafe { nil } {
378 return
379 }
380 for _, ret in b.tc.fn_ret_types {
381 if ret is types.MultiReturn {
382 b.register_multi_return_type(ret)
383 }
384 }
385}
386
387// register_multi_return_type updates register multi return type state for ssa.
388fn (mut b Builder) register_multi_return_type(ret types.MultiReturn) TypeID {
389 name := b.multi_return_c_type(ret)
390 if typ := b.struct_types[name] {
391 return typ
392 }
393 typ_id := b.m.type_store.register(Type{
394 kind: .struct_t
395 })
396 b.struct_types[name] = typ_id
397 mut field_types := []TypeID{}
398 mut field_names := []string{}
399 for i, field_type in ret.types {
400 field_types << b.ssa_type_from_checker_type(field_type)
401 field_name := 'arg${i}'
402 field_names << field_name
403 b.struct_field_types[name + '.' + field_name] = field_type.name()
404 }
405 b.m.type_store.types[typ_id] = Type{
406 kind: .struct_t
407 fields: field_types
408 field_names: field_names
409 }
410 return typ_id
411}
412
413// multi_return_c_type supports multi return c type handling for Builder.
414fn (b &Builder) multi_return_c_type(ret types.MultiReturn) string {
415 mut parts := []string{}
416 for field_type in ret.types {
417 parts << b.tc.c_type(field_type)
418 }
419 return 'multi_return_${parts.join('_')}'
420}
421
422// primitive_type_name supports primitive type name handling for ssa.
423fn primitive_type_name(typ types.Primitive) string {
424 if typ.props.has(.boolean) {
425 return 'bool'
426 }
427 if typ.props.has(.integer) {
428 if typ.props.has(.unsigned) {
429 return match typ.size {
430 8 { 'u8' }
431 16 { 'u16' }
432 32 { 'u32' }
433 64 { 'u64' }
434 else { 'u${typ.size}' }
435 }
436 }
437 return match typ.size {
438 0 { 'int' }
439 8 { 'i8' }
440 16 { 'i16' }
441 32 { 'i32' }
442 64 { 'i64' }
443 else { 'i${typ.size}' }
444 }
445 }
446 if typ.props.has(.float) {
447 return match typ.size {
448 32 { 'f32' }
449 else { 'f64' }
450 }
451 }
452 return 'int'
453}
454
455// ssa_type_from_checker_type converts ssa type from checker type data for ssa.
456fn (mut b Builder) ssa_type_from_checker_type(typ types.Type) TypeID {
457 if typ is types.Void {
458 return b.void_type
459 }
460 if typ is types.String {
461 return b.str_type
462 }
463 if typ is types.Char {
464 return b.i8_type
465 }
466 if typ is types.Rune {
467 return b.i32_type
468 }
469 if typ is types.ISize || typ is types.USize {
470 return b.i64_type
471 }
472 if typ is types.Primitive {
473 return b.resolve_type(primitive_type_name(typ))
474 }
475 if typ is types.Array {
476 return b.array_type
477 }
478 if typ is types.ArrayFixed {
479 return b.m.type_store.get_ptr(b.ssa_type_from_checker_type(typ.elem_type))
480 }
481 if typ is types.Map {
482 return b.map_type
483 }
484 if typ is types.Pointer {
485 return b.m.type_store.get_ptr(b.ssa_type_from_checker_type(typ.base_type))
486 }
487 if typ is types.OptionType {
488 return b.option_type_id(typ.base_type.name())
489 }
490 if typ is types.ResultType {
491 return b.option_type_id(typ.base_type.name())
492 }
493 if typ is types.Enum {
494 return b.i32_type
495 }
496 if typ is types.MultiReturn {
497 return b.register_multi_return_type(typ)
498 }
499 if typ is types.Alias {
500 return b.ssa_type_from_checker_type(typ.base_type)
501 }
502 return b.resolve_type(typ.name())
503}
504
505// register_enum_values updates register enum values state for ssa.
506fn (mut b Builder) register_enum_values(node flat.Node, module_name string) {
507 short_name := node.value.all_after('.')
508 mut enum_names := []string{}
509 enum_names << node.value
510 if short_name != node.value {
511 enum_names << short_name
512 }
513 if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
514 enum_names << module_name + '.' + short_name
515 }
516 mut is_flag_enum := false
517 for enum_name in enum_names {
518 if b.checker_has_flag_enum(enum_name) {
519 is_flag_enum = true
520 break
521 }
522 }
523 for enum_name in enum_names {
524 b.enum_types[enum_name] = true
525 if is_flag_enum {
526 b.flag_enum_types[enum_name] = true
527 }
528 }
529 mut value := 0
530 for i in 0 .. node.children_count {
531 field := b.a.child_node(&node, i)
532 if field.kind != .enum_field {
533 continue
534 }
535 if field.children_count > 0 {
536 if explicit := b.enum_field_expr_value(b.a.child(field, 0)) {
537 value = explicit
538 }
539 }
540 for enum_name in enum_names {
541 enum_key := enum_name + '.' + field.value
542 b.enum_values[enum_key] = value
543 }
544 if existing := b.enum_member_values[field.value] {
545 if existing != value {
546 b.enum_member_dupes[field.value] = true
547 }
548 } else {
549 b.enum_member_values[field.value] = value
550 }
551 value++
552 }
553}
554
555// register_struct_type_name updates register struct type name state for ssa.
556fn (mut b Builder) register_struct_type_name(name string, module_name string, typ_id TypeID) {
557 b.struct_types[name] = typ_id
558 short_name := name.all_after('.')
559 if short_name !in b.struct_types {
560 b.struct_types[short_name] = typ_id
561 }
562 if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
563 qualified_name := module_name + '.' + short_name
564 b.struct_types[qualified_name] = typ_id
565 }
566}
567
568// register_sum_type_name updates register sum type name state for ssa.
569fn (mut b Builder) register_sum_type_name(name string, module_name string, typ_id TypeID) {
570 qualified_name := qualify_type_name(name, module_name)
571 b.register_struct_type_name(name, module_name, typ_id)
572 mut variants := []string{}
573 if b.tc != unsafe { nil } {
574 if tc_variants := b.tc.sum_types[qualified_name] {
575 variants = tc_variants.clone()
576 }
577 }
578 if variants.len == 0 {
579 for node in b.a.nodes {
580 if node.kind != .type_decl || node.value != name || node.children_count == 0 {
581 continue
582 }
583 for i in 0 .. node.children_count {
584 variant := b.a.child_node(&node, i)
585 variants << qualify_type_name(variant.value, module_name)
586 }
587 break
588 }
589 }
590 b.sum_type_variants[qualified_name] = variants
591 b.sum_type_canonical[qualified_name] = qualified_name
592 if name != qualified_name {
593 b.sum_type_variants[name] = variants
594 b.sum_type_canonical[name] = qualified_name
595 }
596 short_name := qualified_name.all_after('.')
597 b.sum_type_variants[short_name] = variants
598 b.sum_type_canonical[short_name] = qualified_name
599}
600
601// qualify_type_name supports qualify type name handling for ssa.
602fn qualify_type_name(name string, module_name string) string {
603 if name.contains('.') || module_name.len == 0 || module_name == 'main'
604 || module_name == 'builtin' {
605 return name
606 }
607 return '${module_name}.${name}'
608}
609
610// qualify_type_ref_name supports qualify type ref name handling for ssa.
611fn qualify_type_ref_name(name string, module_name string) string {
612 if name.len == 0 {
613 return name
614 }
615 if name.starts_with('&') {
616 return '&' + qualify_type_ref_name(name[1..], module_name)
617 }
618 if name.len > 1 && (name[0] == `?` || name[0] == `!`) {
619 return name[..1] + qualify_type_ref_name(name[1..], module_name)
620 }
621 if name.starts_with('[]') {
622 return '[]' + qualify_type_ref_name(name[2..], module_name)
623 }
624 if name.starts_with('[') {
625 idx := name.index_u8(`]`)
626 if idx > 0 && idx + 1 < name.len {
627 return name[..idx + 1] + qualify_type_ref_name(name[idx + 1..], module_name)
628 }
629 }
630 if name.contains('[') && name.ends_with(']') {
631 idx := name.index_u8(`[`)
632 if idx > 0 && idx < name.len - 1 {
633 return qualify_type_ref_name(name[..idx], module_name) + name[idx..]
634 }
635 }
636 if name.starts_with('map[') {
637 key_type, val_type := map_type_parts(name)
638 if key_type.len > 0 && val_type.len > 0 {
639 return 'map[' + qualify_type_ref_name(key_type, module_name) + ']' +
640 qualify_type_ref_name(val_type, module_name)
641 }
642 }
643 if type_ref_is_builtin(name) || name.contains('.') || module_name.len == 0
644 || module_name == 'main' || module_name == 'builtin' {
645 return name
646 }
647 return module_name + '.' + name
648}
649
650// type_ref_is_builtin returns type ref is builtin data for ssa.
651fn type_ref_is_builtin(name string) bool {
652 return name in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'byte', 'u16', 'u32', 'u64', 'f32',
653 'f64', 'bool', 'string', 'void', 'voidptr', 'rune', 'char', 'array', 'map']
654}
655
656// sum_type_variants_for_decl supports sum type variants for decl handling for Builder.
657fn (b &Builder) sum_type_variants_for_decl(node flat.Node, module_name string) []string {
658 mut variants := []string{}
659 for i in 0 .. node.children_count {
660 variant := b.a.child_node(&node, i).value
661 variants << qualify_type_name(variant, module_name)
662 }
663 return variants
664}
665
666// register_sum_field_type updates register sum field type state for ssa.
667fn (mut b Builder) register_sum_field_type(name string, module_name string, field_name string, field_type string) {
668 short_name := name.all_after('.')
669 b.struct_field_types[name + '.' + field_name] = field_type
670 b.struct_field_types[short_name + '.' + field_name] = field_type
671 if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
672 b.struct_field_types[module_name + '.' + short_name + '.' + field_name] = field_type
673 }
674}
675
676// struct_type_id_for_decl supports struct type id for decl handling for Builder.
677fn (b &Builder) struct_type_id_for_decl(name string, module_name string) TypeID {
678 short_name := name.all_after('.')
679 if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
680 qualified_name := module_name + '.' + short_name
681 if typ := b.struct_types[qualified_name] {
682 return typ
683 }
684 }
685 if typ := b.struct_types[name] {
686 return typ
687 }
688 if typ := b.struct_types[short_name] {
689 return typ
690 }
691 return TypeID(0)
692}
693
694// register_consts updates register consts state for ssa.
695fn (mut b Builder) register_consts() {
696 mut cur_module := ''
697 for node in b.a.nodes {
698 if node.kind == .module_decl {
699 cur_module = node.value
700 continue
701 }
702 if node.kind == .const_decl {
703 for i in 0 .. node.children_count {
704 field_id := b.a.child(&node, i)
705 if int(field_id) < 0 {
706 continue
707 }
708 field := b.a.nodes[int(field_id)]
709 if field.kind != .const_field || field.children_count == 0 {
710 continue
711 }
712 expr_id := b.a.child(&field, 0)
713 if cur_module.len > 0 && cur_module != 'main' {
714 b.const_exprs['${cur_module}.${field.value}'] = expr_id
715 } else {
716 b.const_exprs[field.value] = expr_id
717 }
718 }
719 }
720 }
721}
722
723// register_globals updates register globals state for ssa.
724fn (mut b Builder) register_globals() {
725 for node in b.a.nodes {
726 if node.kind == .global_decl {
727 for i in 0 .. node.children_count {
728 f := b.a.child_node(&node, i)
729 typ := b.resolve_type(f.typ)
730 b.vars[f.value] = b.m.add_global(f.value, typ)
731 }
732 }
733 }
734 b.ensure_runtime_global('g_main_argc', b.i64_type)
735 b.ensure_runtime_global('g_main_argv',
736 b.m.type_store.get_ptr(b.m.type_store.get_ptr(b.i8_type)))
737}
738
739// ensure_runtime_global supports ensure runtime global handling for Builder.
740fn (mut b Builder) ensure_runtime_global(name string, typ TypeID) {
741 if name in b.vars {
742 return
743 }
744 for v in b.m.values {
745 if v.kind == .global && v.name == name {
746 b.vars[name] = v.id
747 return
748 }
749 }
750 b.vars[name] = b.m.add_global(name, typ)
751}
752
753// register_functions updates register functions state for ssa.
754fn (mut b Builder) register_functions() {
755 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
756 mut p1 := []TypeID{}
757 mut p2 := []TypeID{}
758 mut p3 := []TypeID{}
759 // C library externs
760 p3 = []TypeID{}
761 p3 << b.i64_type
762 p3 << ptr_i8
763 p3 << b.i64_type
764 b.register_extern('write', b.i64_type, p3)
765 b.register_extern('read', b.i64_type, p3)
766 p3 = []TypeID{}
767 p3 << ptr_i8
768 p3 << b.i64_type
769 p3 << b.i64_type
770 b.register_extern('open', b.i64_type, p3)
771 p1 = []TypeID{}
772 p1 << b.i64_type
773 b.register_extern('putchar', b.i64_type, p1)
774 b.register_extern('getchar', b.i64_type, []TypeID{})
775 p1 = []TypeID{}
776 p1 << b.i64_type
777 b.register_extern('close', b.i64_type, p1)
778 p1 = []TypeID{}
779 p1 << ptr_i8
780 b.register_extern('puts', b.i64_type, p1)
781 p1 = []TypeID{}
782 p1 << ptr_i8
783 b.register_extern('strlen', b.i64_type, p1)
784 p1 = []TypeID{}
785 p1 << b.i64_type
786 b.register_extern('strerror', ptr_i8, p1)
787 p1 = []TypeID{}
788 p1 << ptr_i8
789 b.register_extern('feof', b.i64_type, p1)
790 p1 = []TypeID{}
791 p1 << ptr_i8
792 b.register_extern('ferror', b.i64_type, p1)
793 p1 = []TypeID{}
794 p1 << ptr_i8
795 b.register_extern('fclose', b.i64_type, p1)
796 p1 = []TypeID{}
797 p1 << ptr_i8
798 b.register_extern('pclose', b.i64_type, p1)
799 p1 = []TypeID{}
800 p1 << ptr_i8
801 b.register_extern('ftell', b.i64_type, p1)
802 p1 = []TypeID{}
803 p1 << ptr_i8
804 b.register_extern('rewind', b.void_type, p1)
805 p2 = []TypeID{}
806 p2 << ptr_i8
807 p2 << ptr_i8
808 b.register_extern('fputs', b.i64_type, p2)
809 b.register_extern('stat', b.i64_type, p2)
810 b.register_extern('lstat', b.i64_type, p2)
811 b.register_extern('statvfs', b.i64_type, p2)
812 b.register_extern('rename', b.i64_type, p2)
813 b.register_extern('strcmp', b.i64_type, p2)
814 p2 = []TypeID{}
815 p2 << ptr_i8
816 p2 << b.i64_type
817 b.register_extern('strchr', ptr_i8, p2)
818 b.register_extern('strrchr', ptr_i8, p2)
819 p2 = []TypeID{}
820 p2 << ptr_i8
821 p2 << ptr_i8
822 b.register_extern('fopen', ptr_i8, p2)
823 p2 = []TypeID{}
824 p2 << ptr_i8
825 p2 << ptr_i8
826 b.register_extern('popen', ptr_i8, p2)
827 mut p4 := []TypeID{}
828 p4 << ptr_i8
829 p4 << b.i64_type
830 p4 << b.i64_type
831 p4 << ptr_i8
832 b.register_extern('fread', b.i64_type, p4)
833 p4 = []TypeID{}
834 p4 << ptr_i8
835 p4 << b.i64_type
836 p4 << b.i64_type
837 p4 << ptr_i8
838 b.register_extern('fwrite', b.i64_type, p4)
839 p3 = []TypeID{}
840 p3 << ptr_i8
841 p3 << b.i64_type
842 p3 << b.i64_type
843 b.register_extern('fseek', b.i64_type, p3)
844 p1 = []TypeID{}
845 p1 << b.i64_type
846 b.register_extern('malloc', ptr_i8, p1)
847 p2 = []TypeID{}
848 p2 << b.i64_type
849 p2 << b.i64_type
850 b.register_extern('calloc', ptr_i8, p2)
851 p2 = []TypeID{}
852 p2 << ptr_i8
853 p2 << b.i64_type
854 b.register_extern('realloc', ptr_i8, p2)
855 p3 = []TypeID{}
856 p3 << ptr_i8
857 p3 << ptr_i8
858 p3 << b.i64_type
859 b.register_extern('memcpy', ptr_i8, p3)
860 p3 = []TypeID{}
861 p3 << ptr_i8
862 p3 << ptr_i8
863 p3 << b.i64_type
864 b.register_extern('memmove', ptr_i8, p3)
865 p3 = []TypeID{}
866 p3 << ptr_i8
867 p3 << b.i64_type
868 p3 << b.i64_type
869 b.register_extern('memset', ptr_i8, p3)
870 p3 = []TypeID{}
871 p3 << ptr_i8
872 p3 << ptr_i8
873 p3 << b.i64_type
874 b.register_extern('memcmp', b.i64_type, p3)
875 p1 = []TypeID{}
876 p1 << b.i64_type
877 b.register_extern('exit', b.void_type, p1)
878 p1 = []TypeID{}
879 p1 << ptr_i8
880 b.register_extern('fflush', b.void_type, p1)
881 p2 = []TypeID{}
882 p2 << ptr_i8
883 p2 << b.i64_type
884 b.register_extern('access', b.i64_type, p2)
885 b.register_extern('mkdir', b.i64_type, p2)
886 b.register_extern('chmod', b.i64_type, p2)
887 b.register_extern('getcwd', ptr_i8, p2)
888 p1 = []TypeID{}
889 p1 << ptr_i8
890 b.register_extern('remove', b.i64_type, p1)
891 b.register_extern('unlink', b.i64_type, p1)
892 b.register_extern('rmdir', b.i64_type, p1)
893 b.register_extern('chdir', b.i64_type, p1)
894 b.register_extern('getenv', ptr_i8, p1)
895 b.register_extern('system', b.i64_type, p1)
896 b.register_extern('opendir', ptr_i8, p1)
897 b.register_extern('closedir', b.i64_type, p1)
898 b.register_extern('fileno', b.i64_type, p1)
899 b.register_extern('getc', b.i64_type, p1)
900 b.register_extern('strdup', ptr_i8, p1)
901 b.register_extern('atoi', b.i64_type, p1)
902 p3 = []TypeID{}
903 p3 << ptr_i8
904 p3 << ptr_i8
905 p3 << b.i64_type
906 b.register_extern('readlink', b.i64_type, p3)
907 b.register_extern('strncmp', b.i64_type, p3)
908 p1 = []TypeID{}
909 p1 << ptr_i8
910 b.register_extern('readdir', ptr_i8, p1)
911 b.register_extern('pipe', b.i64_type, p1)
912 p3 = []TypeID{}
913 p3 << ptr_i8
914 p3 << ptr_i8
915 p3 << b.i64_type
916 b.register_extern('setenv', b.i64_type, p3)
917 p3 = []TypeID{}
918 p3 << b.i64_type
919 p3 << ptr_i8
920 p3 << b.i64_type
921 b.register_extern('waitpid', b.i64_type, p3)
922 p2 = []TypeID{}
923 p2 << ptr_i8
924 p2 << ptr_i8
925 b.register_extern('realpath', ptr_i8, p2)
926 b.register_extern('pthread_mutex_init', b.i64_type, p2)
927 p1 = []TypeID{}
928 p1 << ptr_i8
929 b.register_extern('pthread_mutex_lock', b.i64_type, p1)
930 b.register_extern('pthread_mutex_trylock', b.i64_type, p1)
931 b.register_extern('pthread_mutex_unlock', b.i64_type, p1)
932 b.register_extern('pthread_mutex_destroy', b.i64_type, p1)
933 b.register_extern('pthread_self', b.i64_type, []TypeID{})
934 b.register_extern('getpid', b.i64_type, []TypeID{})
935 b.register_extern('clock', b.i64_type, []TypeID{})
936 b.register_extern('mach_absolute_time', b.i64_type, []TypeID{})
937 p1 = []TypeID{}
938 p1 << ptr_i8
939 b.register_extern('mach_timebase_info', b.void_type, p1)
940 p1 = []TypeID{}
941 p1 << b.i64_type
942 b.register_extern('clock_gettime_nsec_np', b.i64_type, p1)
943 p1 = []TypeID{}
944 p1 << ptr_i8
945 b.register_extern('time', b.i64_type, p1)
946 b.register_extern('mktime', b.i64_type, p1)
947 p2 = []TypeID{}
948 p2 << ptr_i8
949 p2 << ptr_i8
950 b.register_extern('localtime_r', ptr_i8, p2)
951 b.register_extern('gmtime_r', ptr_i8, p2)
952 p4 = []TypeID{}
953 p4 << ptr_i8
954 p4 << b.i64_type
955 p4 << ptr_i8
956 p4 << ptr_i8
957 b.register_extern('strftime', b.i64_type, p4)
958 p2 = []TypeID{}
959 p2 << ptr_i8
960 p2 << ptr_i8
961 b.register_extern('gettimeofday', b.i64_type, p2)
962 p1 = []TypeID{}
963 p1 << b.i64_type
964 b.register_extern('sleep', b.i64_type, p1)
965 b.register_extern('usleep', b.i64_type, p1)
966 b.register_extern('dup', b.i64_type, p1)
967 b.register_extern('isatty', b.i64_type, p1)
968 p2 = []TypeID{}
969 p2 << b.i64_type
970 p2 << b.i64_type
971 b.register_extern('dup2', b.i64_type, p2)
972 p2 = []TypeID{}
973 p2 << ptr_i8
974 p2 << ptr_i8
975 b.register_extern('nanosleep', b.i64_type, p2)
976 p2 = []TypeID{}
977 p2 << b.i64_type
978 p2 << ptr_i8
979 b.register_extern('clock_gettime', b.i64_type, p2)
980 b.register_basic_format_stubs()
981 b.register_string_plus_stubs()
982 b.register_bench_runtime_stubs()
983 p1 = []TypeID{}
984 p1 << ptr_i8
985 b.register_extern('free', b.void_type, p1)
986 p2 = []TypeID{}
987 p2 << ptr_i8
988 p2 << ptr_i8
989 b.register_extern('fprintf', b.void_type, p2)
990
991 for node in b.a.nodes {
992 if node.kind != .c_fn_decl {
993 continue
994 }
995 ret_type := b.resolve_c_decl_type(node.typ)
996 mut param_types := []TypeID{}
997 for i in 0 .. node.children_count {
998 child := b.a.child_node(&node, i)
999 if child.kind == .param {
1000 param_types << b.resolve_c_decl_type(child.typ)
1001 }
1002 }
1003 b.register_extern(node.value, ret_type, param_types)
1004 if node.value.starts_with('C.') {
1005 b.register_extern(node.value[2..], ret_type, param_types)
1006 }
1007 }
1008
1009 mut cur_module := ''
1010 for node in b.a.nodes {
1011 if node.kind == .module_decl {
1012 cur_module = node.value
1013 continue
1014 }
1015 if node.kind == .fn_decl {
1016 if b.skip_source_fn_in_module(node.value, cur_module) {
1017 continue
1018 }
1019 fn_name := ssa_fn_name_in_module(cur_module, node.value)
1020 if b.used_fns.len > 0 && !b.fn_is_used(node.value) && !b.fn_is_used(fn_name) {
1021 continue
1022 }
1023 ret_type := b.checker_return_type(node.value, cur_module) or {
1024 b.resolve_type_in_module(node.typ, cur_module)
1025 }
1026 mut param_types := []TypeID{}
1027 mut param_idx := 0
1028 for i in 0 .. node.children_count {
1029 child := b.a.child_node(&node, i)
1030 if child.kind == .param {
1031 typ_name := if child.typ.starts_with('&') {
1032 child.typ
1033 } else {
1034 b.checker_param_type_name(node.value, cur_module, param_idx) or {
1035 child.typ
1036 }
1037 }
1038 param_types << b.resolve_type_in_module(typ_name, cur_module)
1039 param_idx++
1040 }
1041 }
1042 fn_type := b.m.type_store.register(Type{
1043 kind: .func_t
1044 ret_type: ret_type
1045 params: param_types
1046 })
1047 b.fn_types[fn_name] = fn_type
1048 func_id := b.m.new_function(fn_name, ret_type)
1049 b.fn_ids[fn_name] = func_id
1050 }
1051 }
1052 b.register_top_level_main()
1053 b.register_wyhash_stubs()
1054 b.register_string_eq_stub()
1055 b.register_fast_string_eq_stub()
1056 b.register_string_lt_stub()
1057 b.register_string_trim_stubs()
1058 b.register_string_last_part_stubs()
1059 b.register_ierror_stubs()
1060 b.register_array_runtime_stubs()
1061 b.register_arguments_stub()
1062 b.register_panic_stub()
1063 b.register_string_builder_stubs()
1064 b.register_path_runtime_stubs()
1065 b.register_map_runtime_stubs()
1066 b.register_u8_runtime_stubs()
1067 b.register_heap_tracking_stubs()
1068 b.register_printing_stubs()
1069 b.register_prealloc_atomic_stubs()
1070 b.register_process_capture_stubs()
1071 b.register_file_check_stubs()
1072 b.register_fd_macro_stubs()
1073 b.register_signal_macro_stubs()
1074 b.register_os_stat_stubs()
1075 b.register_array_string_stubs()
1076 b.register_fixed_array_contains_stubs()
1077 b.register_array_contains_stubs()
1078 b.register_enum_autostr_fns()
1079}
1080
1081// register_enum_autostr_fns synthesizes a `<Enum>__autostr` helper per enum decl,
1082// mirroring cgen's enum_str_defs (gen/c/types.v). The transformer rewrites `${enum}`
1083// interpolation (when no custom `.str()` exists) into a call to this helper, so the
1084// ARM64/SSA path must provide it just like the C backend does. The body switches on the
1085// value and returns the matching field NAME, falling back to the integer rendering.
1086fn (mut b Builder) register_enum_autostr_fns() {
1087 mut cur_module := ''
1088 for node in b.a.nodes {
1089 match node.kind {
1090 .file {
1091 cur_module = ''
1092 }
1093 .module_decl {
1094 cur_module = node.value
1095 }
1096 .enum_decl {
1097 qualified := if cur_module.len > 0 && cur_module != 'main'
1098 && cur_module != 'builtin' {
1099 '${cur_module}.${node.value}'
1100 } else {
1101 node.value
1102 }
1103 fn_name := '${qualified.replace('.', '__')}__autostr'
1104 if fn_name in b.fn_ids {
1105 continue
1106 }
1107 is_flag := b.is_flag_enum_type_name(qualified)
1108 mut names := []string{}
1109 mut values := []int{}
1110 mut val := 0
1111 for i in 0 .. node.children_count {
1112 field := b.a.child_node(&node, i)
1113 if field.kind != .enum_field {
1114 continue
1115 }
1116 if field.children_count > 0 {
1117 if explicit := b.enum_field_expr_value(b.a.child(field, 0)) {
1118 val = explicit
1119 }
1120 }
1121 names << field.value
1122 // Flag enums store one bit per field, matching `enum_value_for_type`.
1123 values << if is_flag { 1 << val } else { val }
1124 val++
1125 }
1126 mut p1 := []TypeID{}
1127 p1 << b.i64_type
1128 fid := b.register_synthetic_function(fn_name, b.str_type, p1)
1129 if is_flag {
1130 // `@[flag]` enums stringify as `Name{.a | .b}` (and `Name{}` for 0),
1131 // matching the C backend, rather than a single field name.
1132 b.generate_flag_enum_autostr_body(fid, node.value.all_after_last('.'), names,
1133 values)
1134 } else {
1135 b.generate_enum_autostr_body(fid, names, values)
1136 }
1137 }
1138 else {}
1139 }
1140 }
1141}
1142
1143// enum_field_expr_value evaluates the constant integer value of an enum-field initializer
1144// expression, mirroring cgen's enum_field_expr_value (gen/c/types.v). Without this the
1145// autostr helpers above would only honour bare `.int_literal` initializers and leave a
1146// field like `a = 1 << 3` / `a = -1` at the previous sequential value, so the generated
1147// `<Enum>__autostr` would compare against the wrong number.
1148fn (b &Builder) enum_field_expr_value(id flat.NodeId) ?int {
1149 if int(id) < 0 {
1150 return none
1151 }
1152 node := b.a.nodes[int(id)]
1153 match node.kind {
1154 .int_literal {
1155 return parse_int_literal(node.value)
1156 }
1157 .paren {
1158 if node.children_count == 0 {
1159 return none
1160 }
1161 return b.enum_field_expr_value(b.a.child(&node, 0))
1162 }
1163 .prefix {
1164 if node.children_count == 0 {
1165 return none
1166 }
1167 value := b.enum_field_expr_value(b.a.child(&node, 0))?
1168 return match node.op {
1169 .plus { value }
1170 .minus { -value }
1171 .bit_not { ~value }
1172 else { none }
1173 }
1174 }
1175 .infix {
1176 if node.children_count < 2 {
1177 return none
1178 }
1179 left := b.enum_field_expr_value(b.a.child(&node, 0))?
1180 right := b.enum_field_expr_value(b.a.child(&node, 1))?
1181 return match node.op {
1182 .plus {
1183 left + right
1184 }
1185 .minus {
1186 left - right
1187 }
1188 .mul {
1189 left * right
1190 }
1191 .div {
1192 if right == 0 {
1193 none
1194 } else {
1195 left / right
1196 }
1197 }
1198 .mod {
1199 if right == 0 {
1200 none
1201 } else {
1202 left % right
1203 }
1204 }
1205 .amp {
1206 left & right
1207 }
1208 .pipe {
1209 left | right
1210 }
1211 .xor {
1212 left ^ right
1213 }
1214 .left_shift {
1215 int(u64(left) << right)
1216 }
1217 .right_shift, .right_shift_unsigned {
1218 left >> right
1219 }
1220 else {
1221 none
1222 }
1223 }
1224 }
1225 else {
1226 return none
1227 }
1228 }
1229}
1230
1231// generate_enum_autostr_body emits the body of a `<Enum>__autostr` helper: a chain of
1232// equality tests returning each field's name as a string literal, with an `int_str`
1233// fallback for out-of-range (or combined-flag) values.
1234fn (mut b Builder) generate_enum_autostr_body(func_id int, field_names []string, field_values []int) {
1235 entry := b.m.add_block(func_id, 'entry')
1236 it := b.func_add_argument(func_id, b.i64_type, 'it')
1237 mut cur_block := entry
1238 mut seen := map[int]bool{}
1239 for i, fname in field_names {
1240 val := field_values[i]
1241 // Duplicate values would create unreachable cases; keep the first name.
1242 if val in seen {
1243 continue
1244 }
1245 seen[val] = true
1246 const_val := b.m.get_or_add_const(b.i64_type, val.str())
1247 cmp := b.block_instr2(.eq, cur_block, b.i1_type, it, const_val)
1248 match_block := b.m.add_block(func_id, 'autostr_match')
1249 next_block := b.m.add_block(func_id, 'autostr_next')
1250 b.block_instr3(.br, cur_block, b.void_type, cmp, ValueID(match_block), ValueID(next_block))
1251 name_val := b.m.add_value(.string_literal, b.str_type, fname, 0)
1252 b.block_instr1(.ret, match_block, b.void_type, name_val)
1253 cur_block = next_block
1254 }
1255 int_str_ref := b.m.add_value(.func_ref, b.str_type, 'int_str', b.fn_ids['int_str'])
1256 default_val := b.block_instr2(.call, cur_block, b.str_type, int_str_ref, it)
1257 b.block_instr1(.ret, cur_block, b.void_type, default_val)
1258}
1259
1260// generate_flag_enum_autostr_body emits the body of a `<FlagEnum>__autostr` helper that
1261// renders a `@[flag]` value as `Name{.a | .b}` (and `Name{}` for 0), mirroring cgen's
1262// emit_flag_enum_autostr: start with `Name{`, append `.field` for every set bit (joined
1263// by ` | `), then append `}`. `field_values` holds each field's bit (1 << index).
1264fn (mut b Builder) generate_flag_enum_autostr_body(func_id int, short_name string, field_names []string, field_values []int) {
1265 ptr_string := b.m.type_store.get_ptr(b.str_type)
1266 ptr_i1 := b.m.type_store.get_ptr(b.i1_type)
1267 entry := b.m.add_block(func_id, 'entry')
1268 it := b.func_add_argument(func_id, b.i64_type, 'it')
1269
1270 // res = "Name{"; first = true. Both live in stack slots so they can be updated
1271 // across the per-field branches (this body uses plain control flow, not phis).
1272 res_slot := b.block_instr0(.alloca, entry, ptr_string)
1273 first_slot := b.block_instr0(.alloca, entry, ptr_i1)
1274 open_lit := b.m.add_value(.string_literal, b.str_type, '${short_name}{', 0)
1275 b.block_instr2(.store, entry, b.void_type, open_lit, res_slot)
1276 true_const := b.m.get_or_add_const(b.i1_type, '1')
1277 b.block_instr2(.store, entry, b.void_type, true_const, first_slot)
1278 false_const := b.m.get_or_add_const(b.i1_type, '0')
1279 plus_ref := b.m.add_value(.func_ref, b.str_type, 'string__plus', b.fn_ids['string__plus'])
1280
1281 mut cur_block := entry
1282 mut seen := map[int]bool{}
1283 for i, fname in field_names {
1284 bit := field_values[i]
1285 // A zero bit can never be "set" (matches the cgen `bit != 0` guard); skip it.
1286 if bit == 0 || bit in seen {
1287 continue
1288 }
1289 seen[bit] = true
1290 bit_const := b.m.get_or_add_const(b.i64_type, bit.str())
1291 masked := b.block_instr2(.and_, cur_block, b.i64_type, it, bit_const)
1292 is_set := b.block_instr2(.eq, cur_block, b.i1_type, masked, bit_const)
1293 append_block := b.m.add_block(func_id, 'fe_append')
1294 after_block := b.m.add_block(func_id, 'fe_after')
1295 b.block_instr3(.br, cur_block, b.void_type, is_set, ValueID(append_block),
1296 ValueID(after_block))
1297
1298 // append_block: if not first, prepend the ` | ` separator first.
1299 first := b.block_instr1(.load, append_block, b.i1_type, first_slot)
1300 sep_block := b.m.add_block(func_id, 'fe_sep')
1301 name_block := b.m.add_block(func_id, 'fe_name')
1302 b.block_instr3(.br, append_block, b.void_type, first, ValueID(name_block),
1303 ValueID(sep_block))
1304
1305 sep_res := b.block_instr1(.load, sep_block, b.str_type, res_slot)
1306 sep_lit := b.m.add_value(.string_literal, b.str_type, ' | ', 0)
1307 sep_joined := b.block_instr3(.call, sep_block, b.str_type, plus_ref, sep_res, sep_lit)
1308 b.block_instr2(.store, sep_block, b.void_type, sep_joined, res_slot)
1309 b.block_instr1(.jmp, sep_block, b.void_type, ValueID(name_block))
1310
1311 name_res := b.block_instr1(.load, name_block, b.str_type, res_slot)
1312 name_lit := b.m.add_value(.string_literal, b.str_type, '.${fname}', 0)
1313 name_joined := b.block_instr3(.call, name_block, b.str_type, plus_ref, name_res, name_lit)
1314 b.block_instr2(.store, name_block, b.void_type, name_joined, res_slot)
1315 b.block_instr2(.store, name_block, b.void_type, false_const, first_slot)
1316 b.block_instr1(.jmp, name_block, b.void_type, ValueID(after_block))
1317
1318 cur_block = after_block
1319 }
1320
1321 close_res := b.block_instr1(.load, cur_block, b.str_type, res_slot)
1322 close_lit := b.m.add_value(.string_literal, b.str_type, '}', 0)
1323 final_res := b.block_instr3(.call, cur_block, b.str_type, plus_ref, close_res, close_lit)
1324 b.block_instr1(.ret, cur_block, b.void_type, final_res)
1325}
1326
1327// ssa_fn_name_in_module supports ssa fn name in module handling for ssa.
1328fn ssa_fn_name_in_module(module_name string, name string) string {
1329 if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
1330 return module_name + '.' + name
1331 }
1332 return name
1333}
1334
1335// register_extern updates register extern state for ssa.
1336fn (mut b Builder) register_extern(name string, ret TypeID, params []TypeID) {
1337 fn_type := b.m.type_store.register(Type{
1338 kind: .func_t
1339 ret_type: ret
1340 params: params
1341 })
1342 extern_name := 'C.${name}'
1343 func_id := b.m.new_function(extern_name, ret)
1344 b.fn_types[name] = fn_type
1345 b.fn_ids[name] = func_id
1346 b.fn_types[extern_name] = fn_type
1347 b.c_fn_types[name] = fn_type
1348 b.c_fn_ids[name] = func_id
1349 mut f := b.m.funcs[func_id]
1350 f.is_c_extern = true
1351 f.is_prototype = true
1352 f.linkage = .external
1353 b.m.funcs[func_id] = f
1354}
1355
1356// register_runtime_extern updates register runtime extern state for ssa.
1357fn (mut b Builder) register_runtime_extern(name string, ret TypeID, params []TypeID) {
1358 if b.has_fn_decl(name) {
1359 return
1360 }
1361 b.register_extern(name, ret, params)
1362}
1363
1364// has_fn_decl reports whether has fn decl applies in ssa.
1365fn (b &Builder) has_fn_decl(name string) bool {
1366 for node in b.a.nodes {
1367 if node.kind == .fn_decl && node.value == name {
1368 return true
1369 }
1370 }
1371 return false
1372}
1373
1374// skip_source_fn supports skip source fn handling for Builder.
1375fn (b &Builder) skip_source_fn(name string) bool {
1376 if name in bench_runtime_stub_names {
1377 return true
1378 }
1379 if name in ['_wymix', 'wyhash', 'wyhash64', 'string__eq', 'string__lt', 'array_new', 'array_get',
1380 'string__plus', 'string_plus_many', 'string.trim_right', 'array_push', 'array_push_many',
1381 'array.push_many', 'array_clone', 'panic', 'fast_string_eq', 'strings.new_builder',
1382 'strings.Builder.write_string', 'strings.Builder.writeln', 'strings.Builder.str',
1383 'strings.Builder.write_ptr', 'strings.Builder.write_u8', 'strings.Builder.write_runes',
1384 'strings.Builder.free', 'strings.Builder.last_n', 'Builder.write_string', 'Builder.writeln',
1385 'Builder.str', 'Builder.write_ptr', 'Builder.write_u8', 'Builder.write_runes', 'Builder.free',
1386 'Builder.last_n', 'new_map', 'map__set', 'map__get', 'map__exists', 'map__get_check',
1387 'map__get_or_set', 'map__delete', 'map__clear', 'map__clone', 'v3_map_find',
1388 'v3_map_set_sized', 'u8.is_digit', 'u8.is_letter', 'u8.is_alnum', 'u8.is_capital', 'bytestr',
1389 '[]u8.bytestr', 'Array_u8__bytestr', 'Array_u8__hex', 'Array_rune__string',
1390 'array.repeat_to_depth', 'string.all_before_last', 'string__all_before_last',
1391 'all_before_last', 'string.all_after_last', 'string__all_after_last', 'all_after_last',
1392 '_ht_alloc', '_ht_free', 'f32_to_str_l', 'f32_to_str_l_with_dot', 'f64_to_str_l',
1393 'f64_to_str_l_with_dot', 'print', 'println', 'eprint', 'eprintln', 'arguments', 'tos2',
1394 'tos3', 'tos_clone', 'v_prealloc_atomic_add_i32', 'v_prealloc_atomic_load_i32',
1395 'v_prealloc_atomic_store_i32', 'v_prealloc_atomic_cas_i32', 'FD_ZERO', 'FD_SET', 'FD_ISSET',
1396 'v_signal_with_handler_cast', 'normalize_path_in_builder', 'check_fwrite', 'check_fread',
1397 'os.check_fwrite', 'os.check_fread', 'fxx_to_str_l_parse', 'fxx_to_str_l_parse_with_dot',
1398 'u8.vstring', 'u8.vstring_with_len', 'char.vstring', 'char.vstring_with_len',
1399 'byteptr.vstring', 'byteptr.vstring_with_len', 'charptr.vstring', 'charptr.vstring_with_len',
1400 'u8.vstring_literal', 'u8.vstring_literal_with_len', 'char.vstring_literal',
1401 'char.vstring_literal_with_len', 'byteptr.vstring_literal',
1402 'byteptr.vstring_literal_with_len', 'charptr.vstring_literal',
1403 'charptr.vstring_literal_with_len'] {
1404 return true
1405 }
1406 return name.starts_with('print_backtrace') || name.starts_with('backtrace_')
1407 || name.starts_with('map.')
1408 || name in ['print_libbacktrace', 'eprint_libbacktrace', 'bsd_backtrace_resolve_atos']
1409}
1410
1411// skip_source_fn_in_module supports skip source fn in module handling for Builder.
1412fn (b &Builder) skip_source_fn_in_module(name string, module_name string) bool {
1413 if module_name == 'builtin' && name in b.c_fn_ids {
1414 return true
1415 }
1416 if module_name == 'ast' && name in ['Expr.name', 'SelectorExpr.name', '[]Expr.name_list'] {
1417 return true
1418 }
1419 if module_name == 'c' && name.starts_with('Gen.') {
1420 return true
1421 }
1422 if module_name == 'os' && name in ['is_dir', 'is_link', 'ls'] {
1423 return true
1424 }
1425 return b.skip_source_fn(name)
1426}
1427
1428// register_top_level_main updates register top level main state for ssa.
1429fn (mut b Builder) register_top_level_main() {
1430 if 'main' in b.fn_ids || !b.has_top_level_stmts() {
1431 return
1432 }
1433 fn_type := b.m.type_store.register(Type{
1434 kind: .func_t
1435 ret_type: b.void_type
1436 })
1437 b.fn_types['main'] = fn_type
1438 b.fn_ids['main'] = b.m.new_function('main', b.void_type)
1439 b.top_level_main = true
1440}
1441
1442// has_top_level_stmts reports whether has top level stmts applies in ssa.
1443fn (b &Builder) has_top_level_stmts() bool {
1444 return b.top_level_stmt_ids().len > 0
1445}
1446
1447// top_level_stmt_ids supports top level stmt ids handling for Builder.
1448fn (b &Builder) top_level_stmt_ids() []flat.NodeId {
1449 mut ids := []flat.NodeId{}
1450 for file_idx, file_node in b.a.nodes {
1451 if file_idx < b.a.user_code_start || file_node.kind != .file
1452 || file_node.children_count == 0 {
1453 continue
1454 }
1455 for i in 0 .. file_node.children_count {
1456 child_id := b.a.child(&file_node, i)
1457 if int(child_id) < b.a.user_code_start {
1458 continue
1459 }
1460 child := b.a.nodes[int(child_id)]
1461 if b.is_top_level_stmt(child) {
1462 ids << child_id
1463 }
1464 }
1465 }
1466 return ids
1467}
1468
1469// is_top_level_stmt reports whether is top level stmt applies in ssa.
1470fn (b &Builder) is_top_level_stmt(node flat.Node) bool {
1471 return match node.kind {
1472 .expr_stmt, .assign, .decl_assign, .selector_assign, .index_assign, .for_stmt,
1473 .for_in_stmt, .if_expr, .assert_stmt, .defer_stmt, .block {
1474 true
1475 }
1476 else {
1477 false
1478 }
1479 }
1480}
1481
1482// register_wyhash_stubs updates register wyhash stubs state for ssa.
1483fn (mut b Builder) register_wyhash_stubs() {
1484 mut p2 := []TypeID{}
1485 p2 << b.i64_type
1486 p2 << b.i64_type
1487 wymix_id := b.register_synthetic_function('_wymix', b.i64_type, p2)
1488 b.generate_wymix_body(wymix_id)
1489 wyhash64_id := b.register_synthetic_function('wyhash64', b.i64_type, p2)
1490 b.generate_wyhash64_body(wyhash64_id)
1491
1492 ptr_u8 := b.m.type_store.get_ptr(b.i8_type)
1493 ptr_u64 := b.m.type_store.get_ptr(b.u64_type)
1494 mut p4 := []TypeID{}
1495 p4 << ptr_u8
1496 p4 << b.i64_type
1497 p4 << b.i64_type
1498 p4 << ptr_u64
1499 wyhash_id := b.register_synthetic_function('wyhash', b.i64_type, p4)
1500 b.generate_wyhash_body(wyhash_id)
1501}
1502
1503// register_synthetic_function updates register synthetic function state for ssa.
1504fn (mut b Builder) register_synthetic_function(name string, ret TypeID, params []TypeID) int {
1505 fn_type := b.m.type_store.register(Type{
1506 kind: .func_t
1507 ret_type: ret
1508 params: params
1509 })
1510 b.fn_types[name] = fn_type
1511 func_id := b.m.new_function(name, ret)
1512 b.fn_ids[name] = func_id
1513 mut f := b.m.funcs[func_id]
1514 f.typ = ret
1515 f.blocks = []BlockID{}
1516 f.params = []ValueID{}
1517 f.is_c_extern = false
1518 b.m.funcs[func_id] = f
1519 return func_id
1520}
1521
1522// func_add_argument supports func add argument handling for Builder.
1523fn (mut b Builder) func_add_argument(func_id int, typ TypeID, name string) ValueID {
1524 mut f := b.m.funcs[func_id]
1525 param := b.m.add_value(.argument, typ, name, f.params.len)
1526 f.params << param
1527 b.m.funcs[func_id] = f
1528 return param
1529}
1530
1531// block_instr0 supports block instr0 handling for Builder.
1532fn (mut b Builder) block_instr0(op OpCode, block_id BlockID, typ TypeID) ValueID {
1533 ops := []ValueID{}
1534 return b.m.add_instr(op, block_id, typ, ops)
1535}
1536
1537// block_instr1 supports block instr1 handling for Builder.
1538fn (mut b Builder) block_instr1(op OpCode, block_id BlockID, typ TypeID, a ValueID) ValueID {
1539 mut ops := []ValueID{}
1540 ops << a
1541 return b.m.add_instr(op, block_id, typ, ops)
1542}
1543
1544// block_instr2 supports block instr2 handling for Builder.
1545fn (mut b Builder) block_instr2(op OpCode, block_id BlockID, typ TypeID, a ValueID, c ValueID) ValueID {
1546 mut ops := []ValueID{}
1547 ops << a
1548 ops << c
1549 return b.m.add_instr(op, block_id, typ, ops)
1550}
1551
1552// block_instr3 supports block instr3 handling for Builder.
1553fn (mut b Builder) block_instr3(op OpCode, block_id BlockID, typ TypeID, a ValueID, c ValueID, d ValueID) ValueID {
1554 mut ops := []ValueID{}
1555 ops << a
1556 ops << c
1557 ops << d
1558 return b.m.add_instr(op, block_id, typ, ops)
1559}
1560
1561// block_instr4 supports block instr4 handling for Builder.
1562fn (mut b Builder) block_instr4(op OpCode, block_id BlockID, typ TypeID, a ValueID, c ValueID, d ValueID, e ValueID) ValueID {
1563 mut ops := []ValueID{}
1564 ops << a
1565 ops << c
1566 ops << d
1567 ops << e
1568 return b.m.add_instr(op, block_id, typ, ops)
1569}
1570
1571// block_struct_field_ptr supports block struct field ptr handling for Builder.
1572fn (mut b Builder) block_struct_field_ptr(block_id BlockID, base_addr ValueID, typ_id TypeID, field_idx int) ValueID {
1573 mut builtin_field_type := TypeID(0)
1574 mut builtin_offset := 0
1575 mut has_builtin_layout := false
1576 if typ_id == b.str_type {
1577 has_builtin_layout = true
1578 if field_idx == 0 {
1579 builtin_field_type = b.m.type_store.get_ptr(b.i8_type)
1580 builtin_offset = 0
1581 } else if field_idx == 1 {
1582 builtin_field_type = b.i32_type
1583 builtin_offset = 8
1584 } else if field_idx == 2 {
1585 // is_lit (i32) shares the trailing word with len; it sits at offset 12,
1586 // matching `struct_field_offset` used by the field readers.
1587 builtin_field_type = b.i32_type
1588 builtin_offset = 12
1589 } else {
1590 builtin_field_type = b.i64_type
1591 builtin_offset = field_idx * 8
1592 }
1593 } else if typ_id == b.array_type {
1594 has_builtin_layout = true
1595 if field_idx == 0 {
1596 builtin_field_type = b.m.type_store.get_ptr(b.i8_type)
1597 builtin_offset = 0
1598 } else {
1599 builtin_field_type = b.i32_type
1600 builtin_offset = 8 + (field_idx - 1) * 4
1601 }
1602 } else if typ_id == b.map_state_type {
1603 has_builtin_layout = true
1604 if field_idx == 0 || field_idx == 1 {
1605 builtin_field_type = b.m.type_store.get_ptr(b.i8_type)
1606 } else {
1607 builtin_field_type = b.i64_type
1608 }
1609 builtin_offset = field_idx * 8
1610 } else if typ_id == b.map_type {
1611 has_builtin_layout = true
1612 builtin_field_type = b.m.type_store.get_ptr(b.map_state_type)
1613 builtin_offset = 0
1614 }
1615 if has_builtin_layout {
1616 off_const := b.m.get_or_add_const(b.i64_type, '${builtin_offset}')
1617 return b.block_instr2(.get_element_ptr, block_id,
1618 b.m.type_store.get_ptr(builtin_field_type), base_addr, off_const)
1619 }
1620 typ := b.m.type_store.types[typ_id]
1621 field_type := if field_idx < typ.fields.len { typ.fields[field_idx] } else { b.i64_type }
1622 offset := b.m.struct_field_offset(typ_id, field_idx)
1623 off_const := b.m.get_or_add_const(b.i64_type, '${offset}')
1624 return b.block_instr2(.get_element_ptr, block_id, b.m.type_store.get_ptr(field_type),
1625 base_addr, off_const)
1626}
1627
1628// block_load_array_int_field supports block load array int field handling for Builder.
1629fn (mut b Builder) block_load_array_int_field(block_id BlockID, arr_ptr ValueID, field_idx int) ValueID {
1630 field_ptr := b.block_struct_field_ptr(block_id, arr_ptr, b.array_type, field_idx)
1631 field32 := b.block_instr1(.load, block_id, b.i32_type, field_ptr)
1632 return b.block_instr1(.zext, block_id, b.i64_type, field32)
1633}
1634
1635// register_basic_format_stubs converts register basic format stubs data for ssa.
1636fn (mut b Builder) register_basic_format_stubs() {
1637 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
1638 mut p1_ptr := []TypeID{}
1639 p1_ptr << ptr_i8
1640 tos2_id := b.register_synthetic_function('tos2', b.str_type, p1_ptr)
1641 b.generate_tos2_body(tos2_id, 0)
1642 tos3_id := b.register_synthetic_function('tos3', b.str_type, p1_ptr)
1643 b.generate_tos2_body(tos3_id, 0)
1644 tos_clone_id := b.register_synthetic_function('tos_clone', b.str_type, p1_ptr)
1645 b.generate_tos_clone_body(tos_clone_id)
1646 b.register_pointer_string_stubs()
1647
1648 mut p1_i64 := []TypeID{}
1649 p1_i64 << b.i64_type
1650 int_str_id := b.register_synthetic_function('int_str', b.str_type, p1_i64)
1651 b.generate_int_format_body(int_str_id, true, false)
1652
1653 mut p1_i1 := []TypeID{}
1654 p1_i1 << b.i1_type
1655 bool_str_id := b.register_synthetic_function('bool_str', b.str_type, p1_i1)
1656 b.generate_bool_str_body(bool_str_id)
1657
1658 mut p1_string := []TypeID{}
1659 p1_string << b.str_type
1660 string_int_id := b.register_synthetic_function('string.int', b.i64_type, p1_string)
1661 b.generate_string_int_body(string_int_id)
1662 string_int_c_id := b.register_synthetic_function('string__int', b.i64_type, p1_string)
1663 b.generate_string_int_body(string_int_c_id)
1664
1665 mut p2_i64 := []TypeID{}
1666 p2_i64 << b.i64_type
1667 p2_i64 << b.i64_type
1668 format_int_id := b.register_synthetic_function('strconv__format_int', b.str_type, p2_i64)
1669 b.generate_int_format_body(format_int_id, true, true)
1670 format_uint_id := b.register_synthetic_function('strconv__format_uint', b.str_type, p2_i64)
1671 b.generate_int_format_body(format_uint_id, false, true)
1672
1673 for name in ['strconv__f32_to_str_l', 'strconv__f32_to_str_l_with_dot', 'f32_to_str_l',
1674 'f32_to_str_l_with_dot', 'strconv__f64_to_str_l', 'strconv__f64_to_str_l_with_dot',
1675 'f64_to_str_l', 'f64_to_str_l_with_dot'] {
1676 float_str_id := b.register_synthetic_function(name, b.str_type, p1_i64)
1677 b.generate_const_string_body(float_str_id, '0.0')
1678 }
1679}
1680
1681// register_pointer_string_stubs updates register pointer string stubs state for ssa.
1682fn (mut b Builder) register_pointer_string_stubs() {
1683 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
1684 mut p1_ptr := []TypeID{}
1685 p1_ptr << ptr_i8
1686 for name in ['u8.vstring', 'char.vstring', 'byteptr.vstring', 'charptr.vstring',
1687 'u8.vstring_literal', 'char.vstring_literal', 'byteptr.vstring_literal',
1688 'charptr.vstring_literal'] {
1689 func_id := b.register_synthetic_function(name, b.str_type, p1_ptr)
1690 // The `_literal` wrappers borrow static C data (is_lit=1, never freed); the plain
1691 // ones return a non-copying view of a runtime buffer (is_lit=0), matching V.
1692 b.generate_tos2_body(func_id, if name.contains('_literal') { 1 } else { 0 })
1693 }
1694
1695 mut p2_ptr_len := []TypeID{}
1696 p2_ptr_len << ptr_i8
1697 p2_ptr_len << b.i64_type
1698 for name in ['u8.vstring_with_len', 'char.vstring_with_len', 'byteptr.vstring_with_len',
1699 'charptr.vstring_with_len', 'u8.vstring_literal_with_len', 'char.vstring_literal_with_len',
1700 'byteptr.vstring_literal_with_len', 'charptr.vstring_literal_with_len'] {
1701 func_id := b.register_synthetic_function(name, b.str_type, p2_ptr_len)
1702 b.generate_vstring_with_len_body(func_id, if name.contains('_literal') { 1 } else { 0 })
1703 }
1704}
1705
1706// generate_const_string_body supports generate const string body handling for Builder.
1707fn (mut b Builder) generate_const_string_body(func_id int, value string) {
1708 entry := b.m.add_block(func_id, 'entry')
1709 result := b.m.add_value(.string_literal, b.str_type, value, 0)
1710 b.block_instr1(.ret, entry, b.void_type, result)
1711}
1712
1713// generate_bool_str_body supports generate bool str body handling for Builder.
1714fn (mut b Builder) generate_bool_str_body(func_id int) {
1715 entry := b.m.add_block(func_id, 'entry')
1716 value := b.func_add_argument(func_id, b.i1_type, 'value')
1717 true_block := b.m.add_block(func_id, 'bool_str_true')
1718 false_block := b.m.add_block(func_id, 'bool_str_false')
1719 b.block_instr3(.br, entry, b.void_type, value, ValueID(true_block), ValueID(false_block))
1720 true_val := b.m.add_value(.string_literal, b.str_type, 'true', 0)
1721 false_val := b.m.add_value(.string_literal, b.str_type, 'false', 0)
1722 b.block_instr1(.ret, true_block, b.void_type, true_val)
1723 b.block_instr1(.ret, false_block, b.void_type, false_val)
1724}
1725
1726// generate_int_format_body converts generate int format body data for ssa.
1727fn (mut b Builder) generate_int_format_body(func_id int, is_signed bool, has_radix bool) {
1728 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
1729 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
1730 ptr_ptr_i8 := b.m.type_store.get_ptr(ptr_i8)
1731
1732 entry := b.m.add_block(func_id, 'entry')
1733 value := b.func_add_argument(func_id, b.i64_type, 'value')
1734 if has_radix {
1735 _ := b.func_add_argument(func_id, b.i64_type, 'radix')
1736 }
1737
1738 alloca_n := b.block_instr0(.alloca, entry, ptr_i64)
1739 alloca_len := b.block_instr0(.alloca, entry, ptr_i64)
1740 alloca_pos := b.block_instr0(.alloca, entry, ptr_ptr_i8)
1741 alloca_neg := b.block_instr0(.alloca, entry, b.m.type_store.get_ptr(b.i1_type))
1742
1743 zero := b.m.get_or_add_const(b.i64_type, '0')
1744 one := b.m.get_or_add_const(b.i64_type, '1')
1745 ten := b.m.get_or_add_const(b.i64_type, '10')
1746 buf_size := b.m.get_or_add_const(b.i64_type, '32')
1747 last_off := b.m.get_or_add_const(b.i64_type, '31')
1748 minus_one := b.m.get_or_add_const(b.i64_type, '-1')
1749 ascii_zero := b.m.get_or_add_const(b.i64_type, '48')
1750 ascii_minus := b.m.get_or_add_const(b.i64_type, '45')
1751 false_val := b.m.get_or_add_const(b.i1_type, '0')
1752 true_val := b.m.get_or_add_const(b.i1_type, '1')
1753
1754 malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
1755 buf := b.block_instr2(.call, entry, ptr_i8, malloc_ref, buf_size)
1756 end := b.block_instr2(.add, entry, ptr_i8, buf, last_off)
1757 nul := b.m.get_or_add_const(b.i8_type, '0')
1758 b.block_instr2(.store, entry, b.void_type, nul, end)
1759 b.block_instr2(.store, entry, b.void_type, end, alloca_pos)
1760 b.block_instr2(.store, entry, b.void_type, zero, alloca_len)
1761 b.block_instr2(.store, entry, b.void_type, false_val, alloca_neg)
1762
1763 init_block := b.m.add_block(func_id, 'int_str_init')
1764 neg_block := b.m.add_block(func_id, 'int_str_neg')
1765 pos_block := b.m.add_block(func_id, 'int_str_pos')
1766 if is_signed {
1767 is_neg := b.block_instr2(.lt, entry, b.i1_type, value, zero)
1768 b.block_instr3(.br, entry, b.void_type, is_neg, ValueID(neg_block), ValueID(pos_block))
1769 abs_value := b.block_instr2(.sub, neg_block, b.i64_type, zero, value)
1770 b.block_instr2(.store, neg_block, b.void_type, abs_value, alloca_n)
1771 b.block_instr2(.store, neg_block, b.void_type, true_val, alloca_neg)
1772 b.block_instr1(.jmp, neg_block, b.void_type, ValueID(init_block))
1773 b.block_instr2(.store, pos_block, b.void_type, value, alloca_n)
1774 b.block_instr1(.jmp, pos_block, b.void_type, ValueID(init_block))
1775 } else {
1776 b.block_instr2(.store, entry, b.void_type, value, alloca_n)
1777 b.block_instr1(.jmp, entry, b.void_type, ValueID(init_block))
1778 }
1779
1780 zero_digit_block := b.m.add_block(func_id, 'int_str_zero')
1781 loop_block := b.m.add_block(func_id, 'int_str_loop')
1782 sign_block := b.m.add_block(func_id, 'int_str_sign')
1783 sign_write_block := b.m.add_block(func_id, 'int_str_sign_write')
1784 done_block := b.m.add_block(func_id, 'int_str_done')
1785
1786 n_init := b.block_instr1(.load, init_block, b.i64_type, alloca_n)
1787 is_zero := b.block_instr2(.eq, init_block, b.i1_type, n_init, zero)
1788 b.block_instr3(.br, init_block, b.void_type, is_zero, ValueID(zero_digit_block),
1789 ValueID(loop_block))
1790
1791 pos_zero := b.block_instr1(.load, zero_digit_block, ptr_i8, alloca_pos)
1792 pos_zero_next := b.block_instr2(.add, zero_digit_block, ptr_i8, pos_zero, minus_one)
1793 b.block_instr2(.store, zero_digit_block, b.void_type, ascii_zero, pos_zero_next)
1794 b.block_instr2(.store, zero_digit_block, b.void_type, pos_zero_next, alloca_pos)
1795 b.block_instr2(.store, zero_digit_block, b.void_type, one, alloca_len)
1796 b.block_instr1(.jmp, zero_digit_block, b.void_type, ValueID(sign_block))
1797
1798 n_cur := b.block_instr1(.load, loop_block, b.i64_type, alloca_n)
1799 rem := b.block_instr2(.srem, loop_block, b.i64_type, n_cur, ten)
1800 digit := b.block_instr2(.add, loop_block, b.i64_type, rem, ascii_zero)
1801 pos := b.block_instr1(.load, loop_block, ptr_i8, alloca_pos)
1802 pos_next := b.block_instr2(.add, loop_block, ptr_i8, pos, minus_one)
1803 b.block_instr2(.store, loop_block, b.void_type, digit, pos_next)
1804 b.block_instr2(.store, loop_block, b.void_type, pos_next, alloca_pos)
1805 old_len := b.block_instr1(.load, loop_block, b.i64_type, alloca_len)
1806 new_len := b.block_instr2(.add, loop_block, b.i64_type, old_len, one)
1807 b.block_instr2(.store, loop_block, b.void_type, new_len, alloca_len)
1808 quot := b.block_instr2(.sdiv, loop_block, b.i64_type, n_cur, ten)
1809 b.block_instr2(.store, loop_block, b.void_type, quot, alloca_n)
1810 has_more := b.block_instr2(.gt, loop_block, b.i1_type, quot, zero)
1811 b.block_instr3(.br, loop_block, b.void_type, has_more, ValueID(loop_block), ValueID(sign_block))
1812
1813 neg_flag := b.block_instr1(.load, sign_block, b.i1_type, alloca_neg)
1814 b.block_instr3(.br, sign_block, b.void_type, neg_flag, ValueID(sign_write_block),
1815 ValueID(done_block))
1816
1817 pos_sign := b.block_instr1(.load, sign_write_block, ptr_i8, alloca_pos)
1818 pos_sign_next := b.block_instr2(.add, sign_write_block, ptr_i8, pos_sign, minus_one)
1819 b.block_instr2(.store, sign_write_block, b.void_type, ascii_minus, pos_sign_next)
1820 b.block_instr2(.store, sign_write_block, b.void_type, pos_sign_next, alloca_pos)
1821 len_sign := b.block_instr1(.load, sign_write_block, b.i64_type, alloca_len)
1822 len_sign_next := b.block_instr2(.add, sign_write_block, b.i64_type, len_sign, one)
1823 b.block_instr2(.store, sign_write_block, b.void_type, len_sign_next, alloca_len)
1824 b.block_instr1(.jmp, sign_write_block, b.void_type, ValueID(done_block))
1825
1826 data := b.block_instr1(.load, done_block, ptr_i8, alloca_pos)
1827 len := b.block_instr1(.load, done_block, b.i64_type, alloca_len)
1828 // The digits were written backwards, so `data` points into the middle of `buf`.
1829 // Shift them to the allocation start (memmove handles the overlap) and NUL-terminate,
1830 // so the returned owned string's `str` is `buf` itself and free() gets a valid pointer.
1831 memmove_ref := b.m.add_value(.func_ref, b.void_type, 'memmove', b.fn_ids['memmove'])
1832 b.block_instr4(.call, done_block, ptr_i8, memmove_ref, buf, data, len)
1833 buf_term := b.block_instr2(.add, done_block, ptr_i8, buf, len)
1834 b.block_instr2(.store, done_block, b.void_type, nul, buf_term)
1835 result := b.emit_make_string(done_block, buf, len, 0)
1836 b.block_instr1(.ret, done_block, b.void_type, result)
1837}
1838
1839// generate_string_int_body supports generate string int body handling for Builder.
1840fn (mut b Builder) generate_string_int_body(func_id int) {
1841 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
1842 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
1843 ptr_string := b.m.type_store.get_ptr(b.str_type)
1844 entry := b.m.add_block(func_id, 'entry')
1845 s := b.func_add_argument(func_id, b.str_type, 's')
1846 s_slot := b.block_instr0(.alloca, entry, ptr_string)
1847 result_slot := b.block_instr0(.alloca, entry, ptr_i64)
1848 i_slot := b.block_instr0(.alloca, entry, ptr_i64)
1849 sign_slot := b.block_instr0(.alloca, entry, ptr_i64)
1850 b.block_instr2(.store, entry, b.void_type, s, s_slot)
1851
1852 data_ptr := b.block_struct_field_ptr(entry, s_slot, b.str_type, 0)
1853 len_ptr := b.block_struct_field_ptr(entry, s_slot, b.str_type, 1)
1854 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
1855 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
1856 len := b.block_instr1(.zext, entry, b.i64_type, len32)
1857
1858 zero64 := b.m.get_or_add_const(b.i64_type, '0')
1859 one64 := b.m.get_or_add_const(b.i64_type, '1')
1860 minus_one := b.m.get_or_add_const(b.i64_type, '-1')
1861 ten64 := b.m.get_or_add_const(b.i64_type, '10')
1862 ascii_zero := b.m.get_or_add_const(b.i64_type, '48')
1863 ascii_minus := b.m.get_or_add_const(b.i8_type, '45')
1864 b.block_instr2(.store, entry, b.void_type, zero64, result_slot)
1865 b.block_instr2(.store, entry, b.void_type, zero64, i_slot)
1866 b.block_instr2(.store, entry, b.void_type, one64, sign_slot)
1867
1868 check_sign := b.m.add_block(func_id, 'string_int_check_sign')
1869 sign_body := b.m.add_block(func_id, 'string_int_sign_body')
1870 loop := b.m.add_block(func_id, 'string_int_loop')
1871 body := b.m.add_block(func_id, 'string_int_body')
1872 done := b.m.add_block(func_id, 'string_int_done')
1873 has_chars := b.block_instr2(.gt, entry, b.i1_type, len, zero64)
1874 b.block_instr3(.br, entry, b.void_type, has_chars, ValueID(check_sign), ValueID(done))
1875
1876 first_ch := b.block_instr1(.load, check_sign, b.i8_type, data)
1877 is_minus := b.block_instr2(.eq, check_sign, b.i1_type, first_ch, ascii_minus)
1878 b.block_instr3(.br, check_sign, b.void_type, is_minus, ValueID(sign_body), ValueID(loop))
1879
1880 b.block_instr2(.store, sign_body, b.void_type, minus_one, sign_slot)
1881 b.block_instr2(.store, sign_body, b.void_type, one64, i_slot)
1882 b.block_instr1(.jmp, sign_body, b.void_type, ValueID(loop))
1883
1884 i := b.block_instr1(.load, loop, b.i64_type, i_slot)
1885 more := b.block_instr2(.lt, loop, b.i1_type, i, len)
1886 b.block_instr3(.br, loop, b.void_type, more, ValueID(body), ValueID(done))
1887
1888 body_i := b.block_instr1(.load, body, b.i64_type, i_slot)
1889 ch_ptr := b.block_instr2(.add, body, ptr_i8, data, body_i)
1890 ch := b.block_instr1(.load, body, b.i8_type, ch_ptr)
1891 ch64 := b.block_instr1(.zext, body, b.i64_type, ch)
1892 digit := b.block_instr2(.sub, body, b.i64_type, ch64, ascii_zero)
1893 cur := b.block_instr1(.load, body, b.i64_type, result_slot)
1894 scaled := b.block_instr2(.mul, body, b.i64_type, cur, ten64)
1895 next := b.block_instr2(.add, body, b.i64_type, scaled, digit)
1896 b.block_instr2(.store, body, b.void_type, next, result_slot)
1897 next_i := b.block_instr2(.add, body, b.i64_type, body_i, one64)
1898 b.block_instr2(.store, body, b.void_type, next_i, i_slot)
1899 b.block_instr1(.jmp, body, b.void_type, ValueID(loop))
1900
1901 parsed := b.block_instr1(.load, done, b.i64_type, result_slot)
1902 sign := b.block_instr1(.load, done, b.i64_type, sign_slot)
1903 signed := b.block_instr2(.mul, done, b.i64_type, parsed, sign)
1904 b.block_instr1(.ret, done, b.void_type, signed)
1905}
1906
1907// register_bench_runtime_stubs updates register bench runtime stubs state for ssa.
1908fn (mut b Builder) register_bench_runtime_stubs() {
1909 for name in bench_runtime_stub_names {
1910 id := b.register_synthetic_function(name, b.i64_type, []TypeID{})
1911 b.generate_const_i64_body(id, '0')
1912 }
1913}
1914
1915// generate_tos2_body supports generate tos2 body handling for Builder. It backs both the
1916// non-copying `tos2`/`vstring` wrappers (is_lit=0) and the `vstring_literal` wrappers
1917// (is_lit=1, the borrowed pointer is static C data that must never be freed).
1918fn (mut b Builder) generate_tos2_body(func_id int, is_lit int) {
1919 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
1920 entry := b.m.add_block(func_id, 'entry')
1921 s := b.func_add_argument(func_id, ptr_i8, 's')
1922 strlen_ref := b.m.add_value(.func_ref, b.i64_type, 'strlen', b.fn_ids['strlen'])
1923 len := b.block_instr2(.call, entry, b.i64_type, strlen_ref, s)
1924 result := b.emit_make_string(entry, s, len, is_lit)
1925 b.block_instr1(.ret, entry, b.void_type, result)
1926}
1927
1928// generate_tos_clone_body supports generate tos clone body handling for Builder.
1929fn (mut b Builder) generate_tos_clone_body(func_id int) {
1930 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
1931 entry := b.m.add_block(func_id, 'entry')
1932 s := b.func_add_argument(func_id, ptr_i8, 's')
1933 strlen_ref := b.m.add_value(.func_ref, b.i64_type, 'strlen', b.fn_ids['strlen'])
1934 len := b.block_instr2(.call, entry, b.i64_type, strlen_ref, s)
1935 result := b.emit_make_owned_string(entry, s, len)
1936 b.block_instr1(.ret, entry, b.void_type, result)
1937}
1938
1939// generate_vstring_with_len_body backs `vstring_with_len` (is_lit=0) and
1940// `vstring_literal_with_len` (is_lit=1; borrowed static C data that must never be freed).
1941fn (mut b Builder) generate_vstring_with_len_body(func_id int, is_lit int) {
1942 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
1943 entry := b.m.add_block(func_id, 'entry')
1944 s := b.func_add_argument(func_id, ptr_i8, 's')
1945 len := b.func_add_argument(func_id, b.i64_type, 'len')
1946 result := b.emit_make_string(entry, s, len, is_lit)
1947 b.block_instr1(.ret, entry, b.void_type, result)
1948}
1949
1950// register_string_plus_stubs updates register string plus stubs state for ssa.
1951fn (mut b Builder) register_string_plus_stubs() {
1952 mut p2 := []TypeID{}
1953 p2 << b.str_type
1954 p2 << b.str_type
1955 plus_id := b.register_synthetic_function('string__plus', b.str_type, p2)
1956 b.generate_string_plus_body(plus_id)
1957
1958 mut p_many := []TypeID{}
1959 p_many << b.i64_type
1960 p_many << b.m.type_store.get_ptr(b.str_type)
1961 many_id := b.register_synthetic_function('string_plus_many', b.str_type, p_many)
1962 b.generate_string_plus_many_body(many_id)
1963}
1964
1965// emit_make_string emits emit make string output for ssa.
1966// emit_make_string builds a `string{ str: data, len: len64, is_lit: is_lit }` value.
1967// `is_lit` must be set explicitly because the field shares the trailing struct word and
1968// is otherwise stack garbage that string.free could misread: pass 1 for a literal /
1969// borrowed view of static data that must never be freed, and 0 for an owned heap string
1970// whose `data` is an allocation start (so free() gets the right pointer). For an owned
1971// copy of a borrowed/interior buffer use emit_make_owned_string.
1972fn (mut b Builder) emit_make_string(block_id BlockID, data ValueID, len64 ValueID, is_lit int) ValueID {
1973 ptr_string := b.m.type_store.get_ptr(b.str_type)
1974 alloca_out := b.block_instr0(.alloca, block_id, ptr_string)
1975 data_ptr := b.block_struct_field_ptr(block_id, alloca_out, b.str_type, 0)
1976 len_ptr := b.block_struct_field_ptr(block_id, alloca_out, b.str_type, 1)
1977 is_lit_ptr := b.block_struct_field_ptr(block_id, alloca_out, b.str_type, 2)
1978 b.block_instr2(.store, block_id, b.void_type, data, data_ptr)
1979 b.block_instr2(.store, block_id, b.void_type, len64, len_ptr)
1980 is_lit_const := b.m.get_or_add_const(b.i32_type, is_lit.str())
1981 b.block_instr2(.store, block_id, b.void_type, is_lit_const, is_lit_ptr)
1982 return b.block_instr1(.load, block_id, b.str_type, alloca_out)
1983}
1984
1985// emit_make_owned_string allocates `len + 1` bytes, copies `len` bytes from `src`,
1986// NUL-terminates, and returns an owned (non-literal, freeable) string. Use this where a
1987// helper must return its own buffer rather than a view into a caller-owned one — e.g. a
1988// substring of a strings.Builder. The returned string is marked non-literal by
1989// emit_make_string, so returning an interior/aliased pointer here would later hand a
1990// non-allocation-start pointer to free().
1991fn (mut b Builder) emit_make_owned_string(block_id BlockID, src ValueID, len ValueID) ValueID {
1992 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
1993 one := b.m.get_or_add_const(b.i64_type, '1')
1994 alloc_len := b.block_instr2(.add, block_id, b.i64_type, len, one)
1995 malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
1996 out_data := b.block_instr2(.call, block_id, ptr_i8, malloc_ref, alloc_len)
1997 memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
1998 b.block_instr4(.call, block_id, ptr_i8, memcpy_ref, out_data, src, len)
1999 zero8 := b.m.get_or_add_const(b.i8_type, '0')
2000 term_ptr := b.block_instr2(.add, block_id, ptr_i8, out_data, len)
2001 b.block_instr2(.store, block_id, b.void_type, zero8, term_ptr)
2002 return b.emit_make_string(block_id, out_data, len, 0)
2003}
2004
2005// generate_string_plus_body supports generate string plus body handling for Builder.
2006fn (mut b Builder) generate_string_plus_body(func_id int) {
2007 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2008 ptr_string := b.m.type_store.get_ptr(b.str_type)
2009 entry := b.m.add_block(func_id, 'entry')
2010 left := b.func_add_argument(func_id, b.str_type, 'left')
2011 right := b.func_add_argument(func_id, b.str_type, 'right')
2012
2013 alloca_left := b.block_instr0(.alloca, entry, ptr_string)
2014 alloca_right := b.block_instr0(.alloca, entry, ptr_string)
2015 b.block_instr2(.store, entry, b.void_type, left, alloca_left)
2016 b.block_instr2(.store, entry, b.void_type, right, alloca_right)
2017
2018 left_data_ptr := b.block_struct_field_ptr(entry, alloca_left, b.str_type, 0)
2019 left_len_ptr := b.block_struct_field_ptr(entry, alloca_left, b.str_type, 1)
2020 right_data_ptr := b.block_struct_field_ptr(entry, alloca_right, b.str_type, 0)
2021 right_len_ptr := b.block_struct_field_ptr(entry, alloca_right, b.str_type, 1)
2022 left_data := b.block_instr1(.load, entry, ptr_i8, left_data_ptr)
2023 right_data := b.block_instr1(.load, entry, ptr_i8, right_data_ptr)
2024 left_len32 := b.block_instr1(.load, entry, b.i32_type, left_len_ptr)
2025 right_len32 := b.block_instr1(.load, entry, b.i32_type, right_len_ptr)
2026 left_len := b.block_instr1(.zext, entry, b.i64_type, left_len32)
2027 right_len := b.block_instr1(.zext, entry, b.i64_type, right_len32)
2028 total_len := b.block_instr2(.add, entry, b.i64_type, left_len, right_len)
2029 one := b.m.get_or_add_const(b.i64_type, '1')
2030 alloc_len := b.block_instr2(.add, entry, b.i64_type, total_len, one)
2031
2032 malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
2033 out_data := b.block_instr2(.call, entry, ptr_i8, malloc_ref, alloc_len)
2034 memcpy_ref_left := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
2035 b.block_instr4(.call, entry, ptr_i8, memcpy_ref_left, out_data, left_data, left_len)
2036 right_dest := b.block_instr2(.add, entry, ptr_i8, out_data, left_len)
2037 memcpy_ref_right := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
2038 b.block_instr4(.call, entry, ptr_i8, memcpy_ref_right, right_dest, right_data, right_len)
2039 zero8 := b.m.get_or_add_const(b.i8_type, '0')
2040 term_ptr := b.block_instr2(.add, entry, ptr_i8, out_data, total_len)
2041 b.block_instr2(.store, entry, b.void_type, zero8, term_ptr)
2042
2043 result := b.emit_make_string(entry, out_data, total_len, 0)
2044 b.block_instr1(.ret, entry, b.void_type, result)
2045}
2046
2047// generate_string_plus_many_body supports generate string plus many body handling for Builder.
2048fn (mut b Builder) generate_string_plus_many_body(func_id int) {
2049 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2050 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
2051 ptr_string := b.m.type_store.get_ptr(b.str_type)
2052 entry := b.m.add_block(func_id, 'entry')
2053 count := b.func_add_argument(func_id, b.i64_type, 'count')
2054 input_base := b.func_add_argument(func_id, ptr_string, 'input_base')
2055
2056 alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
2057 alloca_total := b.block_instr0(.alloca, entry, ptr_i64)
2058 alloca_written := b.block_instr0(.alloca, entry, ptr_i64)
2059 zero64 := b.m.get_or_add_const(b.i64_type, '0')
2060 one64 := b.m.get_or_add_const(b.i64_type, '1')
2061 stride := b.m.get_or_add_const(b.i64_type, '16')
2062 b.block_instr2(.store, entry, b.void_type, zero64, alloca_i)
2063 b.block_instr2(.store, entry, b.void_type, zero64, alloca_total)
2064
2065 sum_check := b.m.add_block(func_id, 'string_plus_many_sum_check')
2066 sum_body := b.m.add_block(func_id, 'string_plus_many_sum_body')
2067 alloc_block := b.m.add_block(func_id, 'string_plus_many_alloc')
2068 b.block_instr1(.jmp, entry, b.void_type, ValueID(sum_check))
2069
2070 i_sum := b.block_instr1(.load, sum_check, b.i64_type, alloca_i)
2071 more_sum := b.block_instr2(.lt, sum_check, b.i1_type, i_sum, count)
2072 b.block_instr3(.br, sum_check, b.void_type, more_sum, ValueID(sum_body), ValueID(alloc_block))
2073
2074 sum_item_off := b.block_instr2(.mul, sum_body, b.i64_type, i_sum, stride)
2075 sum_item_ptr := b.block_instr2(.get_element_ptr, sum_body, ptr_string, input_base, sum_item_off)
2076 sum_len_ptr := b.block_struct_field_ptr(sum_body, sum_item_ptr, b.str_type, 1)
2077 sum_len32 := b.block_instr1(.load, sum_body, b.i32_type, sum_len_ptr)
2078 sum_len := b.block_instr1(.zext, sum_body, b.i64_type, sum_len32)
2079 old_total := b.block_instr1(.load, sum_body, b.i64_type, alloca_total)
2080 new_total := b.block_instr2(.add, sum_body, b.i64_type, old_total, sum_len)
2081 next_i_sum := b.block_instr2(.add, sum_body, b.i64_type, i_sum, one64)
2082 b.block_instr2(.store, sum_body, b.void_type, new_total, alloca_total)
2083 b.block_instr2(.store, sum_body, b.void_type, next_i_sum, alloca_i)
2084 b.block_instr1(.jmp, sum_body, b.void_type, ValueID(sum_check))
2085
2086 total_len := b.block_instr1(.load, alloc_block, b.i64_type, alloca_total)
2087 alloc_len := b.block_instr2(.add, alloc_block, b.i64_type, total_len, one64)
2088 malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
2089 out_data := b.block_instr2(.call, alloc_block, ptr_i8, malloc_ref, alloc_len)
2090 b.block_instr2(.store, alloc_block, b.void_type, zero64, alloca_i)
2091 b.block_instr2(.store, alloc_block, b.void_type, zero64, alloca_written)
2092
2093 copy_check := b.m.add_block(func_id, 'string_plus_many_copy_check')
2094 copy_body := b.m.add_block(func_id, 'string_plus_many_copy_body')
2095 done := b.m.add_block(func_id, 'string_plus_many_done')
2096 b.block_instr1(.jmp, alloc_block, b.void_type, ValueID(copy_check))
2097
2098 i_copy := b.block_instr1(.load, copy_check, b.i64_type, alloca_i)
2099 more_copy := b.block_instr2(.lt, copy_check, b.i1_type, i_copy, count)
2100 b.block_instr3(.br, copy_check, b.void_type, more_copy, ValueID(copy_body), ValueID(done))
2101
2102 copy_item_off := b.block_instr2(.mul, copy_body, b.i64_type, i_copy, stride)
2103 copy_item_ptr := b.block_instr2(.get_element_ptr, copy_body, ptr_string, input_base,
2104 copy_item_off)
2105 copy_data_ptr := b.block_struct_field_ptr(copy_body, copy_item_ptr, b.str_type, 0)
2106 copy_len_ptr := b.block_struct_field_ptr(copy_body, copy_item_ptr, b.str_type, 1)
2107 copy_data := b.block_instr1(.load, copy_body, ptr_i8, copy_data_ptr)
2108 copy_len32 := b.block_instr1(.load, copy_body, b.i32_type, copy_len_ptr)
2109 copy_len := b.block_instr1(.zext, copy_body, b.i64_type, copy_len32)
2110 written := b.block_instr1(.load, copy_body, b.i64_type, alloca_written)
2111 dest := b.block_instr2(.add, copy_body, ptr_i8, out_data, written)
2112 memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
2113 b.block_instr4(.call, copy_body, ptr_i8, memcpy_ref, dest, copy_data, copy_len)
2114 new_written := b.block_instr2(.add, copy_body, b.i64_type, written, copy_len)
2115 next_i_copy := b.block_instr2(.add, copy_body, b.i64_type, i_copy, one64)
2116 b.block_instr2(.store, copy_body, b.void_type, new_written, alloca_written)
2117 b.block_instr2(.store, copy_body, b.void_type, next_i_copy, alloca_i)
2118 b.block_instr1(.jmp, copy_body, b.void_type, ValueID(copy_check))
2119
2120 zero8 := b.m.get_or_add_const(b.i8_type, '0')
2121 term_ptr := b.block_instr2(.add, done, ptr_i8, out_data, total_len)
2122 b.block_instr2(.store, done, b.void_type, zero8, term_ptr)
2123 result := b.emit_make_string(done, out_data, total_len, 0)
2124 b.block_instr1(.ret, done, b.void_type, result)
2125}
2126
2127// register_array_runtime_stubs updates register array runtime stubs state for ssa.
2128fn (mut b Builder) register_array_runtime_stubs() {
2129 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2130 ptr_array := b.m.type_store.get_ptr(b.array_type)
2131
2132 mut p3 := []TypeID{}
2133 p3 << b.i64_type
2134 p3 << b.i64_type
2135 p3 << b.i64_type
2136 array_new_id := b.register_synthetic_function('array_new', b.array_type, p3)
2137 b.generate_array_new_body(array_new_id)
2138
2139 mut p2 := []TypeID{}
2140 p2 << b.array_type
2141 p2 << b.i64_type
2142 array_get_id := b.register_synthetic_function('array_get', ptr_i8, p2)
2143 b.generate_array_get_body(array_get_id)
2144
2145 p3 = []TypeID{}
2146 p3 << b.array_type
2147 p3 << b.i64_type
2148 p3 << b.i64_type
2149 array_slice_id := b.register_synthetic_function('array_slice', b.array_type, p3)
2150 b.generate_array_slice_body(array_slice_id)
2151
2152 p2 = []TypeID{}
2153 p2 << ptr_array
2154 p2 << ptr_i8
2155 array_push_id := b.register_synthetic_function('array_push', b.void_type, p2)
2156 b.generate_array_push_body(array_push_id)
2157
2158 mut p3_push_many := []TypeID{}
2159 p3_push_many << ptr_array
2160 p3_push_many << ptr_i8
2161 p3_push_many << b.i64_type
2162 array_push_many_id := b.register_synthetic_function('array.push_many', b.void_type,
2163 p3_push_many)
2164 b.generate_array_push_many_body(array_push_many_id)
2165 // `array_push_many_ptr` is the C-macro name the transformer emits for
2166 // `arr << ptr_value` / fixed-array appends; it has the same
2167 // (ptr_array, ptr_value, count) signature as `array.push_many`. It needs its
2168 // own definition so the native linker resolves the `_array_push_many_ptr` symbol.
2169 array_push_many_ptr_id := b.register_synthetic_function('array_push_many_ptr', b.void_type,
2170 p3_push_many)
2171 b.generate_array_push_many_body(array_push_many_ptr_id)
2172
2173 mut p2_push_many := []TypeID{}
2174 p2_push_many << ptr_array
2175 p2_push_many << b.array_type
2176 array_push_many_wrapper_id := b.register_synthetic_function('array_push_many', b.void_type,
2177 p2_push_many)
2178 b.generate_array_push_many_array_body(array_push_many_wrapper_id)
2179
2180 mut p1 := []TypeID{}
2181 p1 << b.array_type
2182 array_clone_id := b.register_synthetic_function('array_clone', b.array_type, p1)
2183 b.generate_array_clone_body(array_clone_id)
2184
2185 // The transformer lowers `arr.clone()` to `array__clone(&arr)` (a pointer arg, matching
2186 // cgen's `array array__clone(array* a)`). Without a synthetic under this exact name, the
2187 // `__`->`.` fallback resolves it to the builtin `array.clone`, which allocates data past
2188 // an 8-byte header (alloc_array_data) — incompatible with the synthetic runtime that
2189 // grows/frees buffers via `realloc(data)`/`free(data)` assuming no header. Provide a
2190 // header-less (calloc-based) clone taking the array by pointer.
2191 mut p1_ptr_arr := []TypeID{}
2192 p1_ptr_arr << b.m.type_store.get_ptr(b.array_type)
2193 array_clone_ptr_id := b.register_synthetic_function('array__clone', b.array_type, p1_ptr_arr)
2194 b.generate_array_clone_ptr_body(array_clone_ptr_id)
2195
2196 mut p3_repeat := []TypeID{}
2197 p3_repeat << b.array_type
2198 p3_repeat << b.i64_type
2199 p3_repeat << b.i64_type
2200 array_repeat_id := b.register_synthetic_function('array.repeat_to_depth', b.array_type,
2201 p3_repeat)
2202 b.generate_array_repeat_to_depth_body(array_repeat_id)
2203}
2204
2205// register_panic_stub updates register panic stub state for ssa.
2206fn (mut b Builder) register_panic_stub() {
2207 mut p1 := []TypeID{}
2208 p1 << b.str_type
2209 panic_id := b.register_synthetic_function('panic', b.void_type, p1)
2210 b.generate_panic_body(panic_id)
2211}
2212
2213// register_printing_stubs updates register printing stubs state for ssa.
2214fn (mut b Builder) register_printing_stubs() {
2215 mut p1 := []TypeID{}
2216 p1 << b.str_type
2217 print_id := b.register_synthetic_function('print', b.void_type, p1)
2218 b.generate_print_body(print_id, '1', false)
2219 println_id := b.register_synthetic_function('println', b.void_type, p1)
2220 b.generate_print_body(println_id, '1', true)
2221 eprint_id := b.register_synthetic_function('eprint', b.void_type, p1)
2222 b.generate_print_body(eprint_id, '2', false)
2223 eprintln_id := b.register_synthetic_function('eprintln', b.void_type, p1)
2224 b.generate_print_body(eprintln_id, '2', true)
2225}
2226
2227// generate_print_body supports generate print body handling for Builder.
2228fn (mut b Builder) generate_print_body(func_id int, fd_value string, newline bool) {
2229 ptr_string := b.m.type_store.get_ptr(b.str_type)
2230 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2231 entry := b.m.add_block(func_id, 'entry')
2232 message := b.func_add_argument(func_id, b.str_type, 'message')
2233 message_slot := b.block_instr0(.alloca, entry, ptr_string)
2234 b.block_instr2(.store, entry, b.void_type, message, message_slot)
2235 data_ptr := b.block_struct_field_ptr(entry, message_slot, b.str_type, 0)
2236 len_ptr := b.block_struct_field_ptr(entry, message_slot, b.str_type, 1)
2237 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
2238 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
2239 len := b.block_instr1(.zext, entry, b.i64_type, len32)
2240 fd := b.m.get_or_add_const(b.i64_type, fd_value)
2241 write_ref := b.m.add_value(.func_ref, b.void_type, 'write', b.fn_ids['write'])
2242 b.block_instr4(.call, entry, b.i64_type, write_ref, fd, data, len)
2243 if newline {
2244 newline_str := b.m.add_value(.string_literal, b.str_type, '\n', 0)
2245 newline_slot := b.block_instr0(.alloca, entry, ptr_string)
2246 b.block_instr2(.store, entry, b.void_type, newline_str, newline_slot)
2247 newline_data_ptr := b.block_struct_field_ptr(entry, newline_slot, b.str_type, 0)
2248 newline_data := b.block_instr1(.load, entry, ptr_i8, newline_data_ptr)
2249 one := b.m.get_or_add_const(b.i64_type, '1')
2250 b.block_instr4(.call, entry, b.i64_type, write_ref, fd, newline_data, one)
2251 }
2252 b.block_instr0(.ret, entry, b.void_type)
2253}
2254
2255// generate_panic_body supports generate panic body handling for Builder.
2256fn (mut b Builder) generate_panic_body(func_id int) {
2257 entry := b.m.add_block(func_id, 'entry')
2258 message := b.func_add_argument(func_id, b.str_type, 'message')
2259 if eprintln_id := b.fn_ids['eprintln'] {
2260 fn_ref := b.m.add_value(.func_ref, b.void_type, 'eprintln', eprintln_id)
2261 b.block_instr2(.call, entry, b.void_type, fn_ref, message)
2262 }
2263 if exit_id := b.fn_ids['exit'] {
2264 one := b.m.get_or_add_const(b.i64_type, '1')
2265 fn_ref := b.m.add_value(.func_ref, b.void_type, 'exit', exit_id)
2266 b.block_instr2(.call, entry, b.void_type, fn_ref, one)
2267 }
2268 b.block_instr0(.ret, entry, b.void_type)
2269}
2270
2271// register_string_builder_stubs updates register string builder stubs state for ssa.
2272fn (mut b Builder) register_string_builder_stubs() {
2273 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2274 ptr_builder := b.m.type_store.get_ptr(b.array_type)
2275
2276 mut p1 := []TypeID{}
2277 p1 << b.i64_type
2278 new_id := b.register_synthetic_function('strings.new_builder', b.array_type, p1)
2279 b.generate_builder_new_body(new_id)
2280
2281 mut p2 := []TypeID{}
2282 p2 << ptr_builder
2283 p2 << b.str_type
2284 write_string_id :=
2285 b.register_synthetic_function('strings.Builder.write_string', b.void_type, p2)
2286 b.generate_builder_write_string_body(write_string_id, false)
2287 writeln_id := b.register_synthetic_function('strings.Builder.writeln', b.void_type, p2)
2288 b.generate_builder_write_string_body(writeln_id, true)
2289
2290 p1 = []TypeID{}
2291 p1 << ptr_builder
2292 str_id := b.register_synthetic_function('strings.Builder.str', b.str_type, p1)
2293 b.generate_builder_str_body(str_id)
2294 free_id := b.register_synthetic_function('strings.Builder.free', b.void_type, p1)
2295 b.generate_builder_free_body(free_id)
2296
2297 p2 = []TypeID{}
2298 p2 << ptr_builder
2299 p2 << b.i8_type
2300 write_u8_id := b.register_synthetic_function('strings.Builder.write_u8', b.void_type, p2)
2301 b.generate_builder_write_u8_body(write_u8_id)
2302
2303 mut p3 := []TypeID{}
2304 p3 << ptr_builder
2305 p3 << ptr_i8
2306 p3 << b.i64_type
2307 write_ptr_id := b.register_synthetic_function('strings.Builder.write_ptr', b.void_type, p3)
2308 b.generate_builder_write_ptr_body(write_ptr_id)
2309 push_many_id := b.register_synthetic_function('strings.Builder.push_many', b.void_type, p3)
2310 b.generate_builder_write_ptr_body(push_many_id)
2311
2312 p2 = []TypeID{}
2313 p2 << ptr_builder
2314 p2 << b.array_type
2315 write_runes_id := b.register_synthetic_function('strings.Builder.write_runes', b.void_type, p2)
2316 b.generate_builder_free_body(write_runes_id)
2317
2318 p2 = []TypeID{}
2319 p2 << ptr_builder
2320 p2 << b.i64_type
2321 last_n_id := b.register_synthetic_function('strings.Builder.last_n', b.str_type, p2)
2322 b.generate_builder_last_n_body(last_n_id)
2323}
2324
2325// generate_builder_new_body supports generate builder new body handling for Builder.
2326fn (mut b Builder) generate_builder_new_body(func_id int) {
2327 entry := b.m.add_block(func_id, 'entry')
2328 initial_size := b.func_add_argument(func_id, b.i64_type, 'initial_size')
2329 one := b.m.get_or_add_const(b.i64_type, '1')
2330 zero := b.m.get_or_add_const(b.i64_type, '0')
2331 fn_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', b.fn_ids['array_new'])
2332 builder := b.block_instr4(.call, entry, b.array_type, fn_ref, one, zero, initial_size)
2333 b.block_instr1(.ret, entry, b.void_type, builder)
2334}
2335
2336// emit_builder_append emits emit builder append output for ssa.
2337fn (mut b Builder) emit_builder_append(func_id int, entry BlockID, builder_ptr ValueID, src_ptr ValueID, add_len ValueID) BlockID {
2338 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2339 data_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 0)
2340 len_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 2)
2341 cap_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 3)
2342 old_len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
2343 old_len := b.block_instr1(.zext, entry, b.i64_type, old_len32)
2344 cap32 := b.block_instr1(.load, entry, b.i32_type, cap_ptr)
2345 cap := b.block_instr1(.zext, entry, b.i64_type, cap32)
2346 needed := b.block_instr2(.add, entry, b.i64_type, old_len, add_len)
2347 needs_grow := b.block_instr2(.gt, entry, b.i1_type, needed, cap)
2348
2349 blk_grow := b.m.add_block(func_id, 'builder_append_grow')
2350 blk_copy := b.m.add_block(func_id, 'builder_append_copy')
2351 b.block_instr3(.br, entry, b.void_type, needs_grow, ValueID(blk_grow), ValueID(blk_copy))
2352
2353 two := b.m.get_or_add_const(b.i64_type, '2')
2354 old_data := b.block_instr1(.load, blk_grow, ptr_i8, data_ptr)
2355 double_needed := b.block_instr2(.mul, blk_grow, b.i64_type, needed, two)
2356 new_cap := b.block_instr2(.add, blk_grow, b.i64_type, double_needed, two)
2357 realloc_ref := b.m.add_value(.func_ref, b.void_type, 'realloc', b.fn_ids['realloc'])
2358 new_data := b.block_instr3(.call, blk_grow, ptr_i8, realloc_ref, old_data, new_cap)
2359 b.block_instr2(.store, blk_grow, b.void_type, new_data, data_ptr)
2360 b.block_instr2(.store, blk_grow, b.void_type, new_cap, cap_ptr)
2361 b.block_instr1(.jmp, blk_grow, b.void_type, ValueID(blk_copy))
2362
2363 data := b.block_instr1(.load, blk_copy, ptr_i8, data_ptr)
2364 dest := b.block_instr2(.add, blk_copy, ptr_i8, data, old_len)
2365 memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
2366 b.block_instr4(.call, blk_copy, ptr_i8, memcpy_ref, dest, src_ptr, add_len)
2367 b.block_instr2(.store, blk_copy, b.void_type, needed, len_ptr)
2368 return blk_copy
2369}
2370
2371// generate_builder_write_string_body
2372// supports helper handling in ssa.
2373fn (mut b Builder) generate_builder_write_string_body(func_id int, add_newline bool) {
2374 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2375 ptr_builder := b.m.type_store.get_ptr(b.array_type)
2376 ptr_string := b.m.type_store.get_ptr(b.str_type)
2377 ptr_i32 := b.m.type_store.get_ptr(b.i32_type)
2378 entry := b.m.add_block(func_id, 'entry')
2379 builder_ptr := b.func_add_argument(func_id, ptr_builder, 'builder')
2380 s := b.func_add_argument(func_id, b.str_type, 's')
2381
2382 alloca_s := b.block_instr0(.alloca, entry, ptr_string)
2383 b.block_instr2(.store, entry, b.void_type, s, alloca_s)
2384 zero := b.m.get_or_add_const(b.i64_type, '0')
2385 len_off := b.m.get_or_add_const(b.i64_type, '8')
2386 str_ptr_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i8, alloca_s, zero)
2387 len_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i32, alloca_s, len_off)
2388 str_ptr := b.block_instr1(.load, entry, ptr_i8, str_ptr_ptr)
2389 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
2390 len64 := b.block_instr1(.zext, entry, b.i64_type, len32)
2391 end_block := b.emit_builder_append(func_id, entry, builder_ptr, str_ptr, len64)
2392 if add_newline {
2393 nl := b.m.get_or_add_const(b.i8_type, '10')
2394 alloca_nl := b.block_instr0(.alloca, end_block, ptr_i8)
2395 b.block_instr2(.store, end_block, b.void_type, nl, alloca_nl)
2396 one := b.m.get_or_add_const(b.i64_type, '1')
2397 final_block := b.emit_builder_append(func_id, end_block, builder_ptr, alloca_nl, one)
2398 b.block_instr0(.ret, final_block, b.void_type)
2399 } else {
2400 b.block_instr0(.ret, end_block, b.void_type)
2401 }
2402}
2403
2404// generate_builder_write_ptr_body supports generate builder write ptr body handling for Builder.
2405fn (mut b Builder) generate_builder_write_ptr_body(func_id int) {
2406 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2407 ptr_builder := b.m.type_store.get_ptr(b.array_type)
2408 entry := b.m.add_block(func_id, 'entry')
2409 builder_ptr := b.func_add_argument(func_id, ptr_builder, 'builder')
2410 src_ptr := b.func_add_argument(func_id, ptr_i8, 'ptr')
2411 len := b.func_add_argument(func_id, b.i64_type, 'len')
2412 end_block := b.emit_builder_append(func_id, entry, builder_ptr, src_ptr, len)
2413 b.block_instr0(.ret, end_block, b.void_type)
2414}
2415
2416// generate_builder_write_u8_body supports generate builder write u8 body handling for Builder.
2417fn (mut b Builder) generate_builder_write_u8_body(func_id int) {
2418 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2419 ptr_builder := b.m.type_store.get_ptr(b.array_type)
2420 entry := b.m.add_block(func_id, 'entry')
2421 builder_ptr := b.func_add_argument(func_id, ptr_builder, 'builder')
2422 ch := b.func_add_argument(func_id, b.i8_type, 'ch')
2423 alloca_ch := b.block_instr0(.alloca, entry, ptr_i8)
2424 b.block_instr2(.store, entry, b.void_type, ch, alloca_ch)
2425 one := b.m.get_or_add_const(b.i64_type, '1')
2426 end_block := b.emit_builder_append(func_id, entry, builder_ptr, alloca_ch, one)
2427 b.block_instr0(.ret, end_block, b.void_type)
2428}
2429
2430// generate_builder_str_body supports generate builder str body handling for Builder.
2431fn (mut b Builder) generate_builder_str_body(func_id int) {
2432 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2433 ptr_builder := b.m.type_store.get_ptr(b.array_type)
2434 entry := b.m.add_block(func_id, 'entry')
2435 builder_ptr := b.func_add_argument(func_id, ptr_builder, 'builder')
2436 data_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 0)
2437 len_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 2)
2438 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
2439 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
2440 len := b.block_instr1(.zext, entry, b.i64_type, len32)
2441 // Return an owned copy, not the builder's live buffer (matches strings.Builder.str,
2442 // which memdups): the result is marked non-literal and may be freed independently.
2443 result := b.emit_make_owned_string(entry, data, len)
2444 b.block_instr1(.ret, entry, b.void_type, result)
2445}
2446
2447// generate_builder_last_n_body supports generate builder last n body handling for Builder.
2448fn (mut b Builder) generate_builder_last_n_body(func_id int) {
2449 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2450 ptr_builder := b.m.type_store.get_ptr(b.array_type)
2451 entry := b.m.add_block(func_id, 'entry')
2452 builder_ptr := b.func_add_argument(func_id, ptr_builder, 'builder')
2453 n := b.func_add_argument(func_id, b.i64_type, 'n')
2454 data_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 0)
2455 len_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 2)
2456 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
2457 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
2458 len := b.block_instr1(.zext, entry, b.i64_type, len32)
2459 start := b.block_instr2(.sub, entry, b.i64_type, len, n)
2460 src := b.block_instr2(.add, entry, ptr_i8, data, start)
2461 // Copy out an owned substring (matches strings.Builder.last_n -> spart): the result
2462 // must not alias the interior of the builder buffer, since string.free would later
2463 // pass that non-allocation-start pointer to free().
2464 result := b.emit_make_owned_string(entry, src, n)
2465 b.block_instr1(.ret, entry, b.void_type, result)
2466}
2467
2468// generate_builder_free_body supports generate builder free body handling for Builder.
2469fn (mut b Builder) generate_builder_free_body(func_id int) {
2470 ptr_builder := b.m.type_store.get_ptr(b.array_type)
2471 entry := b.m.add_block(func_id, 'entry')
2472 _ := b.func_add_argument(func_id, ptr_builder, 'builder')
2473 b.block_instr0(.ret, entry, b.void_type)
2474}
2475
2476// register_path_runtime_stubs updates register path runtime stubs state for ssa.
2477fn (mut b Builder) register_path_runtime_stubs() {
2478 ptr_builder := b.m.type_store.get_ptr(b.array_type)
2479 mut p1 := []TypeID{}
2480 p1 << ptr_builder
2481 for name in ['normalize_path_in_builder', 'os.normalize_path_in_builder'] {
2482 normalize_id := b.register_synthetic_function(name, b.void_type, p1)
2483 b.generate_builder_free_body(normalize_id)
2484 }
2485
2486 mut p2 := []TypeID{}
2487 p2 << b.str_type
2488 p2 << b.str_type
2489 for name in ['join_path_single', 'os.join_path_single'] {
2490 join_id := b.register_synthetic_function(name, b.str_type, p2)
2491 b.generate_join_path_single_body(join_id)
2492 }
2493}
2494
2495// generate_join_path_single_body supports generate join path single body handling for Builder.
2496fn (mut b Builder) generate_join_path_single_body(func_id int) {
2497 entry := b.m.add_block(func_id, 'entry')
2498 base := b.func_add_argument(func_id, b.str_type, 'base')
2499 elem := b.func_add_argument(func_id, b.str_type, 'elem')
2500 slash := b.m.add_value(.string_literal, b.str_type, '/', 0)
2501 plus_ref := b.m.add_value(.func_ref, b.str_type, 'string__plus', b.fn_ids['string__plus'])
2502 with_sep := b.block_instr3(.call, entry, b.str_type, plus_ref, base, slash)
2503 result := b.block_instr3(.call, entry, b.str_type, plus_ref, with_sep, elem)
2504 b.block_instr1(.ret, entry, b.void_type, result)
2505}
2506
2507// register_map_runtime_stubs updates register map runtime stubs state for ssa.
2508fn (mut b Builder) register_map_runtime_stubs() {
2509 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2510 ptr_map := b.m.type_store.get_ptr(b.map_type)
2511
2512 mut p2 := []TypeID{}
2513 p2 << ptr_map
2514 p2 << ptr_i8
2515 find_id := b.register_synthetic_function('v3_map_find', b.i64_type, p2)
2516 b.generate_map_find_body(find_id)
2517
2518 mut p6 := []TypeID{}
2519 for _ in 0 .. 6 {
2520 p6 << b.i64_type
2521 }
2522 new_id := b.register_synthetic_function('new_map', b.map_type, p6)
2523 b.generate_new_map_body(new_id)
2524
2525 mut p3 := []TypeID{}
2526 p3 << ptr_map
2527 p3 << ptr_i8
2528 p3 << ptr_i8
2529
2530 mut p5 := []TypeID{}
2531 p5 << ptr_map
2532 p5 << ptr_i8
2533 p5 << ptr_i8
2534 p5 << b.i64_type
2535 p5 << b.i64_type
2536 sized_set_id := b.register_synthetic_function('v3_map_set_sized', b.void_type, p5)
2537 b.generate_map_set_sized_body(sized_set_id)
2538 set_id := b.register_synthetic_function('map__set', b.void_type, p3)
2539 b.generate_map_set_default_body(set_id)
2540
2541 p3 = []TypeID{}
2542 p3 << ptr_map
2543 p3 << ptr_i8
2544 p3 << ptr_i8
2545 get_id := b.register_synthetic_function('map__get', ptr_i8, p3)
2546 b.generate_map_get_body(get_id)
2547 mut p2_map_key := []TypeID{}
2548 p2_map_key << ptr_map
2549 p2_map_key << ptr_i8
2550 get_check_id := b.register_synthetic_function('map__get_check', ptr_i8, p2_map_key)
2551 b.generate_map_get_check_body(get_check_id)
2552 get_or_set_id := b.register_synthetic_function('map__get_or_set', ptr_i8, p3)
2553 b.generate_map_get_body(get_or_set_id)
2554
2555 p2 = []TypeID{}
2556 p2 << ptr_map
2557 p2 << ptr_i8
2558 exists_id := b.register_synthetic_function('map__exists', b.i1_type, p2)
2559 b.generate_map_exists_body(exists_id)
2560
2561 mut p1_ptr := []TypeID{}
2562 p1_ptr << ptr_map
2563 clear_id := b.register_synthetic_function('map__clear', b.void_type, p1_ptr)
2564 b.generate_map_ptr_noop_body(clear_id)
2565
2566 p2 = []TypeID{}
2567 p2 << ptr_map
2568 p2 << ptr_i8
2569 delete_id := b.register_synthetic_function('map__delete', b.void_type, p2)
2570 b.generate_map_delete_body(delete_id)
2571
2572 mut p1_map := []TypeID{}
2573 p1_map << b.map_type
2574 clone_id := b.register_synthetic_function('map__clone', b.map_type, p1_map)
2575 b.generate_map_clone_body(clone_id)
2576}
2577
2578// generate_map_ptr_noop_body supports generate map ptr noop body handling for Builder.
2579fn (mut b Builder) generate_map_ptr_noop_body(func_id int) {
2580 ptr_map := b.m.type_store.get_ptr(b.map_type)
2581 entry := b.m.add_block(func_id, 'entry')
2582 _ := b.func_add_argument(func_id, ptr_map, 'm')
2583 b.block_instr0(.ret, entry, b.void_type)
2584}
2585
2586// generate_map_delete_body supports generate map delete body handling for Builder.
2587fn (mut b Builder) generate_map_delete_body(func_id int) {
2588 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2589 ptr_map := b.m.type_store.get_ptr(b.map_type)
2590 entry := b.m.add_block(func_id, 'entry')
2591 map_ptr := b.func_add_argument(func_id, ptr_map, 'm')
2592 key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
2593 find_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_find', b.fn_ids['v3_map_find'])
2594 idx := b.block_instr3(.call, entry, b.i64_type, find_ref, map_ptr, key_ptr)
2595 zero := b.m.get_or_add_const(b.i64_type, '0')
2596 found := b.block_instr2(.ge, entry, b.i1_type, idx, zero)
2597 blk_found := b.m.add_block(func_id, 'map_delete_found')
2598 blk_done := b.m.add_block(func_id, 'map_delete_done')
2599 b.block_instr3(.br, entry, b.void_type, found, ValueID(blk_found), ValueID(blk_done))
2600
2601 state := b.map_state_ptr(blk_found, map_ptr)
2602 len_ptr := b.map_state_field_ptr(blk_found, state, 3)
2603 len := b.block_instr1(.load, blk_found, b.i64_type, len_ptr)
2604 one := b.m.get_or_add_const(b.i64_type, '1')
2605 new_len := b.block_instr2(.sub, blk_found, b.i64_type, len, one)
2606 b.block_instr2(.store, blk_found, b.void_type, new_len, len_ptr)
2607 b.block_instr1(.jmp, blk_found, b.void_type, ValueID(blk_done))
2608
2609 b.block_instr0(.ret, blk_done, b.void_type)
2610}
2611
2612// generate_map_clone_body supports generate map clone body handling for Builder.
2613fn (mut b Builder) generate_map_clone_body(func_id int) {
2614 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2615 ptr_map := b.m.type_store.get_ptr(b.map_type)
2616 ptr_state := b.m.type_store.get_ptr(b.map_state_type)
2617 entry := b.m.add_block(func_id, 'entry')
2618 m := b.func_add_argument(func_id, b.map_type, 'm')
2619 map_slot := b.block_instr0(.alloca, entry, ptr_map)
2620 b.block_instr2(.store, entry, b.void_type, m, map_slot)
2621 state_field := b.block_struct_field_ptr(entry, map_slot, b.map_type, 0)
2622 old_state := b.block_instr1(.load, entry, ptr_state, state_field)
2623 zero_state := b.m.get_or_add_const(ptr_state, '0')
2624 has_state := b.block_instr2(.ne, entry, b.i1_type, old_state, zero_state)
2625 blk_clone := b.m.add_block(func_id, 'map_clone_copy')
2626 blk_empty := b.m.add_block(func_id, 'map_clone_empty')
2627 b.block_instr3(.br, entry, b.void_type, has_state, ValueID(blk_clone), ValueID(blk_empty))
2628
2629 one := b.m.get_or_add_const(b.i64_type, '1')
2630 state_size := b.m.get_or_add_const(b.i64_type, '48')
2631 calloc_ref := b.m.add_value(.func_ref, b.void_type, 'calloc', b.fn_ids['calloc'])
2632 new_state_raw := b.block_instr3(.call, blk_clone, ptr_i8, calloc_ref, one, state_size)
2633 new_state := b.block_instr1(.bitcast, blk_clone, ptr_state, new_state_raw)
2634
2635 old_keys_ptr := b.map_state_field_ptr(blk_clone, old_state, 0)
2636 old_vals_ptr := b.map_state_field_ptr(blk_clone, old_state, 1)
2637 old_cap_ptr := b.map_state_field_ptr(blk_clone, old_state, 2)
2638 old_len_ptr := b.map_state_field_ptr(blk_clone, old_state, 3)
2639 old_key_size_ptr := b.map_state_field_ptr(blk_clone, old_state, 4)
2640 old_val_size_ptr := b.map_state_field_ptr(blk_clone, old_state, 5)
2641 old_keys := b.block_instr1(.load, blk_clone, ptr_i8, old_keys_ptr)
2642 old_vals := b.block_instr1(.load, blk_clone, ptr_i8, old_vals_ptr)
2643 cap := b.block_instr1(.load, blk_clone, b.i64_type, old_cap_ptr)
2644 len := b.block_instr1(.load, blk_clone, b.i64_type, old_len_ptr)
2645 key_size := b.block_instr1(.load, blk_clone, b.i64_type, old_key_size_ptr)
2646 val_size := b.block_instr1(.load, blk_clone, b.i64_type, old_val_size_ptr)
2647 new_keys := b.block_instr3(.call, blk_clone, ptr_i8, calloc_ref, cap, key_size)
2648 new_vals := b.block_instr3(.call, blk_clone, ptr_i8, calloc_ref, cap, val_size)
2649 key_bytes := b.block_instr2(.mul, blk_clone, b.i64_type, len, key_size)
2650 val_bytes := b.block_instr2(.mul, blk_clone, b.i64_type, len, val_size)
2651 memcpy_ref_keys := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
2652 memcpy_ref_vals := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
2653 b.block_instr4(.call, blk_clone, ptr_i8, memcpy_ref_keys, new_keys, old_keys, key_bytes)
2654 b.block_instr4(.call, blk_clone, ptr_i8, memcpy_ref_vals, new_vals, old_vals, val_bytes)
2655
2656 new_keys_ptr := b.map_state_field_ptr(blk_clone, new_state, 0)
2657 new_vals_ptr := b.map_state_field_ptr(blk_clone, new_state, 1)
2658 new_cap_ptr := b.map_state_field_ptr(blk_clone, new_state, 2)
2659 new_len_ptr := b.map_state_field_ptr(blk_clone, new_state, 3)
2660 new_key_size_ptr := b.map_state_field_ptr(blk_clone, new_state, 4)
2661 new_val_size_ptr := b.map_state_field_ptr(blk_clone, new_state, 5)
2662 b.block_instr2(.store, blk_clone, b.void_type, new_keys, new_keys_ptr)
2663 b.block_instr2(.store, blk_clone, b.void_type, new_vals, new_vals_ptr)
2664 b.block_instr2(.store, blk_clone, b.void_type, cap, new_cap_ptr)
2665 b.block_instr2(.store, blk_clone, b.void_type, len, new_len_ptr)
2666 b.block_instr2(.store, blk_clone, b.void_type, key_size, new_key_size_ptr)
2667 b.block_instr2(.store, blk_clone, b.void_type, val_size, new_val_size_ptr)
2668
2669 result_slot := b.block_instr0(.alloca, blk_clone, ptr_map)
2670 result_state_field := b.block_struct_field_ptr(blk_clone, result_slot, b.map_type, 0)
2671 b.block_instr2(.store, blk_clone, b.void_type, new_state, result_state_field)
2672 result := b.block_instr1(.load, blk_clone, b.map_type, result_slot)
2673 b.block_instr1(.ret, blk_clone, b.void_type, result)
2674
2675 b.block_instr1(.ret, blk_empty, b.void_type, m)
2676}
2677
2678// register_u8_runtime_stubs updates register u8 runtime stubs state for ssa.
2679fn (mut b Builder) register_u8_runtime_stubs() {
2680 mut p1 := []TypeID{}
2681 p1 << b.i8_type
2682 for name in ['u8.is_digit', 'u8.is_letter', 'u8.is_alnum', 'u8.is_capital'] {
2683 func_id := b.register_synthetic_function(name, b.i1_type, p1)
2684 b.generate_u8_predicate_body(func_id, name)
2685 }
2686}
2687
2688// generate_u8_predicate_body supports generate u8 predicate body handling for Builder.
2689fn (mut b Builder) generate_u8_predicate_body(func_id int, name string) {
2690 entry := b.m.add_block(func_id, 'entry')
2691 c := b.func_add_argument(func_id, b.i8_type, 'c')
2692 digit := b.u8_in_range(entry, c, `0`, `9`)
2693 lower := b.u8_in_range(entry, c, `a`, `z`)
2694 upper := b.u8_in_range(entry, c, `A`, `Z`)
2695 letter := b.block_instr2(.or_, entry, b.i1_type, lower, upper)
2696 result := match name {
2697 'u8.is_digit' {
2698 digit
2699 }
2700 'u8.is_letter' {
2701 letter
2702 }
2703 'u8.is_alnum' {
2704 b.block_instr2(.or_, entry, b.i1_type, digit, letter)
2705 }
2706 else {
2707 upper
2708 }
2709 }
2710
2711 b.block_instr1(.ret, entry, b.void_type, result)
2712}
2713
2714// u8_in_range supports u8 in range handling for Builder.
2715fn (mut b Builder) u8_in_range(block_id BlockID, c ValueID, low u8, high u8) ValueID {
2716 low_v := b.m.get_or_add_const(b.i8_type, '${int(low)}')
2717 high_v := b.m.get_or_add_const(b.i8_type, '${int(high)}')
2718 ge_low := b.block_instr2(.ge, block_id, b.i1_type, c, low_v)
2719 le_high := b.block_instr2(.le, block_id, b.i1_type, c, high_v)
2720 return b.block_instr2(.and_, block_id, b.i1_type, ge_low, le_high)
2721}
2722
2723// register_heap_tracking_stubs updates register heap tracking stubs state for ssa.
2724fn (mut b Builder) register_heap_tracking_stubs() {
2725 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2726 mut p2 := []TypeID{}
2727 p2 << ptr_i8
2728 p2 << b.i64_type
2729 alloc_id := b.register_synthetic_function('_ht_alloc', b.void_type, p2)
2730 b.generate_void_noop_body(alloc_id)
2731 mut p1 := []TypeID{}
2732 p1 << ptr_i8
2733 free_id := b.register_synthetic_function('_ht_free', b.void_type, p1)
2734 b.generate_void_noop_body(free_id)
2735}
2736
2737// generate_void_noop_body supports generate void noop body handling for Builder.
2738fn (mut b Builder) generate_void_noop_body(func_id int) {
2739 entry := b.m.add_block(func_id, 'entry')
2740 b.block_instr0(.ret, entry, b.void_type)
2741}
2742
2743// register_process_capture_stubs updates register process capture stubs state for ssa.
2744fn (mut b Builder) register_process_capture_stubs() {
2745 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2746 mut p3 := []TypeID{}
2747 p3 << ptr_i8
2748 p3 << ptr_i8
2749 p3 << ptr_i8
2750 for name in ['v_os_execute_capture_start', 'v_os_exec_capture_start'] {
2751 func_id := b.register_synthetic_function(name, b.i64_type, p3)
2752 b.generate_const_i64_body(func_id, '1')
2753 }
2754}
2755
2756// register_file_check_stubs updates register file check stubs state for ssa.
2757fn (mut b Builder) register_file_check_stubs() {
2758 mut p1 := []TypeID{}
2759 p1 << b.i64_type
2760 for name in ['check_fwrite', 'os.check_fwrite', 'check_fread', 'os.check_fread'] {
2761 func_id := b.register_synthetic_function(name, b.i64_type, p1)
2762 b.generate_identity_i64_body(func_id)
2763 }
2764}
2765
2766// generate_identity_i64_body supports generate identity i64 body handling for Builder.
2767fn (mut b Builder) generate_identity_i64_body(func_id int) {
2768 entry := b.m.add_block(func_id, 'entry')
2769 n := b.func_add_argument(func_id, b.i64_type, 'n')
2770 b.block_instr1(.ret, entry, b.void_type, n)
2771}
2772
2773// register_fd_macro_stubs updates register fd macro stubs state for ssa.
2774fn (mut b Builder) register_fd_macro_stubs() {
2775 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2776 mut fd_set_params := []TypeID{}
2777 fd_set_params << ptr_i8
2778 zero_id := b.register_synthetic_function('FD_ZERO', b.void_type, fd_set_params)
2779 b.generate_void_noop_with_params_body(zero_id, fd_set_params)
2780
2781 mut fd_check_params := []TypeID{}
2782 fd_check_params << b.i64_type
2783 fd_check_params << ptr_i8
2784 set_id := b.register_synthetic_function('FD_SET', b.void_type, fd_check_params)
2785 b.generate_void_noop_with_params_body(set_id, fd_check_params)
2786 isset_id := b.register_synthetic_function('FD_ISSET', b.i64_type, fd_check_params)
2787 b.generate_const_i64_with_params_body(isset_id, fd_check_params, '0')
2788}
2789
2790// register_signal_macro_stubs updates register signal macro stubs state for ssa.
2791fn (mut b Builder) register_signal_macro_stubs() {
2792 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2793 mut params := []TypeID{}
2794 params << b.i64_type
2795 params << ptr_i8
2796 name := 'v_signal_with_handler_cast'
2797 func_id := b.register_synthetic_function(name, ptr_i8, params)
2798 if fn_type := b.fn_types[name] {
2799 b.c_fn_types[name] = fn_type
2800 }
2801 b.c_fn_ids[name] = func_id
2802 b.generate_const_ptr_with_params_body(func_id, params, ptr_i8, '0')
2803}
2804
2805// generate_void_noop_with_params_body supports generate_void_noop_with_params_body handling in ssa.
2806fn (mut b Builder) generate_void_noop_with_params_body(func_id int, params []TypeID) {
2807 entry := b.m.add_block(func_id, 'entry')
2808 for i, param in params {
2809 _ := b.func_add_argument(func_id, param, 'p${i}')
2810 }
2811 b.block_instr0(.ret, entry, b.void_type)
2812}
2813
2814// generate_const_i64_with_params_body supports generate_const_i64_with_params_body handling in ssa.
2815fn (mut b Builder) generate_const_i64_with_params_body(func_id int, params []TypeID, value string) {
2816 entry := b.m.add_block(func_id, 'entry')
2817 for i, param in params {
2818 _ := b.func_add_argument(func_id, param, 'p${i}')
2819 }
2820 result := b.m.get_or_add_const(b.i64_type, value)
2821 b.block_instr1(.ret, entry, b.void_type, result)
2822}
2823
2824// generate_const_ptr_with_params_body supports generate_const_ptr_with_params_body handling in ssa.
2825fn (mut b Builder) generate_const_ptr_with_params_body(func_id int, params []TypeID, ret_type TypeID, value string) {
2826 entry := b.m.add_block(func_id, 'entry')
2827 for i, param in params {
2828 _ := b.func_add_argument(func_id, param, 'p${i}')
2829 }
2830 result := b.m.get_or_add_const(ret_type, value)
2831 b.block_instr1(.ret, entry, b.void_type, result)
2832}
2833
2834// register_os_stat_stubs updates register os stat stubs state for ssa.
2835fn (mut b Builder) register_os_stat_stubs() {
2836 mut p1 := []TypeID{}
2837 p1 << b.str_type
2838 for name in ['is_dir', 'os.is_dir'] {
2839 is_dir_id := b.register_synthetic_function(name, b.i1_type, p1)
2840 b.generate_os_stat_kind_body(is_dir_id, 'stat', '16384')
2841 }
2842 for name in ['is_link', 'os.is_link'] {
2843 is_link_id := b.register_synthetic_function(name, b.i1_type, p1)
2844 b.generate_os_stat_kind_body(is_link_id, 'lstat', '40960')
2845 }
2846 ls_result_type := b.option_type_id('[]string')
2847 for name in ['ls', 'os.ls'] {
2848 ls_id := b.register_synthetic_function(name, ls_result_type, p1)
2849 b.generate_os_ls_body(ls_id, ls_result_type)
2850 }
2851}
2852
2853// generate_os_stat_kind_body supports generate os stat kind body handling for Builder.
2854fn (mut b Builder) generate_os_stat_kind_body(func_id int, stat_fn string, expected_mode string) {
2855 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2856 ptr_i32 := b.m.type_store.get_ptr(b.i32_type)
2857 entry := b.m.add_block(func_id, 'entry')
2858 path := b.func_add_argument(func_id, b.str_type, 'path')
2859 path_data := b.emit_cstring_from_string(entry, path)
2860
2861 stat_size := b.m.get_or_add_const(b.i64_type, '256')
2862 malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
2863 stat_buf := b.block_instr2(.call, entry, ptr_i8, malloc_ref, stat_size)
2864 stat_idx := b.c_fn_ids[stat_fn] or {
2865 false_val := b.m.get_or_add_const(b.i1_type, '0')
2866 b.block_instr1(.ret, entry, b.void_type, false_val)
2867 return
2868 }
2869 stat_ref := b.m.add_value(.func_ref, b.void_type, stat_fn, stat_idx)
2870 res := b.block_instr3(.call, entry, b.i64_type, stat_ref, path_data, stat_buf)
2871 zero64 := b.m.get_or_add_const(b.i64_type, '0')
2872 ok := b.block_instr2(.eq, entry, b.i1_type, res, zero64)
2873 ok_block := b.m.add_block(func_id, 'stat_ok')
2874 fail_block := b.m.add_block(func_id, 'stat_fail')
2875 b.block_instr3(.br, entry, b.void_type, ok, ValueID(ok_block), ValueID(fail_block))
2876
2877 false_val := b.m.get_or_add_const(b.i1_type, '0')
2878 b.block_instr1(.ret, fail_block, b.void_type, false_val)
2879
2880 mode_off := b.m.get_or_add_const(b.i64_type, '4')
2881 mode_raw_ptr := b.block_instr2(.get_element_ptr, ok_block, ptr_i8, stat_buf, mode_off)
2882 mode_ptr := b.block_instr1(.bitcast, ok_block, ptr_i32, mode_raw_ptr)
2883 mode := b.block_instr1(.load, ok_block, b.i32_type, mode_ptr)
2884 mask := b.m.get_or_add_const(b.i32_type, '61440')
2885 masked := b.block_instr2(.and_, ok_block, b.i32_type, mode, mask)
2886 expected := b.m.get_or_add_const(b.i32_type, expected_mode)
2887 is_kind := b.block_instr2(.eq, ok_block, b.i1_type, masked, expected)
2888 b.block_instr1(.ret, ok_block, b.void_type, is_kind)
2889}
2890
2891// generate_os_ls_body supports generate os ls body handling for Builder.
2892fn (mut b Builder) generate_os_ls_body(func_id int, result_type TypeID) {
2893 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2894 ptr_array := b.m.type_store.get_ptr(b.array_type)
2895 ptr_string := b.m.type_store.get_ptr(b.str_type)
2896 entry := b.m.add_block(func_id, 'entry')
2897 path := b.func_add_argument(func_id, b.str_type, 'path')
2898 path_data := b.emit_cstring_from_string(entry, path)
2899
2900 str_size := b.m.get_or_add_const(b.i64_type, '16')
2901 zero64 := b.m.get_or_add_const(b.i64_type, '0')
2902 cap := b.m.get_or_add_const(b.i64_type, '50')
2903 array_new_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', b.fn_ids['array_new'])
2904 arr := b.block_instr4(.call, entry, b.array_type, array_new_ref, str_size, zero64, cap)
2905 arr_alloca := b.block_instr0(.alloca, entry, ptr_array)
2906 b.block_instr2(.store, entry, b.void_type, arr, arr_alloca)
2907
2908 opendir_ref := b.m.add_value(.func_ref, b.void_type, 'opendir', b.c_fn_ids['opendir'])
2909 dir_ptr := b.block_instr2(.call, entry, ptr_i8, opendir_ref, path_data)
2910 null_ptr := b.m.get_or_add_const(ptr_i8, '0')
2911 dir_is_null := b.block_instr2(.eq, entry, b.i1_type, dir_ptr, null_ptr)
2912 blk_loop := b.m.add_block(func_id, 'ls_loop')
2913 blk_empty := b.m.add_block(func_id, 'ls_empty')
2914 b.block_instr3(.br, entry, b.void_type, dir_is_null, ValueID(blk_empty), ValueID(blk_loop))
2915
2916 empty_result := b.block_instr1(.load, blk_empty, b.array_type, arr_alloca)
2917 empty_wrapped := b.block_option_value(blk_empty, result_type, false, empty_result)
2918 b.block_instr1(.ret, blk_empty, b.void_type, empty_wrapped)
2919
2920 readdir_ref := b.m.add_value(.func_ref, b.void_type, 'readdir', b.c_fn_ids['readdir'])
2921 ent := b.block_instr2(.call, blk_loop, ptr_i8, readdir_ref, dir_ptr)
2922 ent_is_null := b.block_instr2(.eq, blk_loop, b.i1_type, ent, null_ptr)
2923 blk_done := b.m.add_block(func_id, 'ls_done')
2924 blk_check := b.m.add_block(func_id, 'ls_check')
2925 b.block_instr3(.br, blk_loop, b.void_type, ent_is_null, ValueID(blk_done), ValueID(blk_check))
2926
2927 name_off := b.m.get_or_add_const(b.i64_type, '21')
2928 name_ptr := b.block_instr2(.get_element_ptr, blk_check, ptr_i8, ent, name_off)
2929 first := b.block_instr1(.load, blk_check, b.i8_type, name_ptr)
2930 zero8 := b.m.get_or_add_const(b.i8_type, '0')
2931 has_name := b.block_instr2(.ne, blk_check, b.i1_type, first, zero8)
2932 blk_push := b.m.add_block(func_id, 'ls_push')
2933 blk_next := b.m.add_block(func_id, 'ls_next')
2934 b.block_instr3(.br, blk_check, b.void_type, has_name, ValueID(blk_push), ValueID(blk_next))
2935
2936 tos_clone_ref := b.m.add_value(.func_ref, b.str_type, 'tos_clone', b.fn_ids['tos_clone'])
2937 name_str := b.block_instr2(.call, blk_push, b.str_type, tos_clone_ref, name_ptr)
2938 name_alloca := b.block_instr0(.alloca, blk_push, ptr_string)
2939 b.block_instr2(.store, blk_push, b.void_type, name_str, name_alloca)
2940 name_elem := b.block_instr1(.bitcast, blk_push, ptr_i8, name_alloca)
2941 array_push_ref := b.m.add_value(.func_ref, b.void_type, 'array_push', b.fn_ids['array_push'])
2942 b.block_instr3(.call, blk_push, b.void_type, array_push_ref, arr_alloca, name_elem)
2943 b.block_instr1(.jmp, blk_push, b.void_type, ValueID(blk_loop))
2944
2945 b.block_instr1(.jmp, blk_next, b.void_type, ValueID(blk_loop))
2946
2947 closedir_ref := b.m.add_value(.func_ref, b.void_type, 'closedir', b.c_fn_ids['closedir'])
2948 b.block_instr2(.call, blk_done, b.i64_type, closedir_ref, dir_ptr)
2949 result := b.block_instr1(.load, blk_done, b.array_type, arr_alloca)
2950 wrapped := b.block_option_value(blk_done, result_type, true, result)
2951 b.block_instr1(.ret, blk_done, b.void_type, wrapped)
2952}
2953
2954// block_option_value supports block option value handling for Builder.
2955fn (mut b Builder) block_option_value(block_id BlockID, opt_typ TypeID, ok bool, raw_value ValueID) ValueID {
2956 ptr_opt := b.m.type_store.get_ptr(opt_typ)
2957 alloca := b.block_instr0(.alloca, block_id, ptr_opt)
2958 ok_ptr := b.block_struct_field_ptr(block_id, alloca, opt_typ, 0)
2959 ok_val := b.m.get_or_add_const(b.i1_type, if ok { '1' } else { '0' })
2960 b.block_instr2(.store, block_id, b.void_type, ok_val, ok_ptr)
2961 value_typ := b.option_value_type(opt_typ)
2962 if value_typ != b.void_type {
2963 value_ptr := b.block_struct_field_ptr(block_id, alloca, opt_typ, 1)
2964 mut value := raw_value
2965 if value <= 0 {
2966 value = b.m.get_or_add_const(value_typ, '0')
2967 }
2968 b.block_instr2(.store, block_id, b.void_type, value, value_ptr)
2969 }
2970 return b.block_instr1(.load, block_id, opt_typ, alloca)
2971}
2972
2973// emit_cstring_from_string converts emit cstring from string data for ssa.
2974fn (mut b Builder) emit_cstring_from_string(block_id BlockID, value ValueID) ValueID {
2975 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
2976 ptr_string := b.m.type_store.get_ptr(b.str_type)
2977 value_alloca := b.block_instr0(.alloca, block_id, ptr_string)
2978 b.block_instr2(.store, block_id, b.void_type, value, value_alloca)
2979 data_ptr := b.block_struct_field_ptr(block_id, value_alloca, b.str_type, 0)
2980 len_ptr := b.block_struct_field_ptr(block_id, value_alloca, b.str_type, 1)
2981 data := b.block_instr1(.load, block_id, ptr_i8, data_ptr)
2982 len32 := b.block_instr1(.load, block_id, b.i32_type, len_ptr)
2983 len := b.block_instr1(.zext, block_id, b.i64_type, len32)
2984 one := b.m.get_or_add_const(b.i64_type, '1')
2985 alloc_len := b.block_instr2(.add, block_id, b.i64_type, len, one)
2986 malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
2987 out_data := b.block_instr2(.call, block_id, ptr_i8, malloc_ref, alloc_len)
2988 memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
2989 b.block_instr4(.call, block_id, ptr_i8, memcpy_ref, out_data, data, len)
2990 zero8 := b.m.get_or_add_const(b.i8_type, '0')
2991 term_ptr := b.block_instr2(.add, block_id, ptr_i8, out_data, len)
2992 b.block_instr2(.store, block_id, b.void_type, zero8, term_ptr)
2993 return out_data
2994}
2995
2996// generate_const_i64_body supports generate const i64 body handling for Builder.
2997fn (mut b Builder) generate_const_i64_body(func_id int, value string) {
2998 entry := b.m.add_block(func_id, 'entry')
2999 result := b.m.get_or_add_const(b.i64_type, value)
3000 b.block_instr1(.ret, entry, b.void_type, result)
3001}
3002
3003// register_prealloc_atomic_stubs updates register prealloc atomic stubs state for ssa.
3004fn (mut b Builder) register_prealloc_atomic_stubs() {
3005 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3006 mut p1_ptr := []TypeID{}
3007 p1_ptr << ptr_i64
3008 load_id := b.register_synthetic_function('v_prealloc_atomic_load_i32', b.i64_type, p1_ptr)
3009 b.generate_atomic_load_i64_body(load_id)
3010
3011 mut p2 := []TypeID{}
3012 p2 << ptr_i64
3013 p2 << b.i64_type
3014 add_id := b.register_synthetic_function('v_prealloc_atomic_add_i32', b.i64_type, p2)
3015 b.generate_atomic_add_i64_body(add_id)
3016 store_id := b.register_synthetic_function('v_prealloc_atomic_store_i32', b.i64_type, p2)
3017 b.generate_atomic_store_i64_body(store_id)
3018
3019 mut p3 := []TypeID{}
3020 p3 << ptr_i64
3021 p3 << b.i64_type
3022 p3 << b.i64_type
3023 cas_id := b.register_synthetic_function('v_prealloc_atomic_cas_i32', b.i64_type, p3)
3024 b.generate_atomic_cas_i64_body(cas_id)
3025}
3026
3027// generate_atomic_load_i64_body supports generate atomic load i64 body handling for Builder.
3028fn (mut b Builder) generate_atomic_load_i64_body(func_id int) {
3029 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3030 entry := b.m.add_block(func_id, 'entry')
3031 ptr := b.func_add_argument(func_id, ptr_i64, 'ptr')
3032 value := b.block_instr1(.load, entry, b.i64_type, ptr)
3033 b.block_instr1(.ret, entry, b.void_type, value)
3034}
3035
3036// generate_atomic_store_i64_body supports generate atomic store i64 body handling for Builder.
3037fn (mut b Builder) generate_atomic_store_i64_body(func_id int) {
3038 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3039 entry := b.m.add_block(func_id, 'entry')
3040 ptr := b.func_add_argument(func_id, ptr_i64, 'ptr')
3041 value := b.func_add_argument(func_id, b.i64_type, 'value')
3042 b.block_instr2(.store, entry, b.void_type, value, ptr)
3043 b.block_instr1(.ret, entry, b.void_type, value)
3044}
3045
3046// generate_atomic_add_i64_body supports generate atomic add i64 body handling for Builder.
3047fn (mut b Builder) generate_atomic_add_i64_body(func_id int) {
3048 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3049 entry := b.m.add_block(func_id, 'entry')
3050 ptr := b.func_add_argument(func_id, ptr_i64, 'ptr')
3051 delta := b.func_add_argument(func_id, b.i64_type, 'delta')
3052 old := b.block_instr1(.load, entry, b.i64_type, ptr)
3053 new_value := b.block_instr2(.add, entry, b.i64_type, old, delta)
3054 b.block_instr2(.store, entry, b.void_type, new_value, ptr)
3055 b.block_instr1(.ret, entry, b.void_type, new_value)
3056}
3057
3058// generate_atomic_cas_i64_body converts generate atomic cas i64 body data for ssa.
3059fn (mut b Builder) generate_atomic_cas_i64_body(func_id int) {
3060 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3061 entry := b.m.add_block(func_id, 'entry')
3062 ptr := b.func_add_argument(func_id, ptr_i64, 'ptr')
3063 expected := b.func_add_argument(func_id, b.i64_type, 'expected')
3064 desired := b.func_add_argument(func_id, b.i64_type, 'desired')
3065 old := b.block_instr1(.load, entry, b.i64_type, ptr)
3066 ok := b.block_instr2(.eq, entry, b.i1_type, old, expected)
3067 then_block := b.m.add_block(func_id, 'cas_store')
3068 else_block := b.m.add_block(func_id, 'cas_done')
3069 b.block_instr3(.br, entry, b.void_type, ok, ValueID(then_block), ValueID(else_block))
3070 b.block_instr2(.store, then_block, b.void_type, desired, ptr)
3071 one := b.m.get_or_add_const(b.i64_type, '1')
3072 zero := b.m.get_or_add_const(b.i64_type, '0')
3073 b.block_instr1(.ret, then_block, b.void_type, one)
3074 b.block_instr1(.ret, else_block, b.void_type, zero)
3075}
3076
3077// register_array_string_stubs updates register array string stubs state for ssa.
3078fn (mut b Builder) register_array_string_stubs() {
3079 mut p1 := []TypeID{}
3080 p1 << b.array_type
3081 array_str_id := b.register_synthetic_function('Array_str', b.str_type, p1)
3082 b.generate_const_string_body(array_str_id, '[]')
3083 bytestr_id := b.register_synthetic_function('bytestr', b.str_type, p1)
3084 b.generate_array_bytestr_body(bytestr_id)
3085 array_bytestr_id := b.register_synthetic_function('Array_u8__bytestr', b.str_type, p1)
3086 b.generate_array_bytestr_body(array_bytestr_id)
3087 array_hex_id := b.register_synthetic_function('Array_u8__hex', b.str_type, p1)
3088 b.generate_const_string_body(array_hex_id, '')
3089 rune_string_id := b.register_synthetic_function('Array_rune__string', b.str_type, p1)
3090 b.generate_const_string_body(rune_string_id, '')
3091
3092 mut p2 := []TypeID{}
3093 p2 << b.array_type
3094 p2 << b.str_type
3095 for name in ['array_string_join', 'Array_string__join'] {
3096 func_id := b.register_synthetic_function(name, b.str_type, p2)
3097 b.generate_array_string_join_body(func_id)
3098 }
3099}
3100
3101// generate_array_string_join_body supports generate array string join body handling for Builder.
3102fn (mut b Builder) generate_array_string_join_body(func_id int) {
3103 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3104 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3105 ptr_array := b.m.type_store.get_ptr(b.array_type)
3106 entry := b.m.add_block(func_id, 'entry')
3107 arr := b.func_add_argument(func_id, b.array_type, 'arr')
3108 sep := b.func_add_argument(func_id, b.str_type, 'sep')
3109 arr_alloca := b.block_instr0(.alloca, entry, ptr_array)
3110 builder_alloca := b.block_instr0(.alloca, entry, ptr_array)
3111 i_alloca := b.block_instr0(.alloca, entry, ptr_i64)
3112 b.block_instr2(.store, entry, b.void_type, arr, arr_alloca)
3113 zero := b.m.get_or_add_const(b.i64_type, '0')
3114 one := b.m.get_or_add_const(b.i64_type, '1')
3115 b.block_instr2(.store, entry, b.void_type, zero, i_alloca)
3116
3117 new_ref := b.m.add_value(.func_ref, b.array_type, 'strings.new_builder',
3118 b.fn_ids['strings.new_builder'])
3119 initial_cap := b.m.get_or_add_const(b.i64_type, '16')
3120 builder := b.block_instr2(.call, entry, b.array_type, new_ref, initial_cap)
3121 b.block_instr2(.store, entry, b.void_type, builder, builder_alloca)
3122
3123 data_ptr := b.block_struct_field_ptr(entry, arr_alloca, b.array_type, 0)
3124 len_ptr := b.block_struct_field_ptr(entry, arr_alloca, b.array_type, 2)
3125 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
3126 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
3127 len := b.block_instr1(.zext, entry, b.i64_type, len32)
3128
3129 loop := b.m.add_block(func_id, 'array_join_loop')
3130 body := b.m.add_block(func_id, 'array_join_body')
3131 write_sep := b.m.add_block(func_id, 'array_join_write_sep')
3132 write_elem := b.m.add_block(func_id, 'array_join_write_elem')
3133 done := b.m.add_block(func_id, 'array_join_done')
3134 b.block_instr1(.jmp, entry, b.void_type, ValueID(loop))
3135
3136 i_val := b.block_instr1(.load, loop, b.i64_type, i_alloca)
3137 more := b.block_instr2(.lt, loop, b.i1_type, i_val, len)
3138 b.block_instr3(.br, loop, b.void_type, more, ValueID(body), ValueID(done))
3139
3140 needs_sep := b.block_instr2(.gt, body, b.i1_type, i_val, zero)
3141 b.block_instr3(.br, body, b.void_type, needs_sep, ValueID(write_sep), ValueID(write_elem))
3142
3143 write_ref := b.m.add_value(.func_ref, b.void_type, 'strings.Builder.write_string',
3144 b.fn_ids['strings.Builder.write_string'])
3145 b.block_instr3(.call, write_sep, b.void_type, write_ref, builder_alloca, sep)
3146 b.block_instr1(.jmp, write_sep, b.void_type, ValueID(write_elem))
3147
3148 elem_size := b.m.get_or_add_const(b.i64_type, '${b.m.type_size(b.str_type)}')
3149 offset := b.block_instr2(.mul, write_elem, b.i64_type, i_val, elem_size)
3150 elem_ptr_raw := b.block_instr2(.add, write_elem, ptr_i8, data, offset)
3151 elem_ptr := b.block_instr1(.bitcast, write_elem, b.m.type_store.get_ptr(b.str_type),
3152 elem_ptr_raw)
3153 elem := b.block_instr1(.load, write_elem, b.str_type, elem_ptr)
3154 write_ref2 := b.m.add_value(.func_ref, b.void_type, 'strings.Builder.write_string',
3155 b.fn_ids['strings.Builder.write_string'])
3156 b.block_instr3(.call, write_elem, b.void_type, write_ref2, builder_alloca, elem)
3157 next_i := b.block_instr2(.add, write_elem, b.i64_type, i_val, one)
3158 b.block_instr2(.store, write_elem, b.void_type, next_i, i_alloca)
3159 b.block_instr1(.jmp, write_elem, b.void_type, ValueID(loop))
3160
3161 str_ref := b.m.add_value(.func_ref, b.str_type, 'strings.Builder.str',
3162 b.fn_ids['strings.Builder.str'])
3163 result := b.block_instr2(.call, done, b.str_type, str_ref, builder_alloca)
3164 b.block_instr1(.ret, done, b.void_type, result)
3165}
3166
3167// generate_array_bytestr_body supports generate array bytestr body handling for Builder.
3168fn (mut b Builder) generate_array_bytestr_body(func_id int) {
3169 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3170 ptr_array := b.m.type_store.get_ptr(b.array_type)
3171 entry := b.m.add_block(func_id, 'entry')
3172 arr := b.func_add_argument(func_id, b.array_type, 'arr')
3173
3174 alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
3175 b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
3176 data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
3177 len_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 2)
3178 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
3179 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
3180 len := b.block_instr1(.zext, entry, b.i64_type, len32)
3181
3182 one := b.m.get_or_add_const(b.i64_type, '1')
3183 alloc_len := b.block_instr2(.add, entry, b.i64_type, len, one)
3184 malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
3185 out_data := b.block_instr2(.call, entry, ptr_i8, malloc_ref, alloc_len)
3186 memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
3187 b.block_instr4(.call, entry, ptr_i8, memcpy_ref, out_data, data, len)
3188 zero8 := b.m.get_or_add_const(b.i8_type, '0')
3189 term_ptr := b.block_instr2(.add, entry, ptr_i8, out_data, len)
3190 b.block_instr2(.store, entry, b.void_type, zero8, term_ptr)
3191
3192 result := b.emit_make_string(entry, out_data, len, 0)
3193 b.block_instr1(.ret, entry, b.void_type, result)
3194}
3195
3196// register_ierror_stubs updates register ierror stubs state for ssa.
3197fn (mut b Builder) register_ierror_stubs() {
3198 ptr_ierror := b.m.type_store.get_ptr(b.i64_type)
3199 mut p1 := []TypeID{}
3200 p1 << ptr_ierror
3201 msg_id := b.register_synthetic_function('IError.msg', b.str_type, p1)
3202 b.generate_ierror_msg_body(msg_id, ptr_ierror)
3203 code_id := b.register_synthetic_function('IError.code', b.i64_type, p1)
3204 b.generate_ierror_code_body(code_id, ptr_ierror)
3205}
3206
3207// generate_ierror_msg_body supports generate ierror msg body handling for Builder.
3208fn (mut b Builder) generate_ierror_msg_body(func_id int, ptr_ierror TypeID) {
3209 entry := b.m.add_block(func_id, 'entry')
3210 _ := b.func_add_argument(func_id, ptr_ierror, 'err')
3211 result := b.m.add_value(.string_literal, b.str_type, '', 0)
3212 b.block_instr1(.ret, entry, b.void_type, result)
3213}
3214
3215// generate_ierror_code_body supports generate ierror code body handling for Builder.
3216fn (mut b Builder) generate_ierror_code_body(func_id int, ptr_ierror TypeID) {
3217 entry := b.m.add_block(func_id, 'entry')
3218 _ := b.func_add_argument(func_id, ptr_ierror, 'err')
3219 result := b.m.get_or_add_const(b.i64_type, '0')
3220 b.block_instr1(.ret, entry, b.void_type, result)
3221}
3222
3223// register_fixed_array_contains_stubs reports register_fixed_array_contains_stubs logic in ssa.
3224fn (mut b Builder) register_fixed_array_contains_stubs() {
3225 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3226 mut p3_string := []TypeID{}
3227 p3_string << ptr_i8
3228 p3_string << b.i64_type
3229 p3_string << b.str_type
3230 contains_string_id := b.register_synthetic_function('fixed_array_contains_string', b.i1_type,
3231 p3_string)
3232 b.generate_fixed_array_contains_string_body(contains_string_id)
3233
3234 mut p3_int := []TypeID{}
3235 p3_int << ptr_i8
3236 p3_int << b.i64_type
3237 p3_int << b.i64_type
3238 contains_int_id := b.register_synthetic_function('fixed_array_contains_int', b.i1_type, p3_int)
3239 b.generate_const_bool_body(contains_int_id, false)
3240}
3241
3242// generate_fixed_array_contains_string_body
3243// builds helper data for ssa.
3244fn (mut b Builder) generate_fixed_array_contains_string_body(func_id int) {
3245 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3246 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3247 ptr_string := b.m.type_store.get_ptr(b.str_type)
3248 entry := b.m.add_block(func_id, 'entry')
3249 arr := b.func_add_argument(func_id, ptr_i8, 'arr')
3250 len := b.func_add_argument(func_id, b.i64_type, 'len')
3251 needle := b.func_add_argument(func_id, b.str_type, 'needle')
3252 alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
3253 zero := b.m.get_or_add_const(b.i64_type, '0')
3254 one := b.m.get_or_add_const(b.i64_type, '1')
3255 b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
3256
3257 blk_loop := b.m.add_block(func_id, 'fixed_array_contains_string_loop')
3258 blk_body := b.m.add_block(func_id, 'fixed_array_contains_string_body')
3259 blk_next := b.m.add_block(func_id, 'fixed_array_contains_string_next')
3260 blk_found := b.m.add_block(func_id, 'fixed_array_contains_string_found')
3261 blk_not_found := b.m.add_block(func_id, 'fixed_array_contains_string_not_found')
3262 b.block_instr1(.jmp, entry, b.void_type, ValueID(blk_loop))
3263
3264 i := b.block_instr1(.load, blk_loop, b.i64_type, alloca_i)
3265 in_range := b.block_instr2(.lt, blk_loop, b.i1_type, i, len)
3266 b.block_instr3(.br, blk_loop, b.void_type, in_range, ValueID(blk_body), ValueID(blk_not_found))
3267
3268 stride := b.m.get_or_add_const(b.i64_type, '16')
3269 offset := b.block_instr2(.mul, blk_body, b.i64_type, i, stride)
3270 slot := b.block_instr2(.add, blk_body, ptr_i8, arr, offset)
3271 slot_string_ptr := b.block_instr1(.bitcast, blk_body, ptr_string, slot)
3272 slot_string := b.block_instr1(.load, blk_body, b.str_type, slot_string_ptr)
3273 eq_ref := b.m.add_value(.func_ref, b.void_type, 'string__eq', b.fn_ids['string__eq'])
3274 is_eq := b.block_instr3(.call, blk_body, b.i1_type, eq_ref, slot_string, needle)
3275 b.block_instr3(.br, blk_body, b.void_type, is_eq, ValueID(blk_found), ValueID(blk_next))
3276
3277 next_i := b.block_instr2(.add, blk_next, b.i64_type, i, one)
3278 b.block_instr2(.store, blk_next, b.void_type, next_i, alloca_i)
3279 b.block_instr1(.jmp, blk_next, b.void_type, ValueID(blk_loop))
3280
3281 true_value := b.m.get_or_add_const(b.i1_type, '1')
3282 false_value := b.m.get_or_add_const(b.i1_type, '0')
3283 b.block_instr1(.ret, blk_found, b.void_type, true_value)
3284 b.block_instr1(.ret, blk_not_found, b.void_type, false_value)
3285}
3286
3287// generate_const_bool_body supports generate const bool body handling for Builder.
3288fn (mut b Builder) generate_const_bool_body(func_id int, value bool) {
3289 entry := b.m.add_block(func_id, 'entry')
3290 result := b.m.get_or_add_const(b.i1_type, if value { '1' } else { '0' })
3291 b.block_instr1(.ret, entry, b.void_type, result)
3292}
3293
3294// register_array_contains_stubs reports whether register array contains stubs applies in ssa.
3295fn (mut b Builder) register_array_contains_stubs() {
3296 mut p2_string := []TypeID{}
3297 p2_string << b.array_type
3298 p2_string << b.str_type
3299 index_string_id := b.register_synthetic_function('array_index_string', b.i64_type, p2_string)
3300 b.generate_array_index_string_body(index_string_id)
3301 contains_string_id := b.register_synthetic_function('array_contains_string', b.i1_type,
3302 p2_string)
3303 b.generate_array_contains_from_index_body(contains_string_id, 'array_index_string', b.str_type)
3304
3305 mut p2_int := []TypeID{}
3306 p2_int << b.array_type
3307 p2_int << b.i64_type
3308 index_int_id := b.register_synthetic_function('array_index_int', b.i64_type, p2_int)
3309 b.generate_array_index_int_body(index_int_id)
3310 contains_int_id := b.register_synthetic_function('array_contains_int', b.i1_type, p2_int)
3311 b.generate_array_contains_from_index_body(contains_int_id, 'array_index_int', b.i64_type)
3312}
3313
3314// generate_array_contains_from_index_body
3315// supports helper handling in ssa.
3316fn (mut b Builder) generate_array_contains_from_index_body(func_id int, index_name string, needle_type TypeID) {
3317 entry := b.m.add_block(func_id, 'entry')
3318 arr := b.func_add_argument(func_id, b.array_type, 'arr')
3319 needle := b.func_add_argument(func_id, needle_type, 'needle')
3320 index_ref := b.m.add_value(.func_ref, b.void_type, index_name, b.fn_ids[index_name])
3321 idx := b.block_instr3(.call, entry, b.i64_type, index_ref, arr, needle)
3322 zero := b.m.get_or_add_const(b.i64_type, '0')
3323 found := b.block_instr2(.ge, entry, b.i1_type, idx, zero)
3324 b.block_instr1(.ret, entry, b.void_type, found)
3325}
3326
3327// generate_array_index_string_body supports generate array index string body handling for Builder.
3328fn (mut b Builder) generate_array_index_string_body(func_id int) {
3329 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3330 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3331 ptr_array := b.m.type_store.get_ptr(b.array_type)
3332 ptr_string := b.m.type_store.get_ptr(b.str_type)
3333 entry := b.m.add_block(func_id, 'entry')
3334 arr := b.func_add_argument(func_id, b.array_type, 'arr')
3335 needle := b.func_add_argument(func_id, b.str_type, 'needle')
3336 alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
3337 alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
3338 b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
3339 zero := b.m.get_or_add_const(b.i64_type, '0')
3340 one := b.m.get_or_add_const(b.i64_type, '1')
3341 b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
3342
3343 data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
3344 len_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 2)
3345 elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
3346 blk_loop := b.m.add_block(func_id, 'array_index_string_loop')
3347 blk_body := b.m.add_block(func_id, 'array_index_string_body')
3348 blk_next := b.m.add_block(func_id, 'array_index_string_next')
3349 blk_found := b.m.add_block(func_id, 'array_index_string_found')
3350 blk_not_found := b.m.add_block(func_id, 'array_index_string_not_found')
3351 b.block_instr1(.jmp, entry, b.void_type, ValueID(blk_loop))
3352
3353 i := b.block_instr1(.load, blk_loop, b.i64_type, alloca_i)
3354 len32 := b.block_instr1(.load, blk_loop, b.i32_type, len_ptr)
3355 len := b.block_instr1(.zext, blk_loop, b.i64_type, len32)
3356 in_range := b.block_instr2(.lt, blk_loop, b.i1_type, i, len)
3357 b.block_instr3(.br, blk_loop, b.void_type, in_range, ValueID(blk_body), ValueID(blk_not_found))
3358
3359 data := b.block_instr1(.load, blk_body, ptr_i8, data_ptr)
3360 elem_size32 := b.block_instr1(.load, blk_body, b.i32_type, elem_size_ptr)
3361 elem_size := b.block_instr1(.zext, blk_body, b.i64_type, elem_size32)
3362 offset := b.block_instr2(.mul, blk_body, b.i64_type, i, elem_size)
3363 slot := b.block_instr2(.add, blk_body, ptr_i8, data, offset)
3364 slot_string_ptr := b.block_instr1(.bitcast, blk_body, ptr_string, slot)
3365 slot_string := b.block_instr1(.load, blk_body, b.str_type, slot_string_ptr)
3366 eq_ref := b.m.add_value(.func_ref, b.void_type, 'string__eq', b.fn_ids['string__eq'])
3367 is_eq := b.block_instr3(.call, blk_body, b.i1_type, eq_ref, slot_string, needle)
3368 b.block_instr3(.br, blk_body, b.void_type, is_eq, ValueID(blk_found), ValueID(blk_next))
3369
3370 next_i := b.block_instr2(.add, blk_next, b.i64_type, i, one)
3371 b.block_instr2(.store, blk_next, b.void_type, next_i, alloca_i)
3372 b.block_instr1(.jmp, blk_next, b.void_type, ValueID(blk_loop))
3373
3374 b.block_instr1(.ret, blk_found, b.void_type, i)
3375 not_found := b.m.get_or_add_const(b.i64_type, '-1')
3376 b.block_instr1(.ret, blk_not_found, b.void_type, not_found)
3377}
3378
3379// generate_array_index_int_body supports generate array index int body handling for Builder.
3380fn (mut b Builder) generate_array_index_int_body(func_id int) {
3381 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3382 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3383 ptr_i32 := b.m.type_store.get_ptr(b.i32_type)
3384 ptr_array := b.m.type_store.get_ptr(b.array_type)
3385 entry := b.m.add_block(func_id, 'entry')
3386 arr := b.func_add_argument(func_id, b.array_type, 'arr')
3387 needle := b.func_add_argument(func_id, b.i64_type, 'needle')
3388 alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
3389 alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
3390 b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
3391 zero := b.m.get_or_add_const(b.i64_type, '0')
3392 one := b.m.get_or_add_const(b.i64_type, '1')
3393 b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
3394
3395 data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
3396 len_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 2)
3397 elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
3398 blk_loop := b.m.add_block(func_id, 'array_index_int_loop')
3399 blk_body := b.m.add_block(func_id, 'array_index_int_body')
3400 blk_next := b.m.add_block(func_id, 'array_index_int_next')
3401 blk_found := b.m.add_block(func_id, 'array_index_int_found')
3402 blk_not_found := b.m.add_block(func_id, 'array_index_int_not_found')
3403 b.block_instr1(.jmp, entry, b.void_type, ValueID(blk_loop))
3404
3405 i := b.block_instr1(.load, blk_loop, b.i64_type, alloca_i)
3406 len32 := b.block_instr1(.load, blk_loop, b.i32_type, len_ptr)
3407 len := b.block_instr1(.zext, blk_loop, b.i64_type, len32)
3408 in_range := b.block_instr2(.lt, blk_loop, b.i1_type, i, len)
3409 b.block_instr3(.br, blk_loop, b.void_type, in_range, ValueID(blk_body), ValueID(blk_not_found))
3410
3411 data := b.block_instr1(.load, blk_body, ptr_i8, data_ptr)
3412 elem_size32 := b.block_instr1(.load, blk_body, b.i32_type, elem_size_ptr)
3413 elem_size := b.block_instr1(.zext, blk_body, b.i64_type, elem_size32)
3414 offset := b.block_instr2(.mul, blk_body, b.i64_type, i, elem_size)
3415 slot := b.block_instr2(.add, blk_body, ptr_i8, data, offset)
3416 slot_i32_ptr := b.block_instr1(.bitcast, blk_body, ptr_i32, slot)
3417 slot_i32 := b.block_instr1(.load, blk_body, b.i32_type, slot_i32_ptr)
3418 slot_i64 := b.block_instr1(.sext, blk_body, b.i64_type, slot_i32)
3419 is_eq := b.block_instr2(.eq, blk_body, b.i1_type, slot_i64, needle)
3420 b.block_instr3(.br, blk_body, b.void_type, is_eq, ValueID(blk_found), ValueID(blk_next))
3421
3422 next_i := b.block_instr2(.add, blk_next, b.i64_type, i, one)
3423 b.block_instr2(.store, blk_next, b.void_type, next_i, alloca_i)
3424 b.block_instr1(.jmp, blk_next, b.void_type, ValueID(blk_loop))
3425
3426 b.block_instr1(.ret, blk_found, b.void_type, i)
3427 not_found := b.m.get_or_add_const(b.i64_type, '-1')
3428 b.block_instr1(.ret, blk_not_found, b.void_type, not_found)
3429}
3430
3431// emit_map_state_alloc emits emit map state alloc output for ssa.
3432fn (mut b Builder) emit_map_state_alloc(block_id BlockID, key_size ValueID, val_size ValueID) ValueID {
3433 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3434 ptr_state := b.m.type_store.get_ptr(b.map_state_type)
3435 cap := b.m.get_or_add_const(b.i64_type, '8')
3436 one := b.m.get_or_add_const(b.i64_type, '1')
3437 zero := b.m.get_or_add_const(b.i64_type, '0')
3438 state_size := b.m.get_or_add_const(b.i64_type, '48')
3439 calloc_ref := b.m.add_value(.func_ref, b.void_type, 'calloc', b.fn_ids['calloc'])
3440 state_raw := b.block_instr3(.call, block_id, ptr_i8, calloc_ref, one, state_size)
3441 state := b.block_instr1(.bitcast, block_id, ptr_state, state_raw)
3442 keys := b.block_instr3(.call, block_id, ptr_i8, calloc_ref, cap, key_size)
3443 vals := b.block_instr3(.call, block_id, ptr_i8, calloc_ref, cap, val_size)
3444
3445 keys_ptr := b.map_state_field_ptr(block_id, state, 0)
3446 vals_ptr := b.map_state_field_ptr(block_id, state, 1)
3447 cap_ptr := b.map_state_field_ptr(block_id, state, 2)
3448 len_ptr := b.map_state_field_ptr(block_id, state, 3)
3449 key_size_ptr := b.map_state_field_ptr(block_id, state, 4)
3450 val_size_ptr := b.map_state_field_ptr(block_id, state, 5)
3451 b.block_instr2(.store, block_id, b.void_type, keys, keys_ptr)
3452 b.block_instr2(.store, block_id, b.void_type, vals, vals_ptr)
3453 b.block_instr2(.store, block_id, b.void_type, cap, cap_ptr)
3454 b.block_instr2(.store, block_id, b.void_type, zero, len_ptr)
3455 b.block_instr2(.store, block_id, b.void_type, key_size, key_size_ptr)
3456 b.block_instr2(.store, block_id, b.void_type, val_size, val_size_ptr)
3457 return state
3458}
3459
3460// map_state_ptr supports map state ptr handling for Builder.
3461fn (mut b Builder) map_state_ptr(block_id BlockID, map_ptr ValueID) ValueID {
3462 ptr_state := b.m.type_store.get_ptr(b.map_state_type)
3463 state_field_ptr := b.block_struct_field_ptr(block_id, map_ptr, b.map_type, 0)
3464 return b.block_instr1(.load, block_id, ptr_state, state_field_ptr)
3465}
3466
3467// map_state_field_ptr supports map state field ptr handling for Builder.
3468fn (mut b Builder) map_state_field_ptr(block_id BlockID, state_ptr ValueID, field_idx int) ValueID {
3469 return b.block_struct_field_ptr(block_id, state_ptr, b.map_state_type, field_idx)
3470}
3471
3472// generate_new_map_body supports generate new map body handling for Builder.
3473fn (mut b Builder) generate_new_map_body(func_id int) {
3474 ptr_map := b.m.type_store.get_ptr(b.map_type)
3475 entry := b.m.add_block(func_id, 'entry')
3476 key_size := b.func_add_argument(func_id, b.i64_type, 'key_size')
3477 val_size := b.func_add_argument(func_id, b.i64_type, 'val_size')
3478 hash_fn := b.func_add_argument(func_id, b.i64_type, 'hash_fn')
3479 eq_fn := b.func_add_argument(func_id, b.i64_type, 'eq_fn')
3480 clone_fn := b.func_add_argument(func_id, b.i64_type, 'clone_fn')
3481 free_fn := b.func_add_argument(func_id, b.i64_type, 'free_fn')
3482 _ = hash_fn
3483 _ = eq_fn
3484 _ = clone_fn
3485 _ = free_fn
3486
3487 alloca_m := b.block_instr0(.alloca, entry, ptr_map)
3488 state := b.emit_map_state_alloc(entry, key_size, val_size)
3489 state_ptr := b.block_struct_field_ptr(entry, alloca_m, b.map_type, 0)
3490 b.block_instr2(.store, entry, b.void_type, state, state_ptr)
3491 result := b.block_instr1(.load, entry, b.map_type, alloca_m)
3492 b.block_instr1(.ret, entry, b.void_type, result)
3493}
3494
3495// generate_map_find_body supports generate map find body handling for Builder.
3496fn (mut b Builder) generate_map_find_body(func_id int) {
3497 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3498 ptr_map := b.m.type_store.get_ptr(b.map_type)
3499 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3500 ptr_string := b.m.type_store.get_ptr(b.str_type)
3501 ptr_state := b.m.type_store.get_ptr(b.map_state_type)
3502 entry := b.m.add_block(func_id, 'entry')
3503 map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
3504 key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
3505
3506 alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
3507 zero := b.m.get_or_add_const(b.i64_type, '0')
3508 b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
3509
3510 blk_has_map := b.m.add_block(func_id, 'map_find_has_map')
3511 blk_loop := b.m.add_block(func_id, 'map_find_loop')
3512 blk_body := b.m.add_block(func_id, 'map_find_body')
3513 blk_string_cmp := b.m.add_block(func_id, 'map_find_string_cmp')
3514 blk_mem_cmp := b.m.add_block(func_id, 'map_find_mem_cmp')
3515 blk_found := b.m.add_block(func_id, 'map_find_found')
3516 blk_next := b.m.add_block(func_id, 'map_find_next')
3517 blk_not_found := b.m.add_block(func_id, 'map_find_not_found')
3518 zero_map := b.m.get_or_add_const(ptr_map, '0')
3519 has_map := b.block_instr2(.ne, entry, b.i1_type, map_ptr, zero_map)
3520 b.block_instr3(.br, entry, b.void_type, has_map, ValueID(blk_has_map), ValueID(blk_not_found))
3521
3522 state := b.map_state_ptr(blk_has_map, map_ptr)
3523 zero_state := b.m.get_or_add_const(ptr_state, '0')
3524 has_state := b.block_instr2(.ne, blk_has_map, b.i1_type, state, zero_state)
3525 keys_ptr := b.map_state_field_ptr(blk_has_map, state, 0)
3526 len_ptr := b.map_state_field_ptr(blk_has_map, state, 3)
3527 key_size_ptr := b.map_state_field_ptr(blk_has_map, state, 4)
3528 b.block_instr3(.br, blk_has_map, b.void_type, has_state, ValueID(blk_loop),
3529 ValueID(blk_not_found))
3530
3531 i := b.block_instr1(.load, blk_loop, b.i64_type, alloca_i)
3532 len := b.block_instr1(.load, blk_loop, b.i64_type, len_ptr)
3533 in_range := b.block_instr2(.lt, blk_loop, b.i1_type, i, len)
3534 b.block_instr3(.br, blk_loop, b.void_type, in_range, ValueID(blk_body), ValueID(blk_not_found))
3535
3536 keys := b.block_instr1(.load, blk_body, ptr_i8, keys_ptr)
3537 key_size := b.block_instr1(.load, blk_body, b.i64_type, key_size_ptr)
3538 offset := b.block_instr2(.mul, blk_body, b.i64_type, i, key_size)
3539 slot_key := b.block_instr2(.add, blk_body, ptr_i8, keys, offset)
3540 string_key_size := b.m.get_or_add_const(b.i64_type, '16')
3541 is_string_key := b.block_instr2(.eq, blk_body, b.i1_type, key_size, string_key_size)
3542 b.block_instr3(.br, blk_body, b.void_type, is_string_key, ValueID(blk_string_cmp),
3543 ValueID(blk_mem_cmp))
3544
3545 slot_string_ptr := b.block_instr1(.bitcast, blk_string_cmp, ptr_string, slot_key)
3546 key_string_ptr := b.block_instr1(.bitcast, blk_string_cmp, ptr_string, key_ptr)
3547 slot_string := b.block_instr1(.load, blk_string_cmp, b.str_type, slot_string_ptr)
3548 key_string := b.block_instr1(.load, blk_string_cmp, b.str_type, key_string_ptr)
3549 eq_ref := b.m.add_value(.func_ref, b.void_type, 'fast_string_eq', b.fn_ids['fast_string_eq'])
3550 string_eq := b.block_instr3(.call, blk_string_cmp, b.i1_type, eq_ref, slot_string, key_string)
3551 b.block_instr3(.br, blk_string_cmp, b.void_type, string_eq, ValueID(blk_found),
3552 ValueID(blk_next))
3553
3554 memcmp_ref := b.m.add_value(.func_ref, b.void_type, 'memcmp', b.fn_ids['memcmp'])
3555 cmp := b.block_instr4(.call, blk_mem_cmp, b.i64_type, memcmp_ref, slot_key, key_ptr, key_size)
3556 mem_eq := b.block_instr2(.eq, blk_mem_cmp, b.i1_type, cmp, zero)
3557 b.block_instr3(.br, blk_mem_cmp, b.void_type, mem_eq, ValueID(blk_found), ValueID(blk_next))
3558
3559 b.block_instr1(.ret, blk_found, b.void_type, i)
3560
3561 one := b.m.get_or_add_const(b.i64_type, '1')
3562 next_i := b.block_instr2(.add, blk_next, b.i64_type, i, one)
3563 b.block_instr2(.store, blk_next, b.void_type, next_i, alloca_i)
3564 b.block_instr1(.jmp, blk_next, b.void_type, ValueID(blk_loop))
3565
3566 not_found := b.m.get_or_add_const(b.i64_type, '-1')
3567 b.block_instr1(.ret, blk_not_found, b.void_type, not_found)
3568}
3569
3570// generate_map_exists_body supports generate map exists body handling for Builder.
3571fn (mut b Builder) generate_map_exists_body(func_id int) {
3572 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3573 ptr_map := b.m.type_store.get_ptr(b.map_type)
3574 entry := b.m.add_block(func_id, 'entry')
3575 map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
3576 key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
3577 find_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_find', b.fn_ids['v3_map_find'])
3578 idx := b.block_instr3(.call, entry, b.i64_type, find_ref, map_ptr, key_ptr)
3579 zero := b.m.get_or_add_const(b.i64_type, '0')
3580 found := b.block_instr2(.ge, entry, b.i1_type, idx, zero)
3581 b.block_instr1(.ret, entry, b.void_type, found)
3582}
3583
3584// generate_map_get_body supports generate map get body handling for Builder.
3585fn (mut b Builder) generate_map_get_body(func_id int) {
3586 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3587 ptr_map := b.m.type_store.get_ptr(b.map_type)
3588 entry := b.m.add_block(func_id, 'entry')
3589 map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
3590 key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
3591 zero_ptr := b.func_add_argument(func_id, ptr_i8, 'zero')
3592 find_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_find', b.fn_ids['v3_map_find'])
3593 idx := b.block_instr3(.call, entry, b.i64_type, find_r