v4 / 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_ref, map_ptr, key_ptr)
3594 zero := b.m.get_or_add_const(b.i64_type, '0')
3595 found := b.block_instr2(.ge, entry, b.i1_type, idx, zero)
3596
3597 blk_found := b.m.add_block(func_id, 'map_get_found')
3598 blk_missing := b.m.add_block(func_id, 'map_get_missing')
3599 b.block_instr3(.br, entry, b.void_type, found, ValueID(blk_found), ValueID(blk_missing))
3600
3601 state := b.map_state_ptr(blk_found, map_ptr)
3602 vals_ptr := b.map_state_field_ptr(blk_found, state, 1)
3603 val_size_ptr := b.map_state_field_ptr(blk_found, state, 5)
3604 vals := b.block_instr1(.load, blk_found, ptr_i8, vals_ptr)
3605 val_size := b.block_instr1(.load, blk_found, b.i64_type, val_size_ptr)
3606 offset := b.block_instr2(.mul, blk_found, b.i64_type, idx, val_size)
3607 result := b.block_instr2(.add, blk_found, ptr_i8, vals, offset)
3608 b.block_instr1(.ret, blk_found, b.void_type, result)
3609
3610 b.block_instr1(.ret, blk_missing, b.void_type, zero_ptr)
3611}
3612
3613// generate_map_get_check_body supports generate map get check body handling for Builder.
3614fn (mut b Builder) generate_map_get_check_body(func_id int) {
3615 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3616 ptr_map := b.m.type_store.get_ptr(b.map_type)
3617 entry := b.m.add_block(func_id, 'entry')
3618 map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
3619 key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
3620 find_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_find', b.fn_ids['v3_map_find'])
3621 idx := b.block_instr3(.call, entry, b.i64_type, find_ref, map_ptr, key_ptr)
3622 zero := b.m.get_or_add_const(b.i64_type, '0')
3623 found := b.block_instr2(.ge, entry, b.i1_type, idx, zero)
3624
3625 blk_found := b.m.add_block(func_id, 'map_get_check_found')
3626 blk_missing := b.m.add_block(func_id, 'map_get_check_missing')
3627 b.block_instr3(.br, entry, b.void_type, found, ValueID(blk_found), ValueID(blk_missing))
3628
3629 state := b.map_state_ptr(blk_found, map_ptr)
3630 vals_ptr := b.map_state_field_ptr(blk_found, state, 1)
3631 val_size_ptr := b.map_state_field_ptr(blk_found, state, 5)
3632 vals := b.block_instr1(.load, blk_found, ptr_i8, vals_ptr)
3633 val_size := b.block_instr1(.load, blk_found, b.i64_type, val_size_ptr)
3634 offset := b.block_instr2(.mul, blk_found, b.i64_type, idx, val_size)
3635 result := b.block_instr2(.add, blk_found, ptr_i8, vals, offset)
3636 b.block_instr1(.ret, blk_found, b.void_type, result)
3637
3638 zero_ptr := b.m.get_or_add_const(ptr_i8, '0')
3639 b.block_instr1(.ret, blk_missing, b.void_type, zero_ptr)
3640}
3641
3642// generate_map_set_default_body supports generate map set default body handling for Builder.
3643fn (mut b Builder) generate_map_set_default_body(func_id int) {
3644 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3645 ptr_map := b.m.type_store.get_ptr(b.map_type)
3646 entry := b.m.add_block(func_id, 'entry')
3647 map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
3648 key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
3649 val_ptr := b.func_add_argument(func_id, ptr_i8, 'val')
3650 key_size := b.m.get_or_add_const(b.i64_type, '16')
3651 val_size := b.m.get_or_add_const(b.i64_type, '8')
3652 fn_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_set_sized',
3653 b.fn_ids['v3_map_set_sized'])
3654 mut args := []ValueID{}
3655 args << fn_ref
3656 args << map_ptr
3657 args << key_ptr
3658 args << val_ptr
3659 args << key_size
3660 args << val_size
3661 b.m.add_instr(.call, entry, b.void_type, args)
3662 b.block_instr0(.ret, entry, b.void_type)
3663}
3664
3665// generate_map_set_sized_body supports generate map set sized body handling for Builder.
3666fn (mut b Builder) generate_map_set_sized_body(func_id int) {
3667 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3668 ptr_map := b.m.type_store.get_ptr(b.map_type)
3669 ptr_state := b.m.type_store.get_ptr(b.map_state_type)
3670 entry := b.m.add_block(func_id, 'entry')
3671 map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
3672 key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
3673 val_ptr := b.func_add_argument(func_id, ptr_i8, 'val')
3674 key_size_arg := b.func_add_argument(func_id, b.i64_type, 'key_size')
3675 val_size_arg := b.func_add_argument(func_id, b.i64_type, 'val_size')
3676 zero := b.m.get_or_add_const(b.i64_type, '0')
3677
3678 state_slot := b.block_instr0(.alloca, entry, b.m.type_store.get_ptr(ptr_state))
3679 state_field_ptr := b.block_struct_field_ptr(entry, map_ptr, b.map_type, 0)
3680 old_state := b.block_instr1(.load, entry, ptr_state, state_field_ptr)
3681 b.block_instr2(.store, entry, b.void_type, old_state, state_slot)
3682 zero_state := b.m.get_or_add_const(ptr_state, '0')
3683 has_state := b.block_instr2(.ne, entry, b.i1_type, old_state, zero_state)
3684
3685 blk_init := b.m.add_block(func_id, 'map_set_init')
3686 blk_ready := b.m.add_block(func_id, 'map_set_ready')
3687 blk_update := b.m.add_block(func_id, 'map_set_update')
3688 blk_insert := b.m.add_block(func_id, 'map_set_insert')
3689 b.block_instr3(.br, entry, b.void_type, has_state, ValueID(blk_ready), ValueID(blk_init))
3690
3691 new_state := b.emit_map_state_alloc(blk_init, key_size_arg, val_size_arg)
3692 b.block_instr2(.store, blk_init, b.void_type, new_state, state_field_ptr)
3693 b.block_instr2(.store, blk_init, b.void_type, new_state, state_slot)
3694 b.block_instr1(.jmp, blk_init, b.void_type, ValueID(blk_ready))
3695
3696 state := b.block_instr1(.load, blk_ready, ptr_state, state_slot)
3697 keys_ptr := b.map_state_field_ptr(blk_ready, state, 0)
3698 vals_ptr := b.map_state_field_ptr(blk_ready, state, 1)
3699 cap_ptr := b.map_state_field_ptr(blk_ready, state, 2)
3700 len_ptr := b.map_state_field_ptr(blk_ready, state, 3)
3701 key_size_ptr := b.map_state_field_ptr(blk_ready, state, 4)
3702 val_size_ptr := b.map_state_field_ptr(blk_ready, state, 5)
3703
3704 find_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_find', b.fn_ids['v3_map_find'])
3705 idx := b.block_instr3(.call, blk_ready, b.i64_type, find_ref, map_ptr, key_ptr)
3706 found := b.block_instr2(.ge, blk_ready, b.i1_type, idx, zero)
3707 b.block_instr3(.br, blk_ready, b.void_type, found, ValueID(blk_update), ValueID(blk_insert))
3708
3709 vals_update := b.block_instr1(.load, blk_update, ptr_i8, vals_ptr)
3710 val_size_update := b.block_instr1(.load, blk_update, b.i64_type, val_size_ptr)
3711 update_off := b.block_instr2(.mul, blk_update, b.i64_type, idx, val_size_update)
3712 update_dest := b.block_instr2(.add, blk_update, ptr_i8, vals_update, update_off)
3713 memcpy_ref_update := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
3714 b.block_instr4(.call, blk_update, ptr_i8, memcpy_ref_update, update_dest, val_ptr,
3715 val_size_update)
3716 b.block_instr0(.ret, blk_update, b.void_type)
3717
3718 len := b.block_instr1(.load, blk_insert, b.i64_type, len_ptr)
3719 cap := b.block_instr1(.load, blk_insert, b.i64_type, cap_ptr)
3720 needs_grow := b.block_instr2(.ge, blk_insert, b.i1_type, len, cap)
3721 blk_grow := b.m.add_block(func_id, 'map_set_grow')
3722 blk_store := b.m.add_block(func_id, 'map_set_store')
3723 b.block_instr3(.br, blk_insert, b.void_type, needs_grow, ValueID(blk_grow), ValueID(blk_store))
3724
3725 two := b.m.get_or_add_const(b.i64_type, '2')
3726 eight := b.m.get_or_add_const(b.i64_type, '8')
3727 key_size_grow := b.block_instr1(.load, blk_grow, b.i64_type, key_size_ptr)
3728 val_size_grow := b.block_instr1(.load, blk_grow, b.i64_type, val_size_ptr)
3729 keys_old := b.block_instr1(.load, blk_grow, ptr_i8, keys_ptr)
3730 vals_old := b.block_instr1(.load, blk_grow, ptr_i8, vals_ptr)
3731 double_cap := b.block_instr2(.mul, blk_grow, b.i64_type, cap, two)
3732 new_cap := b.block_instr2(.add, blk_grow, b.i64_type, double_cap, eight)
3733 key_bytes := b.block_instr2(.mul, blk_grow, b.i64_type, new_cap, key_size_grow)
3734 val_bytes := b.block_instr2(.mul, blk_grow, b.i64_type, new_cap, val_size_grow)
3735 realloc_ref_keys := b.m.add_value(.func_ref, b.void_type, 'realloc', b.fn_ids['realloc'])
3736 realloc_ref_vals := b.m.add_value(.func_ref, b.void_type, 'realloc', b.fn_ids['realloc'])
3737 keys_new := b.block_instr3(.call, blk_grow, ptr_i8, realloc_ref_keys, keys_old, key_bytes)
3738 vals_new := b.block_instr3(.call, blk_grow, ptr_i8, realloc_ref_vals, vals_old, val_bytes)
3739 b.block_instr2(.store, blk_grow, b.void_type, keys_new, keys_ptr)
3740 b.block_instr2(.store, blk_grow, b.void_type, vals_new, vals_ptr)
3741 b.block_instr2(.store, blk_grow, b.void_type, new_cap, cap_ptr)
3742 b.block_instr1(.jmp, blk_grow, b.void_type, ValueID(blk_store))
3743
3744 keys := b.block_instr1(.load, blk_store, ptr_i8, keys_ptr)
3745 vals := b.block_instr1(.load, blk_store, ptr_i8, vals_ptr)
3746 key_size := b.block_instr1(.load, blk_store, b.i64_type, key_size_ptr)
3747 val_size := b.block_instr1(.load, blk_store, b.i64_type, val_size_ptr)
3748 key_off := b.block_instr2(.mul, blk_store, b.i64_type, len, key_size)
3749 val_off := b.block_instr2(.mul, blk_store, b.i64_type, len, val_size)
3750 key_dest := b.block_instr2(.add, blk_store, ptr_i8, keys, key_off)
3751 val_dest := b.block_instr2(.add, blk_store, ptr_i8, vals, val_off)
3752 memcpy_ref_key := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
3753 memcpy_ref_val := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
3754 b.block_instr4(.call, blk_store, ptr_i8, memcpy_ref_key, key_dest, key_ptr, key_size)
3755 b.block_instr4(.call, blk_store, ptr_i8, memcpy_ref_val, val_dest, val_ptr, val_size)
3756 one := b.m.get_or_add_const(b.i64_type, '1')
3757 new_len := b.block_instr2(.add, blk_store, b.i64_type, len, one)
3758 b.block_instr2(.store, blk_store, b.void_type, new_len, len_ptr)
3759 b.block_instr0(.ret, blk_store, b.void_type)
3760}
3761
3762// generate_array_new_body supports generate array new body handling for Builder.
3763fn (mut b Builder) generate_array_new_body(func_id int) {
3764 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3765 ptr_array := b.m.type_store.get_ptr(b.array_type)
3766 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3767 ptr_ptr_i8 := b.m.type_store.get_ptr(ptr_i8)
3768 entry := b.m.add_block(func_id, 'entry')
3769 elem_size := b.func_add_argument(func_id, b.i64_type, 'elem_size')
3770 len := b.func_add_argument(func_id, b.i64_type, 'len')
3771 cap := b.func_add_argument(func_id, b.i64_type, 'cap')
3772
3773 alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
3774 alloca_cap := b.block_instr0(.alloca, entry, ptr_i64)
3775 alloca_data := b.block_instr0(.alloca, entry, ptr_ptr_i8)
3776 cap_lt_len := b.block_instr2(.lt, entry, b.i1_type, cap, len)
3777
3778 blk_use_len := b.m.add_block(func_id, 'array_new_use_len')
3779 blk_use_cap := b.m.add_block(func_id, 'array_new_use_cap')
3780 blk_init := b.m.add_block(func_id, 'array_new_init')
3781 b.block_instr3(.br, entry, b.void_type, cap_lt_len, ValueID(blk_use_len), ValueID(blk_use_cap))
3782
3783 b.block_instr2(.store, blk_use_len, b.void_type, len, alloca_cap)
3784 b.block_instr1(.jmp, blk_use_len, b.void_type, ValueID(blk_init))
3785
3786 b.block_instr2(.store, blk_use_cap, b.void_type, cap, alloca_cap)
3787 b.block_instr1(.jmp, blk_use_cap, b.void_type, ValueID(blk_init))
3788
3789 final_cap := b.block_instr1(.load, blk_init, b.i64_type, alloca_cap)
3790 zero := b.m.get_or_add_const(b.i64_type, '0')
3791 has_cap := b.block_instr2(.gt, blk_init, b.i1_type, final_cap, zero)
3792 blk_alloc := b.m.add_block(func_id, 'array_new_alloc')
3793 blk_empty := b.m.add_block(func_id, 'array_new_empty')
3794 blk_fields := b.m.add_block(func_id, 'array_new_fields')
3795 b.block_instr3(.br, blk_init, b.void_type, has_cap, ValueID(blk_alloc), ValueID(blk_empty))
3796
3797 fn_ref := b.m.add_value(.func_ref, b.void_type, 'calloc', b.fn_ids['calloc'])
3798 allocated_data := b.block_instr3(.call, blk_alloc, ptr_i8, fn_ref, final_cap, elem_size)
3799 b.block_instr2(.store, blk_alloc, b.void_type, allocated_data, alloca_data)
3800 b.block_instr1(.jmp, blk_alloc, b.void_type, ValueID(blk_fields))
3801
3802 null_data := b.m.get_or_add_const(ptr_i8, '0')
3803 b.block_instr2(.store, blk_empty, b.void_type, null_data, alloca_data)
3804 b.block_instr1(.jmp, blk_empty, b.void_type, ValueID(blk_fields))
3805
3806 data := b.block_instr1(.load, blk_fields, ptr_i8, alloca_data)
3807 data_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 0)
3808 offset_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 1)
3809 len_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 2)
3810 cap_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 3)
3811 flags_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 4)
3812 elem_size_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 5)
3813 b.block_instr2(.store, blk_fields, b.void_type, data, data_ptr)
3814 b.block_instr2(.store, blk_fields, b.void_type, zero, offset_ptr)
3815 b.block_instr2(.store, blk_fields, b.void_type, len, len_ptr)
3816 b.block_instr2(.store, blk_fields, b.void_type, final_cap, cap_ptr)
3817 b.block_instr2(.store, blk_fields, b.void_type, zero, flags_ptr)
3818 b.block_instr2(.store, blk_fields, b.void_type, elem_size, elem_size_ptr)
3819
3820 arr := b.block_instr1(.load, blk_fields, b.array_type, alloca_arr)
3821 b.block_instr1(.ret, blk_fields, b.void_type, arr)
3822}
3823
3824// generate_array_get_body supports generate array get body handling for Builder.
3825fn (mut b Builder) generate_array_get_body(func_id int) {
3826 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3827 ptr_array := b.m.type_store.get_ptr(b.array_type)
3828 entry := b.m.add_block(func_id, 'entry')
3829 arr := b.func_add_argument(func_id, b.array_type, 'arr')
3830 idx := b.func_add_argument(func_id, b.i64_type, 'idx')
3831
3832 alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
3833 b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
3834
3835 data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
3836 elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
3837 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
3838 elem_size32 := b.block_instr1(.load, entry, b.i32_type, elem_size_ptr)
3839 elem_size := b.block_instr1(.zext, entry, b.i64_type, elem_size32)
3840 offset := b.block_instr2(.mul, entry, b.i64_type, idx, elem_size)
3841 result := b.block_instr2(.add, entry, ptr_i8, data, offset)
3842 b.block_instr1(.ret, entry, b.void_type, result)
3843}
3844
3845// generate_array_slice_body supports generate array slice body handling for Builder.
3846fn (mut b Builder) generate_array_slice_body(func_id int) {
3847 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3848 ptr_array := b.m.type_store.get_ptr(b.array_type)
3849 entry := b.m.add_block(func_id, 'entry')
3850 arr := b.func_add_argument(func_id, b.array_type, 'arr')
3851 start := b.func_add_argument(func_id, b.i64_type, 'start')
3852 end := b.func_add_argument(func_id, b.i64_type, 'end')
3853
3854 alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
3855 alloca_out := b.block_instr0(.alloca, entry, ptr_array)
3856 b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
3857
3858 data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
3859 elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
3860 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
3861 elem_size32 := b.block_instr1(.load, entry, b.i32_type, elem_size_ptr)
3862 elem_size := b.block_instr1(.zext, entry, b.i64_type, elem_size32)
3863 offset := b.block_instr2(.mul, entry, b.i64_type, start, elem_size)
3864 slice_data := b.block_instr2(.add, entry, ptr_i8, data, offset)
3865 slice_len := b.block_instr2(.sub, entry, b.i64_type, end, start)
3866 zero := b.m.get_or_add_const(b.i64_type, '0')
3867
3868 out_data_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 0)
3869 out_offset_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 1)
3870 out_len_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 2)
3871 out_cap_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 3)
3872 out_flags_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 4)
3873 out_elem_size_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 5)
3874 b.block_instr2(.store, entry, b.void_type, slice_data, out_data_ptr)
3875 b.block_instr2(.store, entry, b.void_type, zero, out_offset_ptr)
3876 b.block_instr2(.store, entry, b.void_type, slice_len, out_len_ptr)
3877 b.block_instr2(.store, entry, b.void_type, slice_len, out_cap_ptr)
3878 b.block_instr2(.store, entry, b.void_type, zero, out_flags_ptr)
3879 b.block_instr2(.store, entry, b.void_type, elem_size, out_elem_size_ptr)
3880
3881 result := b.block_instr1(.load, entry, b.array_type, alloca_out)
3882 b.block_instr1(.ret, entry, b.void_type, result)
3883}
3884
3885// register_arguments_stub updates register arguments stub state for ssa.
3886fn (mut b Builder) register_arguments_stub() {
3887 arguments_id := b.register_synthetic_function('arguments', b.array_type, []TypeID{})
3888 b.generate_arguments_body(arguments_id)
3889}
3890
3891// generate_arguments_body supports generate arguments body handling for Builder.
3892fn (mut b Builder) generate_arguments_body(func_id int) {
3893 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3894 ptr_ptr_i8 := b.m.type_store.get_ptr(ptr_i8)
3895 ptr_array := b.m.type_store.get_ptr(b.array_type)
3896 ptr_string := b.m.type_store.get_ptr(b.str_type)
3897 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
3898
3899 entry := b.m.add_block(func_id, 'entry')
3900 argc_global := b.vars['g_main_argc']
3901 argv_global := b.vars['g_main_argv']
3902 argc := b.block_instr1(.load, entry, b.i64_type, argc_global)
3903 argv := b.block_instr1(.load, entry, ptr_ptr_i8, argv_global)
3904
3905 elem_size := b.m.get_or_add_const(b.i64_type, '16')
3906 zero := b.m.get_or_add_const(b.i64_type, '0')
3907 one := b.m.get_or_add_const(b.i64_type, '1')
3908 ptr_size := b.m.get_or_add_const(b.i64_type, '8')
3909 new_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', b.fn_ids['array_new'])
3910 arr := b.block_instr4(.call, entry, b.array_type, new_ref, elem_size, zero, argc)
3911
3912 alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
3913 alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
3914 b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
3915 b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
3916
3917 loop := b.m.add_block(func_id, 'arguments_loop')
3918 body := b.m.add_block(func_id, 'arguments_body')
3919 done := b.m.add_block(func_id, 'arguments_done')
3920 b.block_instr1(.jmp, entry, b.void_type, ValueID(loop))
3921
3922 i := b.block_instr1(.load, loop, b.i64_type, alloca_i)
3923 more := b.block_instr2(.lt, loop, b.i1_type, i, argc)
3924 b.block_instr3(.br, loop, b.void_type, more, ValueID(body), ValueID(done))
3925
3926 argv_off := b.block_instr2(.mul, body, b.i64_type, i, ptr_size)
3927 argv_slot := b.block_instr2(.add, body, ptr_ptr_i8, argv, argv_off)
3928 cstr := b.block_instr1(.load, body, ptr_i8, argv_slot)
3929 tos_clone_ref := b.m.add_value(.func_ref, b.str_type, 'tos_clone', b.fn_ids['tos_clone'])
3930 arg_string := b.block_instr2(.call, body, b.str_type, tos_clone_ref, cstr)
3931
3932 data_ptr := b.block_struct_field_ptr(body, alloca_arr, b.array_type, 0)
3933 len_ptr := b.block_struct_field_ptr(body, alloca_arr, b.array_type, 2)
3934 data := b.block_instr1(.load, body, ptr_i8, data_ptr)
3935 elem_off := b.block_instr2(.mul, body, b.i64_type, i, elem_size)
3936 dest := b.block_instr2(.add, body, ptr_i8, data, elem_off)
3937 dest_string := b.block_instr1(.bitcast, body, ptr_string, dest)
3938 b.block_instr2(.store, body, b.void_type, arg_string, dest_string)
3939 next_i := b.block_instr2(.add, body, b.i64_type, i, one)
3940 b.block_instr2(.store, body, b.void_type, next_i, len_ptr)
3941 b.block_instr2(.store, body, b.void_type, next_i, alloca_i)
3942 b.block_instr1(.jmp, body, b.void_type, ValueID(loop))
3943
3944 result := b.block_instr1(.load, done, b.array_type, alloca_arr)
3945 b.block_instr1(.ret, done, b.void_type, result)
3946}
3947
3948// generate_array_clone_body supports generate array clone body handling for Builder.
3949fn (mut b Builder) generate_array_clone_body(func_id int) {
3950 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3951 ptr_array := b.m.type_store.get_ptr(b.array_type)
3952 entry := b.m.add_block(func_id, 'entry')
3953 arr := b.func_add_argument(func_id, b.array_type, 'arr')
3954
3955 alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
3956 alloca_clone := b.block_instr0(.alloca, entry, ptr_array)
3957 b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
3958
3959 data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
3960 len_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 2)
3961 cap_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 3)
3962 elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
3963 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
3964 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
3965 len := b.block_instr1(.zext, entry, b.i64_type, len32)
3966 cap32 := b.block_instr1(.load, entry, b.i32_type, cap_ptr)
3967 cap := b.block_instr1(.zext, entry, b.i64_type, cap32)
3968 elem_size32 := b.block_instr1(.load, entry, b.i32_type, elem_size_ptr)
3969 elem_size := b.block_instr1(.zext, entry, b.i64_type, elem_size32)
3970
3971 new_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', b.fn_ids['array_new'])
3972 clone := b.block_instr4(.call, entry, b.array_type, new_ref, elem_size, len, cap)
3973 b.block_instr2(.store, entry, b.void_type, clone, alloca_clone)
3974
3975 clone_data_ptr := b.block_struct_field_ptr(entry, alloca_clone, b.array_type, 0)
3976 clone_data := b.block_instr1(.load, entry, ptr_i8, clone_data_ptr)
3977 copy_size := b.block_instr2(.mul, entry, b.i64_type, len, elem_size)
3978 memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
3979 b.block_instr4(.call, entry, ptr_i8, memcpy_ref, clone_data, data, copy_size)
3980 result := b.block_instr1(.load, entry, b.array_type, alloca_clone)
3981 b.block_instr1(.ret, entry, b.void_type, result)
3982}
3983
3984// generate_array_clone_ptr_body is generate_array_clone_body for the `array__clone(array* a)`
3985// form the transformer emits: the argument is a pointer to the array, so its fields are read
3986// directly off the pointer. The clone is allocated header-less via `array_new` (calloc).
3987fn (mut b Builder) generate_array_clone_ptr_body(func_id int) {
3988 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
3989 ptr_array := b.m.type_store.get_ptr(b.array_type)
3990 entry := b.m.add_block(func_id, 'entry')
3991 arr_ptr := b.func_add_argument(func_id, ptr_array, 'arr')
3992
3993 alloca_clone := b.block_instr0(.alloca, entry, ptr_array)
3994
3995 data_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 0)
3996 len_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 2)
3997 cap_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 3)
3998 elem_size_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 5)
3999 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
4000 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
4001 len := b.block_instr1(.zext, entry, b.i64_type, len32)
4002 cap32 := b.block_instr1(.load, entry, b.i32_type, cap_ptr)
4003 cap := b.block_instr1(.zext, entry, b.i64_type, cap32)
4004 elem_size32 := b.block_instr1(.load, entry, b.i32_type, elem_size_ptr)
4005 elem_size := b.block_instr1(.zext, entry, b.i64_type, elem_size32)
4006
4007 new_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', b.fn_ids['array_new'])
4008 clone := b.block_instr4(.call, entry, b.array_type, new_ref, elem_size, len, cap)
4009 b.block_instr2(.store, entry, b.void_type, clone, alloca_clone)
4010
4011 clone_data_ptr := b.block_struct_field_ptr(entry, alloca_clone, b.array_type, 0)
4012 clone_data := b.block_instr1(.load, entry, ptr_i8, clone_data_ptr)
4013 copy_size := b.block_instr2(.mul, entry, b.i64_type, len, elem_size)
4014 memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
4015 b.block_instr4(.call, entry, ptr_i8, memcpy_ref, clone_data, data, copy_size)
4016 result := b.block_instr1(.load, entry, b.array_type, alloca_clone)
4017 b.block_instr1(.ret, entry, b.void_type, result)
4018}
4019
4020// generate_array_repeat_to_depth_body converts generate array repeat to depth body data for ssa.
4021fn (mut b Builder) generate_array_repeat_to_depth_body(func_id int) {
4022 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
4023 ptr_array := b.m.type_store.get_ptr(b.array_type)
4024 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
4025 entry := b.m.add_block(func_id, 'entry')
4026 arr := b.func_add_argument(func_id, b.array_type, 'arr')
4027 count := b.func_add_argument(func_id, b.i64_type, 'count')
4028 _ := b.func_add_argument(func_id, b.i64_type, 'depth')
4029
4030 alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
4031 alloca_out := b.block_instr0(.alloca, entry, ptr_array)
4032 alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
4033 b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
4034
4035 data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
4036 len_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 2)
4037 elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
4038 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
4039 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
4040 len := b.block_instr1(.zext, entry, b.i64_type, len32)
4041 elem_size32 := b.block_instr1(.load, entry, b.i32_type, elem_size_ptr)
4042 elem_size := b.block_instr1(.zext, entry, b.i64_type, elem_size32)
4043 total_len := b.block_instr2(.mul, entry, b.i64_type, len, count)
4044
4045 new_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', b.fn_ids['array_new'])
4046 out := b.block_instr4(.call, entry, b.array_type, new_ref, elem_size, total_len, total_len)
4047 b.block_instr2(.store, entry, b.void_type, out, alloca_out)
4048
4049 out_data_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 0)
4050 out_data := b.block_instr1(.load, entry, ptr_i8, out_data_ptr)
4051 chunk_size := b.block_instr2(.mul, entry, b.i64_type, len, elem_size)
4052 zero := b.m.get_or_add_const(b.i64_type, '0')
4053 one := b.m.get_or_add_const(b.i64_type, '1')
4054 b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
4055
4056 loop := b.m.add_block(func_id, 'array_repeat_loop')
4057 body := b.m.add_block(func_id, 'array_repeat_body')
4058 done := b.m.add_block(func_id, 'array_repeat_done')
4059 b.block_instr1(.jmp, entry, b.void_type, ValueID(loop))
4060
4061 i := b.block_instr1(.load, loop, b.i64_type, alloca_i)
4062 more := b.block_instr2(.lt, loop, b.i1_type, i, count)
4063 b.block_instr3(.br, loop, b.void_type, more, ValueID(body), ValueID(done))
4064
4065 dest_off := b.block_instr2(.mul, body, b.i64_type, i, chunk_size)
4066 dest := b.block_instr2(.add, body, ptr_i8, out_data, dest_off)
4067 memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
4068 b.block_instr4(.call, body, ptr_i8, memcpy_ref, dest, data, chunk_size)
4069 next_i := b.block_instr2(.add, body, b.i64_type, i, one)
4070 b.block_instr2(.store, body, b.void_type, next_i, alloca_i)
4071 b.block_instr1(.jmp, body, b.void_type, ValueID(loop))
4072
4073 result := b.block_instr1(.load, done, b.array_type, alloca_out)
4074 b.block_instr1(.ret, done, b.void_type, result)
4075}
4076
4077// generate_array_push_body supports generate array push body handling for Builder.
4078fn (mut b Builder) generate_array_push_body(func_id int) {
4079 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
4080 ptr_array := b.m.type_store.get_ptr(b.array_type)
4081 entry := b.m.add_block(func_id, 'entry')
4082 arr_ptr := b.func_add_argument(func_id, ptr_array, 'arr')
4083 elem_ptr := b.func_add_argument(func_id, ptr_i8, 'elem')
4084
4085 data_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 0)
4086 len_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 2)
4087 cap_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 3)
4088 elem_size_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 5)
4089 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
4090 len := b.block_instr1(.zext, entry, b.i64_type, len32)
4091 cap32 := b.block_instr1(.load, entry, b.i32_type, cap_ptr)
4092 cap := b.block_instr1(.zext, entry, b.i64_type, cap32)
4093 needs_grow := b.block_instr2(.ge, entry, b.i1_type, len, cap)
4094
4095 blk_grow := b.m.add_block(func_id, 'array_push_grow')
4096 blk_store := b.m.add_block(func_id, 'array_push_store')
4097 b.block_instr3(.br, entry, b.void_type, needs_grow, ValueID(blk_grow), ValueID(blk_store))
4098
4099 two := b.m.get_or_add_const(b.i64_type, '2')
4100 old_data := b.block_instr1(.load, blk_grow, ptr_i8, data_ptr)
4101 elem_size_grow32 := b.block_instr1(.load, blk_grow, b.i32_type, elem_size_ptr)
4102 elem_size_grow := b.block_instr1(.zext, blk_grow, b.i64_type, elem_size_grow32)
4103 double_cap := b.block_instr2(.mul, blk_grow, b.i64_type, cap, two)
4104 new_cap := b.block_instr2(.add, blk_grow, b.i64_type, double_cap, two)
4105 new_size := b.block_instr2(.mul, blk_grow, b.i64_type, new_cap, elem_size_grow)
4106 realloc_ref := b.m.add_value(.func_ref, b.void_type, 'realloc', b.fn_ids['realloc'])
4107 new_data := b.block_instr3(.call, blk_grow, ptr_i8, realloc_ref, old_data, new_size)
4108 b.block_instr2(.store, blk_grow, b.void_type, new_data, data_ptr)
4109 b.block_instr2(.store, blk_grow, b.void_type, new_cap, cap_ptr)
4110 b.block_instr1(.jmp, blk_grow, b.void_type, ValueID(blk_store))
4111
4112 data := b.block_instr1(.load, blk_store, ptr_i8, data_ptr)
4113 elem_size32 := b.block_instr1(.load, blk_store, b.i32_type, elem_size_ptr)
4114 elem_size := b.block_instr1(.zext, blk_store, b.i64_type, elem_size32)
4115 offset := b.block_instr2(.mul, blk_store, b.i64_type, len, elem_size)
4116 dest := b.block_instr2(.add, blk_store, ptr_i8, data, offset)
4117 memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
4118 b.block_instr4(.call, blk_store, ptr_i8, memcpy_ref, dest, elem_ptr, elem_size)
4119 one := b.m.get_or_add_const(b.i64_type, '1')
4120 new_len := b.block_instr2(.add, blk_store, b.i64_type, len, one)
4121 b.block_instr2(.store, blk_store, b.void_type, new_len, len_ptr)
4122 b.block_instr0(.ret, blk_store, b.void_type)
4123}
4124
4125// generate_array_push_many_body supports generate array push many body handling for Builder.
4126fn (mut b Builder) generate_array_push_many_body(func_id int) {
4127 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
4128 ptr_array := b.m.type_store.get_ptr(b.array_type)
4129 entry := b.m.add_block(func_id, 'entry')
4130 arr_ptr := b.func_add_argument(func_id, ptr_array, 'arr')
4131 src_ptr := b.func_add_argument(func_id, ptr_i8, 'src')
4132 count := b.func_add_argument(func_id, b.i64_type, 'count')
4133
4134 zero := b.m.get_or_add_const(b.i64_type, '0')
4135 has_items := b.block_instr2(.gt, entry, b.i1_type, count, zero)
4136 blk_check_cap := b.m.add_block(func_id, 'array_push_many_check_cap')
4137 blk_done := b.m.add_block(func_id, 'array_push_many_done')
4138 b.block_instr3(.br, entry, b.void_type, has_items, ValueID(blk_check_cap), ValueID(blk_done))
4139
4140 data_ptr := b.block_struct_field_ptr(blk_check_cap, arr_ptr, b.array_type, 0)
4141 len_ptr := b.block_struct_field_ptr(blk_check_cap, arr_ptr, b.array_type, 2)
4142 cap_ptr := b.block_struct_field_ptr(blk_check_cap, arr_ptr, b.array_type, 3)
4143 elem_size_ptr := b.block_struct_field_ptr(blk_check_cap, arr_ptr, b.array_type, 5)
4144 len32 := b.block_instr1(.load, blk_check_cap, b.i32_type, len_ptr)
4145 len := b.block_instr1(.zext, blk_check_cap, b.i64_type, len32)
4146 cap32 := b.block_instr1(.load, blk_check_cap, b.i32_type, cap_ptr)
4147 cap := b.block_instr1(.zext, blk_check_cap, b.i64_type, cap32)
4148 new_len := b.block_instr2(.add, blk_check_cap, b.i64_type, len, count)
4149 needs_grow := b.block_instr2(.gt, blk_check_cap, b.i1_type, new_len, cap)
4150
4151 blk_grow := b.m.add_block(func_id, 'array_push_many_grow')
4152 blk_copy := b.m.add_block(func_id, 'array_push_many_copy')
4153 b.block_instr3(.br, blk_check_cap, b.void_type, needs_grow, ValueID(blk_grow),
4154 ValueID(blk_copy))
4155
4156 old_data := b.block_instr1(.load, blk_grow, ptr_i8, data_ptr)
4157 elem_size_grow32 := b.block_instr1(.load, blk_grow, b.i32_type, elem_size_ptr)
4158 elem_size_grow := b.block_instr1(.zext, blk_grow, b.i64_type, elem_size_grow32)
4159 two := b.m.get_or_add_const(b.i64_type, '2')
4160 new_cap_base := b.block_instr2(.mul, blk_grow, b.i64_type, new_len, two)
4161 new_cap := b.block_instr2(.add, blk_grow, b.i64_type, new_cap_base, two)
4162 new_size := b.block_instr2(.mul, blk_grow, b.i64_type, new_cap, elem_size_grow)
4163 realloc_ref := b.m.add_value(.func_ref, b.void_type, 'realloc', b.fn_ids['realloc'])
4164 new_data := b.block_instr3(.call, blk_grow, ptr_i8, realloc_ref, old_data, new_size)
4165 b.block_instr2(.store, blk_grow, b.void_type, new_data, data_ptr)
4166 b.block_instr2(.store, blk_grow, b.void_type, new_cap, cap_ptr)
4167 b.block_instr1(.jmp, blk_grow, b.void_type, ValueID(blk_copy))
4168
4169 data := b.block_instr1(.load, blk_copy, ptr_i8, data_ptr)
4170 elem_size32 := b.block_instr1(.load, blk_copy, b.i32_type, elem_size_ptr)
4171 elem_size := b.block_instr1(.zext, blk_copy, b.i64_type, elem_size32)
4172 offset := b.block_instr2(.mul, blk_copy, b.i64_type, len, elem_size)
4173 dest := b.block_instr2(.add, blk_copy, ptr_i8, data, offset)
4174 copy_size := b.block_instr2(.mul, blk_copy, b.i64_type, count, elem_size)
4175 memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
4176 b.block_instr4(.call, blk_copy, ptr_i8, memcpy_ref, dest, src_ptr, copy_size)
4177 b.block_instr2(.store, blk_copy, b.void_type, new_len, len_ptr)
4178 b.block_instr0(.ret, blk_copy, b.void_type)
4179
4180 b.block_instr0(.ret, blk_done, b.void_type)
4181}
4182
4183// generate_array_push_many_array_body supports generate_array_push_many_array_body handling in ssa.
4184fn (mut b Builder) generate_array_push_many_array_body(func_id int) {
4185 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
4186 ptr_array := b.m.type_store.get_ptr(b.array_type)
4187 entry := b.m.add_block(func_id, 'entry')
4188 arr_ptr := b.func_add_argument(func_id, ptr_array, 'arr')
4189 src := b.func_add_argument(func_id, b.array_type, 'src')
4190
4191 src_alloca := b.block_instr0(.alloca, entry, ptr_array)
4192 b.block_instr2(.store, entry, b.void_type, src, src_alloca)
4193 data_ptr := b.block_struct_field_ptr(entry, src_alloca, b.array_type, 0)
4194 len_ptr := b.block_struct_field_ptr(entry, src_alloca, b.array_type, 2)
4195 data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
4196 len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
4197 len := b.block_instr1(.zext, entry, b.i64_type, len32)
4198 push_many_ref := b.m.add_value(.func_ref, b.void_type, 'array.push_many',
4199 b.fn_ids['array.push_many'])
4200 b.block_instr4(.call, entry, b.void_type, push_many_ref, arr_ptr, data, len)
4201 b.block_instr0(.ret, entry, b.void_type)
4202}
4203
4204// register_string_eq_stub updates register string eq stub state for ssa.
4205fn (mut b Builder) register_string_eq_stub() {
4206 mut p2 := []TypeID{}
4207 p2 << b.str_type
4208 p2 << b.str_type
4209 func_id := b.register_synthetic_function('string__eq', b.i1_type, p2)
4210 b.generate_string_eq_body(func_id)
4211}
4212
4213// register_fast_string_eq_stub updates register fast string eq stub state for ssa.
4214fn (mut b Builder) register_fast_string_eq_stub() {
4215 mut p2 := []TypeID{}
4216 p2 << b.str_type
4217 p2 << b.str_type
4218 func_id := b.register_synthetic_function('fast_string_eq', b.i1_type, p2)
4219 b.generate_string_eq_body(func_id)
4220}
4221
4222// register_string_lt_stub updates register string lt stub state for ssa.
4223fn (mut b Builder) register_string_lt_stub() {
4224 mut p2 := []TypeID{}
4225 p2 << b.str_type
4226 p2 << b.str_type
4227 func_id := b.register_synthetic_function('string__lt', b.i1_type, p2)
4228 b.generate_string_lt_body(func_id)
4229}
4230
4231// register_string_trim_stubs updates register string trim stubs state for ssa.
4232fn (mut b Builder) register_string_trim_stubs() {
4233 mut p2 := []TypeID{}
4234 p2 << b.str_type
4235 p2 << b.str_type
4236 trim_right_id := b.register_synthetic_function('string.trim_right', b.str_type, p2)
4237 b.generate_string_trim_right_body(trim_right_id)
4238}
4239
4240// generate_string_trim_right_body supports generate string trim right body handling for Builder.
4241fn (mut b Builder) generate_string_trim_right_body(func_id int) {
4242 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
4243 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
4244 ptr_i1 := b.m.type_store.get_ptr(b.i1_type)
4245 ptr_string := b.m.type_store.get_ptr(b.str_type)
4246 entry := b.m.add_block(func_id, 'entry')
4247 s := b.func_add_argument(func_id, b.str_type, 's')
4248 cutset := b.func_add_argument(func_id, b.str_type, 'cutset')
4249
4250 alloca_s := b.block_instr0(.alloca, entry, ptr_string)
4251 alloca_cutset := b.block_instr0(.alloca, entry, ptr_string)
4252 alloca_end := b.block_instr0(.alloca, entry, ptr_i64)
4253 alloca_j := b.block_instr0(.alloca, entry, ptr_i64)
4254 alloca_found := b.block_instr0(.alloca, entry, ptr_i1)
4255 b.block_instr2(.store, entry, b.void_type, s, alloca_s)
4256 b.block_instr2(.store, entry, b.void_type, cutset, alloca_cutset)
4257
4258 s_data_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 0)
4259 s_len_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 1)
4260 cut_data_ptr := b.block_struct_field_ptr(entry, alloca_cutset, b.str_type, 0)
4261 cut_len_ptr := b.block_struct_field_ptr(entry, alloca_cutset, b.str_type, 1)
4262 s_data := b.block_instr1(.load, entry, ptr_i8, s_data_ptr)
4263 s_len32 := b.block_instr1(.load, entry, b.i32_type, s_len_ptr)
4264 s_len := b.block_instr1(.zext, entry, b.i64_type, s_len32)
4265 cut_data := b.block_instr1(.load, entry, ptr_i8, cut_data_ptr)
4266 cut_len32 := b.block_instr1(.load, entry, b.i32_type, cut_len_ptr)
4267 cut_len := b.block_instr1(.zext, entry, b.i64_type, cut_len32)
4268 zero64 := b.m.get_or_add_const(b.i64_type, '0')
4269 one64 := b.m.get_or_add_const(b.i64_type, '1')
4270 false_val := b.m.get_or_add_const(b.i1_type, '0')
4271 true_val := b.m.get_or_add_const(b.i1_type, '1')
4272 b.block_instr2(.store, entry, b.void_type, s_len, alloca_end)
4273
4274 loop := b.m.add_block(func_id, 'trim_right_loop')
4275 body := b.m.add_block(func_id, 'trim_right_body')
4276 cut_loop := b.m.add_block(func_id, 'trim_right_cut_loop')
4277 cut_body := b.m.add_block(func_id, 'trim_right_cut_body')
4278 cut_found := b.m.add_block(func_id, 'trim_right_cut_found')
4279 cut_next := b.m.add_block(func_id, 'trim_right_cut_next')
4280 after_cut := b.m.add_block(func_id, 'trim_right_after_cut')
4281 trim_one := b.m.add_block(func_id, 'trim_right_trim_one')
4282 done := b.m.add_block(func_id, 'trim_right_done')
4283 b.block_instr1(.jmp, entry, b.void_type, ValueID(loop))
4284
4285 end_val := b.block_instr1(.load, loop, b.i64_type, alloca_end)
4286 has_chars := b.block_instr2(.gt, loop, b.i1_type, end_val, zero64)
4287 b.block_instr3(.br, loop, b.void_type, has_chars, ValueID(body), ValueID(done))
4288
4289 last_idx := b.block_instr2(.sub, body, b.i64_type, end_val, one64)
4290 ch_ptr := b.block_instr2(.add, body, ptr_i8, s_data, last_idx)
4291 ch := b.block_instr1(.load, body, b.i8_type, ch_ptr)
4292 b.block_instr2(.store, body, b.void_type, false_val, alloca_found)
4293 b.block_instr2(.store, body, b.void_type, zero64, alloca_j)
4294 b.block_instr1(.jmp, body, b.void_type, ValueID(cut_loop))
4295
4296 j := b.block_instr1(.load, cut_loop, b.i64_type, alloca_j)
4297 more_cut := b.block_instr2(.lt, cut_loop, b.i1_type, j, cut_len)
4298 b.block_instr3(.br, cut_loop, b.void_type, more_cut, ValueID(cut_body), ValueID(after_cut))
4299
4300 cut_ch_ptr := b.block_instr2(.add, cut_body, ptr_i8, cut_data, j)
4301 cut_ch := b.block_instr1(.load, cut_body, b.i8_type, cut_ch_ptr)
4302 is_match := b.block_instr2(.eq, cut_body, b.i1_type, ch, cut_ch)
4303 b.block_instr3(.br, cut_body, b.void_type, is_match, ValueID(cut_found), ValueID(cut_next))
4304
4305 b.block_instr2(.store, cut_found, b.void_type, true_val, alloca_found)
4306 b.block_instr1(.jmp, cut_found, b.void_type, ValueID(after_cut))
4307
4308 next_j := b.block_instr2(.add, cut_next, b.i64_type, j, one64)
4309 b.block_instr2(.store, cut_next, b.void_type, next_j, alloca_j)
4310 b.block_instr1(.jmp, cut_next, b.void_type, ValueID(cut_loop))
4311
4312 found := b.block_instr1(.load, after_cut, b.i1_type, alloca_found)
4313 b.block_instr3(.br, after_cut, b.void_type, found, ValueID(trim_one), ValueID(done))
4314
4315 cur_end := b.block_instr1(.load, trim_one, b.i64_type, alloca_end)
4316 new_end := b.block_instr2(.sub, trim_one, b.i64_type, cur_end, one64)
4317 b.block_instr2(.store, trim_one, b.void_type, new_end, alloca_end)
4318 b.block_instr1(.jmp, trim_one, b.void_type, ValueID(loop))
4319
4320 final_len := b.block_instr1(.load, done, b.i64_type, alloca_end)
4321 mut result := ValueID(0)
4322 for substr_name in ['string.substr', 'string__substr', 'substr'] {
4323 if substr_id := b.fn_ids[substr_name] {
4324 substr_ref := b.m.add_value(.func_ref, b.str_type, substr_name, substr_id)
4325 result = b.block_instr4(.call, done, b.str_type, substr_ref, s, zero64, final_len)
4326 break
4327 }
4328 }
4329 if result == ValueID(0) {
4330 result = b.emit_make_owned_string(done, s_data, final_len)
4331 }
4332 b.block_instr1(.ret, done, b.void_type, result)
4333}
4334
4335// register_string_last_part_stubs updates register string last part stubs state for ssa.
4336fn (mut b Builder) register_string_last_part_stubs() {
4337 mut p2 := []TypeID{}
4338 p2 << b.str_type
4339 p2 << b.str_type
4340 for name in ['string.all_before_last', 'string__all_before_last', 'all_before_last'] {
4341 func_id := b.register_synthetic_function(name, b.str_type, p2)
4342 b.generate_string_all_last_body(func_id, true)
4343 }
4344 for name in ['string.all_after_last', 'string__all_after_last', 'all_after_last'] {
4345 func_id := b.register_synthetic_function(name, b.str_type, p2)
4346 b.generate_string_all_last_body(func_id, false)
4347 }
4348}
4349
4350// generate_string_all_last_body supports generate string all last body handling for Builder.
4351fn (mut b Builder) generate_string_all_last_body(func_id int, before bool) {
4352 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
4353 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
4354 ptr_string := b.m.type_store.get_ptr(b.str_type)
4355 entry := b.m.add_block(func_id, 'entry')
4356 s := b.func_add_argument(func_id, b.str_type, 's')
4357 sub := b.func_add_argument(func_id, b.str_type, 'sub')
4358 alloca_s := b.block_instr0(.alloca, entry, ptr_string)
4359 alloca_sub := b.block_instr0(.alloca, entry, ptr_string)
4360 alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
4361 alloca_last := b.block_instr0(.alloca, entry, ptr_i64)
4362 b.block_instr2(.store, entry, b.void_type, s, alloca_s)
4363 b.block_instr2(.store, entry, b.void_type, sub, alloca_sub)
4364
4365 s_data_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 0)
4366 s_len_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 1)
4367 sub_data_ptr := b.block_struct_field_ptr(entry, alloca_sub, b.str_type, 0)
4368 sub_len_ptr := b.block_struct_field_ptr(entry, alloca_sub, b.str_type, 1)
4369 s_data := b.block_instr1(.load, entry, ptr_i8, s_data_ptr)
4370 s_len32 := b.block_instr1(.load, entry, b.i32_type, s_len_ptr)
4371 s_len := b.block_instr1(.zext, entry, b.i64_type, s_len32)
4372 sub_data := b.block_instr1(.load, entry, ptr_i8, sub_data_ptr)
4373 sub_len32 := b.block_instr1(.load, entry, b.i32_type, sub_len_ptr)
4374 sub_len := b.block_instr1(.zext, entry, b.i64_type, sub_len32)
4375 zero := b.m.get_or_add_const(b.i64_type, '0')
4376 one := b.m.get_or_add_const(b.i64_type, '1')
4377 minus_one := b.m.get_or_add_const(b.i64_type, '-1')
4378 b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
4379 b.block_instr2(.store, entry, b.void_type, minus_one, alloca_last)
4380
4381 check_size := b.m.add_block(func_id, 'all_last_check_size')
4382 loop := b.m.add_block(func_id, 'all_last_loop')
4383 body := b.m.add_block(func_id, 'all_last_body')
4384 match_block := b.m.add_block(func_id, 'all_last_match')
4385 next := b.m.add_block(func_id, 'all_last_next')
4386 done := b.m.add_block(func_id, 'all_last_done')
4387 return_match := b.m.add_block(func_id, 'all_last_return_match')
4388 return_original := b.m.add_block(func_id, 'all_last_return_original')
4389 has_sub := b.block_instr2(.gt, entry, b.i1_type, sub_len, zero)
4390 b.block_instr3(.br, entry, b.void_type, has_sub, ValueID(check_size), ValueID(return_original))
4391
4392 sub_fits := b.block_instr2(.ge, check_size, b.i1_type, s_len, sub_len)
4393 max_start := b.block_instr2(.sub, check_size, b.i64_type, s_len, sub_len)
4394 b.block_instr3(.br, check_size, b.void_type, sub_fits, ValueID(loop), ValueID(return_original))
4395
4396 i := b.block_instr1(.load, loop, b.i64_type, alloca_i)
4397 in_range := b.block_instr2(.le, loop, b.i1_type, i, max_start)
4398 b.block_instr3(.br, loop, b.void_type, in_range, ValueID(body), ValueID(done))
4399
4400 slot := b.block_instr2(.add, body, ptr_i8, s_data, i)
4401 memcmp_ref := b.m.add_value(.func_ref, b.void_type, 'memcmp', b.fn_ids['memcmp'])
4402 cmp := b.block_instr4(.call, body, b.i64_type, memcmp_ref, slot, sub_data, sub_len)
4403 is_match := b.block_instr2(.eq, body, b.i1_type, cmp, zero)
4404 b.block_instr3(.br, body, b.void_type, is_match, ValueID(match_block), ValueID(next))
4405
4406 b.block_instr2(.store, match_block, b.void_type, i, alloca_last)
4407 b.block_instr1(.jmp, match_block, b.void_type, ValueID(next))
4408
4409 next_i := b.block_instr2(.add, next, b.i64_type, i, one)
4410 b.block_instr2(.store, next, b.void_type, next_i, alloca_i)
4411 b.block_instr1(.jmp, next, b.void_type, ValueID(loop))
4412
4413 last := b.block_instr1(.load, done, b.i64_type, alloca_last)
4414 found := b.block_instr2(.ge, done, b.i1_type, last, zero)
4415 b.block_instr3(.br, done, b.void_type, found, ValueID(return_match), ValueID(return_original))
4416
4417 result := if before {
4418 b.emit_make_owned_string(return_match, s_data, last)
4419 } else {
4420 start := b.block_instr2(.add, return_match, b.i64_type, last, sub_len)
4421 result_data := b.block_instr2(.add, return_match, ptr_i8, s_data, start)
4422 result_len := b.block_instr2(.sub, return_match, b.i64_type, s_len, start)
4423 b.emit_make_owned_string(return_match, result_data, result_len)
4424 }
4425 b.block_instr1(.ret, return_match, b.void_type, result)
4426 b.block_instr1(.ret, return_original, b.void_type, s)
4427}
4428
4429// generate_string_eq_body supports generate string eq body handling for Builder.
4430fn (mut b Builder) generate_string_eq_body(func_id int) {
4431 ptr_string := b.m.type_store.get_ptr(b.str_type)
4432 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
4433 ptr_i32 := b.m.type_store.get_ptr(b.i32_type)
4434 entry := b.m.add_block(func_id, 'entry')
4435 param_a := b.func_add_argument(func_id, b.str_type, 'a')
4436 param_b := b.func_add_argument(func_id, b.str_type, 'b')
4437
4438 alloca_a := b.block_instr0(.alloca, entry, ptr_string)
4439 alloca_b := b.block_instr0(.alloca, entry, ptr_string)
4440 b.block_instr2(.store, entry, b.void_type, param_a, alloca_a)
4441 b.block_instr2(.store, entry, b.void_type, param_b, alloca_b)
4442
4443 zero_64 := b.m.get_or_add_const(b.i64_type, '0')
4444 len_off := b.m.get_or_add_const(b.i64_type, '8')
4445 a_str_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i8, alloca_a, zero_64)
4446 b_str_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i8, alloca_b, zero_64)
4447 a_len_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i32, alloca_a, len_off)
4448 b_len_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i32, alloca_b, len_off)
4449 a_str := b.block_instr1(.load, entry, ptr_i8, a_str_ptr)
4450 b_str := b.block_instr1(.load, entry, ptr_i8, b_str_ptr)
4451 a_len := b.block_instr1(.load, entry, b.i32_type, a_len_ptr)
4452 b_len := b.block_instr1(.load, entry, b.i32_type, b_len_ptr)
4453 len_eq := b.block_instr2(.eq, entry, b.i1_type, a_len, b_len)
4454
4455 blk_cmp := b.m.add_block(func_id, 'string_eq_cmp')
4456 blk_false := b.m.add_block(func_id, 'string_eq_false')
4457 b.block_instr3(.br, entry, b.void_type, len_eq, ValueID(blk_cmp), ValueID(blk_false))
4458
4459 false_val := b.m.get_or_add_const(b.i1_type, '0')
4460 b.block_instr1(.ret, blk_false, b.void_type, false_val)
4461
4462 a_len64 := b.block_instr1(.zext, blk_cmp, b.i64_type, a_len)
4463 fn_ref := b.m.add_value(.func_ref, b.void_type, 'memcmp', b.fn_ids['memcmp'])
4464 cmp := b.block_instr4(.call, blk_cmp, b.i64_type, fn_ref, a_str, b_str, a_len64)
4465 is_eq := b.block_instr2(.eq, blk_cmp, b.i1_type, cmp, zero_64)
4466 b.block_instr1(.ret, blk_cmp, b.void_type, is_eq)
4467}
4468
4469// generate_string_lt_body supports generate string lt body handling for Builder.
4470fn (mut b Builder) generate_string_lt_body(func_id int) {
4471 ptr_string := b.m.type_store.get_ptr(b.str_type)
4472 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
4473 ptr_i32 := b.m.type_store.get_ptr(b.i32_type)
4474 entry := b.m.add_block(func_id, 'entry')
4475 param_a := b.func_add_argument(func_id, b.str_type, 'a')
4476 param_b := b.func_add_argument(func_id, b.str_type, 'b')
4477
4478 alloca_a := b.block_instr0(.alloca, entry, ptr_string)
4479 alloca_b := b.block_instr0(.alloca, entry, ptr_string)
4480 alloca_min := b.block_instr0(.alloca, entry, ptr_i32)
4481 b.block_instr2(.store, entry, b.void_type, param_a, alloca_a)
4482 b.block_instr2(.store, entry, b.void_type, param_b, alloca_b)
4483
4484 zero_64 := b.m.get_or_add_const(b.i64_type, '0')
4485 len_off := b.m.get_or_add_const(b.i64_type, '8')
4486 a_str_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i8, alloca_a, zero_64)
4487 b_str_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i8, alloca_b, zero_64)
4488 a_len_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i32, alloca_a, len_off)
4489 b_len_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i32, alloca_b, len_off)
4490 a_str := b.block_instr1(.load, entry, ptr_i8, a_str_ptr)
4491 b_str := b.block_instr1(.load, entry, ptr_i8, b_str_ptr)
4492 a_len := b.block_instr1(.load, entry, b.i32_type, a_len_ptr)
4493 b_len := b.block_instr1(.load, entry, b.i32_type, b_len_ptr)
4494 a_shorter := b.block_instr2(.lt, entry, b.i1_type, a_len, b_len)
4495
4496 blk_a_min := b.m.add_block(func_id, 'string_lt_a_min')
4497 blk_b_min := b.m.add_block(func_id, 'string_lt_b_min')
4498 blk_cmp := b.m.add_block(func_id, 'string_lt_cmp')
4499 blk_true := b.m.add_block(func_id, 'string_lt_true')
4500 blk_false := b.m.add_block(func_id, 'string_lt_false')
4501 blk_cmp_gt := b.m.add_block(func_id, 'string_lt_cmp_gt')
4502 blk_len := b.m.add_block(func_id, 'string_lt_len')
4503 b.block_instr3(.br, entry, b.void_type, a_shorter, ValueID(blk_a_min), ValueID(blk_b_min))
4504
4505 b.block_instr2(.store, blk_a_min, b.void_type, a_len, alloca_min)
4506 b.block_instr1(.jmp, blk_a_min, b.void_type, ValueID(blk_cmp))
4507
4508 b.block_instr2(.store, blk_b_min, b.void_type, b_len, alloca_min)
4509 b.block_instr1(.jmp, blk_b_min, b.void_type, ValueID(blk_cmp))
4510
4511 min_len := b.block_instr1(.load, blk_cmp, b.i32_type, alloca_min)
4512 min_len64 := b.block_instr1(.zext, blk_cmp, b.i64_type, min_len)
4513 fn_ref := b.m.add_value(.func_ref, b.void_type, 'memcmp', b.fn_ids['memcmp'])
4514 cmp := b.block_instr4(.call, blk_cmp, b.i64_type, fn_ref, a_str, b_str, min_len64)
4515 cmp_lt_zero := b.block_instr2(.lt, blk_cmp, b.i1_type, cmp, zero_64)
4516 b.block_instr3(.br, blk_cmp, b.void_type, cmp_lt_zero, ValueID(blk_true), ValueID(blk_cmp_gt))
4517
4518 cmp_gt_zero := b.block_instr2(.gt, blk_cmp_gt, b.i1_type, cmp, zero_64)
4519 b.block_instr3(.br, blk_cmp_gt, b.void_type, cmp_gt_zero, ValueID(blk_false), ValueID(blk_len))
4520
4521 true_val := b.m.get_or_add_const(b.i1_type, '1')
4522 false_val := b.m.get_or_add_const(b.i1_type, '0')
4523 b.block_instr1(.ret, blk_true, b.void_type, true_val)
4524 b.block_instr1(.ret, blk_false, b.void_type, false_val)
4525
4526 len_lt := b.block_instr2(.lt, blk_len, b.i1_type, a_len, b_len)
4527 b.block_instr1(.ret, blk_len, b.void_type, len_lt)
4528}
4529
4530// generate_wymix_body supports generate wymix body handling for Builder.
4531fn (mut b Builder) generate_wymix_body(func_id int) {
4532 entry := b.m.add_block(func_id, 'entry')
4533 param_a := b.func_add_argument(func_id, b.i64_type, 'a')
4534 param_b := b.func_add_argument(func_id, b.i64_type, 'b')
4535 result := b.wymix_inline(entry, param_a, param_b)
4536 b.block_instr1(.ret, entry, b.void_type, result)
4537}
4538
4539// generate_wyhash64_body supports generate wyhash64 body handling for Builder.
4540fn (mut b Builder) generate_wyhash64_body(func_id int) {
4541 entry := b.m.add_block(func_id, 'entry')
4542 param_a := b.func_add_argument(func_id, b.i64_type, 'a')
4543 param_b := b.func_add_argument(func_id, b.i64_type, 'b')
4544
4545 wyp0 := b.m.get_or_add_const(b.i64_type, '3257665815644502181')
4546 wyp1 := b.m.get_or_add_const(b.i64_type, '10067880064238660809')
4547
4548 x := b.block_instr2(.xor, entry, b.i64_type, param_a, wyp0)
4549 y := b.block_instr2(.xor, entry, b.i64_type, param_b, wyp1)
4550 result := b.wymix_inline(entry, x, y)
4551 b.block_instr1(.ret, entry, b.void_type, result)
4552}
4553
4554// wymum_pair_inline supports wymum pair inline handling for Builder.
4555fn (mut b Builder) wymum_pair_inline(block_id BlockID, a ValueID, b_val ValueID) (ValueID, ValueID) {
4556 mask32 := b.m.get_or_add_const(b.i64_type, '4294967295')
4557 c32 := b.m.get_or_add_const(b.i64_type, '32')
4558
4559 lo := b.block_instr2(.mul, block_id, b.i64_type, a, b_val)
4560
4561 x0 := b.block_instr2(.and_, block_id, b.i64_type, a, mask32)
4562 x1 := b.block_instr2(.lshr, block_id, b.i64_type, a, c32)
4563 y0 := b.block_instr2(.and_, block_id, b.i64_type, b_val, mask32)
4564 y1 := b.block_instr2(.lshr, block_id, b.i64_type, b_val, c32)
4565 w0 := b.block_instr2(.mul, block_id, b.i64_type, x0, y0)
4566 x1y0 := b.block_instr2(.mul, block_id, b.i64_type, x1, y0)
4567 w0_hi := b.block_instr2(.lshr, block_id, b.i64_type, w0, c32)
4568 t := b.block_instr2(.add, block_id, b.i64_type, x1y0, w0_hi)
4569 t_lo := b.block_instr2(.and_, block_id, b.i64_type, t, mask32)
4570 x0y1 := b.block_instr2(.mul, block_id, b.i64_type, x0, y1)
4571 w1 := b.block_instr2(.add, block_id, b.i64_type, t_lo, x0y1)
4572 w2 := b.block_instr2(.lshr, block_id, b.i64_type, t, c32)
4573 x1y1 := b.block_instr2(.mul, block_id, b.i64_type, x1, y1)
4574 w1_hi := b.block_instr2(.lshr, block_id, b.i64_type, w1, c32)
4575 hi_tmp := b.block_instr2(.add, block_id, b.i64_type, x1y1, w2)
4576 hi := b.block_instr2(.add, block_id, b.i64_type, hi_tmp, w1_hi)
4577
4578 return lo, hi
4579}
4580
4581// wymix_inline supports wymix inline handling for Builder.
4582fn (mut b Builder) wymix_inline(block_id BlockID, a ValueID, b_val ValueID) ValueID {
4583 lo, hi := b.wymum_pair_inline(block_id, a, b_val)
4584 return b.block_instr2(.xor, block_id, b.i64_type, hi, lo)
4585}
4586
4587// generate_wyhash_body supports generate wyhash body handling for Builder.
4588fn (mut b Builder) generate_wyhash_body(func_id int) {
4589 ptr_u8 := b.m.type_store.get_ptr(b.i8_type)
4590 ptr_u32 := b.m.type_store.get_ptr(b.u32_type)
4591 ptr_u64 := b.m.type_store.get_ptr(b.u64_type)
4592
4593 entry := b.m.add_block(func_id, 'entry')
4594 param_key := b.func_add_argument(func_id, ptr_u8, 'key')
4595 param_len := b.func_add_argument(func_id, b.i64_type, 'len')
4596 param_seed := b.func_add_argument(func_id, b.i64_type, 'seed')
4597 _ := b.func_add_argument(func_id, ptr_u64, 'secret')
4598
4599 wyp0 := b.m.get_or_add_const(b.i64_type, '3257665815644502181')
4600 wyp1 := b.m.get_or_add_const(b.i64_type, '10067880064238660809')
4601
4602 seed_xor_s0 := b.block_instr2(.xor, entry, b.i64_type, param_seed, wyp0)
4603 seed_xor_s0_len := b.block_instr2(.xor, entry, b.i64_type, seed_xor_s0, param_len)
4604 seed_mix := b.wymix_inline(entry, seed_xor_s0_len, wyp1)
4605 seed_init := b.block_instr2(.xor, entry, b.i64_type, param_seed, seed_mix)
4606
4607 alloca_a := b.block_instr0(.alloca, entry, ptr_u64)
4608 alloca_b := b.block_instr0(.alloca, entry, ptr_u64)
4609 alloca_seed := b.block_instr0(.alloca, entry, ptr_u64)
4610
4611 zero_64 := b.m.get_or_add_const(b.i64_type, '0')
4612 one_64 := b.m.get_or_add_const(b.i64_type, '1')
4613 two_64 := b.m.get_or_add_const(b.i64_type, '2')
4614 three_64 := b.m.get_or_add_const(b.i64_type, '3')
4615 four_64 := b.m.get_or_add_const(b.i64_type, '4')
4616 eight_64 := b.m.get_or_add_const(b.i64_type, '8')
4617 sixteen_64 := b.m.get_or_add_const(b.i64_type, '16')
4618 c32 := b.m.get_or_add_const(b.i64_type, '32')
4619
4620 b.block_instr2(.store, entry, b.void_type, zero_64, alloca_a)
4621 b.block_instr2(.store, entry, b.void_type, zero_64, alloca_b)
4622 b.block_instr2(.store, entry, b.void_type, seed_init, alloca_seed)
4623
4624 blk_short := b.m.add_block(func_id, 'wyhash_short')
4625 blk_short_4_16 := b.m.add_block(func_id, 'wyhash_short_4_16')
4626 blk_short_0_3 := b.m.add_block(func_id, 'wyhash_short_0_3')
4627 blk_wyr3 := b.m.add_block(func_id, 'wyhash_wyr3')
4628 blk_long := b.m.add_block(func_id, 'wyhash_long')
4629 blk_final := b.m.add_block(func_id, 'wyhash_final')
4630
4631 len_le_16 := b.block_instr2(.le, entry, b.i1_type, param_len, sixteen_64)
4632 b.block_instr3(.br, entry, b.void_type, len_le_16, ValueID(blk_short), ValueID(blk_long))
4633
4634 len_ge_4 := b.block_instr2(.ge, blk_short, b.i1_type, param_len, four_64)
4635 b.block_instr3(.br, blk_short, b.void_type, len_ge_4, ValueID(blk_short_4_16),
4636 ValueID(blk_short_0_3))
4637
4638 p_as_u32 := b.block_instr1(.bitcast, blk_short_4_16, ptr_u32, param_key)
4639 wyr4_0 := b.block_instr1(.load, blk_short_4_16, b.u32_type, p_as_u32)
4640 wyr4_0_64 := b.block_instr1(.zext, blk_short_4_16, b.i64_type, wyr4_0)
4641
4642 len_shr3 := b.block_instr2(.lshr, blk_short_4_16, b.i64_type, param_len, three_64)
4643 off1 := b.block_instr2(.shl, blk_short_4_16, b.i64_type, len_shr3, two_64)
4644
4645 p_off1 := b.block_instr2(.add, blk_short_4_16, ptr_u8, param_key, off1)
4646 p_off1_u32 := b.block_instr1(.bitcast, blk_short_4_16, ptr_u32, p_off1)
4647 wyr4_1 := b.block_instr1(.load, blk_short_4_16, b.u32_type, p_off1_u32)
4648 wyr4_1_64 := b.block_instr1(.zext, blk_short_4_16, b.i64_type, wyr4_1)
4649
4650 wyr4_0_shifted := b.block_instr2(.shl, blk_short_4_16, b.i64_type, wyr4_0_64, c32)
4651 a_val := b.block_instr2(.or_, blk_short_4_16, b.i64_type, wyr4_0_shifted, wyr4_1_64)
4652
4653 len_m4 := b.block_instr2(.sub, blk_short_4_16, b.i64_type, param_len, four_64)
4654 p_lm4 := b.block_instr2(.add, blk_short_4_16, ptr_u8, param_key, len_m4)
4655 p_lm4_u32 := b.block_instr1(.bitcast, blk_short_4_16, ptr_u32, p_lm4)
4656 wyr4_2 := b.block_instr1(.load, blk_short_4_16, b.u32_type, p_lm4_u32)
4657 wyr4_2_64 := b.block_instr1(.zext, blk_short_4_16, b.i64_type, wyr4_2)
4658
4659 lm4_moff1 := b.block_instr2(.sub, blk_short_4_16, b.i64_type, len_m4, off1)
4660 p_lm4_moff1 := b.block_instr2(.add, blk_short_4_16, ptr_u8, param_key, lm4_moff1)
4661 p_lm4_moff1_u32 := b.block_instr1(.bitcast, blk_short_4_16, ptr_u32, p_lm4_moff1)
4662 wyr4_3 := b.block_instr1(.load, blk_short_4_16, b.u32_type, p_lm4_moff1_u32)
4663 wyr4_3_64 := b.block_instr1(.zext, blk_short_4_16, b.i64_type, wyr4_3)
4664
4665 wyr4_2_shifted := b.block_instr2(.shl, blk_short_4_16, b.i64_type, wyr4_2_64, c32)
4666 b_val := b.block_instr2(.or_, blk_short_4_16, b.i64_type, wyr4_2_shifted, wyr4_3_64)
4667
4668 b.block_instr2(.store, blk_short_4_16, b.void_type, a_val, alloca_a)
4669 b.block_instr2(.store, blk_short_4_16, b.void_type, b_val, alloca_b)
4670 b.block_instr1(.jmp, blk_short_4_16, b.void_type, ValueID(blk_final))
4671
4672 len_gt_0 := b.block_instr2(.gt, blk_short_0_3, b.i1_type, param_len, zero_64)
4673 b.block_instr3(.br, blk_short_0_3, b.void_type, len_gt_0, ValueID(blk_wyr3), ValueID(blk_final))
4674
4675 byte_0 := b.block_instr1(.load, blk_wyr3, b.i8_type, param_key)
4676 byte_0_64 := b.block_instr1(.zext, blk_wyr3, b.i64_type, byte_0)
4677 byte_0_shifted := b.block_instr2(.shl, blk_wyr3, b.i64_type, byte_0_64, sixteen_64)
4678
4679 len_shr1 := b.block_instr2(.lshr, blk_wyr3, b.i64_type, param_len, one_64)
4680 p_mid := b.block_instr2(.add, blk_wyr3, ptr_u8, param_key, len_shr1)
4681 byte_mid := b.block_instr1(.load, blk_wyr3, b.i8_type, p_mid)
4682 byte_mid_64 := b.block_instr1(.zext, blk_wyr3, b.i64_type, byte_mid)
4683 byte_mid_shifted := b.block_instr2(.shl, blk_wyr3, b.i64_type, byte_mid_64, eight_64)
4684
4685 len_m1 := b.block_instr2(.sub, blk_wyr3, b.i64_type, param_len, one_64)
4686 p_last := b.block_instr2(.add, blk_wyr3, ptr_u8, param_key, len_m1)
4687 byte_last := b.block_instr1(.load, blk_wyr3, b.i8_type, p_last)
4688 byte_last_64 := b.block_instr1(.zext, blk_wyr3, b.i64_type, byte_last)
4689
4690 a_tmp := b.block_instr2(.or_, blk_wyr3, b.i64_type, byte_0_shifted, byte_mid_shifted)
4691 a_wyr3 := b.block_instr2(.or_, blk_wyr3, b.i64_type, a_tmp, byte_last_64)
4692
4693 b.block_instr2(.store, blk_wyr3, b.void_type, a_wyr3, alloca_a)
4694 b.block_instr1(.jmp, blk_wyr3, b.void_type, ValueID(blk_final))
4695
4696 p_as_u64 := b.block_instr1(.bitcast, blk_long, ptr_u64, param_key)
4697 wyr8_first := b.block_instr1(.load, blk_long, b.i64_type, p_as_u64)
4698
4699 p_plus_8 := b.block_instr2(.add, blk_long, ptr_u8, param_key, eight_64)
4700 p_plus_8_u64 := b.block_instr1(.bitcast, blk_long, ptr_u64, p_plus_8)
4701 wyr8_second := b.block_instr1(.load, blk_long, b.i64_type, p_plus_8_u64)
4702
4703 len_m16 := b.block_instr2(.sub, blk_long, b.i64_type, param_len, sixteen_64)
4704 p_end_16 := b.block_instr2(.add, blk_long, ptr_u8, param_key, len_m16)
4705 p_end_16_u64 := b.block_instr1(.bitcast, blk_long, ptr_u64, p_end_16)
4706 wyr8_end_16 := b.block_instr1(.load, blk_long, b.i64_type, p_end_16_u64)
4707
4708 len_m8 := b.block_instr2(.sub, blk_long, b.i64_type, param_len, eight_64)
4709 p_end_8 := b.block_instr2(.add, blk_long, ptr_u8, param_key, len_m8)
4710 p_end_8_u64 := b.block_instr1(.bitcast, blk_long, ptr_u64, p_end_8)
4711 wyr8_end_8 := b.block_instr1(.load, blk_long, b.i64_type, p_end_8_u64)
4712
4713 mix_a := b.block_instr2(.xor, blk_long, b.i64_type, wyr8_first, wyp1)
4714 seed_cur := b.block_instr1(.load, blk_long, b.i64_type, alloca_seed)
4715 mix_b := b.block_instr2(.xor, blk_long, b.i64_type, wyr8_second, seed_cur)
4716 seed_long := b.wymix_inline(blk_long, mix_a, mix_b)
4717
4718 b.block_instr2(.store, blk_long, b.void_type, seed_long, alloca_seed)
4719 b.block_instr2(.store, blk_long, b.void_type, wyr8_end_16, alloca_a)
4720 b.block_instr2(.store, blk_long, b.void_type, wyr8_end_8, alloca_b)
4721 b.block_instr1(.jmp, blk_long, b.void_type, ValueID(blk_final))
4722
4723 final_a := b.block_instr1(.load, blk_final, b.i64_type, alloca_a)
4724 final_b := b.block_instr1(.load, blk_final, b.i64_type, alloca_b)
4725 final_seed := b.block_instr1(.load, blk_final, b.i64_type, alloca_seed)
4726
4727 final_a_xor := b.block_instr2(.xor, blk_final, b.i64_type, final_a, wyp1)
4728 final_b_xor := b.block_instr2(.xor, blk_final, b.i64_type, final_b, final_seed)
4729
4730 mum_lo, mum_hi := b.wymum_pair_inline(blk_final, final_a_xor, final_b_xor)
4731
4732 lo_xor_s0 := b.block_instr2(.xor, blk_final, b.i64_type, mum_lo, wyp0)
4733 lo_xor_s0_len := b.block_instr2(.xor, blk_final, b.i64_type, lo_xor_s0, param_len)
4734 hi_xor_s1 := b.block_instr2(.xor, blk_final, b.i64_type, mum_hi, wyp1)
4735
4736 final_result := b.wymix_inline(blk_final, lo_xor_s0_len, hi_xor_s1)
4737 b.block_instr1(.ret, blk_final, b.void_type, final_result)
4738}
4739
4740// build_functions builds functions data for ssa.
4741fn (mut b Builder) build_functions() {
4742 mut cur_module := ''
4743 for node in b.a.nodes {
4744 if node.kind == .module_decl {
4745 cur_module = node.value
4746 continue
4747 }
4748 if node.kind == .fn_decl {
4749 if b.skip_source_fn_in_module(node.value, cur_module) {
4750 continue
4751 }
4752 fn_name := ssa_fn_name_in_module(cur_module, node.value)
4753 // Dead-module elimination: skip every function from a skipped module.
4754 if b.skip_modules.len > 0 && cur_module in b.skip_modules {
4755 b.mark_fn_prototype(fn_name)
4756 continue
4757 }
4758 if b.used_fns.len > 0 && !b.fn_is_used(node.value) && !b.fn_is_used(fn_name) {
4759 continue
4760 }
4761 // Hot reload: only the named function's body is materialized.
4762 if b.hot_fn.len > 0 && node.value != b.hot_fn && fn_name != b.hot_fn {
4763 b.mark_fn_prototype(fn_name)
4764 continue
4765 }
4766 // Signature-only build: register the prototype, skip the body.
4767 if b.skip_fn_bodies {
4768 b.mark_fn_prototype(fn_name)
4769 continue
4770 }
4771 b.build_function(node, cur_module)
4772 }
4773 }
4774 if b.top_level_main && !b.skip_fn_bodies && b.hot_fn.len == 0 {
4775 b.build_top_level_main()
4776 }
4777}
4778
4779// mark_fn_prototype flags an already-registered function as a declaration whose
4780// body was intentionally not materialized (hot reload / skip-bodies / skip-module).
4781fn (mut b Builder) mark_fn_prototype(fn_name string) {
4782 if fn_id := b.fn_ids[fn_name] {
4783 b.m.func_set_prototype(fn_id, true)
4784 }
4785}
4786
4787// main_module_name returns the program's primary module name (the first
4788// non-builtin module_decl), defaulting to 'main'.
4789fn (b &Builder) main_module_name() string {
4790 for node in b.a.nodes {
4791 if node.kind == .module_decl && node.value.len > 0 && node.value != 'builtin' {
4792 return node.value
4793 }
4794 }
4795 return 'main'
4796}
4797
4798// register_type_aliases records `type Foo = Bar` aliases as SSA base TypeIDs so
4799// resolve_type can resolve them even when no checker is attached.
4800fn (mut b Builder) register_type_aliases() {
4801 mut cur_module := ''
4802 for node in b.a.nodes {
4803 if node.kind == .module_decl {
4804 cur_module = node.value
4805 continue
4806 }
4807 // A type_decl with no children and a non-empty `typ` is an alias
4808 // (`type Foo = Bar`); a type_decl with children is a sum type.
4809 if node.kind == .type_decl && node.children_count == 0 && node.typ.len > 0 {
4810 base := b.resolve_type_in_module(node.typ, cur_module)
4811 if base > 0 {
4812 b.type_aliases[node.value] = base
4813 qualified := qualify_type_name(node.value, cur_module)
4814 b.type_aliases[qualified] = base
4815 }
4816 }
4817 }
4818}
4819
4820// checker_param_type_name supports checker param type name handling for Builder.
4821fn (b &Builder) checker_param_type_name(fn_name string, module_name string, idx int) ?string {
4822 if b.tc == unsafe { nil } {
4823 return none
4824 }
4825 candidates := checker_fn_name_candidates(fn_name, module_name)
4826 for candidate in candidates {
4827 if params := b.tc.fn_param_types[candidate] {
4828 if idx >= 0 && idx < params.len {
4829 return params[idx].name()
4830 }
4831 }
4832 }
4833 return none
4834}
4835
4836// checker_fn_name_candidates supports checker fn name candidates handling for ssa.
4837fn checker_fn_name_candidates(fn_name string, module_name string) []string {
4838 mut candidates := []string{}
4839 if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
4840 candidates << module_name + '.' + fn_name
4841 if fn_name.contains('__') {
4842 candidates << module_name + '.' + fn_name.replace('__', '.')
4843 }
4844 if fn_name.contains('.') {
4845 candidates << module_name + '.' + fn_name.replace('.', '__')
4846 short_name := fn_name.all_after('.')
4847 candidates << module_name + '.' + short_name
4848 }
4849 }
4850 candidates << fn_name
4851 if fn_name.contains('__') {
4852 candidates << fn_name.replace('__', '.')
4853 }
4854 if fn_name.contains('.') {
4855 candidates << fn_name.replace('.', '__')
4856 }
4857 return candidates
4858}
4859
4860// checker_return_type supports checker return type handling for Builder.
4861fn (mut b Builder) checker_return_type(fn_name string, module_name string) ?TypeID {
4862 if b.tc == unsafe { nil } {
4863 return none
4864 }
4865 candidates := checker_fn_name_candidates(fn_name, module_name)
4866 for candidate in candidates {
4867 if ret := b.tc.fn_ret_types[candidate] {
4868 return b.ssa_type_from_checker_type(ret)
4869 }
4870 }
4871 return none
4872}
4873
4874// fn_is_used supports fn is used handling for Builder.
4875fn (b &Builder) fn_is_used(name string) bool {
4876 if name in b.used_fns {
4877 return true
4878 }
4879 if name == 'bench.new' || name == 'bench__new' || name.ends_with('.bench.new')
4880 || name.ends_with('__bench__new') {
4881 return true
4882 }
4883 if name in ['error_file_not_opened', 'error_size_of_type_0', 'vpopen', 'vpclose'] {
4884 return true
4885 }
4886 if name.contains('__') && name.replace('__', '.') in b.used_fns {
4887 return true
4888 }
4889 if !name.contains('.') {
4890 for used_name, _ in b.used_fns {
4891 normalized_used := used_name.replace('__', '.')
4892 if used_name.ends_with('.${name}') || normalized_used.ends_with('.${name}') {
4893 return true
4894 }
4895 }
4896 }
4897 if name.contains('.') {
4898 normalized_name := name.replace('__', '.')
4899 short_name := normalized_name.all_after_last('.')
4900 if short_name in b.used_fns {
4901 return true
4902 }
4903 for used_name, _ in b.used_fns {
4904 normalized_used := used_name.replace('__', '.')
4905 if used_name.ends_with('.${name}') || normalized_used.ends_with('.${name}')
4906 || normalized_name.ends_with('.${normalized_used}') {
4907 return true
4908 }
4909 }
4910 }
4911 if name.starts_with('array_') || name.starts_with('string__') || name.starts_with('strings__')
4912 || name.starts_with('strconv__') || name.starts_with('IError.') {
4913 return true
4914 }
4915 if name in ['new_map', 'memdup', 'int_str', 'bool_str', 'print', 'println', 'eprint', 'eprintln',
4916 'exit', 'arguments', 'tos', 'tos2', 'tos3', 'tos_clone', 'cstring_to_vstring',
4917 'malloc_noscan', 'isnil', 'error', 'error_with_code', 'join_path_single'] {
4918 return true
4919 }
4920 return false
4921}
4922
4923// build_function builds function data for ssa.
4924fn (mut b Builder) build_function(node flat.Node, module_name string) {
4925 fn_name := ssa_fn_name_in_module(module_name, node.value)
4926 func_id := b.fn_ids[fn_name]
4927 b.cur_module = module_name
4928 b.cur_func = func_id
4929 b.cur_func_ret_type = qualify_type_ref_name(node.typ, module_name)
4930 b.vars = map[string]ValueID{}
4931 b.var_type_names = map[string]string{}
4932 b.label_blocks = map[string]BlockID{}
4933 b.defer_body_ids = []flat.NodeId{}
4934
4935 for v in b.m.values {
4936 if v.kind == .global {
4937 b.vars[v.name] = v.id
4938 }
4939 }
4940
4941 entry := b.m.add_block(func_id, 'entry')
4942 b.cur_block = entry
4943
4944 for i in 0 .. node.children_count {
4945 child := b.a.child_node(&node, i)
4946 if child.kind == .param {
4947 typ_name := if child.typ.starts_with('&') {
4948 child.typ
4949 } else {
4950 b.checker_param_type_name(node.value, module_name, b.m.funcs[func_id].params.len) or {
4951 child.typ
4952 }
4953 }
4954 typ := b.resolve_type_in_module(typ_name, module_name)
4955 param_val := b.m.add_value(.argument, typ, child.value, b.m.funcs[func_id].params.len)
4956 mut f := b.m.funcs[func_id]
4957 f.params << param_val
4958 b.m.funcs[func_id] = f
4959
4960 alloca := b.emit0(.alloca, b.m.type_store.get_ptr(typ))
4961 b.emit2(.store, b.void_type, param_val, alloca)
4962 b.vars[child.value] = alloca
4963 b.var_type_names[child.value] = typ_name
4964 }
4965 }
4966
4967 body_ids := b.fn_body_ids(node)
4968 for id in body_ids {
4969 b.build_stmt(id)
4970 }
4971
4972 blk := b.m.blocks[b.cur_block]
4973 if blk.instrs.len == 0 || !b.is_terminator(blk.instrs.last()) {
4974 ret_type := b.m.funcs[func_id].typ
4975 if ret_type == b.void_type {
4976 b.emit_deferred_stmts()
4977 b.emit0(.ret, b.void_type)
4978 }
4979 }
4980}
4981
4982// build_top_level_main builds top level main data for ssa.
4983fn (mut b Builder) build_top_level_main() {
4984 func_id := b.fn_ids['main'] or { return }
4985 b.cur_module = 'main'
4986 b.cur_func = func_id
4987 b.cur_func_ret_type = ''
4988 b.vars = map[string]ValueID{}
4989 b.var_type_names = map[string]string{}
4990 b.label_blocks = map[string]BlockID{}
4991 b.defer_body_ids = []flat.NodeId{}
4992 for v in b.m.values {
4993 if v.kind == .global {
4994 b.vars[v.name] = v.id
4995 }
4996 }
4997 entry := b.m.add_block(func_id, 'entry')
4998 b.cur_block = entry
4999
5000 for id in b.top_level_stmt_ids() {
5001 b.build_stmt(id)
5002 }
5003
5004 blk := b.m.blocks[b.cur_block]
5005 if blk.instrs.len == 0 || !b.is_terminator(blk.instrs.last()) {
5006 b.emit_deferred_stmts()
5007 b.emit0(.ret, b.void_type)
5008 }
5009}
5010
5011// fn_body_ids supports fn body ids handling for Builder.
5012fn (b &Builder) fn_body_ids(node flat.Node) []flat.NodeId {
5013 mut ids := []flat.NodeId{}
5014 for i in 0 .. node.children_count {
5015 child_id := b.a.child(&node, i)
5016 child := b.a.nodes[int(child_id)]
5017 if child.kind != .param && !b.is_declaration_node(child) {
5018 ids << child_id
5019 }
5020 }
5021 return ids
5022}
5023
5024// is_declaration_node reports whether is declaration node applies in ssa.
5025fn (b &Builder) is_declaration_node(node flat.Node) bool {
5026 return match node.kind {
5027 .fn_decl, .c_fn_decl, .struct_decl, .field_decl, .global_decl, .const_decl, .const_field,
5028 .enum_decl, .enum_field, .type_decl, .interface_decl, .interface_field, .import_decl,
5029 .module_decl, .directive {
5030 true
5031 }
5032 else {
5033 false
5034 }
5035 }
5036}
5037
5038// is_terminator reports whether is terminator applies in ssa.
5039fn (b &Builder) is_terminator(val_id ValueID) bool {
5040 if val_id <= 0 || val_id >= b.m.values.len {
5041 return false
5042 }
5043 v := b.m.values[val_id]
5044 if v.kind != .instruction {
5045 return false
5046 }
5047 instr := b.m.instrs[v.index]
5048 return instr.op == .ret || instr.op == .br || instr.op == .jmp || instr.op == .unreachable
5049}
5050
5051// valid_node_id supports valid node id handling for Builder.
5052fn (b &Builder) valid_node_id(id flat.NodeId) bool {
5053 return b.a != unsafe { nil } && int(id) >= 0 && int(id) < b.a.nodes.len
5054}
5055
5056// ident_name supports ident name handling for Builder.
5057fn (b &Builder) ident_name(id flat.NodeId) string {
5058 if !b.valid_node_id(id) {
5059 return ''
5060 }
5061 node := b.a.nodes[int(id)]
5062 if node.kind != .ident {
5063 return ''
5064 }
5065 return node.value
5066}
5067
5068// save_var_binding updates save var binding state for ssa.
5069fn (b &Builder) save_var_binding(name string) VarBinding {
5070 if name.len == 0 {
5071 return VarBinding{}
5072 }
5073 if addr := b.vars[name] {
5074 return VarBinding{
5075 exists: true
5076 addr: addr
5077 typ: b.var_type_names[name] or { '' }
5078 }
5079 }
5080 return VarBinding{}
5081}
5082
5083// restore_var_binding supports restore var binding handling for Builder.
5084fn (mut b Builder) restore_var_binding(name string, binding VarBinding) {
5085 if name.len == 0 {
5086 return
5087 }
5088 if binding.exists {
5089 b.vars[name] = binding.addr
5090 if binding.typ.len > 0 {
5091 b.var_type_names[name] = binding.typ
5092 } else {
5093 b.var_type_names.delete(name)
5094 }
5095 } else {
5096 b.vars.delete(name)
5097 b.var_type_names.delete(name)
5098 }
5099}
5100
5101// bind_loop_var supports bind loop var handling for Builder.
5102fn (mut b Builder) bind_loop_var(name string, typ TypeID, typ_name string) ValueID {
5103 if name.len == 0 {
5104 return ValueID(0)
5105 }
5106 slot := b.emit0(.alloca, b.m.type_store.get_ptr(typ))
5107 b.vars[name] = slot
5108 if typ_name.len > 0 {
5109 b.var_type_names[name] = typ_name
5110 }
5111 return slot
5112}
5113
5114// for_in_container_type_name supports for in container type name handling for Builder.
5115fn (b &Builder) for_in_container_type_name(container_id flat.NodeId) string {
5116 checked := b.checked_expr_type_name(container_id)
5117 if checked.len > 0 && checked != 'unknown' {
5118 return checked.trim_left('&')
5119 }
5120 if !b.valid_node_id(container_id) {
5121 return ''
5122 }
5123 node := b.a.nodes[int(container_id)]
5124 if node.typ.len > 0 {
5125 return node.typ.trim_left('&')
5126 }
5127 if node.kind == .ident {
5128 return (b.var_type_names[node.value] or { '' }).trim_left('&')
5129 }
5130 if node.kind == .selector && node.children_count > 0 {
5131 base_type := b.for_in_container_type_name(b.a.child(&node, 0)).trim_left('&')
5132 field_key := base_type + '.' + node.value
5133 if field_type := b.struct_field_types[field_key] {
5134 return field_type.trim_left('&')
5135 }
5136 short_type := base_type.all_after('.')
5137 short_key := short_type + '.' + node.value
5138 if field_type := b.struct_field_types[short_key] {
5139 return field_type.trim_left('&')
5140 }
5141 }
5142 return ''
5143}
5144
5145// for_in_array_elem_type_name supports for in array elem type name handling for Builder.
5146fn (b &Builder) for_in_array_elem_type_name(container_id flat.NodeId, container_type string) string {
5147 clean := container_type.trim_left('&')
5148 if clean.starts_with('[]') {
5149 return clean[2..]
5150 }
5151 if clean.starts_with('[') {
5152 idx := clean.index_u8(`]`)
5153 if idx > 0 && idx + 1 < clean.len {
5154 return clean[idx + 1..]
5155 }
5156 }
5157 if clean == 'string' {
5158 return 'u8'
5159 }
5160 if b.valid_node_id(container_id) {
5161 node := b.a.nodes[int(container_id)]
5162 if node.kind == .array_literal && node.children_count > 0 {
5163 first_id := b.a.child(&node, 0)
5164 first_type := b.checked_expr_type_name(first_id)
5165 if first_type.len > 0 && first_type != 'unknown' {
5166 return first_type
5167 }
5168 }
5169 }
5170 return 'int'
5171}
5172
5173// build_stmt builds stmt data for ssa.
5174fn (mut b Builder) build_stmt(id flat.NodeId) {
5175 if int(id) < 0 {
5176 return
5177 }
5178 node := b.a.nodes[int(id)]
5179 match node.kind {
5180 .expr_stmt {
5181 expr_id := b.a.child(&node, 0)
5182 expr := b.a.nodes[int(expr_id)]
5183 if expr.kind == .infix && expr.op == .left_shift && expr.value == 'push' {
5184 b.build_array_push_expr(expr)
5185 return
5186 }
5187 b.build_expr(expr_id)
5188 }
5189 .decl_assign {
5190 b.build_decl_assign(node)
5191 }
5192 .assign {
5193 b.build_assign(node)
5194 }
5195 .selector_assign {
5196 b.build_selector_assign(node)
5197 }
5198 .index_assign {
5199 b.build_index_assign(node)
5200 }
5201 .return_stmt {
5202 if node.children_count > 0 {
5203 expr_id := b.a.child(&node, 0)
5204 ret_type := b.m.funcs[b.cur_func].typ
5205 mut val := if node.children_count > 1 && b.is_struct_type(ret_type) {
5206 b.build_multi_return_value(ret_type, node)
5207 } else {
5208 // Resolve `return .member` against the declared return type so a
5209 // duplicated enum-member name is not built as 0 (see build_assign_rhs).
5210 b.build_field_value(expr_id, b.cur_func_ret_type)
5211 }
5212 if b.is_option_type(ret_type) && b.value_type(val) != ret_type {
5213 expr_node := b.a.nodes[int(expr_id)]
5214 val = b.build_option_value(ret_type, expr_node.kind != .none_expr, val)
5215 }
5216 b.emit_deferred_stmts()
5217 b.emit1(.ret, b.void_type, val)
5218 } else {
5219 b.emit_deferred_stmts()
5220 b.emit0(.ret, b.void_type)
5221 }
5222 }
5223 .defer_stmt {
5224 if node.children_count > 0 {
5225 b.defer_body_ids << b.a.child(&node, 0)
5226 }
5227 }
5228 .for_stmt {
5229 b.build_for(node)
5230 }
5231 .for_in_stmt {
5232 b.build_for_in(node)
5233 }
5234 .break_stmt {
5235 if b.break_targets.len > 0 {
5236 target := b.break_targets.last()
5237 b.emit1(.jmp, b.void_type, ValueID(target))
5238 }
5239 }
5240 .continue_stmt {
5241 if b.continue_targets.len > 0 {
5242 target := b.continue_targets.last()
5243 b.emit1(.jmp, b.void_type, ValueID(target))
5244 }
5245 }
5246 .goto_stmt {
5247 target := b.label_block(node.value)
5248 b.emit1(.jmp, b.void_type, ValueID(target))
5249 }
5250 .label_stmt {
5251 target := b.label_block(node.value)
5252 if !b.current_block_terminated() {
5253 b.emit1(.jmp, b.void_type, ValueID(target))
5254 }
5255 b.cur_block = target
5256 }
5257 .if_expr {
5258 b.build_if(node)
5259 }
5260 .match_stmt {
5261 b.build_match_stmt(node)
5262 }
5263 .block {
5264 for i in 0 .. node.children_count {
5265 b.build_stmt(b.a.child(&node, i))
5266 }
5267 }
5268 .assert_stmt {
5269 b.build_assert(node)
5270 }
5271 .fn_decl, .c_fn_decl, .struct_decl, .field_decl, .global_decl, .const_decl, .const_field,
5272 .enum_decl, .enum_field, .type_decl, .interface_decl, .interface_field, .import_decl,
5273 .module_decl, .directive, .param {}
5274 .empty {}
5275 else {
5276 eprintln('build_stmt: unsupported node kind: ${node.kind}')
5277 }
5278 }
5279}
5280
5281// build_multi_return_value builds multi return value data for ssa.
5282fn (mut b Builder) build_multi_return_value(ret_type TypeID, node flat.Node) ValueID {
5283 alloca := b.emit0(.alloca, b.m.type_store.get_ptr(ret_type))
5284 typ := b.m.type_store.types[ret_type]
5285 for i in 0 .. node.children_count {
5286 if i >= typ.fields.len {
5287 break
5288 }
5289 field_ptr := b.block_struct_field_ptr(b.cur_block, alloca, ret_type, i)
5290 mut value := b.build_expr(b.a.child(&node, i))
5291 field_type := typ.fields[i]
5292 if b.is_int_type(b.value_type(value)) && b.is_int_type(field_type) {
5293 value = b.coerce_int_value(value, field_type)
5294 }
5295 b.emit2(.store, b.void_type, value, field_ptr)
5296 }
5297 return b.emit1(.load, ret_type, alloca)
5298}
5299
5300// emit_deferred_stmts emits emit deferred stmts output for ssa.
5301fn (mut b Builder) emit_deferred_stmts() {
5302 for i := b.defer_body_ids.len; i > 0; i-- {
5303 body_id := b.defer_body_ids[i - 1]
5304 if !b.valid_node_id(body_id) {
5305 continue
5306 }
5307 body := b.a.nodes[int(body_id)]
5308 if body.kind == .block {
5309 for j in 0 .. body.children_count {
5310 b.build_stmt(b.a.child(&body, j))
5311 if b.current_block_terminated() {
5312 return
5313 }
5314 }
5315 } else {
5316 b.build_stmt(body_id)
5317 if b.current_block_terminated() {
5318 return
5319 }
5320 }
5321 }
5322}
5323
5324// label_block supports label block handling for Builder.
5325fn (mut b Builder) label_block(name string) BlockID {
5326 if block := b.label_blocks[name] {
5327 return block
5328 }
5329 block := b.m.add_block(b.cur_func, 'label_${name}')
5330 b.label_blocks[name] = block
5331 return block
5332}
5333
5334// build_decl_assign builds decl assign data for ssa.
5335fn (mut b Builder) build_decl_assign(node flat.Node) {
5336 mut i := 0
5337 for i < node.children_count {
5338 lhs_id := b.a.child(&node, i)
5339 rhs_id := b.a.child(&node, i + 1)
5340 lhs := b.a.nodes[int(lhs_id)]
5341 rhs_val := b.build_expr(rhs_id)
5342 rhs_type := b.value_type(rhs_val)
5343 alloca := b.emit0(.alloca, b.m.type_store.get_ptr(rhs_type))
5344 b.emit2(.store, b.void_type, rhs_val, alloca)
5345 if lhs.kind == .ident {
5346 b.vars[lhs.value] = alloca
5347 declared_type := b.declared_v_type_name(lhs_id, rhs_id)
5348 rhs_type_name := b.checked_expr_type_name(rhs_id)
5349 b.var_type_names[lhs.value] = if declared_type.len > 0 {
5350 declared_type
5351 } else if rhs_type_name.len > 0 && rhs_type_name != 'unknown' {
5352 rhs_type_name
5353 } else {
5354 node.typ
5355 }
5356 }
5357 i += 2
5358 }
5359}
5360
5361// build_assign builds assign data for ssa.
5362// build_assign_rhs builds an assignment RHS, resolving bare enum literals (`x = .member`)
5363// against the LHS's declared type. Without this context a duplicated enum-member name
5364// (e.g. `number`, which exists in several enums) is built as 0, because the checker types
5365// the bare literal as `int` and the member lookup is ambiguous. Mirrors struct-init field
5366// value resolution (`build_field_value`).
5367fn (mut b Builder) build_assign_rhs(lhs flat.Node, rhs_id flat.NodeId) ValueID {
5368 mut type_name := ''
5369 if lhs.kind == .ident {
5370 type_name = b.var_type_names[lhs.value] or { '' }
5371 } else if lhs.kind == .selector {
5372 type_name = b.selector_type_name(lhs) or { '' }
5373 }
5374 if type_name.len > 0 {
5375 return b.build_field_value(rhs_id, type_name)
5376 }
5377 return b.build_expr(rhs_id)
5378}
5379
5380fn (mut b Builder) build_assign(node flat.Node) {
5381 mut i := 0
5382 for i < node.children_count {
5383 lhs_id := b.a.child(&node, i)
5384 rhs_id := b.a.child(&node, i + 1)
5385 lhs := b.a.nodes[int(lhs_id)]
5386
5387 if lhs.kind == .ident && lhs.value == '_' {
5388 b.build_expr(rhs_id)
5389 i += 2
5390 continue
5391 }
5392 if lhs.kind == .ident {
5393 if addr := b.vars[lhs.value] {
5394 if node.op == .assign {
5395 rhs_val := b.build_assign_rhs(lhs, rhs_id)
5396 b.emit2(.store, b.void_type, rhs_val, addr)
5397 } else {
5398 cur := b.emit1(.load, b.deref_type(addr), addr)
5399 rhs_val := b.build_expr(rhs_id)
5400 op := b.compound_to_op(node.op)
5401 result := b.emit2(op, b.value_type(cur), cur, rhs_val)
5402 b.emit2(.store, b.void_type, result, addr)
5403 }
5404 }
5405 } else if lhs.kind == .selector || lhs.kind == .index {
5406 addr := if lhs.kind == .selector {
5407 b.build_selector_addr(lhs)
5408 } else {
5409 b.build_lvalue_addr(lhs_id)
5410 }
5411 if node.op == .assign {
5412 rhs_val := b.build_assign_rhs(lhs, rhs_id)
5413 b.emit2(.store, b.void_type, rhs_val, addr)
5414 } else {
5415 field_type := b.deref_type(addr)
5416 cur := b.emit1(.load, field_type, addr)
5417 rhs_val := b.build_expr(rhs_id)
5418 op := b.compound_to_op(node.op)
5419 result := b.emit2(op, field_type, cur, rhs_val)
5420 b.emit2(.store, b.void_type, result, addr)
5421 }
5422 }
5423 i += 2
5424 }
5425}
5426
5427// build_selector_assign builds selector assign data for ssa.
5428fn (mut b Builder) build_selector_assign(node flat.Node) {
5429 mut i := 0
5430 for i < node.children_count {
5431 lhs_id := b.a.child(&node, i)
5432 rhs_id := b.a.child(&node, i + 1)
5433 lhs := b.a.nodes[int(lhs_id)]
5434
5435 if lhs.kind == .selector {
5436 field_ptr := b.build_selector_addr(lhs)
5437 if node.op == .assign {
5438 rhs_val := b.build_assign_rhs(lhs, rhs_id)
5439 b.emit2(.store, b.void_type, rhs_val, field_ptr)
5440 } else {
5441 field_type := b.deref_type(field_ptr)
5442 cur := b.emit1(.load, field_type, field_ptr)
5443 rhs_val := b.build_expr(rhs_id)
5444 op := b.compound_to_op(node.op)
5445 result := b.emit2(op, field_type, cur, rhs_val)
5446 b.emit2(.store, b.void_type, result, field_ptr)
5447 }
5448 }
5449 i += 2
5450 }
5451}
5452
5453// build_index_assign builds index assign data for ssa.
5454fn (mut b Builder) build_index_assign(node flat.Node) {
5455 mut i := 0
5456 for i < node.children_count {
5457 lhs_id := b.a.child(&node, i)
5458 rhs_id := b.a.child(&node, i + 1)
5459 addr := b.build_lvalue_addr(lhs_id)
5460 if node.op == .assign {
5461 rhs_val := b.build_expr(rhs_id)
5462 b.emit2(.store, b.void_type, rhs_val, addr)
5463 } else {
5464 field_type := b.deref_type(addr)
5465 cur := b.emit1(.load, field_type, addr)
5466 rhs_val := b.build_expr(rhs_id)
5467 op := b.compound_to_op(node.op)
5468 result := b.emit2(op, field_type, cur, rhs_val)
5469 b.emit2(.store, b.void_type, result, addr)
5470 }
5471 i += 2
5472 }
5473}
5474
5475// build_for builds for data for ssa.
5476fn (mut b Builder) build_for(node flat.Node) {
5477 init_id := b.a.child(&node, 0)
5478 cond_id := b.a.child(&node, 1)
5479 post_id := b.a.child(&node, 2)
5480 init_node := b.a.nodes[int(init_id)]
5481 cond_node := b.a.nodes[int(cond_id)]
5482 post_node := b.a.nodes[int(post_id)]
5483
5484 if init_node.kind != .empty {
5485 b.build_stmt(init_id)
5486 }
5487
5488 cond_block := b.m.add_block(b.cur_func, 'for_cond')
5489 body_block := b.m.add_block(b.cur_func, 'for_body')
5490 post_block := b.m.add_block(b.cur_func, 'for_post')
5491 exit_block := b.m.add_block(b.cur_func, 'for_exit')
5492
5493 b.emit1(.jmp, b.void_type, ValueID(cond_block))
5494 b.cur_block = cond_block
5495
5496 if cond_node.kind != .empty {
5497 cond_val := b.build_expr(cond_id)
5498 b.emit3(.br, b.void_type, cond_val, ValueID(body_block), ValueID(exit_block))
5499 } else {
5500 b.emit1(.jmp, b.void_type, ValueID(body_block))
5501 }
5502
5503 b.cur_block = body_block
5504 b.break_targets << exit_block
5505 b.continue_targets << if post_node.kind != .empty { post_block } else { cond_block }
5506
5507 for i in 3 .. node.children_count {
5508 b.build_stmt(b.a.child(&node, i))
5509 }
5510 b.break_targets.delete_last()
5511 b.continue_targets.delete_last()
5512
5513 blk := b.m.blocks[b.cur_block]
5514 if blk.instrs.len == 0 || !b.is_terminator(blk.instrs.last()) {
5515 if post_node.kind != .empty {
5516 b.emit1(.jmp, b.void_type, ValueID(post_block))
5517 } else {
5518 b.emit1(.jmp, b.void_type, ValueID(cond_block))
5519 }
5520 }
5521
5522 if post_node.kind != .empty {
5523 b.cur_block = post_block
5524 b.build_stmt(post_id)
5525 post_blk := b.m.blocks[b.cur_block]
5526 if post_blk.instrs.len == 0 || !b.is_terminator(post_blk.instrs.last()) {
5527 b.emit1(.jmp, b.void_type, ValueID(cond_block))
5528 }
5529 }
5530
5531 b.cur_block = exit_block
5532}
5533
5534// build_for_in builds for in data for ssa.
5535fn (mut b Builder) build_for_in(node flat.Node) {
5536 if node.children_count < 3 {
5537 return
5538 }
5539 key_id := b.a.child(&node, 0)
5540 val_id := b.a.child(&node, 1)
5541 container_id := b.a.child(&node, 2)
5542 body_start := if node.value == '4' { 4 } else { 3 }
5543 if body_start == 4 {
5544 end_id := b.a.child(&node, 3)
5545 b.build_range_for_in(node, key_id, container_id, end_id, body_start)
5546 return
5547 }
5548 container_type := b.for_in_container_type_name(container_id)
5549 if container_type == 'string' {
5550 b.build_string_for_in(node, key_id, val_id, container_id, body_start)
5551 return
5552 }
5553 if container_type.starts_with('map[') || container_type == 'map' || container_type == 'Map' {
5554 b.build_map_for_in(node, key_id, val_id, container_id, body_start, container_type)
5555 return
5556 }
5557 b.build_array_for_in(node, key_id, val_id, container_id, body_start, container_type)
5558}
5559
5560// build_range_for_in builds range for in data for ssa.
5561fn (mut b Builder) build_range_for_in(node flat.Node, key_id flat.NodeId, start_id flat.NodeId, end_id flat.NodeId, body_start int) {
5562 start_val := b.build_expr(start_id)
5563 end_val := b.build_expr(end_id)
5564 idx_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.i64_type))
5565 b.emit2(.store, b.void_type, start_val, idx_alloca)
5566 key_name := b.ident_name(key_id)
5567 old_key := b.save_var_binding(key_name)
5568 key_alloca := b.bind_loop_var(key_name, b.i64_type, 'int')
5569
5570 cond_block := b.m.add_block(b.cur_func, 'for_in_range_cond')
5571 body_block := b.m.add_block(b.cur_func, 'for_in_range_body')
5572 post_block := b.m.add_block(b.cur_func, 'for_in_range_post')
5573 exit_block := b.m.add_block(b.cur_func, 'for_in_range_exit')
5574 b.emit1(.jmp, b.void_type, ValueID(cond_block))
5575
5576 b.cur_block = cond_block
5577 idx := b.emit1(.load, b.i64_type, idx_alloca)
5578 in_range := b.emit2(.lt, b.i1_type, idx, end_val)
5579 b.emit3(.br, b.void_type, in_range, ValueID(body_block), ValueID(exit_block))
5580
5581 b.cur_block = body_block
5582 if key_alloca > 0 {
5583 b.emit2(.store, b.void_type, idx, key_alloca)
5584 }
5585 b.break_targets << exit_block
5586 b.continue_targets << post_block
5587 b.build_for_in_body(node, body_start)
5588 b.break_targets.delete_last()
5589 b.continue_targets.delete_last()
5590 if !b.current_block_terminated() {
5591 b.emit1(.jmp, b.void_type, ValueID(post_block))
5592 }
5593
5594 b.cur_block = post_block
5595 one := b.m.get_or_add_const(b.i64_type, '1')
5596 cur_idx := b.emit1(.load, b.i64_type, idx_alloca)
5597 next_idx := b.emit2(.add, b.i64_type, cur_idx, one)
5598 b.emit2(.store, b.void_type, next_idx, idx_alloca)
5599 b.emit1(.jmp, b.void_type, ValueID(cond_block))
5600
5601 b.restore_var_binding(key_name, old_key)
5602 b.cur_block = exit_block
5603}
5604
5605// build_array_for_in builds array for in data for ssa.
5606fn (mut b Builder) build_array_for_in(node flat.Node, key_id flat.NodeId, val_id flat.NodeId, container_id flat.NodeId, body_start int, container_type string) {
5607 arr_val := b.build_expr(container_id)
5608 ptr_array := b.m.type_store.get_ptr(b.array_type)
5609 arr_alloca := b.emit0(.alloca, ptr_array)
5610 b.emit2(.store, b.void_type, arr_val, arr_alloca)
5611 idx_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.i64_type))
5612 zero := b.m.get_or_add_const(b.i64_type, '0')
5613 b.emit2(.store, b.void_type, zero, idx_alloca)
5614
5615 elem_type_name := b.for_in_array_elem_type_name(container_id, container_type)
5616 elem_type := b.resolve_type(elem_type_name)
5617 has_value_var := b.valid_node_id(val_id)
5618 key_name := b.ident_name(key_id)
5619 val_name := if has_value_var { b.ident_name(val_id) } else { key_name }
5620 old_key := b.save_var_binding(key_name)
5621 old_val := if has_value_var { b.save_var_binding(val_name) } else { VarBinding{} }
5622 key_alloca := if has_value_var {
5623 b.bind_loop_var(key_name, b.i64_type, 'int')
5624 } else {
5625 ValueID(0)
5626 }
5627 val_alloca := b.bind_loop_var(val_name, elem_type, elem_type_name)
5628
5629 cond_block := b.m.add_block(b.cur_func, 'for_in_array_cond')
5630 body_block := b.m.add_block(b.cur_func, 'for_in_array_body')
5631 post_block := b.m.add_block(b.cur_func, 'for_in_array_post')
5632 exit_block := b.m.add_block(b.cur_func, 'for_in_array_exit')
5633 b.emit1(.jmp, b.void_type, ValueID(cond_block))
5634
5635 b.cur_block = cond_block
5636 idx := b.emit1(.load, b.i64_type, idx_alloca)
5637 len := b.block_load_array_int_field(cond_block, arr_alloca, 2)
5638 in_range := b.emit2(.lt, b.i1_type, idx, len)
5639 b.emit3(.br, b.void_type, in_range, ValueID(body_block), ValueID(exit_block))
5640
5641 b.cur_block = body_block
5642 body_idx := b.emit1(.load, b.i64_type, idx_alloca)
5643 if key_alloca > 0 {
5644 b.emit2(.store, b.void_type, body_idx, key_alloca)
5645 }
5646 if val_alloca > 0 {
5647 elem_ptr := b.build_array_data_index_addr(arr_alloca, body_idx, elem_type)
5648 elem_val := b.emit1(.load, elem_type, elem_ptr)
5649 b.emit2(.store, b.void_type, elem_val, val_alloca)
5650 }
5651 b.break_targets << exit_block
5652 b.continue_targets << post_block
5653 b.build_for_in_body(node, body_start)
5654 b.break_targets.delete_last()
5655 b.continue_targets.delete_last()
5656 if !b.current_block_terminated() {
5657 b.emit1(.jmp, b.void_type, ValueID(post_block))
5658 }
5659
5660 b.cur_block = post_block
5661 one := b.m.get_or_add_const(b.i64_type, '1')
5662 cur_idx := b.emit1(.load, b.i64_type, idx_alloca)
5663 next_idx := b.emit2(.add, b.i64_type, cur_idx, one)
5664 b.emit2(.store, b.void_type, next_idx, idx_alloca)
5665 b.emit1(.jmp, b.void_type, ValueID(cond_block))
5666
5667 b.restore_var_binding(key_name, old_key)
5668 if has_value_var {
5669 b.restore_var_binding(val_name, old_val)
5670 }
5671 b.cur_block = exit_block
5672}
5673
5674// build_string_for_in builds string for in data for ssa.
5675fn (mut b Builder) build_string_for_in(node flat.Node, key_id flat.NodeId, val_id flat.NodeId, container_id flat.NodeId, body_start int) {
5676 str_val := b.build_expr(container_id)
5677 ptr_string := b.m.type_store.get_ptr(b.str_type)
5678 str_alloca := b.emit0(.alloca, ptr_string)
5679 b.emit2(.store, b.void_type, str_val, str_alloca)
5680 idx_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.i64_type))
5681 zero := b.m.get_or_add_const(b.i64_type, '0')
5682 b.emit2(.store, b.void_type, zero, idx_alloca)
5683
5684 has_value_var := b.valid_node_id(val_id)
5685 key_name := b.ident_name(key_id)
5686 val_name := if has_value_var { b.ident_name(val_id) } else { key_name }
5687 old_key := b.save_var_binding(key_name)
5688 old_val := if has_value_var { b.save_var_binding(val_name) } else { VarBinding{} }
5689 key_alloca := if has_value_var {
5690 b.bind_loop_var(key_name, b.i64_type, 'int')
5691 } else {
5692 ValueID(0)
5693 }
5694 val_alloca := b.bind_loop_var(val_name, b.i8_type, 'u8')
5695
5696 cond_block := b.m.add_block(b.cur_func, 'for_in_string_cond')
5697 body_block := b.m.add_block(b.cur_func, 'for_in_string_body')
5698 post_block := b.m.add_block(b.cur_func, 'for_in_string_post')
5699 exit_block := b.m.add_block(b.cur_func, 'for_in_string_exit')
5700 b.emit1(.jmp, b.void_type, ValueID(cond_block))
5701
5702 b.cur_block = cond_block
5703 idx := b.emit1(.load, b.i64_type, idx_alloca)
5704 len_ptr := b.get_field_ptr(str_alloca, 'len')
5705 len32 := b.emit1(.load, b.i32_type, len_ptr)
5706 len := b.emit1(.zext, b.i64_type, len32)
5707 in_range := b.emit2(.lt, b.i1_type, idx, len)
5708 b.emit3(.br, b.void_type, in_range, ValueID(body_block), ValueID(exit_block))
5709
5710 b.cur_block = body_block
5711 body_idx := b.emit1(.load, b.i64_type, idx_alloca)
5712 if key_alloca > 0 {
5713 b.emit2(.store, b.void_type, body_idx, key_alloca)
5714 }
5715 data_ptr := b.get_field_ptr(str_alloca, 'str')
5716 data := b.emit1(.load, b.m.type_store.get_ptr(b.i8_type), data_ptr)
5717 ch_ptr := b.emit2(.add, b.m.type_store.get_ptr(b.i8_type), data, body_idx)
5718 ch := b.emit1(.load, b.i8_type, ch_ptr)
5719 if val_alloca > 0 {
5720 b.emit2(.store, b.void_type, ch, val_alloca)
5721 }
5722 b.break_targets << exit_block
5723 b.continue_targets << post_block
5724 b.build_for_in_body(node, body_start)
5725 b.break_targets.delete_last()
5726 b.continue_targets.delete_last()
5727 if !b.current_block_terminated() {
5728 b.emit1(.jmp, b.void_type, ValueID(post_block))
5729 }
5730
5731 b.cur_block = post_block
5732 one := b.m.get_or_add_const(b.i64_type, '1')
5733 cur_idx := b.emit1(.load, b.i64_type, idx_alloca)
5734 next_idx := b.emit2(.add, b.i64_type, cur_idx, one)
5735 b.emit2(.store, b.void_type, next_idx, idx_alloca)
5736 b.emit1(.jmp, b.void_type, ValueID(cond_block))
5737
5738 b.restore_var_binding(key_name, old_key)
5739 if has_value_var {
5740 b.restore_var_binding(val_name, old_val)
5741 }
5742 b.cur_block = exit_block
5743}
5744
5745// build_map_for_in builds map for in data for ssa.
5746fn (mut b Builder) build_map_for_in(node flat.Node, key_id flat.NodeId, val_id flat.NodeId, container_id flat.NodeId, body_start int, container_type string) {
5747 key_type_name, val_type_name := map_type_parts(container_type)
5748 key_type := if key_type_name.len > 0 { b.resolve_type(key_type_name) } else { b.str_type }
5749 val_type := if val_type_name.len > 0 { b.resolve_type(val_type_name) } else { b.i64_type }
5750 map_val := b.build_expr(container_id)
5751 ptr_map := b.m.type_store.get_ptr(b.map_type)
5752 ptr_state := b.m.type_store.get_ptr(b.map_state_type)
5753 map_alloca := b.emit0(.alloca, ptr_map)
5754 b.emit2(.store, b.void_type, map_val, map_alloca)
5755 state := b.map_state_ptr(b.cur_block, map_alloca)
5756 zero_state := b.m.get_or_add_const(ptr_state, '0')
5757 has_state := b.emit2(.ne, b.i1_type, state, zero_state)
5758
5759 idx_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.i64_type))
5760 zero := b.m.get_or_add_const(b.i64_type, '0')
5761 b.emit2(.store, b.void_type, zero, idx_alloca)
5762
5763 has_value_var := b.valid_node_id(val_id)
5764 key_name := b.ident_name(key_id)
5765 val_name := if has_value_var { b.ident_name(val_id) } else { '' }
5766 old_key := b.save_var_binding(key_name)
5767 old_val := if has_value_var { b.save_var_binding(val_name) } else { VarBinding{} }
5768 key_alloca := b.bind_loop_var(key_name, key_type, key_type_name)
5769 val_alloca := if has_value_var {
5770 b.bind_loop_var(val_name, val_type, val_type_name)
5771 } else {
5772 ValueID(0)
5773 }
5774
5775 cond_block := b.m.add_block(b.cur_func, 'for_in_map_cond')
5776 body_block := b.m.add_block(b.cur_func, 'for_in_map_body')
5777 post_block := b.m.add_block(b.cur_func, 'for_in_map_post')
5778 exit_block := b.m.add_block(b.cur_func, 'for_in_map_exit')
5779 b.emit3(.br, b.void_type, has_state, ValueID(cond_block), ValueID(exit_block))
5780
5781 b.cur_block = cond_block
5782 idx := b.emit1(.load, b.i64_type, idx_alloca)
5783 len_ptr := b.map_state_field_ptr(cond_block, state, 3)
5784 len := b.emit1(.load, b.i64_type, len_ptr)
5785 in_range := b.emit2(.lt, b.i1_type, idx, len)
5786 b.emit3(.br, b.void_type, in_range, ValueID(body_block), ValueID(exit_block))
5787
5788 b.cur_block = body_block
5789 body_idx := b.emit1(.load, b.i64_type, idx_alloca)
5790 keys_ptr := b.map_state_field_ptr(body_block, state, 0)
5791 vals_ptr := b.map_state_field_ptr(body_block, state, 1)
5792 key_size_ptr := b.map_state_field_ptr(body_block, state, 4)
5793 val_size_ptr := b.map_state_field_ptr(body_block, state, 5)
5794 keys := b.emit1(.load, b.m.type_store.get_ptr(b.i8_type), keys_ptr)
5795 vals := b.emit1(.load, b.m.type_store.get_ptr(b.i8_type), vals_ptr)
5796 key_size := b.emit1(.load, b.i64_type, key_size_ptr)
5797 val_size := b.emit1(.load, b.i64_type, val_size_ptr)
5798 key_off := b.emit2(.mul, b.i64_type, body_idx, key_size)
5799 val_off := b.emit2(.mul, b.i64_type, body_idx, val_size)
5800 key_raw := b.emit2(.add, b.m.type_store.get_ptr(b.i8_type), keys, key_off)
5801 val_raw := b.emit2(.add, b.m.type_store.get_ptr(b.i8_type), vals, val_off)
5802 if key_alloca > 0 {
5803 key_ptr := b.emit1(.bitcast, b.m.type_store.get_ptr(key_type), key_raw)
5804 key_val := b.emit1(.load, key_type, key_ptr)
5805 b.emit2(.store, b.void_type, key_val, key_alloca)
5806 }
5807 if val_alloca > 0 {
5808 val_ptr := b.emit1(.bitcast, b.m.type_store.get_ptr(val_type), val_raw)
5809 val_val := b.emit1(.load, val_type, val_ptr)
5810 b.emit2(.store, b.void_type, val_val, val_alloca)
5811 }
5812 b.break_targets << exit_block
5813 b.continue_targets << post_block
5814 b.build_for_in_body(node, body_start)
5815 b.break_targets.delete_last()
5816 b.continue_targets.delete_last()
5817 if !b.current_block_terminated() {
5818 b.emit1(.jmp, b.void_type, ValueID(post_block))
5819 }
5820
5821 b.cur_block = post_block
5822 one := b.m.get_or_add_const(b.i64_type, '1')
5823 cur_idx := b.emit1(.load, b.i64_type, idx_alloca)
5824 next_idx := b.emit2(.add, b.i64_type, cur_idx, one)
5825 b.emit2(.store, b.void_type, next_idx, idx_alloca)
5826 b.emit1(.jmp, b.void_type, ValueID(cond_block))
5827
5828 b.restore_var_binding(key_name, old_key)
5829 if has_value_var {
5830 b.restore_var_binding(val_name, old_val)
5831 }
5832 b.cur_block = exit_block
5833}
5834
5835// build_for_in_body builds for in body data for ssa.
5836fn (mut b Builder) build_for_in_body(node flat.Node, body_start int) {
5837 for i in body_start .. node.children_count {
5838 b.build_stmt(b.a.child(&node, i))
5839 }
5840}
5841
5842// build_if builds if data for ssa.
5843fn (mut b Builder) build_if(node flat.Node) {
5844 cond_node := b.a.child_node(&node, 0)
5845 then_block := b.m.add_block(b.cur_func, 'if_then')
5846 merge_block := b.m.add_block(b.cur_func, 'if_merge')
5847 mut guard := IfGuardState{}
5848
5849 if cond_node.kind == .empty {
5850 b.emit1(.jmp, b.void_type, ValueID(then_block))
5851 } else {
5852 mut cond_val := ValueID(0)
5853 if cond_node.kind == .decl_assign {
5854 cond_val, guard = b.build_if_guard_condition(*cond_node)
5855 } else {
5856 cond_val = b.build_expr(b.a.child(&node, 0))
5857 }
5858 if node.children_count > 2 {
5859 else_block := b.m.add_block(b.cur_func, 'if_else')
5860 b.emit3(.br, b.void_type, cond_val, ValueID(then_block), ValueID(else_block))
5861
5862 b.cur_block = else_block
5863 else_node := b.a.child_node(&node, 2)
5864 if else_node.kind == .if_expr {
5865 b.build_if(*else_node)
5866 } else if else_node.kind == .block {
5867 for i in 0 .. else_node.children_count {
5868 b.build_stmt(b.a.child(else_node, i))
5869 }
5870 }
5871 eblk := b.m.blocks[b.cur_block]
5872 if eblk.instrs.len == 0 || !b.is_terminator(eblk.instrs.last()) {
5873 b.emit1(.jmp, b.void_type, ValueID(merge_block))
5874 }
5875 } else {
5876 b.emit3(.br, b.void_type, cond_val, ValueID(then_block), ValueID(merge_block))
5877 }
5878 }
5879
5880 b.cur_block = then_block
5881 if guard.active {
5882 b.activate_if_guard(guard)
5883 }
5884 then_node := b.a.child_node(&node, 1)
5885 for i in 0 .. then_node.children_count {
5886 b.build_stmt(b.a.child(then_node, i))
5887 }
5888 tblk := b.m.blocks[b.cur_block]
5889 if tblk.instrs.len == 0 || !b.is_terminator(tblk.instrs.last()) {
5890 b.emit1(.jmp, b.void_type, ValueID(merge_block))
5891 }
5892 if guard.active {
5893 b.restore_var_binding(guard.name, guard.old)
5894 }
5895
5896 b.cur_block = merge_block
5897}
5898
5899// build_if_guard_condition builds if guard condition data for ssa.
5900fn (mut b Builder) build_if_guard_condition(node flat.Node) (ValueID, IfGuardState) {
5901 if node.children_count < 2 {
5902 return b.m.get_or_add_const(b.i1_type, '1'), IfGuardState{}
5903 }
5904 lhs_id := b.a.child(&node, 0)
5905 rhs_id := b.a.child(&node, 1)
5906 lhs := b.a.nodes[int(lhs_id)]
5907 rhs := b.a.nodes[int(rhs_id)]
5908 if lhs.kind == .ident && rhs.kind == .index && rhs.children_count >= 2 && rhs.value != 'range' {
5909 ok, cond, guard := b.build_map_index_if_guard(lhs.value, rhs)
5910 if ok {
5911 return cond, guard
5912 }
5913 }
5914 if lhs.kind == .ident {
5915 ok, cond, guard := b.build_option_if_guard(lhs.value, rhs_id)
5916 if ok {
5917 return cond, guard
5918 }
5919 }
5920 b.build_decl_assign(node)
5921 return b.m.get_or_add_const(b.i1_type, '1'), IfGuardState{}
5922}
5923
5924// build_option_if_guard builds option if guard data for ssa.
5925fn (mut b Builder) build_option_if_guard(name string, rhs_id flat.NodeId) (bool, ValueID, IfGuardState) {
5926 if name.len == 0 {
5927 return false, b.m.get_or_add_const(b.i1_type, '0'), IfGuardState{}
5928 }
5929 opt_val := b.build_expr(rhs_id)
5930 opt_typ := b.value_type(opt_val)
5931 value_typ := b.option_value_type(opt_typ)
5932 if value_typ == TypeID(0) || value_typ == b.void_type {
5933 return false, b.m.get_or_add_const(b.i1_type, '0'), IfGuardState{}
5934 }
5935 opt_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(opt_typ))
5936 b.emit2(.store, b.void_type, opt_val, opt_alloca)
5937 ok_ptr := b.get_field_ptr(opt_alloca, 'ok')
5938 cond := b.emit1(.load, b.i1_type, ok_ptr)
5939 value_ptr := b.get_field_ptr(opt_alloca, 'value')
5940 guard := IfGuardState{
5941 active: true
5942 name: name
5943 old: b.save_var_binding(name)
5944 slot: b.emit0(.alloca, b.m.type_store.get_ptr(value_typ))
5945 value_ptr: value_ptr
5946 typ: value_typ
5947 typ_name: b.option_payload_type_name(rhs_id)
5948 }
5949 return true, cond, guard
5950}
5951
5952// build_map_index_if_guard builds map index if guard data for ssa.
5953fn (mut b Builder) build_map_index_if_guard(name string, index_node flat.Node) (bool, ValueID, IfGuardState) {
5954 base_id := b.a.child(&index_node, 0)
5955 key_id := b.a.child(&index_node, 1)
5956 map_type_name := b.expr_type_name_for_map(base_id)
5957 key_type_name, val_type_name := map_type_parts(map_type_name)
5958 if name.len == 0 || key_type_name.len == 0 || val_type_name.len == 0 {
5959 return false, b.m.get_or_add_const(b.i1_type, '0'), IfGuardState{}
5960 }
5961 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
5962 val_type := b.resolve_type(val_type_name)
5963 ptr_val_type := b.m.type_store.get_ptr(val_type)
5964 map_ptr := b.map_expr_ptr(base_id)
5965 key_val := b.build_expr(key_id)
5966 key_type := b.resolve_type(key_type_name)
5967 key_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(key_type))
5968 b.emit2(.store, b.void_type, key_val, key_alloca)
5969 key_ptr := if b.value_type(key_alloca) == ptr_i8 {
5970 key_alloca
5971 } else {
5972 b.emit1(.bitcast, ptr_i8, key_alloca)
5973 }
5974 get_ref := b.m.add_value(.func_ref, ptr_i8, 'map__get_check', b.fn_ids['map__get_check'])
5975 value_ptr := b.emit3(.call, ptr_i8, get_ref, map_ptr, key_ptr)
5976 zero_ptr := b.m.get_or_add_const(ptr_i8, '0')
5977 found := b.emit2(.ne, b.i1_type, value_ptr, zero_ptr)
5978 slot := b.emit0(.alloca, ptr_val_type)
5979 guard := IfGuardState{
5980 active: true
5981 name: name
5982 old: b.save_var_binding(name)
5983 slot: slot
5984 value_ptr: value_ptr
5985 typ: val_type
5986 typ_name: val_type_name
5987 }
5988 return true, found, guard
5989}
5990
5991// activate_if_guard supports activate if guard handling for Builder.
5992fn (mut b Builder) activate_if_guard(guard IfGuardState) {
5993 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
5994 ptr_val_type := b.m.type_store.get_ptr(guard.typ)
5995 typed_value_ptr := if ptr_val_type == ptr_i8 {
5996 guard.value_ptr
5997 } else {
5998 b.emit1(.bitcast, ptr_val_type, guard.value_ptr)
5999 }
6000 value := b.emit1(.load, guard.typ, typed_value_ptr)
6001 b.emit2(.store, b.void_type, value, guard.slot)
6002 b.vars[guard.name] = guard.slot
6003 if guard.typ_name.len > 0 {
6004 b.var_type_names[guard.name] = guard.typ_name
6005 }
6006}
6007
6008// build_if_value builds if value data for ssa.
6009fn (mut b Builder) build_if_value(id flat.NodeId, node flat.Node) ValueID {
6010 if node.children_count < 3 {
6011 b.build_if(node)
6012 return b.default_value_for_type(b.if_value_result_type(id, []ValueID{}))
6013 }
6014 cond_node := b.a.child_node(&node, 0)
6015 then_block := b.m.add_block(b.cur_func, 'if_value_then')
6016 else_block := b.m.add_block(b.cur_func, 'if_value_else')
6017 merge_block := b.m.add_block(b.cur_func, 'if_value_merge')
6018
6019 if cond_node.kind == .empty {
6020 b.emit1(.jmp, b.void_type, ValueID(then_block))
6021 } else {
6022 cond_val := b.build_expr(b.a.child(&node, 0))
6023 b.emit3(.br, b.void_type, cond_val, ValueID(then_block), ValueID(else_block))
6024 }
6025
6026 mut incoming_values := []ValueID{}
6027 mut incoming_blocks := []BlockID{}
6028
6029 b.cur_block = then_block
6030 if then_value := b.build_branch_value(b.a.child(&node, 1), b.a.child_node(&node, 1)) {
6031 then_end := b.cur_block
6032 if !b.current_block_terminated() {
6033 incoming_values << then_value
6034 incoming_blocks << then_end
6035 b.emit1(.jmp, b.void_type, ValueID(merge_block))
6036 }
6037 } else if !b.current_block_terminated() {
6038 b.emit1(.jmp, b.void_type, ValueID(merge_block))
6039 }
6040
6041 b.cur_block = else_block
6042 if else_value := b.build_branch_value(b.a.child(&node, 2), b.a.child_node(&node, 2)) {
6043 else_end := b.cur_block
6044 if !b.current_block_terminated() {
6045 incoming_values << else_value
6046 incoming_blocks << else_end
6047 b.emit1(.jmp, b.void_type, ValueID(merge_block))
6048 }
6049 } else if !b.current_block_terminated() {
6050 b.emit1(.jmp, b.void_type, ValueID(merge_block))
6051 }
6052
6053 b.cur_block = merge_block
6054 result_type := b.if_value_result_type(id, incoming_values)
6055 if incoming_values.len == 0 {
6056 return b.default_value_for_type(result_type)
6057 }
6058 if incoming_values.len == 1 {
6059 return incoming_values[0]
6060 }
6061 mut operands := []ValueID{}
6062 for i, value in incoming_values {
6063 operands << value
6064 operands << ValueID(incoming_blocks[i])
6065 }
6066 return b.m.add_instr(.phi, b.cur_block, result_type, operands)
6067}
6068
6069// build_branch_value builds branch value data for ssa.
6070fn (mut b Builder) build_branch_value(id flat.NodeId, node &flat.Node) ?ValueID {
6071 if int(id) < 0 {
6072 return none
6073 }
6074 if node.kind == .block {
6075 if node.children_count == 0 {
6076 return b.m.get_or_add_const(b.i64_type, '0')
6077 }
6078 for i in 0 .. node.children_count - 1 {
6079 b.build_stmt(b.a.child(node, i))
6080 if b.current_block_terminated() {
6081 return none
6082 }
6083 }
6084 last_id := b.a.child(node, node.children_count - 1)
6085 last := b.a.nodes[int(last_id)]
6086 if last.kind == .expr_stmt && last.children_count > 0 {
6087 return b.build_expr(b.a.child(&last, 0))
6088 }
6089 if last.kind == .match_stmt {
6090 return b.build_match_value(last_id, last)
6091 }
6092 if last.kind == .if_expr {
6093 return b.build_if_value(last_id, last)
6094 }
6095 if b.is_stmt_kind(last.kind) {
6096 b.build_stmt(last_id)
6097 return none
6098 }
6099 return b.build_expr(last_id)
6100 }
6101 if node.kind == .if_expr {
6102 return b.build_if_value(id, *node)
6103 }
6104 if node.kind == .match_stmt {
6105 return b.build_match_value(id, *node)
6106 }
6107 if b.is_stmt_kind(node.kind) {
6108 b.build_stmt(id)
6109 return none
6110 }
6111 return b.build_expr(id)
6112}
6113
6114// build_match_stmt builds match stmt data for ssa.
6115fn (mut b Builder) build_match_stmt(node flat.Node) {
6116 if node.children_count < 2 {
6117 return
6118 }
6119 subject := b.build_expr(b.a.child(&node, 0))
6120 exit_block := b.m.add_block(b.cur_func, 'match_exit')
6121
6122 for i in 1 .. node.children_count {
6123 branch := b.a.child_node(&node, i)
6124 if branch.kind != .match_branch {
6125 continue
6126 }
6127 is_else := branch.value == 'else'
6128 body_block := b.m.add_block(b.cur_func, 'match_body')
6129 next_block := if i + 1 < node.children_count {
6130 b.m.add_block(b.cur_func, 'match_next')
6131 } else {
6132 exit_block
6133 }
6134 if is_else {
6135 b.emit1(.jmp, b.void_type, ValueID(body_block))
6136 } else {
6137 cond := b.build_match_condition(subject, *branch)
6138 b.emit3(.br, b.void_type, cond, ValueID(body_block), ValueID(next_block))
6139 }
6140
6141 b.cur_block = body_block
6142 body_start := b.match_branch_body_start(*branch)
6143 for j in body_start .. branch.children_count {
6144 b.build_stmt(b.a.child(branch, j))
6145 if b.current_block_terminated() {
6146 break
6147 }
6148 }
6149 if !b.current_block_terminated() {
6150 b.emit1(.jmp, b.void_type, ValueID(exit_block))
6151 }
6152 b.cur_block = next_block
6153 }
6154}
6155
6156// build_match_value builds match value data for ssa.
6157fn (mut b Builder) build_match_value(id flat.NodeId, node flat.Node) ValueID {
6158 if node.children_count < 2 {
6159 return b.default_value_for_type(b.if_value_result_type(id, []ValueID{}))
6160 }
6161 subject := b.build_expr(b.a.child(&node, 0))
6162 merge_block := b.m.add_block(b.cur_func, 'match_merge')
6163 mut incoming_values := []ValueID{}
6164 mut incoming_blocks := []BlockID{}
6165
6166 for i in 1 .. node.children_count {
6167 branch := b.a.child_node(&node, i)
6168 if branch.kind != .match_branch {
6169 continue
6170 }
6171 is_else := branch.value == 'else'
6172 body_block := b.m.add_block(b.cur_func, 'match_value_body')
6173 next_block := if i + 1 < node.children_count {
6174 b.m.add_block(b.cur_func, 'match_value_next')
6175 } else {
6176 merge_block
6177 }
6178 if is_else {
6179 b.emit1(.jmp, b.void_type, ValueID(body_block))
6180 } else {
6181 cond := b.build_match_condition(subject, *branch)
6182 b.emit3(.br, b.void_type, cond, ValueID(body_block), ValueID(next_block))
6183 }
6184
6185 b.cur_block = body_block
6186 if value := b.build_match_branch_value(*branch) {
6187 end_block := b.cur_block
6188 if !b.current_block_terminated() {
6189 incoming_values << value
6190 incoming_blocks << end_block
6191 b.emit1(.jmp, b.void_type, ValueID(merge_block))
6192 }
6193 } else if !b.current_block_terminated() {
6194 b.emit1(.jmp, b.void_type, ValueID(merge_block))
6195 }
6196 b.cur_block = next_block
6197 }
6198
6199 b.cur_block = merge_block
6200 result_type := b.if_value_result_type(id, incoming_values)
6201 if incoming_values.len == 0 {
6202 return b.default_value_for_type(result_type)
6203 }
6204 if incoming_values.len == 1 {
6205 return incoming_values[0]
6206 }
6207 mut operands := []ValueID{}
6208 for i, value in incoming_values {
6209 operands << value
6210 operands << ValueID(incoming_blocks[i])
6211 }
6212 return b.m.add_instr(.phi, b.cur_block, result_type, operands)
6213}
6214
6215// build_match_branch_value builds match branch value data for ssa.
6216fn (mut b Builder) build_match_branch_value(branch flat.Node) ?ValueID {
6217 body_start := b.match_branch_body_start(branch)
6218 if body_start >= branch.children_count {
6219 return none
6220 }
6221 for i in body_start .. branch.children_count - 1 {
6222 b.build_stmt(b.a.child(&branch, i))
6223 if b.current_block_terminated() {
6224 return none
6225 }
6226 }
6227 last_id := b.a.child(&branch, branch.children_count - 1)
6228 last := b.a.nodes[int(last_id)]
6229 if last.kind == .expr_stmt && last.children_count > 0 {
6230 return b.build_expr(b.a.child(&last, 0))
6231 }
6232 if last.kind == .match_stmt {
6233 return b.build_match_value(last_id, last)
6234 }
6235 if last.kind == .if_expr {
6236 return b.build_if_value(last_id, last)
6237 }
6238 if b.is_stmt_kind(last.kind) {
6239 b.build_stmt(last_id)
6240 return none
6241 }
6242 return b.build_expr(last_id)
6243}
6244
6245// match_branch_body_start supports match branch body start handling for Builder.
6246fn (b &Builder) match_branch_body_start(branch flat.Node) int {
6247 if branch.value == 'else' {
6248 return 0
6249 }
6250 return branch.value.int()
6251}
6252
6253// build_match_condition builds match condition data for ssa.
6254fn (mut b Builder) build_match_condition(subject ValueID, branch flat.Node) ValueID {
6255 n_conds := b.match_branch_body_start(branch)
6256 mut result := ValueID(0)
6257 for i in 0 .. n_conds {
6258 cond_value := b.build_expr(b.a.child(&branch, i))
6259 cmp := b.emit2(.eq, b.i1_type, subject, cond_value)
6260 if result == 0 {
6261 result = cmp
6262 } else {
6263 result = b.emit2(.or_, b.i1_type, result, cmp)
6264 }
6265 }
6266 if result == 0 {
6267 return b.m.get_or_add_const(b.i1_type, '1')
6268 }
6269 return result
6270}
6271
6272// if_value_result_type supports if value result type handling for Builder.
6273fn (mut b Builder) if_value_result_type(id flat.NodeId, values []ValueID) TypeID {
6274 if b.tc != unsafe { nil } {
6275 if typ := b.tc.expr_type(id) {
6276 name := typ.name()
6277 if name.len > 0 && name != 'unknown' && name != 'void' {
6278 return b.resolve_type(name)
6279 }
6280 }
6281 }
6282 for value in values {
6283 typ := b.value_type(value)
6284 if typ != b.void_type {
6285 return typ
6286 }
6287 }
6288 return b.i64_type
6289}
6290
6291// build_assert builds assert data for ssa.
6292fn (mut b Builder) build_assert(node flat.Node) {
6293 cond_val := b.build_expr(b.a.child(&node, 0))
6294 fail_block := b.m.add_block(b.cur_func, 'assert_fail')
6295 ok_block := b.m.add_block(b.cur_func, 'assert_ok')
6296 b.emit3(.br, b.void_type, cond_val, ValueID(ok_block), ValueID(fail_block))
6297
6298 b.cur_block = fail_block
6299 if exit_fn := b.fn_ids['exit'] {
6300 one := b.m.get_or_add_const(b.i64_type, '1')
6301 fn_ref := b.m.add_value(.func_ref, b.void_type, 'exit', exit_fn)
6302 b.emit2(.call, b.void_type, fn_ref, one)
6303 }
6304 b.emit0(.unreachable, b.void_type)
6305
6306 b.cur_block = ok_block
6307}
6308
6309// build_expr builds expr data for ssa.
6310fn (mut b Builder) build_expr(id flat.NodeId) ValueID {
6311 if int(id) < 0 {
6312 return b.m.get_or_add_const(b.i64_type, '0')
6313 }
6314 node := b.a.nodes[int(id)]
6315 match node.kind {
6316 .empty {
6317 return b.m.get_or_add_const(b.i64_type, '0')
6318 }
6319 .int_literal {
6320 return b.m.get_or_add_const(b.i64_type, node.value)
6321 }
6322 .float_literal {
6323 return b.m.get_or_add_const(b.float_literal_type(id, node), node.value)
6324 }
6325 .bool_literal {
6326 val := if node.value == 'true' { '1' } else { '0' }
6327 return b.m.get_or_add_const(b.i1_type, val)
6328 }
6329 .string_literal {
6330 return b.m.add_value(.string_literal, b.str_type, node.value, 0)
6331 }
6332 .char_literal {
6333 return b.m.get_or_add_const(b.i8_type, '${char_literal_value(node.value)}')
6334 }
6335 .string_interp {
6336 return b.build_string_interp(node)
6337 }
6338 .enum_val {
6339 return b.build_enum_val(id, node)
6340 }
6341 .sizeof_expr {
6342 size := b.m.type_size(b.resolve_type(node.value))
6343 return b.m.get_or_add_const(b.i64_type, '${size}')
6344 }
6345 .ident {
6346 if addr := b.vars[node.value] {
6347 addr_val := b.m.values[addr]
6348 if addr_val.kind == .argument {
6349 return addr
6350 }
6351 if smart_val := b.load_smartcast_sum_value(addr, id) {
6352 return smart_val
6353 }
6354 return b.emit1(.load, b.deref_type(addr), addr)
6355 }
6356 if node.value == 'path_separator' {
6357 return b.m.add_value(.string_literal, b.str_type, '/', 0)
6358 }
6359 if expr_id := b.lookup_const_expr(node.value) {
6360 return b.build_expr(expr_id)
6361 }
6362 if !b.enum_member_dupes[node.value] {
6363 if enum_value := b.enum_member_values[node.value] {
6364 return b.m.get_or_add_const(b.i64_type, enum_value.str())
6365 }
6366 }
6367 if fn_idx := b.fn_ids[node.value] {
6368 return b.m.add_value(.func_ref, b.i64_type, node.value, fn_idx)
6369 }
6370 qualified_fn_name := ssa_fn_name_in_module(b.cur_module, node.value)
6371 if qualified_fn_name != node.value {
6372 if fn_idx := b.fn_ids[qualified_fn_name] {
6373 return b.m.add_value(.func_ref, b.i64_type, qualified_fn_name, fn_idx)
6374 }
6375 }
6376 match node.value {
6377 'min_i8' {
6378 return b.m.get_or_add_const(b.i64_type, '-128')
6379 }
6380 'max_i8' {
6381 return b.m.get_or_add_const(b.i64_type, '127')
6382 }
6383 'min_i16' {
6384 return b.m.get_or_add_const(b.i64_type, '-32768')
6385 }
6386 'max_i16' {
6387 return b.m.get_or_add_const(b.i64_type, '32767')
6388 }
6389 'min_i32', 'min_int' {
6390 return b.m.get_or_add_const(b.i64_type, '-2147483648')
6391 }
6392 'max_i32', 'max_int' {
6393 return b.m.get_or_add_const(b.i64_type, '2147483647')
6394 }
6395 'min_i64' {
6396 return b.m.get_or_add_const(b.i64_type, '-9223372036854775808')
6397 }
6398 'max_i64' {
6399 return b.m.get_or_add_const(b.i64_type, '9223372036854775807')
6400 }
6401 'min_u8', 'min_u16', 'min_u32', 'min_u64' {
6402 return b.m.get_or_add_const(b.i64_type, '0')
6403 }
6404 'max_u8' {
6405 return b.m.get_or_add_const(b.i64_type, '255')
6406 }
6407 'max_u16' {
6408 return b.m.get_or_add_const(b.i64_type, '65535')
6409 }
6410 'max_u32' {
6411 return b.m.get_or_add_const(b.i64_type, '4294967295')
6412 }
6413 'max_u64' {
6414 return b.m.get_or_add_const(b.i64_type, '18446744073709551615')
6415 }
6416 else {}
6417 }
6418
6419 if expr_id := b.lookup_const_expr(node.value) {
6420 return b.build_expr(expr_id)
6421 }
6422
6423 return b.m.get_or_add_const(b.i64_type, '0')
6424 }
6425 .infix {
6426 return b.build_infix(node)
6427 }
6428 .prefix {
6429 return b.build_prefix(node, id)
6430 }
6431 .postfix {
6432 return b.build_postfix(node)
6433 }
6434 .paren {
6435 return b.build_expr(b.a.child(&node, 0))
6436 }
6437 .call {
6438 return b.build_call(id, node)
6439 }
6440 .selector {
6441 return b.build_selector(node)
6442 }
6443 .index {
6444 return b.build_index(id, node)
6445 }
6446 .struct_init {
6447 return b.build_struct_init(node)
6448 }
6449 .array_literal {
6450 return b.build_array_literal(node)
6451 }
6452 .array_init {
6453 return b.build_array_init(node)
6454 }
6455 .map_init {
6456 return b.build_map_init(node)
6457 }
6458 .cast_expr {
6459 return b.build_cast_expr(node)
6460 }
6461 .as_expr {
6462 return b.build_as_expr(node)
6463 }
6464 .is_expr {
6465 return b.build_is_expr(node)
6466 }
6467 .nil_literal {
6468 return b.m.get_or_add_const(b.m.type_store.get_ptr(b.i8_type), '0')
6469 }
6470 .none_expr {
6471 return b.default_value_for_type(b.call_expr_result_type(id, node))
6472 }
6473 .if_expr {
6474 return b.build_if_value(id, node)
6475 }
6476 .match_stmt {
6477 return b.build_match_value(id, node)
6478 }
6479 .or_expr {
6480 return b.build_or_expr(id, node)
6481 }
6482 .field_init {
6483 if node.children_count > 0 {
6484 return b.build_expr(b.a.child(&node, 0))
6485 }
6486 return b.m.get_or_add_const(b.i64_type, '0')
6487 }
6488 .decl_assign {
6489 b.build_decl_assign(node)
6490 return b.m.get_or_add_const(b.i1_type, '1')
6491 }
6492 .assign, .selector_assign, .index_assign {
6493 b.build_stmt(id)
6494 return b.m.get_or_add_const(b.i64_type, '0')
6495 }
6496 .dump_expr {
6497 if node.children_count > 0 {
6498 return b.build_expr(b.a.child(&node, 0))
6499 }
6500 return b.m.get_or_add_const(b.i64_type, '0')
6501 }
6502 .in_expr {
6503 return b.build_in_expr(node)
6504 }
6505 .range {
6506 return b.build_range_expr(node)
6507 }
6508 .block {
6509 return b.build_block_expr(node)
6510 }
6511 else {
6512 eprintln('build_expr: unsupported expr kind: ${node.kind}')
6513 return b.m.get_or_add_const(b.i64_type, '0')
6514 }
6515 }
6516}
6517
6518// build_range_expr builds range expr data for ssa.
6519fn (mut b Builder) build_range_expr(node flat.Node) ValueID {
6520 if node.children_count > 0 {
6521 return b.build_expr(b.a.child(&node, 0))
6522 }
6523 return b.m.get_or_add_const(b.i64_type, '0')
6524}
6525
6526// build_in_expr builds in expr data for ssa.
6527fn (mut b Builder) build_in_expr(node flat.Node) ValueID {
6528 if node.children_count < 2 {
6529 return b.m.get_or_add_const(b.i1_type, '0')
6530 }
6531 lhs_id := b.a.child(&node, 0)
6532 rhs_id := b.a.child(&node, 1)
6533 rhs := b.a.nodes[int(rhs_id)]
6534 if rhs.kind == .range && rhs.children_count >= 2 {
6535 lhs := b.build_expr(lhs_id)
6536 low := b.build_expr(b.a.child(&rhs, 0))
6537 high := b.build_expr(b.a.child(&rhs, 1))
6538 ge_low := b.emit2(.ge, b.i1_type, lhs, low)
6539 lt_high := b.emit2(.lt, b.i1_type, lhs, high)
6540 return b.emit2(.and_, b.i1_type, ge_low, lt_high)
6541 }
6542 map_type_name := b.expr_type_name_for_map(rhs_id)
6543 key_type_name, _ := map_type_parts(map_type_name)
6544 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
6545 map_ptr := b.map_expr_ptr(rhs_id)
6546 key_val := b.build_expr(lhs_id)
6547 mut key_type := if key_type_name.len > 0 {
6548 b.resolve_type(key_type_name)
6549 } else {
6550 b.value_type(key_val)
6551 }
6552 if key_type == b.void_type {
6553 key_type = b.value_type(key_val)
6554 }
6555 key_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(key_type))
6556 b.emit2(.store, b.void_type, key_val, key_alloca)
6557 key_ptr := if b.value_type(key_alloca) == ptr_i8 {
6558 key_alloca
6559 } else {
6560 b.emit1(.bitcast, ptr_i8, key_alloca)
6561 }
6562 exists_ref := b.m.add_value(.func_ref, b.i1_type, 'map__exists', b.fn_ids['map__exists'])
6563 return b.emit3(.call, b.i1_type, exists_ref, map_ptr, key_ptr)
6564}
6565
6566// build_enum_val builds enum val data for ssa.
6567fn (mut b Builder) build_enum_val(id flat.NodeId, node flat.Node) ValueID {
6568 if node.typ.len > 0 {
6569 if value := b.enum_value_for_type(node.typ, node.value) {
6570 return b.m.get_or_add_const(b.i64_type, value.str())
6571 }
6572 }
6573 clean_member0 := node.value.trim_left('.')
6574 if value := b.enum_values[clean_member0] {
6575 return b.m.get_or_add_const(b.i64_type, value.str())
6576 }
6577 if b.tc != unsafe { nil } {
6578 if typ := b.tc.expr_type(id) {
6579 type_name := typ.name()
6580 if value := b.enum_value_for_type(type_name, node.value) {
6581 return b.m.get_or_add_const(b.i64_type, value.str())
6582 }
6583 }
6584 }
6585 if !b.enum_member_dupes[node.value] {
6586 if value := b.enum_member_values[node.value] {
6587 return b.m.get_or_add_const(b.i64_type, value.str())
6588 }
6589 }
6590 return b.m.get_or_add_const(b.i64_type, '0')
6591}
6592
6593// enum_value_for_type supports enum value for type handling for Builder.
6594fn (b &Builder) enum_value_for_type(type_name string, member string) ?int {
6595 if type_name.len == 0 || type_name in ['int', 'unknown'] {
6596 return none
6597 }
6598 clean_member0 := member.trim_left('.')
6599 if value := b.enum_values[clean_member0] {
6600 enum_name := clean_member0.all_before_last('.')
6601 return if b.is_flag_enum_type_name(enum_name) { 1 << value } else { value }
6602 }
6603 clean_member := clean_member0.all_after_last('.')
6604 mut names := []string{}
6605 names << type_name
6606 short_type := type_name.all_after('.')
6607 if short_type != type_name {
6608 names << short_type
6609 }
6610 for name in names {
6611 key := name + '.' + clean_member
6612 if value := b.enum_values[key] {
6613 return if b.is_flag_enum_type_name(name) { 1 << value } else { value }
6614 }
6615 }
6616 return none
6617}
6618
6619// is_flag_enum_type_name reports whether is flag enum type name applies in ssa.
6620fn (b &Builder) is_flag_enum_type_name(type_name string) bool {
6621 clean := type_name.trim_left('&')
6622 if clean in b.flag_enum_types {
6623 return true
6624 }
6625 short_name := clean.all_after('.')
6626 if short_name in b.flag_enum_types {
6627 return true
6628 }
6629 if b.tc != unsafe { nil } {
6630 if clean in b.tc.flag_enums {
6631 return true
6632 }
6633 if short_name in b.tc.flag_enums {
6634 return true
6635 }
6636 if !clean.contains('.') && 'builtin.${clean}' in b.tc.flag_enums {
6637 return true
6638 }
6639 }
6640 return clean == 'ArrayFlags'
6641}
6642
6643// checker_has_flag_enum converts checker has flag enum data for ssa.
6644fn (b &Builder) checker_has_flag_enum(type_name string) bool {
6645 if b.tc == unsafe { nil } {
6646 return false
6647 }
6648 if type_name in b.tc.flag_enums {
6649 return true
6650 }
6651 short_name := type_name.all_after('.')
6652 return short_name in b.tc.flag_enums
6653}
6654
6655// build_enum_val_with_type builds enum val with type data for ssa.
6656fn (mut b Builder) build_enum_val_with_type(node flat.Node, type_name string) ?ValueID {
6657 if value := b.enum_value_for_type(type_name, node.value) {
6658 return b.m.get_or_add_const(b.i64_type, value.str())
6659 }
6660 return none
6661}
6662
6663// char_literal_value supports char literal value handling for ssa.
6664fn char_literal_value(value string) int {
6665 if value.len == 0 {
6666 return 0
6667 }
6668 if value[0] == `\\` && value.len > 1 {
6669 return match value[1] {
6670 `n` { 10 }
6671 `r` { 13 }
6672 `t` { 9 }
6673 `0` { 0 }
6674 `\\` { 92 }
6675 `'` { 39 }
6676 else { int(value[1]) }
6677 }
6678 }
6679 return int(value[0])
6680}
6681
6682fn parse_int_literal(value string) int {
6683 clean := value.replace('_', '')
6684 if clean.len > 2 && clean[0] == `0` && (clean[1] == `x` || clean[1] == `X`) {
6685 mut n := 0
6686 for ch in clean[2..] {
6687 digit := if ch >= `0` && ch <= `9` {
6688 int(ch - `0`)
6689 } else if ch >= `a` && ch <= `f` {
6690 int(ch - `a`) + 10
6691 } else if ch >= `A` && ch <= `F` {
6692 int(ch - `A`) + 10
6693 } else {
6694 0
6695 }
6696 n = n * 16 + digit
6697 }
6698 return n
6699 }
6700 return clean.int()
6701}
6702
6703fn (mut b Builder) build_block_expr(node flat.Node) ValueID {
6704 if node.children_count == 0 {
6705 return b.m.get_or_add_const(b.i64_type, '0')
6706 }
6707 for i in 0 .. node.children_count - 1 {
6708 b.build_stmt(b.a.child(&node, i))
6709 }
6710 last_id := b.a.child(&node, node.children_count - 1)
6711 last := b.a.nodes[int(last_id)]
6712 if last.kind == .expr_stmt && last.children_count > 0 {
6713 return b.build_expr(b.a.child(&last, 0))
6714 }
6715 return b.build_expr(last_id)
6716}
6717
6718fn (mut b Builder) build_array_literal(node flat.Node) ValueID {
6719 if b.is_fixed_array_type_name(node.typ) {
6720 mut fixed_node := node
6721 fixed_node.value = node.typ
6722 return b.build_fixed_array_init(fixed_node)
6723 }
6724 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
6725 ptr_array := b.m.type_store.get_ptr(b.array_type)
6726 mut values := []ValueID{}
6727 mut elem_type := b.i64_type
6728 for i in 0 .. node.children_count {
6729 value := b.build_expr(b.a.child(&node, i))
6730 if i == 0 {
6731 elem_type = b.value_type(value)
6732 }
6733 values << value
6734 }
6735 elem_size := b.m.type_size(elem_type)
6736 actual_elem_size := if elem_size > 0 { elem_size } else { 8 }
6737 elem_size_const := b.m.get_or_add_const(b.i64_type, '${actual_elem_size}')
6738 zero := b.m.get_or_add_const(b.i64_type, '0')
6739 cap_const := b.m.get_or_add_const(b.i64_type, '${node.children_count}')
6740 new_ref := b.m.add_value(.func_ref, b.array_type, 'array_new', b.fn_ids['array_new'])
6741 arr := b.emit4(.call, b.array_type, new_ref, elem_size_const, zero, cap_const)
6742 arr_alloca := b.emit0(.alloca, ptr_array)
6743 b.emit2(.store, b.void_type, arr, arr_alloca)
6744 push_ref := b.m.add_value(.func_ref, b.void_type, 'array_push', b.fn_ids['array_push'])
6745 for value in values {
6746 value_type := b.value_type(value)
6747 value_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(value_type))
6748 b.emit2(.store, b.void_type, value, value_alloca)
6749 value_arg := if b.value_type(value_alloca) == ptr_i8 {
6750 value_alloca
6751 } else {
6752 b.emit1(.bitcast, ptr_i8, value_alloca)
6753 }
6754 b.emit3(.call, b.void_type, push_ref, arr_alloca, value_arg)
6755 }
6756 return b.emit1(.load, b.array_type, arr_alloca)
6757}
6758
6759fn (mut b Builder) build_array_init(node flat.Node) ValueID {
6760 if b.is_fixed_array_type_name(node.value) {
6761 return b.build_fixed_array_init(node)
6762 }
6763 mut elem_type_name := node.value
6764 mut len_val := b.m.get_or_add_const(b.i64_type, '0')
6765 mut cap_val := b.m.get_or_add_const(b.i64_type, '0')
6766 mut init_val := ValueID(0)
6767 if node.value.starts_with('[') {
6768 len_text := node.value.all_after('[').all_before(']')
6769 len_val = b.fixed_array_len_value(len_text)
6770 cap_val = len_val
6771 elem_type_name = node.value.all_after(']')
6772 } else {
6773 mut has_cap := false
6774 for i in 0 .. node.children_count {
6775 child_id := b.a.child(&node, i)
6776 child := b.a.nodes[int(child_id)]
6777 if child.kind == .field_init && child.children_count > 0 {
6778 value := b.build_expr(b.a.child(&child, 0))
6779 if child.value == 'len' {
6780 len_val = value
6781 if !has_cap {
6782 cap_val = value
6783 }
6784 } else if child.value == 'cap' {
6785 cap_val = value
6786 has_cap = true
6787 } else if child.value == 'init' {
6788 init_val = value
6789 }
6790 }
6791 }
6792 }
6793 elem_type := b.resolve_type(elem_type_name)
6794 elem_size := b.m.type_size(elem_type)
6795 actual_elem_size := if elem_size > 0 { elem_size } else { 8 }
6796 elem_size_const := b.m.get_or_add_const(b.i64_type, '${actual_elem_size}')
6797 new_ref := b.m.add_value(.func_ref, b.array_type, 'array_new', b.fn_ids['array_new'])
6798 arr := b.emit4(.call, b.array_type, new_ref, elem_size_const, len_val, cap_val)
6799 if init_val == ValueID(0) {
6800 return arr
6801 }
6802 ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
6803 ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
6804 arr_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.array_type))
6805 i_alloca := b.emit0(.alloca, ptr_i64)
6806 b.emit2(.store, b.void_type, arr, arr_alloca)
6807 zero := b.m.get_or_add_const(b.i64_type, '0')
6808 one := b.m.get_or_add_const(b.i64_type, '1')
6809 b.emit2(.store, b.v