Files
agila_open_tool/tools/v/vlib/v3/ssa/builder.v
T

10158 lines
352 KiB
V

module ssa
import v3.flat
import v3.types
const arm64_force_external_syms = ['_malloc', '_free', '_calloc', '_realloc', '_exit', '_abort',
'_memcpy', '_memmove', '_memset', '_memcmp', '___stdoutp', '___stderrp', '_puts', '_printf',
'_write', '_read', '_open', '_close', '_fwrite', '_fflush', '_fopen', '_fclose', '_putchar',
'_sprintf', '_snprintf', '_fprintf', '_sscanf', '_mmap', '_munmap', '_getcwd', '_access',
'_readlink', '_getenv', '_strlen', '_opendir', '_readdir', '_closedir', '_mkdir', '_rmdir',
'_unlink', '_rename', '_remove', '_stat', '_lstat', '_fstat', '_chmod', '_chdir', '_realpath',
'_symlink', '_link', '_getpid', '_getuid', '_geteuid', '_fork', '_execve', '_execvp', '_waitpid',
'_kill', '_system', '_posix_spawn', '_signal', '_atexit', '_fgets', '_fputs', '_fread', '_fseek',
'_ftell', '_rewind', '_fileno', '_popen', '_pclose', '_dup', '_dup2', '_pipe', '_isatty',
'_freopen', '_dprintf', '_getc', '_strdup', '_strcmp', '_strncmp', '_strchr', '_strrchr',
'_strerror', '_strncasecmp', '_strcasecmp', '_atoi', '_atof', '_qsort', '_time', '_localtime_r',
'_gmtime_r', '_mktime', '_gettimeofday', '_clock', '_clock_gettime_nsec_np',
'_mach_absolute_time', '_mach_timebase_info', '_nanosleep', '_sleep', '_usleep', '_strftime',
'_task_info', '_mach_task_self_', '_rand', '_srand', '_isdigit', '_isspace', '_tolower',
'_toupper', '_setenv', '_unsetenv', '_sysconf', '_uname', '_gethostname', '_pthread_mutex_init',
'_pthread_mutex_lock', '_pthread_mutex_unlock', '_pthread_mutex_destroy', '_pthread_self',
'_pthread_create', '_pthread_join', '_pthread_attr_init', '_pthread_attr_setstacksize',
'_pthread_attr_destroy', '_arc4random_buf', '_proc_pidpath', '_backtrace', '_backtrace_symbols',
'_backtrace_symbols_fd', '_dispatch_semaphore_create', '_dispatch_semaphore_signal',
'_dispatch_semaphore_wait', '_dispatch_time', '_dispatch_release', '_setvbuf', '_setbuf',
'_memchr', '_getlogin_r', '_getppid', '_getgid', '_getegid', '_ftruncate', '_mkstemp', '_statvfs',
'_chown', '_sigaction', '_sigemptyset', '_sigaddset', '_sigprocmask', '_select', '_kqueue',
'_abs', '_tcgetattr', '_tcsetattr', '_ioctl', '_getchar', '_getline', '_fdopen', '_feof',
'_ferror', '_setpgid', '_ptrace', '_wait', '_timegm', '_clock_gettime', '_aligned_alloc',
'_utime', '_getlogin', '_environ', '___error', '___stdinp', '__dyld_get_image_name',
'__dyld_get_image_header', '_cos', '_sin', '_tan', '_acos', '_asin', '_atan', '_atan2', '_cosh',
'_sinh', '_tanh', '_acosh', '_asinh', '_atanh', '_exp', '_exp2', '_log', '_log2', '_log10',
'_pow', '_sqrt', '_cbrt', '_ceil', '_floor', '_round', '_trunc', '_fmod', '_remainder', '_fabs',
'_copysign', '_fmax', '_fmin', '_hypot', '_ldexp', '_frexp', '_modf', '_scalbn', '_ilogb',
'_logb', '_erf', '_erfc', '_lgamma', '_tgamma', '_j0', '_j1', '_jn', '_y0', '_y1', '_yn',
'_mprotect', '_sys_icache_invalidate', '_objc_msgSend', '_objc_getClass', '_sel_registerName',
'_objc_alloc_init', '_objc_autoreleasePoolPush', '_objc_autoreleasePoolPop',
'_MTLCreateSystemDefaultDevice', '_dlopen', '_dlsym']
const bench_runtime_stub_names = ['current_rss_kb', 'macos_peak_rss_kb', 'linux_rss_kb',
'bench.current_rss_kb', 'bench.macos_peak_rss_kb', 'bench.linux_rss_kb']
pub struct Builder {
mut:
m &Module = unsafe { nil }
a &flat.FlatAst = unsafe { nil }
tc &types.TypeChecker = unsafe { nil }
used_fns map[string]bool
cur_module string
cur_func int
cur_block BlockID
vars map[string]ValueID
var_type_names map[string]string
i64_type TypeID
i32_type TypeID
i8_type TypeID
i1_type TypeID
u64_type TypeID
u32_type TypeID
u16_type TypeID
u8_type TypeID
f32_type TypeID
f64_type TypeID
void_type TypeID
str_type TypeID
array_type TypeID
map_type TypeID
map_state_type TypeID
fn_types map[string]TypeID
fn_ids map[string]int
const_exprs map[string]flat.NodeId
struct_types map[string]TypeID
struct_field_types map[string]string
option_types map[string]TypeID
sum_type_variants map[string][]string
sum_type_canonical map[string]string
enum_types map[string]bool
flag_enum_types map[string]bool
enum_values map[string]int
enum_member_values map[string]int
enum_member_dupes map[string]bool
c_fn_ids map[string]int
c_fn_types map[string]TypeID
label_blocks map[string]BlockID
defer_body_ids []flat.NodeId
break_targets []BlockID
continue_targets []BlockID
top_level_main bool
// --- Build-control machinery (ported from v2 build_all) ---
// When set, build_functions only materializes this one function body (hot reload).
hot_fn string
// When set, build_functions registers signatures only and marks them prototypes.
skip_fn_bodies bool
// When non-empty, all functions declared in these modules are skipped (dead
// code elimination for unused backends/modules).
skip_modules map[string]bool
// SSA-level type alias name -> resolved base TypeID. Supplements the checker's
// alias map (used when building without a checker).
type_aliases map[string]TypeID
}
struct VarBinding {
exists bool
addr ValueID
typ string
}
struct IfGuardState {
active bool
name string
old VarBinding
slot ValueID
value_ptr ValueID
typ TypeID
typ_name string
}
// BuildOptions controls which functions a build materializes. The zero value
// reproduces the default whole-program build.
pub struct BuildOptions {
pub:
hot_fn string // build only this function's body (hot reload)
skip_fn_bodies bool // register signatures only, mark them prototypes
skip_modules []string // skip all functions declared in these modules
}
pub fn build(a_ &flat.FlatAst) &Module {
return build_with_used(a_, map[string]bool{}, unsafe { nil })
}
pub fn build_with_used(a_ &flat.FlatAst, used_fns map[string]bool, tc &types.TypeChecker) &Module {
return build_with_options(a_, used_fns, tc, BuildOptions{})
}
pub fn build_with_options(a_ &flat.FlatAst, used_fns map[string]bool, tc &types.TypeChecker, opts BuildOptions) &Module {
mut b := Builder{
m: Module.new()
a: unsafe { a_ }
tc: unsafe { tc }
used_fns: used_fns
vars: map[string]ValueID{}
var_type_names: map[string]string{}
fn_types: map[string]TypeID{}
fn_ids: map[string]int{}
const_exprs: map[string]flat.NodeId{}
struct_types: map[string]TypeID{}
struct_field_types: map[string]string{}
option_types: map[string]TypeID{}
sum_type_variants: map[string][]string{}
sum_type_canonical: map[string]string{}
enum_types: map[string]bool{}
flag_enum_types: map[string]bool{}
enum_values: map[string]int{}
enum_member_values: map[string]int{}
enum_member_dupes: map[string]bool{}
c_fn_ids: map[string]int{}
c_fn_types: map[string]TypeID{}
label_blocks: map[string]BlockID{}
defer_body_ids: []flat.NodeId{}
break_targets: []BlockID{}
continue_targets: []BlockID{}
skip_modules: map[string]bool{}
type_aliases: map[string]TypeID{}
}
b.void_type = TypeID(0)
b.i64_type = b.m.type_store.get_int(64)
b.i32_type = b.m.type_store.get_int(32)
b.i8_type = b.m.type_store.get_int(8)
b.i1_type = b.m.type_store.get_int(1)
b.u64_type = b.m.type_store.get_uint(64)
b.u32_type = b.m.type_store.get_uint(32)
b.u16_type = b.m.type_store.get_uint(16)
b.u8_type = b.m.type_store.get_uint(8)
b.f32_type = b.m.type_store.get_float(32)
b.f64_type = b.m.type_store.get_float(64)
mut str_fields := []TypeID{}
str_fields << b.m.type_store.get_ptr(b.i8_type)
str_fields << b.i32_type
mut str_field_names := []string{}
str_field_names << 'str'
str_field_names << 'len'
b.str_type = b.m.type_store.register(Type{
kind: .struct_t
fields: str_fields
field_names: str_field_names
})
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
mut array_fields := []TypeID{}
array_fields << ptr_i8
array_fields << b.i32_type
array_fields << b.i32_type
array_fields << b.i32_type
array_fields << b.i32_type
array_fields << b.i32_type
mut array_field_names := []string{}
array_field_names << 'data'
array_field_names << 'offset'
array_field_names << 'len'
array_field_names << 'cap'
array_field_names << 'flags'
array_field_names << 'element_size'
b.array_type = b.m.type_store.register(Type{
kind: .struct_t
fields: array_fields
field_names: array_field_names
})
mut map_state_fields := []TypeID{}
map_state_fields << ptr_i8
map_state_fields << ptr_i8
map_state_fields << b.i64_type
map_state_fields << b.i64_type
map_state_fields << b.i64_type
map_state_fields << b.i64_type
mut map_state_field_names := []string{}
map_state_field_names << 'keys'
map_state_field_names << 'vals'
map_state_field_names << 'cap'
map_state_field_names << 'len'
map_state_field_names << 'key_size'
map_state_field_names << 'val_size'
b.map_state_type = b.m.type_store.register(Type{
kind: .struct_t
fields: map_state_fields
field_names: map_state_field_names
})
mut map_fields := []TypeID{}
map_fields << b.m.type_store.get_ptr(b.map_state_type)
mut map_field_names := []string{}
map_field_names << 'state'
b.map_type = b.m.type_store.register(Type{
kind: .struct_t
fields: map_fields
field_names: map_field_names
})
b.hot_fn = opts.hot_fn
b.skip_fn_bodies = opts.skip_fn_bodies
for mname in opts.skip_modules {
b.skip_modules[mname] = true
}
b.m.name = b.main_module_name()
b.register_type_aliases()
b.register_types()
b.register_consts()
b.register_globals()
b.register_functions()
b.build_functions()
return b.m
}
fn (mut b Builder) register_types() {
mut cur_module := ''
for node in b.a.nodes {
if node.kind == .module_decl {
cur_module = node.value
continue
}
if node.kind == .struct_decl {
typ_id := b.m.type_store.register(Type{
kind: .struct_t
})
b.register_struct_type_name(node.value, cur_module, typ_id)
} else if node.kind == .type_decl && node.children_count > 0 {
typ_id := b.m.type_store.register(Type{
kind: .struct_t
})
b.register_sum_type_name(node.value, cur_module, typ_id)
} else if node.kind == .enum_decl {
b.register_enum_values(node, cur_module)
}
}
cur_module = ''
for node in b.a.nodes {
if node.kind == .module_decl {
cur_module = node.value
continue
}
if node.kind == .struct_decl {
mut field_types := []TypeID{}
mut field_names := []string{}
for i in 0 .. node.children_count {
f := b.a.child_node(&node, i)
field_type := b.resolve_type_in_module(f.typ, cur_module)
field_type_name := qualify_type_ref_name(f.typ, cur_module)
field_types << field_type
field_names << f.value
b.struct_field_types[node.value + '.' + f.value] = field_type_name
short_name := node.value.all_after('.')
b.struct_field_types[short_name + '.' + f.value] = field_type_name
if cur_module.len > 0 && cur_module != 'main' && cur_module != 'builtin' {
b.struct_field_types[cur_module + '.' + short_name + '.' + f.value] = field_type_name
}
}
typ_id := b.struct_type_id_for_decl(node.value, cur_module)
b.m.type_store.types[typ_id] = Type{
kind: .struct_t
fields: field_types
field_names: field_names
}
} else if node.kind == .type_decl && node.children_count > 0 {
sum_name := qualify_type_name(node.value, cur_module)
variants := b.sum_type_variants[sum_name] or { []string{} }
mut field_types := []TypeID{}
mut field_names := []string{}
field_types << b.i32_type
field_names << 'typ'
for variant in variants {
mut variant_type := b.resolve_type_in_module(variant, cur_module)
if variant_type > 0 && variant_type < b.m.type_store.types.len
&& b.m.type_store.types[variant_type].kind == .struct_t {
variant_type = b.m.type_store.get_ptr(variant_type)
}
field_types << variant_type
field_names << sum_variant_field_name(variant)
}
typ_id := b.struct_type_id_for_decl(node.value, cur_module)
b.m.type_store.types[typ_id] = Type{
kind: .struct_t
fields: field_types
field_names: field_names
}
}
}
cur_module = ''
for node in b.a.nodes {
if node.kind == .module_decl {
cur_module = node.value
continue
}
if node.kind != .type_decl || node.children_count == 0 {
continue
}
sum_typ_id := b.struct_type_id_for_decl(node.value, cur_module)
mut field_types := []TypeID{}
mut field_names := []string{}
field_types << b.i64_type
field_names << 'typ'
b.register_sum_field_type(node.value, cur_module, 'typ', 'int')
variants := b.sum_type_variants_for_decl(node, cur_module)
for variant in variants {
field_name := sum_variant_field_name(variant)
mut field_type := b.resolve_type_in_module(variant, cur_module)
if field_type > 0 && field_type < b.m.type_store.types.len
&& b.m.type_store.types[field_type].kind == .struct_t {
field_type = b.m.type_store.get_ptr(field_type)
}
field_types << field_type
field_names << field_name
b.register_sum_field_type(node.value, cur_module, field_name, variant)
}
b.m.type_store.types[sum_typ_id] = Type{
kind: .struct_t
fields: field_types
field_names: field_names
}
}
b.register_multi_return_types()
}
fn (mut b Builder) register_multi_return_types() {
if b.tc == unsafe { nil } {
return
}
for _, ret in b.tc.fn_ret_types {
if ret is types.MultiReturn {
b.register_multi_return_type(ret)
}
}
}
fn (mut b Builder) register_multi_return_type(ret types.MultiReturn) TypeID {
name := b.multi_return_c_type(ret)
if typ := b.struct_types[name] {
return typ
}
typ_id := b.m.type_store.register(Type{
kind: .struct_t
})
b.struct_types[name] = typ_id
mut field_types := []TypeID{}
mut field_names := []string{}
for i, field_type in ret.types {
field_types << b.ssa_type_from_checker_type(field_type)
field_name := 'arg${i}'
field_names << field_name
b.struct_field_types[name + '.' + field_name] = field_type.name()
}
b.m.type_store.types[typ_id] = Type{
kind: .struct_t
fields: field_types
field_names: field_names
}
return typ_id
}
fn (b &Builder) multi_return_c_type(ret types.MultiReturn) string {
mut parts := []string{}
for field_type in ret.types {
parts << b.tc.c_type(field_type)
}
return 'multi_return_${parts.join('_')}'
}
fn primitive_type_name(typ types.Primitive) string {
if typ.props.has(.boolean) {
return 'bool'
}
if typ.props.has(.integer) {
if typ.props.has(.unsigned) {
return match typ.size {
8 { 'u8' }
16 { 'u16' }
32 { 'u32' }
64 { 'u64' }
else { 'u${typ.size}' }
}
}
return match typ.size {
0 { 'int' }
8 { 'i8' }
16 { 'i16' }
32 { 'i32' }
64 { 'i64' }
else { 'i${typ.size}' }
}
}
if typ.props.has(.float) {
return match typ.size {
32 { 'f32' }
else { 'f64' }
}
}
return 'int'
}
fn (mut b Builder) ssa_type_from_checker_type(typ types.Type) TypeID {
if typ is types.Void {
return b.void_type
}
if typ is types.String {
return b.str_type
}
if typ is types.Char {
return b.i8_type
}
if typ is types.Rune {
return b.i32_type
}
if typ is types.ISize || typ is types.USize {
return b.i64_type
}
if typ is types.Primitive {
return b.resolve_type(primitive_type_name(typ))
}
if typ is types.Array {
return b.array_type
}
if typ is types.ArrayFixed {
return b.m.type_store.get_ptr(b.ssa_type_from_checker_type(typ.elem_type))
}
if typ is types.Map {
return b.map_type
}
if typ is types.Pointer {
return b.m.type_store.get_ptr(b.ssa_type_from_checker_type(typ.base_type))
}
if typ is types.OptionType {
return b.option_type_id(typ.base_type.name())
}
if typ is types.ResultType {
return b.option_type_id(typ.base_type.name())
}
if typ is types.Enum {
return b.i32_type
}
if typ is types.MultiReturn {
return b.register_multi_return_type(typ)
}
if typ is types.Alias {
return b.ssa_type_from_checker_type(typ.base_type)
}
return b.resolve_type(typ.name())
}
fn (mut b Builder) register_enum_values(node flat.Node, module_name string) {
short_name := node.value.all_after('.')
mut enum_names := []string{}
enum_names << node.value
if short_name != node.value {
enum_names << short_name
}
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
enum_names << module_name + '.' + short_name
}
mut is_flag_enum := false
for enum_name in enum_names {
if b.checker_has_flag_enum(enum_name) {
is_flag_enum = true
break
}
}
for enum_name in enum_names {
b.enum_types[enum_name] = true
if is_flag_enum {
b.flag_enum_types[enum_name] = true
}
}
mut value := 0
for i in 0 .. node.children_count {
field := b.a.child_node(&node, i)
if field.kind != .enum_field {
continue
}
if field.children_count > 0 {
expr := b.a.child_node(field, 0)
if expr.kind == .int_literal {
value = parse_int_literal(expr.value)
}
}
for enum_name in enum_names {
enum_key := enum_name + '.' + field.value
b.enum_values[enum_key] = value
}
if existing := b.enum_member_values[field.value] {
if existing != value {
b.enum_member_dupes[field.value] = true
}
} else {
b.enum_member_values[field.value] = value
}
value++
}
}
fn (mut b Builder) register_struct_type_name(name string, module_name string, typ_id TypeID) {
b.struct_types[name] = typ_id
short_name := name.all_after('.')
if short_name !in b.struct_types {
b.struct_types[short_name] = typ_id
}
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
qualified_name := module_name + '.' + short_name
b.struct_types[qualified_name] = typ_id
}
}
fn (mut b Builder) register_sum_type_name(name string, module_name string, typ_id TypeID) {
qualified_name := qualify_type_name(name, module_name)
b.register_struct_type_name(name, module_name, typ_id)
mut variants := []string{}
if b.tc != unsafe { nil } {
if tc_variants := b.tc.sum_types[qualified_name] {
variants = tc_variants.clone()
}
}
if variants.len == 0 {
for node in b.a.nodes {
if node.kind != .type_decl || node.value != name || node.children_count == 0 {
continue
}
for i in 0 .. node.children_count {
variant := b.a.child_node(&node, i)
variants << qualify_type_name(variant.value, module_name)
}
break
}
}
b.sum_type_variants[qualified_name] = variants
b.sum_type_canonical[qualified_name] = qualified_name
if name != qualified_name {
b.sum_type_variants[name] = variants
b.sum_type_canonical[name] = qualified_name
}
short_name := qualified_name.all_after('.')
b.sum_type_variants[short_name] = variants
b.sum_type_canonical[short_name] = qualified_name
}
fn qualify_type_name(name string, module_name string) string {
if name.contains('.') || module_name.len == 0 || module_name == 'main'
|| module_name == 'builtin' {
return name
}
return '${module_name}.${name}'
}
fn qualify_type_ref_name(name string, module_name string) string {
if name.len == 0 {
return name
}
if name.starts_with('&') {
return '&' + qualify_type_ref_name(name[1..], module_name)
}
if name.len > 1 && (name[0] == `?` || name[0] == `!`) {
return name[..1] + qualify_type_ref_name(name[1..], module_name)
}
if name.starts_with('[]') {
return '[]' + qualify_type_ref_name(name[2..], module_name)
}
if name.starts_with('[') {
idx := name.index_u8(`]`)
if idx > 0 && idx + 1 < name.len {
return name[..idx + 1] + qualify_type_ref_name(name[idx + 1..], module_name)
}
}
if name.contains('[') && name.ends_with(']') {
idx := name.index_u8(`[`)
if idx > 0 && idx < name.len - 1 {
return qualify_type_ref_name(name[..idx], module_name) + name[idx..]
}
}
if name.starts_with('map[') {
key_type, val_type := map_type_parts(name)
if key_type.len > 0 && val_type.len > 0 {
return 'map[' + qualify_type_ref_name(key_type, module_name) + ']' +
qualify_type_ref_name(val_type, module_name)
}
}
if type_ref_is_builtin(name) || name.contains('.') || module_name.len == 0
|| module_name == 'main' || module_name == 'builtin' {
return name
}
return module_name + '.' + name
}
fn type_ref_is_builtin(name string) bool {
return name in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'byte', 'u16', 'u32', 'u64', 'f32',
'f64', 'bool', 'string', 'void', 'voidptr', 'rune', 'char', 'array', 'map']
}
fn (b &Builder) sum_type_variants_for_decl(node flat.Node, module_name string) []string {
mut variants := []string{}
for i in 0 .. node.children_count {
variant := b.a.child_node(&node, i).value
variants << qualify_type_name(variant, module_name)
}
return variants
}
fn (mut b Builder) register_sum_field_type(name string, module_name string, field_name string, field_type string) {
short_name := name.all_after('.')
b.struct_field_types[name + '.' + field_name] = field_type
b.struct_field_types[short_name + '.' + field_name] = field_type
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
b.struct_field_types[module_name + '.' + short_name + '.' + field_name] = field_type
}
}
fn (b &Builder) struct_type_id_for_decl(name string, module_name string) TypeID {
short_name := name.all_after('.')
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
qualified_name := module_name + '.' + short_name
if typ := b.struct_types[qualified_name] {
return typ
}
}
if typ := b.struct_types[name] {
return typ
}
if typ := b.struct_types[short_name] {
return typ
}
return TypeID(0)
}
fn (mut b Builder) register_consts() {
mut cur_module := ''
for node in b.a.nodes {
if node.kind == .module_decl {
cur_module = node.value
continue
}
if node.kind == .const_decl {
for i in 0 .. node.children_count {
field_id := b.a.child(&node, i)
if int(field_id) < 0 {
continue
}
field := b.a.nodes[int(field_id)]
if field.kind != .const_field || field.children_count == 0 {
continue
}
expr_id := b.a.child(&field, 0)
if cur_module.len > 0 && cur_module != 'main' {
b.const_exprs['${cur_module}.${field.value}'] = expr_id
} else {
b.const_exprs[field.value] = expr_id
}
}
}
}
}
fn (mut b Builder) register_globals() {
for node in b.a.nodes {
if node.kind == .global_decl {
for i in 0 .. node.children_count {
f := b.a.child_node(&node, i)
typ := b.resolve_type(f.typ)
b.vars[f.value] = b.m.add_global(f.value, typ)
}
}
}
b.ensure_runtime_global('g_main_argc', b.i64_type)
b.ensure_runtime_global('g_main_argv',
b.m.type_store.get_ptr(b.m.type_store.get_ptr(b.i8_type)))
}
fn (mut b Builder) ensure_runtime_global(name string, typ TypeID) {
if name in b.vars {
return
}
for v in b.m.values {
if v.kind == .global && v.name == name {
b.vars[name] = v.id
return
}
}
b.vars[name] = b.m.add_global(name, typ)
}
fn (mut b Builder) register_functions() {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
mut p1 := []TypeID{}
mut p2 := []TypeID{}
mut p3 := []TypeID{}
// C library externs
p3 = []TypeID{}
p3 << b.i64_type
p3 << ptr_i8
p3 << b.i64_type
b.register_extern('write', b.i64_type, p3)
b.register_extern('read', b.i64_type, p3)
p3 = []TypeID{}
p3 << ptr_i8
p3 << b.i64_type
p3 << b.i64_type
b.register_extern('open', b.i64_type, p3)
p1 = []TypeID{}
p1 << b.i64_type
b.register_extern('putchar', b.i64_type, p1)
b.register_extern('getchar', b.i64_type, []TypeID{})
p1 = []TypeID{}
p1 << b.i64_type
b.register_extern('close', b.i64_type, p1)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('puts', b.i64_type, p1)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('strlen', b.i64_type, p1)
p1 = []TypeID{}
p1 << b.i64_type
b.register_extern('strerror', ptr_i8, p1)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('feof', b.i64_type, p1)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('ferror', b.i64_type, p1)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('fclose', b.i64_type, p1)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('pclose', b.i64_type, p1)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('ftell', b.i64_type, p1)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('rewind', b.void_type, p1)
p2 = []TypeID{}
p2 << ptr_i8
p2 << ptr_i8
b.register_extern('fputs', b.i64_type, p2)
b.register_extern('stat', b.i64_type, p2)
b.register_extern('lstat', b.i64_type, p2)
b.register_extern('statvfs', b.i64_type, p2)
b.register_extern('rename', b.i64_type, p2)
b.register_extern('strcmp', b.i64_type, p2)
p2 = []TypeID{}
p2 << ptr_i8
p2 << b.i64_type
b.register_extern('strchr', ptr_i8, p2)
b.register_extern('strrchr', ptr_i8, p2)
p2 = []TypeID{}
p2 << ptr_i8
p2 << ptr_i8
b.register_extern('fopen', ptr_i8, p2)
p2 = []TypeID{}
p2 << ptr_i8
p2 << ptr_i8
b.register_extern('popen', ptr_i8, p2)
mut p4 := []TypeID{}
p4 << ptr_i8
p4 << b.i64_type
p4 << b.i64_type
p4 << ptr_i8
b.register_extern('fread', b.i64_type, p4)
p4 = []TypeID{}
p4 << ptr_i8
p4 << b.i64_type
p4 << b.i64_type
p4 << ptr_i8
b.register_extern('fwrite', b.i64_type, p4)
p3 = []TypeID{}
p3 << ptr_i8
p3 << b.i64_type
p3 << b.i64_type
b.register_extern('fseek', b.i64_type, p3)
p1 = []TypeID{}
p1 << b.i64_type
b.register_extern('malloc', ptr_i8, p1)
p2 = []TypeID{}
p2 << b.i64_type
p2 << b.i64_type
b.register_extern('calloc', ptr_i8, p2)
p2 = []TypeID{}
p2 << ptr_i8
p2 << b.i64_type
b.register_extern('realloc', ptr_i8, p2)
p3 = []TypeID{}
p3 << ptr_i8
p3 << ptr_i8
p3 << b.i64_type
b.register_extern('memcpy', ptr_i8, p3)
p3 = []TypeID{}
p3 << ptr_i8
p3 << ptr_i8
p3 << b.i64_type
b.register_extern('memmove', ptr_i8, p3)
p3 = []TypeID{}
p3 << ptr_i8
p3 << b.i64_type
p3 << b.i64_type
b.register_extern('memset', ptr_i8, p3)
p3 = []TypeID{}
p3 << ptr_i8
p3 << ptr_i8
p3 << b.i64_type
b.register_extern('memcmp', b.i64_type, p3)
p1 = []TypeID{}
p1 << b.i64_type
b.register_extern('exit', b.void_type, p1)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('fflush', b.void_type, p1)
p2 = []TypeID{}
p2 << ptr_i8
p2 << b.i64_type
b.register_extern('access', b.i64_type, p2)
b.register_extern('mkdir', b.i64_type, p2)
b.register_extern('chmod', b.i64_type, p2)
b.register_extern('getcwd', ptr_i8, p2)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('remove', b.i64_type, p1)
b.register_extern('unlink', b.i64_type, p1)
b.register_extern('rmdir', b.i64_type, p1)
b.register_extern('chdir', b.i64_type, p1)
b.register_extern('getenv', ptr_i8, p1)
b.register_extern('system', b.i64_type, p1)
b.register_extern('opendir', ptr_i8, p1)
b.register_extern('closedir', b.i64_type, p1)
b.register_extern('fileno', b.i64_type, p1)
b.register_extern('getc', b.i64_type, p1)
b.register_extern('strdup', ptr_i8, p1)
b.register_extern('atoi', b.i64_type, p1)
p3 = []TypeID{}
p3 << ptr_i8
p3 << ptr_i8
p3 << b.i64_type
b.register_extern('readlink', b.i64_type, p3)
b.register_extern('strncmp', b.i64_type, p3)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('readdir', ptr_i8, p1)
b.register_extern('pipe', b.i64_type, p1)
p3 = []TypeID{}
p3 << ptr_i8
p3 << ptr_i8
p3 << b.i64_type
b.register_extern('setenv', b.i64_type, p3)
p3 = []TypeID{}
p3 << b.i64_type
p3 << ptr_i8
p3 << b.i64_type
b.register_extern('waitpid', b.i64_type, p3)
p2 = []TypeID{}
p2 << ptr_i8
p2 << ptr_i8
b.register_extern('realpath', ptr_i8, p2)
b.register_extern('pthread_mutex_init', b.i64_type, p2)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('pthread_mutex_lock', b.i64_type, p1)
b.register_extern('pthread_mutex_trylock', b.i64_type, p1)
b.register_extern('pthread_mutex_unlock', b.i64_type, p1)
b.register_extern('pthread_mutex_destroy', b.i64_type, p1)
b.register_extern('pthread_self', b.i64_type, []TypeID{})
b.register_extern('getpid', b.i64_type, []TypeID{})
b.register_extern('clock', b.i64_type, []TypeID{})
b.register_extern('mach_absolute_time', b.i64_type, []TypeID{})
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('mach_timebase_info', b.void_type, p1)
p1 = []TypeID{}
p1 << b.i64_type
b.register_extern('clock_gettime_nsec_np', b.i64_type, p1)
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('time', b.i64_type, p1)
b.register_extern('mktime', b.i64_type, p1)
p2 = []TypeID{}
p2 << ptr_i8
p2 << ptr_i8
b.register_extern('localtime_r', ptr_i8, p2)
b.register_extern('gmtime_r', ptr_i8, p2)
p4 = []TypeID{}
p4 << ptr_i8
p4 << b.i64_type
p4 << ptr_i8
p4 << ptr_i8
b.register_extern('strftime', b.i64_type, p4)
p2 = []TypeID{}
p2 << ptr_i8
p2 << ptr_i8
b.register_extern('gettimeofday', b.i64_type, p2)
p1 = []TypeID{}
p1 << b.i64_type
b.register_extern('sleep', b.i64_type, p1)
b.register_extern('usleep', b.i64_type, p1)
b.register_extern('dup', b.i64_type, p1)
b.register_extern('isatty', b.i64_type, p1)
p2 = []TypeID{}
p2 << b.i64_type
p2 << b.i64_type
b.register_extern('dup2', b.i64_type, p2)
p2 = []TypeID{}
p2 << ptr_i8
p2 << ptr_i8
b.register_extern('nanosleep', b.i64_type, p2)
p2 = []TypeID{}
p2 << b.i64_type
p2 << ptr_i8
b.register_extern('clock_gettime', b.i64_type, p2)
b.register_basic_format_stubs()
b.register_string_plus_stubs()
b.register_bench_runtime_stubs()
p1 = []TypeID{}
p1 << ptr_i8
b.register_extern('free', b.void_type, p1)
p2 = []TypeID{}
p2 << ptr_i8
p2 << ptr_i8
b.register_extern('fprintf', b.void_type, p2)
for node in b.a.nodes {
if node.kind != .c_fn_decl {
continue
}
ret_type := b.resolve_c_decl_type(node.typ)
mut param_types := []TypeID{}
for i in 0 .. node.children_count {
child := b.a.child_node(&node, i)
if child.kind == .param {
param_types << b.resolve_c_decl_type(child.typ)
}
}
b.register_extern(node.value, ret_type, param_types)
if node.value.starts_with('C.') {
b.register_extern(node.value[2..], ret_type, param_types)
}
}
mut cur_module := ''
for node in b.a.nodes {
if node.kind == .module_decl {
cur_module = node.value
continue
}
if node.kind == .fn_decl {
if b.skip_source_fn_in_module(node.value, cur_module) {
continue
}
fn_name := ssa_fn_name_in_module(cur_module, node.value)
if b.used_fns.len > 0 && !b.fn_is_used(node.value) && !b.fn_is_used(fn_name) {
continue
}
ret_type := b.checker_return_type(node.value, cur_module) or {
b.resolve_type_in_module(node.typ, cur_module)
}
mut param_types := []TypeID{}
mut param_idx := 0
for i in 0 .. node.children_count {
child := b.a.child_node(&node, i)
if child.kind == .param {
typ_name := if child.typ.starts_with('&') {
child.typ
} else {
b.checker_param_type_name(node.value, cur_module, param_idx) or {
child.typ
}
}
param_types << b.resolve_type_in_module(typ_name, cur_module)
param_idx++
}
}
fn_type := b.m.type_store.register(Type{
kind: .func_t
ret_type: ret_type
params: param_types
})
b.fn_types[fn_name] = fn_type
func_id := b.m.new_function(fn_name, ret_type)
b.fn_ids[fn_name] = func_id
}
}
b.register_top_level_main()
b.register_wyhash_stubs()
b.register_string_eq_stub()
b.register_fast_string_eq_stub()
b.register_string_lt_stub()
b.register_string_trim_stubs()
b.register_string_last_part_stubs()
b.register_ierror_stubs()
b.register_array_runtime_stubs()
b.register_arguments_stub()
b.register_panic_stub()
b.register_string_builder_stubs()
b.register_path_runtime_stubs()
b.register_map_runtime_stubs()
b.register_u8_runtime_stubs()
b.register_heap_tracking_stubs()
b.register_printing_stubs()
b.register_prealloc_atomic_stubs()
b.register_process_capture_stubs()
b.register_file_check_stubs()
b.register_fd_macro_stubs()
b.register_signal_macro_stubs()
b.register_os_stat_stubs()
b.register_array_string_stubs()
b.register_fixed_array_contains_stubs()
b.register_array_contains_stubs()
}
fn ssa_fn_name_in_module(module_name string, name string) string {
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
return module_name + '.' + name
}
return name
}
fn (mut b Builder) register_extern(name string, ret TypeID, params []TypeID) {
fn_type := b.m.type_store.register(Type{
kind: .func_t
ret_type: ret
params: params
})
extern_name := 'C.${name}'
func_id := b.m.new_function(extern_name, ret)
b.fn_types[name] = fn_type
b.fn_ids[name] = func_id
b.fn_types[extern_name] = fn_type
b.c_fn_types[name] = fn_type
b.c_fn_ids[name] = func_id
mut f := b.m.funcs[func_id]
f.is_c_extern = true
f.is_prototype = true
f.linkage = .external
b.m.funcs[func_id] = f
}
fn (mut b Builder) register_runtime_extern(name string, ret TypeID, params []TypeID) {
if b.has_fn_decl(name) {
return
}
b.register_extern(name, ret, params)
}
fn (b &Builder) has_fn_decl(name string) bool {
for node in b.a.nodes {
if node.kind == .fn_decl && node.value == name {
return true
}
}
return false
}
fn (b &Builder) skip_source_fn(name string) bool {
if name in bench_runtime_stub_names {
return true
}
if name in ['_wymix', 'wyhash', 'wyhash64', 'string__eq', 'string__lt', 'array_new', 'array_get',
'string__plus', 'string_plus_many', 'string.trim_right', 'array_push', 'array_push_many',
'array.push_many', 'array_clone', 'panic', 'fast_string_eq', 'strings.new_builder',
'strings.Builder.write_string', 'strings.Builder.writeln', 'strings.Builder.str',
'strings.Builder.write_ptr', 'strings.Builder.write_u8', 'strings.Builder.write_runes',
'strings.Builder.free', 'strings.Builder.last_n', 'Builder.write_string', 'Builder.writeln',
'Builder.str', 'Builder.write_ptr', 'Builder.write_u8', 'Builder.write_runes', 'Builder.free',
'Builder.last_n', 'new_map', 'map__set', 'map__get', 'map__exists', 'map__get_check',
'map__get_or_set', 'map__delete', 'map__clear', 'map__clone', 'v3_map_find',
'v3_map_set_sized', 'u8.is_digit', 'u8.is_letter', 'u8.is_alnum', 'u8.is_capital', 'bytestr',
'[]u8.bytestr', 'Array_u8__bytestr', 'Array_u8__hex', 'Array_rune__string',
'array.repeat_to_depth', 'string.all_before_last', 'string__all_before_last',
'all_before_last', 'string.all_after_last', 'string__all_after_last', 'all_after_last',
'_ht_alloc', '_ht_free', 'f32_to_str_l', 'f32_to_str_l_with_dot', 'f64_to_str_l',
'f64_to_str_l_with_dot', 'print', 'println', 'eprint', 'eprintln', 'arguments', 'tos2',
'tos3', 'tos_clone', 'v_prealloc_atomic_add_i32', 'v_prealloc_atomic_load_i32',
'v_prealloc_atomic_store_i32', 'v_prealloc_atomic_cas_i32', 'FD_ZERO', 'FD_SET', 'FD_ISSET',
'v_signal_with_handler_cast', 'normalize_path_in_builder', 'check_fwrite', 'check_fread',
'os.check_fwrite', 'os.check_fread', 'fxx_to_str_l_parse', 'fxx_to_str_l_parse_with_dot',
'u8.vstring', 'u8.vstring_with_len', 'char.vstring', 'char.vstring_with_len',
'byteptr.vstring', 'byteptr.vstring_with_len', 'charptr.vstring', 'charptr.vstring_with_len',
'u8.vstring_literal', 'u8.vstring_literal_with_len', 'char.vstring_literal',
'char.vstring_literal_with_len', 'byteptr.vstring_literal',
'byteptr.vstring_literal_with_len', 'charptr.vstring_literal',
'charptr.vstring_literal_with_len'] {
return true
}
return name.starts_with('print_backtrace') || name.starts_with('backtrace_')
|| name.starts_with('map.')
|| name in ['print_libbacktrace', 'eprint_libbacktrace', 'bsd_backtrace_resolve_atos']
}
fn (b &Builder) skip_source_fn_in_module(name string, module_name string) bool {
if module_name == 'builtin' && name in b.c_fn_ids {
return true
}
if module_name == 'ast' && name in ['Expr.name', 'SelectorExpr.name', '[]Expr.name_list'] {
return true
}
if module_name == 'c' && name.starts_with('Gen.') {
return true
}
if module_name == 'os' && name in ['is_dir', 'is_link', 'ls'] {
return true
}
return b.skip_source_fn(name)
}
fn (mut b Builder) register_top_level_main() {
if 'main' in b.fn_ids || !b.has_top_level_stmts() {
return
}
fn_type := b.m.type_store.register(Type{
kind: .func_t
ret_type: b.void_type
})
b.fn_types['main'] = fn_type
b.fn_ids['main'] = b.m.new_function('main', b.void_type)
b.top_level_main = true
}
fn (b &Builder) has_top_level_stmts() bool {
return b.top_level_stmt_ids().len > 0
}
fn (b &Builder) top_level_stmt_ids() []flat.NodeId {
mut ids := []flat.NodeId{}
for file_idx, file_node in b.a.nodes {
if file_idx < b.a.user_code_start || file_node.kind != .file
|| file_node.children_count == 0 {
continue
}
for i in 0 .. file_node.children_count {
child_id := b.a.child(&file_node, i)
if int(child_id) < b.a.user_code_start {
continue
}
child := b.a.nodes[int(child_id)]
if b.is_top_level_stmt(child) {
ids << child_id
}
}
}
return ids
}
fn (b &Builder) is_top_level_stmt(node flat.Node) bool {
return match node.kind {
.expr_stmt, .assign, .decl_assign, .selector_assign, .index_assign, .for_stmt,
.for_in_stmt, .if_expr, .assert_stmt, .block {
true
}
else {
false
}
}
}
fn (mut b Builder) register_wyhash_stubs() {
mut p2 := []TypeID{}
p2 << b.i64_type
p2 << b.i64_type
wymix_id := b.register_synthetic_function('_wymix', b.i64_type, p2)
b.generate_wymix_body(wymix_id)
wyhash64_id := b.register_synthetic_function('wyhash64', b.i64_type, p2)
b.generate_wyhash64_body(wyhash64_id)
ptr_u8 := b.m.type_store.get_ptr(b.i8_type)
ptr_u64 := b.m.type_store.get_ptr(b.u64_type)
mut p4 := []TypeID{}
p4 << ptr_u8
p4 << b.i64_type
p4 << b.i64_type
p4 << ptr_u64
wyhash_id := b.register_synthetic_function('wyhash', b.i64_type, p4)
b.generate_wyhash_body(wyhash_id)
}
fn (mut b Builder) register_synthetic_function(name string, ret TypeID, params []TypeID) int {
fn_type := b.m.type_store.register(Type{
kind: .func_t
ret_type: ret
params: params
})
b.fn_types[name] = fn_type
func_id := b.m.new_function(name, ret)
b.fn_ids[name] = func_id
mut f := b.m.funcs[func_id]
f.typ = ret
f.blocks = []BlockID{}
f.params = []ValueID{}
f.is_c_extern = false
b.m.funcs[func_id] = f
return func_id
}
fn (mut b Builder) func_add_argument(func_id int, typ TypeID, name string) ValueID {
mut f := b.m.funcs[func_id]
param := b.m.add_value(.argument, typ, name, f.params.len)
f.params << param
b.m.funcs[func_id] = f
return param
}
fn (mut b Builder) block_instr0(op OpCode, block_id BlockID, typ TypeID) ValueID {
ops := []ValueID{}
return b.m.add_instr(op, block_id, typ, ops)
}
fn (mut b Builder) block_instr1(op OpCode, block_id BlockID, typ TypeID, a ValueID) ValueID {
mut ops := []ValueID{}
ops << a
return b.m.add_instr(op, block_id, typ, ops)
}
fn (mut b Builder) block_instr2(op OpCode, block_id BlockID, typ TypeID, a ValueID, c ValueID) ValueID {
mut ops := []ValueID{}
ops << a
ops << c
return b.m.add_instr(op, block_id, typ, ops)
}
fn (mut b Builder) block_instr3(op OpCode, block_id BlockID, typ TypeID, a ValueID, c ValueID, d ValueID) ValueID {
mut ops := []ValueID{}
ops << a
ops << c
ops << d
return b.m.add_instr(op, block_id, typ, ops)
}
fn (mut b Builder) block_instr4(op OpCode, block_id BlockID, typ TypeID, a ValueID, c ValueID, d ValueID, e ValueID) ValueID {
mut ops := []ValueID{}
ops << a
ops << c
ops << d
ops << e
return b.m.add_instr(op, block_id, typ, ops)
}
fn (mut b Builder) block_struct_field_ptr(block_id BlockID, base_addr ValueID, typ_id TypeID, field_idx int) ValueID {
mut builtin_field_type := TypeID(0)
mut builtin_offset := 0
mut has_builtin_layout := false
if typ_id == b.str_type {
has_builtin_layout = true
if field_idx == 0 {
builtin_field_type = b.m.type_store.get_ptr(b.i8_type)
builtin_offset = 0
} else if field_idx == 1 {
builtin_field_type = b.i32_type
builtin_offset = 8
} else {
builtin_field_type = b.i64_type
builtin_offset = field_idx * 8
}
} else if typ_id == b.array_type {
has_builtin_layout = true
if field_idx == 0 {
builtin_field_type = b.m.type_store.get_ptr(b.i8_type)
builtin_offset = 0
} else {
builtin_field_type = b.i32_type
builtin_offset = 8 + (field_idx - 1) * 4
}
} else if typ_id == b.map_state_type {
has_builtin_layout = true
if field_idx == 0 || field_idx == 1 {
builtin_field_type = b.m.type_store.get_ptr(b.i8_type)
} else {
builtin_field_type = b.i64_type
}
builtin_offset = field_idx * 8
} else if typ_id == b.map_type {
has_builtin_layout = true
builtin_field_type = b.m.type_store.get_ptr(b.map_state_type)
builtin_offset = 0
}
if has_builtin_layout {
off_const := b.m.get_or_add_const(b.i64_type, '${builtin_offset}')
return b.block_instr2(.get_element_ptr, block_id,
b.m.type_store.get_ptr(builtin_field_type), base_addr, off_const)
}
typ := b.m.type_store.types[typ_id]
field_type := if field_idx < typ.fields.len { typ.fields[field_idx] } else { b.i64_type }
offset := b.m.struct_field_offset(typ_id, field_idx)
off_const := b.m.get_or_add_const(b.i64_type, '${offset}')
return b.block_instr2(.get_element_ptr, block_id, b.m.type_store.get_ptr(field_type),
base_addr, off_const)
}
fn (mut b Builder) block_load_array_int_field(block_id BlockID, arr_ptr ValueID, field_idx int) ValueID {
field_ptr := b.block_struct_field_ptr(block_id, arr_ptr, b.array_type, field_idx)
field32 := b.block_instr1(.load, block_id, b.i32_type, field_ptr)
return b.block_instr1(.zext, block_id, b.i64_type, field32)
}
fn (mut b Builder) register_basic_format_stubs() {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
mut p1_ptr := []TypeID{}
p1_ptr << ptr_i8
tos2_id := b.register_synthetic_function('tos2', b.str_type, p1_ptr)
b.generate_tos2_body(tos2_id)
tos3_id := b.register_synthetic_function('tos3', b.str_type, p1_ptr)
b.generate_tos2_body(tos3_id)
tos_clone_id := b.register_synthetic_function('tos_clone', b.str_type, p1_ptr)
b.generate_tos_clone_body(tos_clone_id)
b.register_pointer_string_stubs()
mut p1_i64 := []TypeID{}
p1_i64 << b.i64_type
int_str_id := b.register_synthetic_function('int_str', b.str_type, p1_i64)
b.generate_int_format_body(int_str_id, true, false)
mut p1_i1 := []TypeID{}
p1_i1 << b.i1_type
bool_str_id := b.register_synthetic_function('bool_str', b.str_type, p1_i1)
b.generate_bool_str_body(bool_str_id)
mut p1_string := []TypeID{}
p1_string << b.str_type
string_int_id := b.register_synthetic_function('string.int', b.i64_type, p1_string)
b.generate_string_int_body(string_int_id)
string_int_c_id := b.register_synthetic_function('string__int', b.i64_type, p1_string)
b.generate_string_int_body(string_int_c_id)
mut p2_i64 := []TypeID{}
p2_i64 << b.i64_type
p2_i64 << b.i64_type
format_int_id := b.register_synthetic_function('strconv__format_int', b.str_type, p2_i64)
b.generate_int_format_body(format_int_id, true, true)
format_uint_id := b.register_synthetic_function('strconv__format_uint', b.str_type, p2_i64)
b.generate_int_format_body(format_uint_id, false, true)
for name in ['strconv__f32_to_str_l', 'strconv__f32_to_str_l_with_dot', 'f32_to_str_l',
'f32_to_str_l_with_dot', 'strconv__f64_to_str_l', 'strconv__f64_to_str_l_with_dot',
'f64_to_str_l', 'f64_to_str_l_with_dot'] {
float_str_id := b.register_synthetic_function(name, b.str_type, p1_i64)
b.generate_const_string_body(float_str_id, '0.0')
}
}
fn (mut b Builder) register_pointer_string_stubs() {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
mut p1_ptr := []TypeID{}
p1_ptr << ptr_i8
for name in ['u8.vstring', 'char.vstring', 'byteptr.vstring', 'charptr.vstring',
'u8.vstring_literal', 'char.vstring_literal', 'byteptr.vstring_literal',
'charptr.vstring_literal'] {
func_id := b.register_synthetic_function(name, b.str_type, p1_ptr)
b.generate_tos2_body(func_id)
}
mut p2_ptr_len := []TypeID{}
p2_ptr_len << ptr_i8
p2_ptr_len << b.i64_type
for name in ['u8.vstring_with_len', 'char.vstring_with_len', 'byteptr.vstring_with_len',
'charptr.vstring_with_len', 'u8.vstring_literal_with_len', 'char.vstring_literal_with_len',
'byteptr.vstring_literal_with_len', 'charptr.vstring_literal_with_len'] {
func_id := b.register_synthetic_function(name, b.str_type, p2_ptr_len)
b.generate_vstring_with_len_body(func_id)
}
}
fn (mut b Builder) generate_const_string_body(func_id int, value string) {
entry := b.m.add_block(func_id, 'entry')
result := b.m.add_value(.string_literal, b.str_type, value, 0)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_bool_str_body(func_id int) {
entry := b.m.add_block(func_id, 'entry')
value := b.func_add_argument(func_id, b.i1_type, 'value')
true_block := b.m.add_block(func_id, 'bool_str_true')
false_block := b.m.add_block(func_id, 'bool_str_false')
b.block_instr3(.br, entry, b.void_type, value, ValueID(true_block), ValueID(false_block))
true_val := b.m.add_value(.string_literal, b.str_type, 'true', 0)
false_val := b.m.add_value(.string_literal, b.str_type, 'false', 0)
b.block_instr1(.ret, true_block, b.void_type, true_val)
b.block_instr1(.ret, false_block, b.void_type, false_val)
}
fn (mut b Builder) generate_int_format_body(func_id int, is_signed bool, has_radix bool) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_ptr_i8 := b.m.type_store.get_ptr(ptr_i8)
entry := b.m.add_block(func_id, 'entry')
value := b.func_add_argument(func_id, b.i64_type, 'value')
if has_radix {
_ := b.func_add_argument(func_id, b.i64_type, 'radix')
}
alloca_n := b.block_instr0(.alloca, entry, ptr_i64)
alloca_len := b.block_instr0(.alloca, entry, ptr_i64)
alloca_pos := b.block_instr0(.alloca, entry, ptr_ptr_i8)
alloca_neg := b.block_instr0(.alloca, entry, b.m.type_store.get_ptr(b.i1_type))
zero := b.m.get_or_add_const(b.i64_type, '0')
one := b.m.get_or_add_const(b.i64_type, '1')
ten := b.m.get_or_add_const(b.i64_type, '10')
buf_size := b.m.get_or_add_const(b.i64_type, '32')
last_off := b.m.get_or_add_const(b.i64_type, '31')
minus_one := b.m.get_or_add_const(b.i64_type, '-1')
ascii_zero := b.m.get_or_add_const(b.i64_type, '48')
ascii_minus := b.m.get_or_add_const(b.i64_type, '45')
false_val := b.m.get_or_add_const(b.i1_type, '0')
true_val := b.m.get_or_add_const(b.i1_type, '1')
malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
buf := b.block_instr2(.call, entry, ptr_i8, malloc_ref, buf_size)
end := b.block_instr2(.add, entry, ptr_i8, buf, last_off)
nul := b.m.get_or_add_const(b.i8_type, '0')
b.block_instr2(.store, entry, b.void_type, nul, end)
b.block_instr2(.store, entry, b.void_type, end, alloca_pos)
b.block_instr2(.store, entry, b.void_type, zero, alloca_len)
b.block_instr2(.store, entry, b.void_type, false_val, alloca_neg)
init_block := b.m.add_block(func_id, 'int_str_init')
neg_block := b.m.add_block(func_id, 'int_str_neg')
pos_block := b.m.add_block(func_id, 'int_str_pos')
if is_signed {
is_neg := b.block_instr2(.lt, entry, b.i1_type, value, zero)
b.block_instr3(.br, entry, b.void_type, is_neg, ValueID(neg_block), ValueID(pos_block))
abs_value := b.block_instr2(.sub, neg_block, b.i64_type, zero, value)
b.block_instr2(.store, neg_block, b.void_type, abs_value, alloca_n)
b.block_instr2(.store, neg_block, b.void_type, true_val, alloca_neg)
b.block_instr1(.jmp, neg_block, b.void_type, ValueID(init_block))
b.block_instr2(.store, pos_block, b.void_type, value, alloca_n)
b.block_instr1(.jmp, pos_block, b.void_type, ValueID(init_block))
} else {
b.block_instr2(.store, entry, b.void_type, value, alloca_n)
b.block_instr1(.jmp, entry, b.void_type, ValueID(init_block))
}
zero_digit_block := b.m.add_block(func_id, 'int_str_zero')
loop_block := b.m.add_block(func_id, 'int_str_loop')
sign_block := b.m.add_block(func_id, 'int_str_sign')
sign_write_block := b.m.add_block(func_id, 'int_str_sign_write')
done_block := b.m.add_block(func_id, 'int_str_done')
n_init := b.block_instr1(.load, init_block, b.i64_type, alloca_n)
is_zero := b.block_instr2(.eq, init_block, b.i1_type, n_init, zero)
b.block_instr3(.br, init_block, b.void_type, is_zero, ValueID(zero_digit_block),
ValueID(loop_block))
pos_zero := b.block_instr1(.load, zero_digit_block, ptr_i8, alloca_pos)
pos_zero_next := b.block_instr2(.add, zero_digit_block, ptr_i8, pos_zero, minus_one)
b.block_instr2(.store, zero_digit_block, b.void_type, ascii_zero, pos_zero_next)
b.block_instr2(.store, zero_digit_block, b.void_type, pos_zero_next, alloca_pos)
b.block_instr2(.store, zero_digit_block, b.void_type, one, alloca_len)
b.block_instr1(.jmp, zero_digit_block, b.void_type, ValueID(sign_block))
n_cur := b.block_instr1(.load, loop_block, b.i64_type, alloca_n)
rem := b.block_instr2(.srem, loop_block, b.i64_type, n_cur, ten)
digit := b.block_instr2(.add, loop_block, b.i64_type, rem, ascii_zero)
pos := b.block_instr1(.load, loop_block, ptr_i8, alloca_pos)
pos_next := b.block_instr2(.add, loop_block, ptr_i8, pos, minus_one)
b.block_instr2(.store, loop_block, b.void_type, digit, pos_next)
b.block_instr2(.store, loop_block, b.void_type, pos_next, alloca_pos)
old_len := b.block_instr1(.load, loop_block, b.i64_type, alloca_len)
new_len := b.block_instr2(.add, loop_block, b.i64_type, old_len, one)
b.block_instr2(.store, loop_block, b.void_type, new_len, alloca_len)
quot := b.block_instr2(.sdiv, loop_block, b.i64_type, n_cur, ten)
b.block_instr2(.store, loop_block, b.void_type, quot, alloca_n)
has_more := b.block_instr2(.gt, loop_block, b.i1_type, quot, zero)
b.block_instr3(.br, loop_block, b.void_type, has_more, ValueID(loop_block), ValueID(sign_block))
neg_flag := b.block_instr1(.load, sign_block, b.i1_type, alloca_neg)
b.block_instr3(.br, sign_block, b.void_type, neg_flag, ValueID(sign_write_block),
ValueID(done_block))
pos_sign := b.block_instr1(.load, sign_write_block, ptr_i8, alloca_pos)
pos_sign_next := b.block_instr2(.add, sign_write_block, ptr_i8, pos_sign, minus_one)
b.block_instr2(.store, sign_write_block, b.void_type, ascii_minus, pos_sign_next)
b.block_instr2(.store, sign_write_block, b.void_type, pos_sign_next, alloca_pos)
len_sign := b.block_instr1(.load, sign_write_block, b.i64_type, alloca_len)
len_sign_next := b.block_instr2(.add, sign_write_block, b.i64_type, len_sign, one)
b.block_instr2(.store, sign_write_block, b.void_type, len_sign_next, alloca_len)
b.block_instr1(.jmp, sign_write_block, b.void_type, ValueID(done_block))
data := b.block_instr1(.load, done_block, ptr_i8, alloca_pos)
len := b.block_instr1(.load, done_block, b.i64_type, alloca_len)
result := b.emit_make_string(done_block, data, len)
b.block_instr1(.ret, done_block, b.void_type, result)
}
fn (mut b Builder) generate_string_int_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
entry := b.m.add_block(func_id, 'entry')
s := b.func_add_argument(func_id, b.str_type, 's')
s_slot := b.block_instr0(.alloca, entry, ptr_string)
result_slot := b.block_instr0(.alloca, entry, ptr_i64)
i_slot := b.block_instr0(.alloca, entry, ptr_i64)
sign_slot := b.block_instr0(.alloca, entry, ptr_i64)
b.block_instr2(.store, entry, b.void_type, s, s_slot)
data_ptr := b.block_struct_field_ptr(entry, s_slot, b.str_type, 0)
len_ptr := b.block_struct_field_ptr(entry, s_slot, b.str_type, 1)
data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
len := b.block_instr1(.zext, entry, b.i64_type, len32)
zero64 := b.m.get_or_add_const(b.i64_type, '0')
one64 := b.m.get_or_add_const(b.i64_type, '1')
minus_one := b.m.get_or_add_const(b.i64_type, '-1')
ten64 := b.m.get_or_add_const(b.i64_type, '10')
ascii_zero := b.m.get_or_add_const(b.i64_type, '48')
ascii_minus := b.m.get_or_add_const(b.i8_type, '45')
b.block_instr2(.store, entry, b.void_type, zero64, result_slot)
b.block_instr2(.store, entry, b.void_type, zero64, i_slot)
b.block_instr2(.store, entry, b.void_type, one64, sign_slot)
check_sign := b.m.add_block(func_id, 'string_int_check_sign')
sign_body := b.m.add_block(func_id, 'string_int_sign_body')
loop := b.m.add_block(func_id, 'string_int_loop')
body := b.m.add_block(func_id, 'string_int_body')
done := b.m.add_block(func_id, 'string_int_done')
has_chars := b.block_instr2(.gt, entry, b.i1_type, len, zero64)
b.block_instr3(.br, entry, b.void_type, has_chars, ValueID(check_sign), ValueID(done))
first_ch := b.block_instr1(.load, check_sign, b.i8_type, data)
is_minus := b.block_instr2(.eq, check_sign, b.i1_type, first_ch, ascii_minus)
b.block_instr3(.br, check_sign, b.void_type, is_minus, ValueID(sign_body), ValueID(loop))
b.block_instr2(.store, sign_body, b.void_type, minus_one, sign_slot)
b.block_instr2(.store, sign_body, b.void_type, one64, i_slot)
b.block_instr1(.jmp, sign_body, b.void_type, ValueID(loop))
i := b.block_instr1(.load, loop, b.i64_type, i_slot)
more := b.block_instr2(.lt, loop, b.i1_type, i, len)
b.block_instr3(.br, loop, b.void_type, more, ValueID(body), ValueID(done))
body_i := b.block_instr1(.load, body, b.i64_type, i_slot)
ch_ptr := b.block_instr2(.add, body, ptr_i8, data, body_i)
ch := b.block_instr1(.load, body, b.i8_type, ch_ptr)
ch64 := b.block_instr1(.zext, body, b.i64_type, ch)
digit := b.block_instr2(.sub, body, b.i64_type, ch64, ascii_zero)
cur := b.block_instr1(.load, body, b.i64_type, result_slot)
scaled := b.block_instr2(.mul, body, b.i64_type, cur, ten64)
next := b.block_instr2(.add, body, b.i64_type, scaled, digit)
b.block_instr2(.store, body, b.void_type, next, result_slot)
next_i := b.block_instr2(.add, body, b.i64_type, body_i, one64)
b.block_instr2(.store, body, b.void_type, next_i, i_slot)
b.block_instr1(.jmp, body, b.void_type, ValueID(loop))
parsed := b.block_instr1(.load, done, b.i64_type, result_slot)
sign := b.block_instr1(.load, done, b.i64_type, sign_slot)
signed := b.block_instr2(.mul, done, b.i64_type, parsed, sign)
b.block_instr1(.ret, done, b.void_type, signed)
}
fn (mut b Builder) register_bench_runtime_stubs() {
for name in bench_runtime_stub_names {
id := b.register_synthetic_function(name, b.i64_type, []TypeID{})
b.generate_const_i64_body(id, '0')
}
}
fn (mut b Builder) generate_tos2_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
entry := b.m.add_block(func_id, 'entry')
s := b.func_add_argument(func_id, ptr_i8, 's')
strlen_ref := b.m.add_value(.func_ref, b.i64_type, 'strlen', b.fn_ids['strlen'])
len := b.block_instr2(.call, entry, b.i64_type, strlen_ref, s)
result := b.emit_make_string(entry, s, len)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_tos_clone_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
entry := b.m.add_block(func_id, 'entry')
s := b.func_add_argument(func_id, ptr_i8, 's')
strlen_ref := b.m.add_value(.func_ref, b.i64_type, 'strlen', b.fn_ids['strlen'])
len := b.block_instr2(.call, entry, b.i64_type, strlen_ref, s)
one := b.m.get_or_add_const(b.i64_type, '1')
alloc_len := b.block_instr2(.add, entry, b.i64_type, len, one)
malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
out_data := b.block_instr2(.call, entry, ptr_i8, malloc_ref, alloc_len)
memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, entry, ptr_i8, memcpy_ref, out_data, s, len)
zero8 := b.m.get_or_add_const(b.i8_type, '0')
term_ptr := b.block_instr2(.add, entry, ptr_i8, out_data, len)
b.block_instr2(.store, entry, b.void_type, zero8, term_ptr)
result := b.emit_make_string(entry, out_data, len)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_vstring_with_len_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
entry := b.m.add_block(func_id, 'entry')
s := b.func_add_argument(func_id, ptr_i8, 's')
len := b.func_add_argument(func_id, b.i64_type, 'len')
result := b.emit_make_string(entry, s, len)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) register_string_plus_stubs() {
mut p2 := []TypeID{}
p2 << b.str_type
p2 << b.str_type
plus_id := b.register_synthetic_function('string__plus', b.str_type, p2)
b.generate_string_plus_body(plus_id)
mut p_many := []TypeID{}
p_many << b.i64_type
p_many << b.m.type_store.get_ptr(b.str_type)
many_id := b.register_synthetic_function('string_plus_many', b.str_type, p_many)
b.generate_string_plus_many_body(many_id)
}
fn (mut b Builder) emit_make_string(block_id BlockID, data ValueID, len64 ValueID) ValueID {
ptr_string := b.m.type_store.get_ptr(b.str_type)
alloca_out := b.block_instr0(.alloca, block_id, ptr_string)
data_ptr := b.block_struct_field_ptr(block_id, alloca_out, b.str_type, 0)
len_ptr := b.block_struct_field_ptr(block_id, alloca_out, b.str_type, 1)
b.block_instr2(.store, block_id, b.void_type, data, data_ptr)
b.block_instr2(.store, block_id, b.void_type, len64, len_ptr)
return b.block_instr1(.load, block_id, b.str_type, alloca_out)
}
fn (mut b Builder) generate_string_plus_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
entry := b.m.add_block(func_id, 'entry')
left := b.func_add_argument(func_id, b.str_type, 'left')
right := b.func_add_argument(func_id, b.str_type, 'right')
alloca_left := b.block_instr0(.alloca, entry, ptr_string)
alloca_right := b.block_instr0(.alloca, entry, ptr_string)
b.block_instr2(.store, entry, b.void_type, left, alloca_left)
b.block_instr2(.store, entry, b.void_type, right, alloca_right)
left_data_ptr := b.block_struct_field_ptr(entry, alloca_left, b.str_type, 0)
left_len_ptr := b.block_struct_field_ptr(entry, alloca_left, b.str_type, 1)
right_data_ptr := b.block_struct_field_ptr(entry, alloca_right, b.str_type, 0)
right_len_ptr := b.block_struct_field_ptr(entry, alloca_right, b.str_type, 1)
left_data := b.block_instr1(.load, entry, ptr_i8, left_data_ptr)
right_data := b.block_instr1(.load, entry, ptr_i8, right_data_ptr)
left_len32 := b.block_instr1(.load, entry, b.i32_type, left_len_ptr)
right_len32 := b.block_instr1(.load, entry, b.i32_type, right_len_ptr)
left_len := b.block_instr1(.zext, entry, b.i64_type, left_len32)
right_len := b.block_instr1(.zext, entry, b.i64_type, right_len32)
total_len := b.block_instr2(.add, entry, b.i64_type, left_len, right_len)
one := b.m.get_or_add_const(b.i64_type, '1')
alloc_len := b.block_instr2(.add, entry, b.i64_type, total_len, one)
malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
out_data := b.block_instr2(.call, entry, ptr_i8, malloc_ref, alloc_len)
memcpy_ref_left := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, entry, ptr_i8, memcpy_ref_left, out_data, left_data, left_len)
right_dest := b.block_instr2(.add, entry, ptr_i8, out_data, left_len)
memcpy_ref_right := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, entry, ptr_i8, memcpy_ref_right, right_dest, right_data, right_len)
zero8 := b.m.get_or_add_const(b.i8_type, '0')
term_ptr := b.block_instr2(.add, entry, ptr_i8, out_data, total_len)
b.block_instr2(.store, entry, b.void_type, zero8, term_ptr)
result := b.emit_make_string(entry, out_data, total_len)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_string_plus_many_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
entry := b.m.add_block(func_id, 'entry')
count := b.func_add_argument(func_id, b.i64_type, 'count')
input_base := b.func_add_argument(func_id, ptr_string, 'input_base')
alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
alloca_total := b.block_instr0(.alloca, entry, ptr_i64)
alloca_written := b.block_instr0(.alloca, entry, ptr_i64)
zero64 := b.m.get_or_add_const(b.i64_type, '0')
one64 := b.m.get_or_add_const(b.i64_type, '1')
stride := b.m.get_or_add_const(b.i64_type, '16')
b.block_instr2(.store, entry, b.void_type, zero64, alloca_i)
b.block_instr2(.store, entry, b.void_type, zero64, alloca_total)
sum_check := b.m.add_block(func_id, 'string_plus_many_sum_check')
sum_body := b.m.add_block(func_id, 'string_plus_many_sum_body')
alloc_block := b.m.add_block(func_id, 'string_plus_many_alloc')
b.block_instr1(.jmp, entry, b.void_type, ValueID(sum_check))
i_sum := b.block_instr1(.load, sum_check, b.i64_type, alloca_i)
more_sum := b.block_instr2(.lt, sum_check, b.i1_type, i_sum, count)
b.block_instr3(.br, sum_check, b.void_type, more_sum, ValueID(sum_body), ValueID(alloc_block))
sum_item_off := b.block_instr2(.mul, sum_body, b.i64_type, i_sum, stride)
sum_item_ptr := b.block_instr2(.get_element_ptr, sum_body, ptr_string, input_base, sum_item_off)
sum_len_ptr := b.block_struct_field_ptr(sum_body, sum_item_ptr, b.str_type, 1)
sum_len32 := b.block_instr1(.load, sum_body, b.i32_type, sum_len_ptr)
sum_len := b.block_instr1(.zext, sum_body, b.i64_type, sum_len32)
old_total := b.block_instr1(.load, sum_body, b.i64_type, alloca_total)
new_total := b.block_instr2(.add, sum_body, b.i64_type, old_total, sum_len)
next_i_sum := b.block_instr2(.add, sum_body, b.i64_type, i_sum, one64)
b.block_instr2(.store, sum_body, b.void_type, new_total, alloca_total)
b.block_instr2(.store, sum_body, b.void_type, next_i_sum, alloca_i)
b.block_instr1(.jmp, sum_body, b.void_type, ValueID(sum_check))
total_len := b.block_instr1(.load, alloc_block, b.i64_type, alloca_total)
alloc_len := b.block_instr2(.add, alloc_block, b.i64_type, total_len, one64)
malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
out_data := b.block_instr2(.call, alloc_block, ptr_i8, malloc_ref, alloc_len)
b.block_instr2(.store, alloc_block, b.void_type, zero64, alloca_i)
b.block_instr2(.store, alloc_block, b.void_type, zero64, alloca_written)
copy_check := b.m.add_block(func_id, 'string_plus_many_copy_check')
copy_body := b.m.add_block(func_id, 'string_plus_many_copy_body')
done := b.m.add_block(func_id, 'string_plus_many_done')
b.block_instr1(.jmp, alloc_block, b.void_type, ValueID(copy_check))
i_copy := b.block_instr1(.load, copy_check, b.i64_type, alloca_i)
more_copy := b.block_instr2(.lt, copy_check, b.i1_type, i_copy, count)
b.block_instr3(.br, copy_check, b.void_type, more_copy, ValueID(copy_body), ValueID(done))
copy_item_off := b.block_instr2(.mul, copy_body, b.i64_type, i_copy, stride)
copy_item_ptr := b.block_instr2(.get_element_ptr, copy_body, ptr_string, input_base,
copy_item_off)
copy_data_ptr := b.block_struct_field_ptr(copy_body, copy_item_ptr, b.str_type, 0)
copy_len_ptr := b.block_struct_field_ptr(copy_body, copy_item_ptr, b.str_type, 1)
copy_data := b.block_instr1(.load, copy_body, ptr_i8, copy_data_ptr)
copy_len32 := b.block_instr1(.load, copy_body, b.i32_type, copy_len_ptr)
copy_len := b.block_instr1(.zext, copy_body, b.i64_type, copy_len32)
written := b.block_instr1(.load, copy_body, b.i64_type, alloca_written)
dest := b.block_instr2(.add, copy_body, ptr_i8, out_data, written)
memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, copy_body, ptr_i8, memcpy_ref, dest, copy_data, copy_len)
new_written := b.block_instr2(.add, copy_body, b.i64_type, written, copy_len)
next_i_copy := b.block_instr2(.add, copy_body, b.i64_type, i_copy, one64)
b.block_instr2(.store, copy_body, b.void_type, new_written, alloca_written)
b.block_instr2(.store, copy_body, b.void_type, next_i_copy, alloca_i)
b.block_instr1(.jmp, copy_body, b.void_type, ValueID(copy_check))
zero8 := b.m.get_or_add_const(b.i8_type, '0')
term_ptr := b.block_instr2(.add, done, ptr_i8, out_data, total_len)
b.block_instr2(.store, done, b.void_type, zero8, term_ptr)
result := b.emit_make_string(done, out_data, total_len)
b.block_instr1(.ret, done, b.void_type, result)
}
fn (mut b Builder) register_array_runtime_stubs() {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
mut p3 := []TypeID{}
p3 << b.i64_type
p3 << b.i64_type
p3 << b.i64_type
array_new_id := b.register_synthetic_function('array_new', b.array_type, p3)
b.generate_array_new_body(array_new_id)
mut p2 := []TypeID{}
p2 << b.array_type
p2 << b.i64_type
array_get_id := b.register_synthetic_function('array_get', ptr_i8, p2)
b.generate_array_get_body(array_get_id)
p3 = []TypeID{}
p3 << b.array_type
p3 << b.i64_type
p3 << b.i64_type
array_slice_id := b.register_synthetic_function('array_slice', b.array_type, p3)
b.generate_array_slice_body(array_slice_id)
p2 = []TypeID{}
p2 << ptr_array
p2 << ptr_i8
array_push_id := b.register_synthetic_function('array_push', b.void_type, p2)
b.generate_array_push_body(array_push_id)
mut p3_push_many := []TypeID{}
p3_push_many << ptr_array
p3_push_many << ptr_i8
p3_push_many << b.i64_type
array_push_many_id := b.register_synthetic_function('array.push_many', b.void_type,
p3_push_many)
b.generate_array_push_many_body(array_push_many_id)
mut p2_push_many := []TypeID{}
p2_push_many << ptr_array
p2_push_many << b.array_type
array_push_many_wrapper_id := b.register_synthetic_function('array_push_many', b.void_type,
p2_push_many)
b.generate_array_push_many_array_body(array_push_many_wrapper_id)
mut p1 := []TypeID{}
p1 << b.array_type
array_clone_id := b.register_synthetic_function('array_clone', b.array_type, p1)
b.generate_array_clone_body(array_clone_id)
mut p3_repeat := []TypeID{}
p3_repeat << b.array_type
p3_repeat << b.i64_type
p3_repeat << b.i64_type
array_repeat_id := b.register_synthetic_function('array.repeat_to_depth', b.array_type,
p3_repeat)
b.generate_array_repeat_to_depth_body(array_repeat_id)
}
fn (mut b Builder) register_panic_stub() {
mut p1 := []TypeID{}
p1 << b.str_type
panic_id := b.register_synthetic_function('panic', b.void_type, p1)
b.generate_panic_body(panic_id)
}
fn (mut b Builder) register_printing_stubs() {
mut p1 := []TypeID{}
p1 << b.str_type
print_id := b.register_synthetic_function('print', b.void_type, p1)
b.generate_print_body(print_id, '1', false)
println_id := b.register_synthetic_function('println', b.void_type, p1)
b.generate_print_body(println_id, '1', true)
eprint_id := b.register_synthetic_function('eprint', b.void_type, p1)
b.generate_print_body(eprint_id, '2', false)
eprintln_id := b.register_synthetic_function('eprintln', b.void_type, p1)
b.generate_print_body(eprintln_id, '2', true)
}
fn (mut b Builder) generate_print_body(func_id int, fd_value string, newline bool) {
ptr_string := b.m.type_store.get_ptr(b.str_type)
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
entry := b.m.add_block(func_id, 'entry')
message := b.func_add_argument(func_id, b.str_type, 'message')
message_slot := b.block_instr0(.alloca, entry, ptr_string)
b.block_instr2(.store, entry, b.void_type, message, message_slot)
data_ptr := b.block_struct_field_ptr(entry, message_slot, b.str_type, 0)
len_ptr := b.block_struct_field_ptr(entry, message_slot, b.str_type, 1)
data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
len := b.block_instr1(.zext, entry, b.i64_type, len32)
fd := b.m.get_or_add_const(b.i64_type, fd_value)
write_ref := b.m.add_value(.func_ref, b.void_type, 'write', b.fn_ids['write'])
b.block_instr4(.call, entry, b.i64_type, write_ref, fd, data, len)
if newline {
newline_str := b.m.add_value(.string_literal, b.str_type, '\n', 0)
newline_slot := b.block_instr0(.alloca, entry, ptr_string)
b.block_instr2(.store, entry, b.void_type, newline_str, newline_slot)
newline_data_ptr := b.block_struct_field_ptr(entry, newline_slot, b.str_type, 0)
newline_data := b.block_instr1(.load, entry, ptr_i8, newline_data_ptr)
one := b.m.get_or_add_const(b.i64_type, '1')
b.block_instr4(.call, entry, b.i64_type, write_ref, fd, newline_data, one)
}
b.block_instr0(.ret, entry, b.void_type)
}
fn (mut b Builder) generate_panic_body(func_id int) {
entry := b.m.add_block(func_id, 'entry')
message := b.func_add_argument(func_id, b.str_type, 'message')
if eprintln_id := b.fn_ids['eprintln'] {
fn_ref := b.m.add_value(.func_ref, b.void_type, 'eprintln', eprintln_id)
b.block_instr2(.call, entry, b.void_type, fn_ref, message)
}
if exit_id := b.fn_ids['exit'] {
one := b.m.get_or_add_const(b.i64_type, '1')
fn_ref := b.m.add_value(.func_ref, b.void_type, 'exit', exit_id)
b.block_instr2(.call, entry, b.void_type, fn_ref, one)
}
b.block_instr0(.ret, entry, b.void_type)
}
fn (mut b Builder) register_string_builder_stubs() {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_builder := b.m.type_store.get_ptr(b.array_type)
mut p1 := []TypeID{}
p1 << b.i64_type
new_id := b.register_synthetic_function('strings.new_builder', b.array_type, p1)
b.generate_builder_new_body(new_id)
mut p2 := []TypeID{}
p2 << ptr_builder
p2 << b.str_type
write_string_id :=
b.register_synthetic_function('strings.Builder.write_string', b.void_type, p2)
b.generate_builder_write_string_body(write_string_id, false)
writeln_id := b.register_synthetic_function('strings.Builder.writeln', b.void_type, p2)
b.generate_builder_write_string_body(writeln_id, true)
p1 = []TypeID{}
p1 << ptr_builder
str_id := b.register_synthetic_function('strings.Builder.str', b.str_type, p1)
b.generate_builder_str_body(str_id)
free_id := b.register_synthetic_function('strings.Builder.free', b.void_type, p1)
b.generate_builder_free_body(free_id)
p2 = []TypeID{}
p2 << ptr_builder
p2 << b.i8_type
write_u8_id := b.register_synthetic_function('strings.Builder.write_u8', b.void_type, p2)
b.generate_builder_write_u8_body(write_u8_id)
mut p3 := []TypeID{}
p3 << ptr_builder
p3 << ptr_i8
p3 << b.i64_type
write_ptr_id := b.register_synthetic_function('strings.Builder.write_ptr', b.void_type, p3)
b.generate_builder_write_ptr_body(write_ptr_id)
push_many_id := b.register_synthetic_function('strings.Builder.push_many', b.void_type, p3)
b.generate_builder_write_ptr_body(push_many_id)
p2 = []TypeID{}
p2 << ptr_builder
p2 << b.array_type
write_runes_id := b.register_synthetic_function('strings.Builder.write_runes', b.void_type, p2)
b.generate_builder_free_body(write_runes_id)
p2 = []TypeID{}
p2 << ptr_builder
p2 << b.i64_type
last_n_id := b.register_synthetic_function('strings.Builder.last_n', b.str_type, p2)
b.generate_builder_last_n_body(last_n_id)
}
fn (mut b Builder) generate_builder_new_body(func_id int) {
entry := b.m.add_block(func_id, 'entry')
initial_size := b.func_add_argument(func_id, b.i64_type, 'initial_size')
one := b.m.get_or_add_const(b.i64_type, '1')
zero := b.m.get_or_add_const(b.i64_type, '0')
fn_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', b.fn_ids['array_new'])
builder := b.block_instr4(.call, entry, b.array_type, fn_ref, one, zero, initial_size)
b.block_instr1(.ret, entry, b.void_type, builder)
}
fn (mut b Builder) emit_builder_append(func_id int, entry BlockID, builder_ptr ValueID, src_ptr ValueID, add_len ValueID) BlockID {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
data_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 2)
cap_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 3)
old_len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
old_len := b.block_instr1(.zext, entry, b.i64_type, old_len32)
cap32 := b.block_instr1(.load, entry, b.i32_type, cap_ptr)
cap := b.block_instr1(.zext, entry, b.i64_type, cap32)
needed := b.block_instr2(.add, entry, b.i64_type, old_len, add_len)
needs_grow := b.block_instr2(.gt, entry, b.i1_type, needed, cap)
blk_grow := b.m.add_block(func_id, 'builder_append_grow')
blk_copy := b.m.add_block(func_id, 'builder_append_copy')
b.block_instr3(.br, entry, b.void_type, needs_grow, ValueID(blk_grow), ValueID(blk_copy))
two := b.m.get_or_add_const(b.i64_type, '2')
old_data := b.block_instr1(.load, blk_grow, ptr_i8, data_ptr)
double_needed := b.block_instr2(.mul, blk_grow, b.i64_type, needed, two)
new_cap := b.block_instr2(.add, blk_grow, b.i64_type, double_needed, two)
realloc_ref := b.m.add_value(.func_ref, b.void_type, 'realloc', b.fn_ids['realloc'])
new_data := b.block_instr3(.call, blk_grow, ptr_i8, realloc_ref, old_data, new_cap)
b.block_instr2(.store, blk_grow, b.void_type, new_data, data_ptr)
b.block_instr2(.store, blk_grow, b.void_type, new_cap, cap_ptr)
b.block_instr1(.jmp, blk_grow, b.void_type, ValueID(blk_copy))
data := b.block_instr1(.load, blk_copy, ptr_i8, data_ptr)
dest := b.block_instr2(.add, blk_copy, ptr_i8, data, old_len)
memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, blk_copy, ptr_i8, memcpy_ref, dest, src_ptr, add_len)
b.block_instr2(.store, blk_copy, b.void_type, needed, len_ptr)
return blk_copy
}
fn (mut b Builder) generate_builder_write_string_body(func_id int, add_newline bool) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_builder := b.m.type_store.get_ptr(b.array_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
ptr_i32 := b.m.type_store.get_ptr(b.i32_type)
entry := b.m.add_block(func_id, 'entry')
builder_ptr := b.func_add_argument(func_id, ptr_builder, 'builder')
s := b.func_add_argument(func_id, b.str_type, 's')
alloca_s := b.block_instr0(.alloca, entry, ptr_string)
b.block_instr2(.store, entry, b.void_type, s, alloca_s)
zero := b.m.get_or_add_const(b.i64_type, '0')
len_off := b.m.get_or_add_const(b.i64_type, '8')
str_ptr_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i8, alloca_s, zero)
len_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i32, alloca_s, len_off)
str_ptr := b.block_instr1(.load, entry, ptr_i8, str_ptr_ptr)
len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
len64 := b.block_instr1(.zext, entry, b.i64_type, len32)
end_block := b.emit_builder_append(func_id, entry, builder_ptr, str_ptr, len64)
if add_newline {
nl := b.m.get_or_add_const(b.i8_type, '10')
alloca_nl := b.block_instr0(.alloca, end_block, ptr_i8)
b.block_instr2(.store, end_block, b.void_type, nl, alloca_nl)
one := b.m.get_or_add_const(b.i64_type, '1')
final_block := b.emit_builder_append(func_id, end_block, builder_ptr, alloca_nl, one)
b.block_instr0(.ret, final_block, b.void_type)
} else {
b.block_instr0(.ret, end_block, b.void_type)
}
}
fn (mut b Builder) generate_builder_write_ptr_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_builder := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
builder_ptr := b.func_add_argument(func_id, ptr_builder, 'builder')
src_ptr := b.func_add_argument(func_id, ptr_i8, 'ptr')
len := b.func_add_argument(func_id, b.i64_type, 'len')
end_block := b.emit_builder_append(func_id, entry, builder_ptr, src_ptr, len)
b.block_instr0(.ret, end_block, b.void_type)
}
fn (mut b Builder) generate_builder_write_u8_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_builder := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
builder_ptr := b.func_add_argument(func_id, ptr_builder, 'builder')
ch := b.func_add_argument(func_id, b.i8_type, 'ch')
alloca_ch := b.block_instr0(.alloca, entry, ptr_i8)
b.block_instr2(.store, entry, b.void_type, ch, alloca_ch)
one := b.m.get_or_add_const(b.i64_type, '1')
end_block := b.emit_builder_append(func_id, entry, builder_ptr, alloca_ch, one)
b.block_instr0(.ret, end_block, b.void_type)
}
fn (mut b Builder) generate_builder_str_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_builder := b.m.type_store.get_ptr(b.array_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
entry := b.m.add_block(func_id, 'entry')
builder_ptr := b.func_add_argument(func_id, ptr_builder, 'builder')
alloca_s := b.block_instr0(.alloca, entry, ptr_string)
data_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 2)
data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
len := b.block_instr1(.zext, entry, b.i64_type, len32)
out_data_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 0)
out_len_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 1)
b.block_instr2(.store, entry, b.void_type, data, out_data_ptr)
b.block_instr2(.store, entry, b.void_type, len, out_len_ptr)
result := b.block_instr1(.load, entry, b.str_type, alloca_s)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_builder_last_n_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_builder := b.m.type_store.get_ptr(b.array_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
entry := b.m.add_block(func_id, 'entry')
builder_ptr := b.func_add_argument(func_id, ptr_builder, 'builder')
n := b.func_add_argument(func_id, b.i64_type, 'n')
alloca_s := b.block_instr0(.alloca, entry, ptr_string)
data_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(entry, builder_ptr, b.array_type, 2)
data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
len := b.block_instr1(.zext, entry, b.i64_type, len32)
start := b.block_instr2(.sub, entry, b.i64_type, len, n)
out_data := b.block_instr2(.add, entry, ptr_i8, data, start)
out_data_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 0)
out_len_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 1)
b.block_instr2(.store, entry, b.void_type, out_data, out_data_ptr)
b.block_instr2(.store, entry, b.void_type, n, out_len_ptr)
result := b.block_instr1(.load, entry, b.str_type, alloca_s)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_builder_free_body(func_id int) {
ptr_builder := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
_ := b.func_add_argument(func_id, ptr_builder, 'builder')
b.block_instr0(.ret, entry, b.void_type)
}
fn (mut b Builder) register_path_runtime_stubs() {
ptr_builder := b.m.type_store.get_ptr(b.array_type)
mut p1 := []TypeID{}
p1 << ptr_builder
for name in ['normalize_path_in_builder', 'os.normalize_path_in_builder'] {
normalize_id := b.register_synthetic_function(name, b.void_type, p1)
b.generate_builder_free_body(normalize_id)
}
mut p2 := []TypeID{}
p2 << b.str_type
p2 << b.str_type
for name in ['join_path_single', 'os.join_path_single'] {
join_id := b.register_synthetic_function(name, b.str_type, p2)
b.generate_join_path_single_body(join_id)
}
}
fn (mut b Builder) generate_join_path_single_body(func_id int) {
entry := b.m.add_block(func_id, 'entry')
base := b.func_add_argument(func_id, b.str_type, 'base')
elem := b.func_add_argument(func_id, b.str_type, 'elem')
slash := b.m.add_value(.string_literal, b.str_type, '/', 0)
plus_ref := b.m.add_value(.func_ref, b.str_type, 'string__plus', b.fn_ids['string__plus'])
with_sep := b.block_instr3(.call, entry, b.str_type, plus_ref, base, slash)
result := b.block_instr3(.call, entry, b.str_type, plus_ref, with_sep, elem)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) register_map_runtime_stubs() {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_map := b.m.type_store.get_ptr(b.map_type)
mut p2 := []TypeID{}
p2 << ptr_map
p2 << ptr_i8
find_id := b.register_synthetic_function('v3_map_find', b.i64_type, p2)
b.generate_map_find_body(find_id)
mut p6 := []TypeID{}
for _ in 0 .. 6 {
p6 << b.i64_type
}
new_id := b.register_synthetic_function('new_map', b.map_type, p6)
b.generate_new_map_body(new_id)
mut p3 := []TypeID{}
p3 << ptr_map
p3 << ptr_i8
p3 << ptr_i8
mut p5 := []TypeID{}
p5 << ptr_map
p5 << ptr_i8
p5 << ptr_i8
p5 << b.i64_type
p5 << b.i64_type
sized_set_id := b.register_synthetic_function('v3_map_set_sized', b.void_type, p5)
b.generate_map_set_sized_body(sized_set_id)
set_id := b.register_synthetic_function('map__set', b.void_type, p3)
b.generate_map_set_default_body(set_id)
p3 = []TypeID{}
p3 << ptr_map
p3 << ptr_i8
p3 << ptr_i8
get_id := b.register_synthetic_function('map__get', ptr_i8, p3)
b.generate_map_get_body(get_id)
mut p2_map_key := []TypeID{}
p2_map_key << ptr_map
p2_map_key << ptr_i8
get_check_id := b.register_synthetic_function('map__get_check', ptr_i8, p2_map_key)
b.generate_map_get_check_body(get_check_id)
get_or_set_id := b.register_synthetic_function('map__get_or_set', ptr_i8, p3)
b.generate_map_get_body(get_or_set_id)
p2 = []TypeID{}
p2 << ptr_map
p2 << ptr_i8
exists_id := b.register_synthetic_function('map__exists', b.i1_type, p2)
b.generate_map_exists_body(exists_id)
mut p1_ptr := []TypeID{}
p1_ptr << ptr_map
clear_id := b.register_synthetic_function('map__clear', b.void_type, p1_ptr)
b.generate_map_ptr_noop_body(clear_id)
p2 = []TypeID{}
p2 << ptr_map
p2 << ptr_i8
delete_id := b.register_synthetic_function('map__delete', b.void_type, p2)
b.generate_map_delete_body(delete_id)
mut p1_map := []TypeID{}
p1_map << b.map_type
clone_id := b.register_synthetic_function('map__clone', b.map_type, p1_map)
b.generate_map_clone_body(clone_id)
}
fn (mut b Builder) generate_map_ptr_noop_body(func_id int) {
ptr_map := b.m.type_store.get_ptr(b.map_type)
entry := b.m.add_block(func_id, 'entry')
_ := b.func_add_argument(func_id, ptr_map, 'm')
b.block_instr0(.ret, entry, b.void_type)
}
fn (mut b Builder) generate_map_delete_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_map := b.m.type_store.get_ptr(b.map_type)
entry := b.m.add_block(func_id, 'entry')
map_ptr := b.func_add_argument(func_id, ptr_map, 'm')
key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
find_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_find', b.fn_ids['v3_map_find'])
idx := b.block_instr3(.call, entry, b.i64_type, find_ref, map_ptr, key_ptr)
zero := b.m.get_or_add_const(b.i64_type, '0')
found := b.block_instr2(.ge, entry, b.i1_type, idx, zero)
blk_found := b.m.add_block(func_id, 'map_delete_found')
blk_done := b.m.add_block(func_id, 'map_delete_done')
b.block_instr3(.br, entry, b.void_type, found, ValueID(blk_found), ValueID(blk_done))
state := b.map_state_ptr(blk_found, map_ptr)
len_ptr := b.map_state_field_ptr(blk_found, state, 3)
len := b.block_instr1(.load, blk_found, b.i64_type, len_ptr)
one := b.m.get_or_add_const(b.i64_type, '1')
new_len := b.block_instr2(.sub, blk_found, b.i64_type, len, one)
b.block_instr2(.store, blk_found, b.void_type, new_len, len_ptr)
b.block_instr1(.jmp, blk_found, b.void_type, ValueID(blk_done))
b.block_instr0(.ret, blk_done, b.void_type)
}
fn (mut b Builder) generate_map_clone_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_map := b.m.type_store.get_ptr(b.map_type)
ptr_state := b.m.type_store.get_ptr(b.map_state_type)
entry := b.m.add_block(func_id, 'entry')
m := b.func_add_argument(func_id, b.map_type, 'm')
map_slot := b.block_instr0(.alloca, entry, ptr_map)
b.block_instr2(.store, entry, b.void_type, m, map_slot)
state_field := b.block_struct_field_ptr(entry, map_slot, b.map_type, 0)
old_state := b.block_instr1(.load, entry, ptr_state, state_field)
zero_state := b.m.get_or_add_const(ptr_state, '0')
has_state := b.block_instr2(.ne, entry, b.i1_type, old_state, zero_state)
blk_clone := b.m.add_block(func_id, 'map_clone_copy')
blk_empty := b.m.add_block(func_id, 'map_clone_empty')
b.block_instr3(.br, entry, b.void_type, has_state, ValueID(blk_clone), ValueID(blk_empty))
one := b.m.get_or_add_const(b.i64_type, '1')
state_size := b.m.get_or_add_const(b.i64_type, '48')
calloc_ref := b.m.add_value(.func_ref, b.void_type, 'calloc', b.fn_ids['calloc'])
new_state_raw := b.block_instr3(.call, blk_clone, ptr_i8, calloc_ref, one, state_size)
new_state := b.block_instr1(.bitcast, blk_clone, ptr_state, new_state_raw)
old_keys_ptr := b.map_state_field_ptr(blk_clone, old_state, 0)
old_vals_ptr := b.map_state_field_ptr(blk_clone, old_state, 1)
old_cap_ptr := b.map_state_field_ptr(blk_clone, old_state, 2)
old_len_ptr := b.map_state_field_ptr(blk_clone, old_state, 3)
old_key_size_ptr := b.map_state_field_ptr(blk_clone, old_state, 4)
old_val_size_ptr := b.map_state_field_ptr(blk_clone, old_state, 5)
old_keys := b.block_instr1(.load, blk_clone, ptr_i8, old_keys_ptr)
old_vals := b.block_instr1(.load, blk_clone, ptr_i8, old_vals_ptr)
cap := b.block_instr1(.load, blk_clone, b.i64_type, old_cap_ptr)
len := b.block_instr1(.load, blk_clone, b.i64_type, old_len_ptr)
key_size := b.block_instr1(.load, blk_clone, b.i64_type, old_key_size_ptr)
val_size := b.block_instr1(.load, blk_clone, b.i64_type, old_val_size_ptr)
new_keys := b.block_instr3(.call, blk_clone, ptr_i8, calloc_ref, cap, key_size)
new_vals := b.block_instr3(.call, blk_clone, ptr_i8, calloc_ref, cap, val_size)
key_bytes := b.block_instr2(.mul, blk_clone, b.i64_type, len, key_size)
val_bytes := b.block_instr2(.mul, blk_clone, b.i64_type, len, val_size)
memcpy_ref_keys := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
memcpy_ref_vals := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, blk_clone, ptr_i8, memcpy_ref_keys, new_keys, old_keys, key_bytes)
b.block_instr4(.call, blk_clone, ptr_i8, memcpy_ref_vals, new_vals, old_vals, val_bytes)
new_keys_ptr := b.map_state_field_ptr(blk_clone, new_state, 0)
new_vals_ptr := b.map_state_field_ptr(blk_clone, new_state, 1)
new_cap_ptr := b.map_state_field_ptr(blk_clone, new_state, 2)
new_len_ptr := b.map_state_field_ptr(blk_clone, new_state, 3)
new_key_size_ptr := b.map_state_field_ptr(blk_clone, new_state, 4)
new_val_size_ptr := b.map_state_field_ptr(blk_clone, new_state, 5)
b.block_instr2(.store, blk_clone, b.void_type, new_keys, new_keys_ptr)
b.block_instr2(.store, blk_clone, b.void_type, new_vals, new_vals_ptr)
b.block_instr2(.store, blk_clone, b.void_type, cap, new_cap_ptr)
b.block_instr2(.store, blk_clone, b.void_type, len, new_len_ptr)
b.block_instr2(.store, blk_clone, b.void_type, key_size, new_key_size_ptr)
b.block_instr2(.store, blk_clone, b.void_type, val_size, new_val_size_ptr)
result_slot := b.block_instr0(.alloca, blk_clone, ptr_map)
result_state_field := b.block_struct_field_ptr(blk_clone, result_slot, b.map_type, 0)
b.block_instr2(.store, blk_clone, b.void_type, new_state, result_state_field)
result := b.block_instr1(.load, blk_clone, b.map_type, result_slot)
b.block_instr1(.ret, blk_clone, b.void_type, result)
b.block_instr1(.ret, blk_empty, b.void_type, m)
}
fn (mut b Builder) register_u8_runtime_stubs() {
mut p1 := []TypeID{}
p1 << b.i8_type
for name in ['u8.is_digit', 'u8.is_letter', 'u8.is_alnum', 'u8.is_capital'] {
func_id := b.register_synthetic_function(name, b.i1_type, p1)
b.generate_u8_predicate_body(func_id, name)
}
}
fn (mut b Builder) generate_u8_predicate_body(func_id int, name string) {
entry := b.m.add_block(func_id, 'entry')
c := b.func_add_argument(func_id, b.i8_type, 'c')
digit := b.u8_in_range(entry, c, `0`, `9`)
lower := b.u8_in_range(entry, c, `a`, `z`)
upper := b.u8_in_range(entry, c, `A`, `Z`)
letter := b.block_instr2(.or_, entry, b.i1_type, lower, upper)
result := match name {
'u8.is_digit' {
digit
}
'u8.is_letter' {
letter
}
'u8.is_alnum' {
b.block_instr2(.or_, entry, b.i1_type, digit, letter)
}
else {
upper
}
}
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) u8_in_range(block_id BlockID, c ValueID, low u8, high u8) ValueID {
low_v := b.m.get_or_add_const(b.i8_type, '${int(low)}')
high_v := b.m.get_or_add_const(b.i8_type, '${int(high)}')
ge_low := b.block_instr2(.ge, block_id, b.i1_type, c, low_v)
le_high := b.block_instr2(.le, block_id, b.i1_type, c, high_v)
return b.block_instr2(.and_, block_id, b.i1_type, ge_low, le_high)
}
fn (mut b Builder) register_heap_tracking_stubs() {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
mut p2 := []TypeID{}
p2 << ptr_i8
p2 << b.i64_type
alloc_id := b.register_synthetic_function('_ht_alloc', b.void_type, p2)
b.generate_void_noop_body(alloc_id)
mut p1 := []TypeID{}
p1 << ptr_i8
free_id := b.register_synthetic_function('_ht_free', b.void_type, p1)
b.generate_void_noop_body(free_id)
}
fn (mut b Builder) generate_void_noop_body(func_id int) {
entry := b.m.add_block(func_id, 'entry')
b.block_instr0(.ret, entry, b.void_type)
}
fn (mut b Builder) register_process_capture_stubs() {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
mut p3 := []TypeID{}
p3 << ptr_i8
p3 << ptr_i8
p3 << ptr_i8
for name in ['v_os_execute_capture_start', 'v_os_exec_capture_start'] {
func_id := b.register_synthetic_function(name, b.i64_type, p3)
b.generate_const_i64_body(func_id, '1')
}
}
fn (mut b Builder) register_file_check_stubs() {
mut p1 := []TypeID{}
p1 << b.i64_type
for name in ['check_fwrite', 'os.check_fwrite', 'check_fread', 'os.check_fread'] {
func_id := b.register_synthetic_function(name, b.i64_type, p1)
b.generate_identity_i64_body(func_id)
}
}
fn (mut b Builder) generate_identity_i64_body(func_id int) {
entry := b.m.add_block(func_id, 'entry')
n := b.func_add_argument(func_id, b.i64_type, 'n')
b.block_instr1(.ret, entry, b.void_type, n)
}
fn (mut b Builder) register_fd_macro_stubs() {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
mut fd_set_params := []TypeID{}
fd_set_params << ptr_i8
zero_id := b.register_synthetic_function('FD_ZERO', b.void_type, fd_set_params)
b.generate_void_noop_with_params_body(zero_id, fd_set_params)
mut fd_check_params := []TypeID{}
fd_check_params << b.i64_type
fd_check_params << ptr_i8
set_id := b.register_synthetic_function('FD_SET', b.void_type, fd_check_params)
b.generate_void_noop_with_params_body(set_id, fd_check_params)
isset_id := b.register_synthetic_function('FD_ISSET', b.i64_type, fd_check_params)
b.generate_const_i64_with_params_body(isset_id, fd_check_params, '0')
}
fn (mut b Builder) register_signal_macro_stubs() {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
mut params := []TypeID{}
params << b.i64_type
params << ptr_i8
name := 'v_signal_with_handler_cast'
func_id := b.register_synthetic_function(name, ptr_i8, params)
if fn_type := b.fn_types[name] {
b.c_fn_types[name] = fn_type
}
b.c_fn_ids[name] = func_id
b.generate_const_ptr_with_params_body(func_id, params, ptr_i8, '0')
}
fn (mut b Builder) generate_void_noop_with_params_body(func_id int, params []TypeID) {
entry := b.m.add_block(func_id, 'entry')
for i, param in params {
_ := b.func_add_argument(func_id, param, 'p${i}')
}
b.block_instr0(.ret, entry, b.void_type)
}
fn (mut b Builder) generate_const_i64_with_params_body(func_id int, params []TypeID, value string) {
entry := b.m.add_block(func_id, 'entry')
for i, param in params {
_ := b.func_add_argument(func_id, param, 'p${i}')
}
result := b.m.get_or_add_const(b.i64_type, value)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_const_ptr_with_params_body(func_id int, params []TypeID, ret_type TypeID, value string) {
entry := b.m.add_block(func_id, 'entry')
for i, param in params {
_ := b.func_add_argument(func_id, param, 'p${i}')
}
result := b.m.get_or_add_const(ret_type, value)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) register_os_stat_stubs() {
mut p1 := []TypeID{}
p1 << b.str_type
for name in ['is_dir', 'os.is_dir'] {
is_dir_id := b.register_synthetic_function(name, b.i1_type, p1)
b.generate_os_stat_kind_body(is_dir_id, 'stat', '16384')
}
for name in ['is_link', 'os.is_link'] {
is_link_id := b.register_synthetic_function(name, b.i1_type, p1)
b.generate_os_stat_kind_body(is_link_id, 'lstat', '40960')
}
ls_result_type := b.option_type_id('[]string')
for name in ['ls', 'os.ls'] {
ls_id := b.register_synthetic_function(name, ls_result_type, p1)
b.generate_os_ls_body(ls_id, ls_result_type)
}
}
fn (mut b Builder) generate_os_stat_kind_body(func_id int, stat_fn string, expected_mode string) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i32 := b.m.type_store.get_ptr(b.i32_type)
entry := b.m.add_block(func_id, 'entry')
path := b.func_add_argument(func_id, b.str_type, 'path')
path_data := b.emit_cstring_from_string(entry, path)
stat_size := b.m.get_or_add_const(b.i64_type, '256')
malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
stat_buf := b.block_instr2(.call, entry, ptr_i8, malloc_ref, stat_size)
stat_idx := b.c_fn_ids[stat_fn] or {
false_val := b.m.get_or_add_const(b.i1_type, '0')
b.block_instr1(.ret, entry, b.void_type, false_val)
return
}
stat_ref := b.m.add_value(.func_ref, b.void_type, stat_fn, stat_idx)
res := b.block_instr3(.call, entry, b.i64_type, stat_ref, path_data, stat_buf)
zero64 := b.m.get_or_add_const(b.i64_type, '0')
ok := b.block_instr2(.eq, entry, b.i1_type, res, zero64)
ok_block := b.m.add_block(func_id, 'stat_ok')
fail_block := b.m.add_block(func_id, 'stat_fail')
b.block_instr3(.br, entry, b.void_type, ok, ValueID(ok_block), ValueID(fail_block))
false_val := b.m.get_or_add_const(b.i1_type, '0')
b.block_instr1(.ret, fail_block, b.void_type, false_val)
mode_off := b.m.get_or_add_const(b.i64_type, '4')
mode_raw_ptr := b.block_instr2(.get_element_ptr, ok_block, ptr_i8, stat_buf, mode_off)
mode_ptr := b.block_instr1(.bitcast, ok_block, ptr_i32, mode_raw_ptr)
mode := b.block_instr1(.load, ok_block, b.i32_type, mode_ptr)
mask := b.m.get_or_add_const(b.i32_type, '61440')
masked := b.block_instr2(.and_, ok_block, b.i32_type, mode, mask)
expected := b.m.get_or_add_const(b.i32_type, expected_mode)
is_kind := b.block_instr2(.eq, ok_block, b.i1_type, masked, expected)
b.block_instr1(.ret, ok_block, b.void_type, is_kind)
}
fn (mut b Builder) generate_os_ls_body(func_id int, result_type TypeID) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
entry := b.m.add_block(func_id, 'entry')
path := b.func_add_argument(func_id, b.str_type, 'path')
path_data := b.emit_cstring_from_string(entry, path)
str_size := b.m.get_or_add_const(b.i64_type, '16')
zero64 := b.m.get_or_add_const(b.i64_type, '0')
cap := b.m.get_or_add_const(b.i64_type, '50')
array_new_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', b.fn_ids['array_new'])
arr := b.block_instr4(.call, entry, b.array_type, array_new_ref, str_size, zero64, cap)
arr_alloca := b.block_instr0(.alloca, entry, ptr_array)
b.block_instr2(.store, entry, b.void_type, arr, arr_alloca)
opendir_ref := b.m.add_value(.func_ref, b.void_type, 'opendir', b.c_fn_ids['opendir'])
dir_ptr := b.block_instr2(.call, entry, ptr_i8, opendir_ref, path_data)
null_ptr := b.m.get_or_add_const(ptr_i8, '0')
dir_is_null := b.block_instr2(.eq, entry, b.i1_type, dir_ptr, null_ptr)
blk_loop := b.m.add_block(func_id, 'ls_loop')
blk_empty := b.m.add_block(func_id, 'ls_empty')
b.block_instr3(.br, entry, b.void_type, dir_is_null, ValueID(blk_empty), ValueID(blk_loop))
empty_result := b.block_instr1(.load, blk_empty, b.array_type, arr_alloca)
empty_wrapped := b.block_option_value(blk_empty, result_type, false, empty_result)
b.block_instr1(.ret, blk_empty, b.void_type, empty_wrapped)
readdir_ref := b.m.add_value(.func_ref, b.void_type, 'readdir', b.c_fn_ids['readdir'])
ent := b.block_instr2(.call, blk_loop, ptr_i8, readdir_ref, dir_ptr)
ent_is_null := b.block_instr2(.eq, blk_loop, b.i1_type, ent, null_ptr)
blk_done := b.m.add_block(func_id, 'ls_done')
blk_check := b.m.add_block(func_id, 'ls_check')
b.block_instr3(.br, blk_loop, b.void_type, ent_is_null, ValueID(blk_done), ValueID(blk_check))
name_off := b.m.get_or_add_const(b.i64_type, '21')
name_ptr := b.block_instr2(.get_element_ptr, blk_check, ptr_i8, ent, name_off)
first := b.block_instr1(.load, blk_check, b.i8_type, name_ptr)
zero8 := b.m.get_or_add_const(b.i8_type, '0')
has_name := b.block_instr2(.ne, blk_check, b.i1_type, first, zero8)
blk_push := b.m.add_block(func_id, 'ls_push')
blk_next := b.m.add_block(func_id, 'ls_next')
b.block_instr3(.br, blk_check, b.void_type, has_name, ValueID(blk_push), ValueID(blk_next))
tos_clone_ref := b.m.add_value(.func_ref, b.str_type, 'tos_clone', b.fn_ids['tos_clone'])
name_str := b.block_instr2(.call, blk_push, b.str_type, tos_clone_ref, name_ptr)
name_alloca := b.block_instr0(.alloca, blk_push, ptr_string)
b.block_instr2(.store, blk_push, b.void_type, name_str, name_alloca)
name_elem := b.block_instr1(.bitcast, blk_push, ptr_i8, name_alloca)
array_push_ref := b.m.add_value(.func_ref, b.void_type, 'array_push', b.fn_ids['array_push'])
b.block_instr3(.call, blk_push, b.void_type, array_push_ref, arr_alloca, name_elem)
b.block_instr1(.jmp, blk_push, b.void_type, ValueID(blk_loop))
b.block_instr1(.jmp, blk_next, b.void_type, ValueID(blk_loop))
closedir_ref := b.m.add_value(.func_ref, b.void_type, 'closedir', b.c_fn_ids['closedir'])
b.block_instr2(.call, blk_done, b.i64_type, closedir_ref, dir_ptr)
result := b.block_instr1(.load, blk_done, b.array_type, arr_alloca)
wrapped := b.block_option_value(blk_done, result_type, true, result)
b.block_instr1(.ret, blk_done, b.void_type, wrapped)
}
fn (mut b Builder) block_option_value(block_id BlockID, opt_typ TypeID, ok bool, raw_value ValueID) ValueID {
ptr_opt := b.m.type_store.get_ptr(opt_typ)
alloca := b.block_instr0(.alloca, block_id, ptr_opt)
ok_ptr := b.block_struct_field_ptr(block_id, alloca, opt_typ, 0)
ok_val := b.m.get_or_add_const(b.i1_type, if ok { '1' } else { '0' })
b.block_instr2(.store, block_id, b.void_type, ok_val, ok_ptr)
value_typ := b.option_value_type(opt_typ)
if value_typ != b.void_type {
value_ptr := b.block_struct_field_ptr(block_id, alloca, opt_typ, 1)
mut value := raw_value
if value <= 0 {
value = b.m.get_or_add_const(value_typ, '0')
}
b.block_instr2(.store, block_id, b.void_type, value, value_ptr)
}
return b.block_instr1(.load, block_id, opt_typ, alloca)
}
fn (mut b Builder) emit_cstring_from_string(block_id BlockID, value ValueID) ValueID {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
value_alloca := b.block_instr0(.alloca, block_id, ptr_string)
b.block_instr2(.store, block_id, b.void_type, value, value_alloca)
data_ptr := b.block_struct_field_ptr(block_id, value_alloca, b.str_type, 0)
len_ptr := b.block_struct_field_ptr(block_id, value_alloca, b.str_type, 1)
data := b.block_instr1(.load, block_id, ptr_i8, data_ptr)
len32 := b.block_instr1(.load, block_id, b.i32_type, len_ptr)
len := b.block_instr1(.zext, block_id, b.i64_type, len32)
one := b.m.get_or_add_const(b.i64_type, '1')
alloc_len := b.block_instr2(.add, block_id, b.i64_type, len, one)
malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
out_data := b.block_instr2(.call, block_id, ptr_i8, malloc_ref, alloc_len)
memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, block_id, ptr_i8, memcpy_ref, out_data, data, len)
zero8 := b.m.get_or_add_const(b.i8_type, '0')
term_ptr := b.block_instr2(.add, block_id, ptr_i8, out_data, len)
b.block_instr2(.store, block_id, b.void_type, zero8, term_ptr)
return out_data
}
fn (mut b Builder) generate_const_i64_body(func_id int, value string) {
entry := b.m.add_block(func_id, 'entry')
result := b.m.get_or_add_const(b.i64_type, value)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) register_prealloc_atomic_stubs() {
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
mut p1_ptr := []TypeID{}
p1_ptr << ptr_i64
load_id := b.register_synthetic_function('v_prealloc_atomic_load_i32', b.i64_type, p1_ptr)
b.generate_atomic_load_i64_body(load_id)
mut p2 := []TypeID{}
p2 << ptr_i64
p2 << b.i64_type
add_id := b.register_synthetic_function('v_prealloc_atomic_add_i32', b.i64_type, p2)
b.generate_atomic_add_i64_body(add_id)
store_id := b.register_synthetic_function('v_prealloc_atomic_store_i32', b.i64_type, p2)
b.generate_atomic_store_i64_body(store_id)
mut p3 := []TypeID{}
p3 << ptr_i64
p3 << b.i64_type
p3 << b.i64_type
cas_id := b.register_synthetic_function('v_prealloc_atomic_cas_i32', b.i64_type, p3)
b.generate_atomic_cas_i64_body(cas_id)
}
fn (mut b Builder) generate_atomic_load_i64_body(func_id int) {
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
entry := b.m.add_block(func_id, 'entry')
ptr := b.func_add_argument(func_id, ptr_i64, 'ptr')
value := b.block_instr1(.load, entry, b.i64_type, ptr)
b.block_instr1(.ret, entry, b.void_type, value)
}
fn (mut b Builder) generate_atomic_store_i64_body(func_id int) {
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
entry := b.m.add_block(func_id, 'entry')
ptr := b.func_add_argument(func_id, ptr_i64, 'ptr')
value := b.func_add_argument(func_id, b.i64_type, 'value')
b.block_instr2(.store, entry, b.void_type, value, ptr)
b.block_instr1(.ret, entry, b.void_type, value)
}
fn (mut b Builder) generate_atomic_add_i64_body(func_id int) {
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
entry := b.m.add_block(func_id, 'entry')
ptr := b.func_add_argument(func_id, ptr_i64, 'ptr')
delta := b.func_add_argument(func_id, b.i64_type, 'delta')
old := b.block_instr1(.load, entry, b.i64_type, ptr)
new_value := b.block_instr2(.add, entry, b.i64_type, old, delta)
b.block_instr2(.store, entry, b.void_type, new_value, ptr)
b.block_instr1(.ret, entry, b.void_type, new_value)
}
fn (mut b Builder) generate_atomic_cas_i64_body(func_id int) {
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
entry := b.m.add_block(func_id, 'entry')
ptr := b.func_add_argument(func_id, ptr_i64, 'ptr')
expected := b.func_add_argument(func_id, b.i64_type, 'expected')
desired := b.func_add_argument(func_id, b.i64_type, 'desired')
old := b.block_instr1(.load, entry, b.i64_type, ptr)
ok := b.block_instr2(.eq, entry, b.i1_type, old, expected)
then_block := b.m.add_block(func_id, 'cas_store')
else_block := b.m.add_block(func_id, 'cas_done')
b.block_instr3(.br, entry, b.void_type, ok, ValueID(then_block), ValueID(else_block))
b.block_instr2(.store, then_block, b.void_type, desired, ptr)
one := b.m.get_or_add_const(b.i64_type, '1')
zero := b.m.get_or_add_const(b.i64_type, '0')
b.block_instr1(.ret, then_block, b.void_type, one)
b.block_instr1(.ret, else_block, b.void_type, zero)
}
fn (mut b Builder) register_array_string_stubs() {
mut p1 := []TypeID{}
p1 << b.array_type
array_str_id := b.register_synthetic_function('Array_str', b.str_type, p1)
b.generate_const_string_body(array_str_id, '[]')
bytestr_id := b.register_synthetic_function('bytestr', b.str_type, p1)
b.generate_array_bytestr_body(bytestr_id)
array_bytestr_id := b.register_synthetic_function('Array_u8__bytestr', b.str_type, p1)
b.generate_array_bytestr_body(array_bytestr_id)
array_hex_id := b.register_synthetic_function('Array_u8__hex', b.str_type, p1)
b.generate_const_string_body(array_hex_id, '')
rune_string_id := b.register_synthetic_function('Array_rune__string', b.str_type, p1)
b.generate_const_string_body(rune_string_id, '')
mut p2 := []TypeID{}
p2 << b.array_type
p2 << b.str_type
for name in ['array_string_join', 'Array_string__join'] {
func_id := b.register_synthetic_function(name, b.str_type, p2)
b.generate_array_string_join_body(func_id)
}
}
fn (mut b Builder) generate_array_string_join_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
arr := b.func_add_argument(func_id, b.array_type, 'arr')
sep := b.func_add_argument(func_id, b.str_type, 'sep')
arr_alloca := b.block_instr0(.alloca, entry, ptr_array)
builder_alloca := b.block_instr0(.alloca, entry, ptr_array)
i_alloca := b.block_instr0(.alloca, entry, ptr_i64)
b.block_instr2(.store, entry, b.void_type, arr, arr_alloca)
zero := b.m.get_or_add_const(b.i64_type, '0')
one := b.m.get_or_add_const(b.i64_type, '1')
b.block_instr2(.store, entry, b.void_type, zero, i_alloca)
new_ref := b.m.add_value(.func_ref, b.array_type, 'strings.new_builder',
b.fn_ids['strings.new_builder'])
initial_cap := b.m.get_or_add_const(b.i64_type, '16')
builder := b.block_instr2(.call, entry, b.array_type, new_ref, initial_cap)
b.block_instr2(.store, entry, b.void_type, builder, builder_alloca)
data_ptr := b.block_struct_field_ptr(entry, arr_alloca, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(entry, arr_alloca, b.array_type, 2)
data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
len := b.block_instr1(.zext, entry, b.i64_type, len32)
loop := b.m.add_block(func_id, 'array_join_loop')
body := b.m.add_block(func_id, 'array_join_body')
write_sep := b.m.add_block(func_id, 'array_join_write_sep')
write_elem := b.m.add_block(func_id, 'array_join_write_elem')
done := b.m.add_block(func_id, 'array_join_done')
b.block_instr1(.jmp, entry, b.void_type, ValueID(loop))
i_val := b.block_instr1(.load, loop, b.i64_type, i_alloca)
more := b.block_instr2(.lt, loop, b.i1_type, i_val, len)
b.block_instr3(.br, loop, b.void_type, more, ValueID(body), ValueID(done))
needs_sep := b.block_instr2(.gt, body, b.i1_type, i_val, zero)
b.block_instr3(.br, body, b.void_type, needs_sep, ValueID(write_sep), ValueID(write_elem))
write_ref := b.m.add_value(.func_ref, b.void_type, 'strings.Builder.write_string',
b.fn_ids['strings.Builder.write_string'])
b.block_instr3(.call, write_sep, b.void_type, write_ref, builder_alloca, sep)
b.block_instr1(.jmp, write_sep, b.void_type, ValueID(write_elem))
elem_size := b.m.get_or_add_const(b.i64_type, '${b.m.type_size(b.str_type)}')
offset := b.block_instr2(.mul, write_elem, b.i64_type, i_val, elem_size)
elem_ptr_raw := b.block_instr2(.add, write_elem, ptr_i8, data, offset)
elem_ptr := b.block_instr1(.bitcast, write_elem, b.m.type_store.get_ptr(b.str_type),
elem_ptr_raw)
elem := b.block_instr1(.load, write_elem, b.str_type, elem_ptr)
write_ref2 := b.m.add_value(.func_ref, b.void_type, 'strings.Builder.write_string',
b.fn_ids['strings.Builder.write_string'])
b.block_instr3(.call, write_elem, b.void_type, write_ref2, builder_alloca, elem)
next_i := b.block_instr2(.add, write_elem, b.i64_type, i_val, one)
b.block_instr2(.store, write_elem, b.void_type, next_i, i_alloca)
b.block_instr1(.jmp, write_elem, b.void_type, ValueID(loop))
str_ref := b.m.add_value(.func_ref, b.str_type, 'strings.Builder.str',
b.fn_ids['strings.Builder.str'])
result := b.block_instr2(.call, done, b.str_type, str_ref, builder_alloca)
b.block_instr1(.ret, done, b.void_type, result)
}
fn (mut b Builder) generate_array_bytestr_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
arr := b.func_add_argument(func_id, b.array_type, 'arr')
alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 2)
data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
len := b.block_instr1(.zext, entry, b.i64_type, len32)
one := b.m.get_or_add_const(b.i64_type, '1')
alloc_len := b.block_instr2(.add, entry, b.i64_type, len, one)
malloc_ref := b.m.add_value(.func_ref, b.void_type, 'malloc', b.fn_ids['malloc'])
out_data := b.block_instr2(.call, entry, ptr_i8, malloc_ref, alloc_len)
memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, entry, ptr_i8, memcpy_ref, out_data, data, len)
zero8 := b.m.get_or_add_const(b.i8_type, '0')
term_ptr := b.block_instr2(.add, entry, ptr_i8, out_data, len)
b.block_instr2(.store, entry, b.void_type, zero8, term_ptr)
result := b.emit_make_string(entry, out_data, len)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) register_ierror_stubs() {
ptr_ierror := b.m.type_store.get_ptr(b.i64_type)
mut p1 := []TypeID{}
p1 << ptr_ierror
msg_id := b.register_synthetic_function('IError.msg', b.str_type, p1)
b.generate_ierror_msg_body(msg_id, ptr_ierror)
code_id := b.register_synthetic_function('IError.code', b.i64_type, p1)
b.generate_ierror_code_body(code_id, ptr_ierror)
}
fn (mut b Builder) generate_ierror_msg_body(func_id int, ptr_ierror TypeID) {
entry := b.m.add_block(func_id, 'entry')
_ := b.func_add_argument(func_id, ptr_ierror, 'err')
result := b.m.add_value(.string_literal, b.str_type, '', 0)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_ierror_code_body(func_id int, ptr_ierror TypeID) {
entry := b.m.add_block(func_id, 'entry')
_ := b.func_add_argument(func_id, ptr_ierror, 'err')
result := b.m.get_or_add_const(b.i64_type, '0')
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) register_fixed_array_contains_stubs() {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
mut p3_string := []TypeID{}
p3_string << ptr_i8
p3_string << b.i64_type
p3_string << b.str_type
contains_string_id := b.register_synthetic_function('fixed_array_contains_string', b.i1_type,
p3_string)
b.generate_fixed_array_contains_string_body(contains_string_id)
mut p3_int := []TypeID{}
p3_int << ptr_i8
p3_int << b.i64_type
p3_int << b.i64_type
contains_int_id := b.register_synthetic_function('fixed_array_contains_int', b.i1_type, p3_int)
b.generate_const_bool_body(contains_int_id, false)
}
fn (mut b Builder) generate_fixed_array_contains_string_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
entry := b.m.add_block(func_id, 'entry')
arr := b.func_add_argument(func_id, ptr_i8, 'arr')
len := b.func_add_argument(func_id, b.i64_type, 'len')
needle := b.func_add_argument(func_id, b.str_type, 'needle')
alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
zero := b.m.get_or_add_const(b.i64_type, '0')
one := b.m.get_or_add_const(b.i64_type, '1')
b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
blk_loop := b.m.add_block(func_id, 'fixed_array_contains_string_loop')
blk_body := b.m.add_block(func_id, 'fixed_array_contains_string_body')
blk_next := b.m.add_block(func_id, 'fixed_array_contains_string_next')
blk_found := b.m.add_block(func_id, 'fixed_array_contains_string_found')
blk_not_found := b.m.add_block(func_id, 'fixed_array_contains_string_not_found')
b.block_instr1(.jmp, entry, b.void_type, ValueID(blk_loop))
i := b.block_instr1(.load, blk_loop, b.i64_type, alloca_i)
in_range := b.block_instr2(.lt, blk_loop, b.i1_type, i, len)
b.block_instr3(.br, blk_loop, b.void_type, in_range, ValueID(blk_body), ValueID(blk_not_found))
stride := b.m.get_or_add_const(b.i64_type, '16')
offset := b.block_instr2(.mul, blk_body, b.i64_type, i, stride)
slot := b.block_instr2(.add, blk_body, ptr_i8, arr, offset)
slot_string_ptr := b.block_instr1(.bitcast, blk_body, ptr_string, slot)
slot_string := b.block_instr1(.load, blk_body, b.str_type, slot_string_ptr)
eq_ref := b.m.add_value(.func_ref, b.void_type, 'string__eq', b.fn_ids['string__eq'])
is_eq := b.block_instr3(.call, blk_body, b.i1_type, eq_ref, slot_string, needle)
b.block_instr3(.br, blk_body, b.void_type, is_eq, ValueID(blk_found), ValueID(blk_next))
next_i := b.block_instr2(.add, blk_next, b.i64_type, i, one)
b.block_instr2(.store, blk_next, b.void_type, next_i, alloca_i)
b.block_instr1(.jmp, blk_next, b.void_type, ValueID(blk_loop))
true_value := b.m.get_or_add_const(b.i1_type, '1')
false_value := b.m.get_or_add_const(b.i1_type, '0')
b.block_instr1(.ret, blk_found, b.void_type, true_value)
b.block_instr1(.ret, blk_not_found, b.void_type, false_value)
}
fn (mut b Builder) generate_const_bool_body(func_id int, value bool) {
entry := b.m.add_block(func_id, 'entry')
result := b.m.get_or_add_const(b.i1_type, if value { '1' } else { '0' })
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) register_array_contains_stubs() {
mut p2_string := []TypeID{}
p2_string << b.array_type
p2_string << b.str_type
index_string_id := b.register_synthetic_function('array_index_string', b.i64_type, p2_string)
b.generate_array_index_string_body(index_string_id)
contains_string_id := b.register_synthetic_function('array_contains_string', b.i1_type,
p2_string)
b.generate_array_contains_from_index_body(contains_string_id, 'array_index_string', b.str_type)
mut p2_int := []TypeID{}
p2_int << b.array_type
p2_int << b.i64_type
index_int_id := b.register_synthetic_function('array_index_int', b.i64_type, p2_int)
b.generate_array_index_int_body(index_int_id)
contains_int_id := b.register_synthetic_function('array_contains_int', b.i1_type, p2_int)
b.generate_array_contains_from_index_body(contains_int_id, 'array_index_int', b.i64_type)
}
fn (mut b Builder) generate_array_contains_from_index_body(func_id int, index_name string, needle_type TypeID) {
entry := b.m.add_block(func_id, 'entry')
arr := b.func_add_argument(func_id, b.array_type, 'arr')
needle := b.func_add_argument(func_id, needle_type, 'needle')
index_ref := b.m.add_value(.func_ref, b.void_type, index_name, b.fn_ids[index_name])
idx := b.block_instr3(.call, entry, b.i64_type, index_ref, arr, needle)
zero := b.m.get_or_add_const(b.i64_type, '0')
found := b.block_instr2(.ge, entry, b.i1_type, idx, zero)
b.block_instr1(.ret, entry, b.void_type, found)
}
fn (mut b Builder) generate_array_index_string_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
entry := b.m.add_block(func_id, 'entry')
arr := b.func_add_argument(func_id, b.array_type, 'arr')
needle := b.func_add_argument(func_id, b.str_type, 'needle')
alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
zero := b.m.get_or_add_const(b.i64_type, '0')
one := b.m.get_or_add_const(b.i64_type, '1')
b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 2)
elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
blk_loop := b.m.add_block(func_id, 'array_index_string_loop')
blk_body := b.m.add_block(func_id, 'array_index_string_body')
blk_next := b.m.add_block(func_id, 'array_index_string_next')
blk_found := b.m.add_block(func_id, 'array_index_string_found')
blk_not_found := b.m.add_block(func_id, 'array_index_string_not_found')
b.block_instr1(.jmp, entry, b.void_type, ValueID(blk_loop))
i := b.block_instr1(.load, blk_loop, b.i64_type, alloca_i)
len32 := b.block_instr1(.load, blk_loop, b.i32_type, len_ptr)
len := b.block_instr1(.zext, blk_loop, b.i64_type, len32)
in_range := b.block_instr2(.lt, blk_loop, b.i1_type, i, len)
b.block_instr3(.br, blk_loop, b.void_type, in_range, ValueID(blk_body), ValueID(blk_not_found))
data := b.block_instr1(.load, blk_body, ptr_i8, data_ptr)
elem_size32 := b.block_instr1(.load, blk_body, b.i32_type, elem_size_ptr)
elem_size := b.block_instr1(.zext, blk_body, b.i64_type, elem_size32)
offset := b.block_instr2(.mul, blk_body, b.i64_type, i, elem_size)
slot := b.block_instr2(.add, blk_body, ptr_i8, data, offset)
slot_string_ptr := b.block_instr1(.bitcast, blk_body, ptr_string, slot)
slot_string := b.block_instr1(.load, blk_body, b.str_type, slot_string_ptr)
eq_ref := b.m.add_value(.func_ref, b.void_type, 'string__eq', b.fn_ids['string__eq'])
is_eq := b.block_instr3(.call, blk_body, b.i1_type, eq_ref, slot_string, needle)
b.block_instr3(.br, blk_body, b.void_type, is_eq, ValueID(blk_found), ValueID(blk_next))
next_i := b.block_instr2(.add, blk_next, b.i64_type, i, one)
b.block_instr2(.store, blk_next, b.void_type, next_i, alloca_i)
b.block_instr1(.jmp, blk_next, b.void_type, ValueID(blk_loop))
b.block_instr1(.ret, blk_found, b.void_type, i)
not_found := b.m.get_or_add_const(b.i64_type, '-1')
b.block_instr1(.ret, blk_not_found, b.void_type, not_found)
}
fn (mut b Builder) generate_array_index_int_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_i32 := b.m.type_store.get_ptr(b.i32_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
arr := b.func_add_argument(func_id, b.array_type, 'arr')
needle := b.func_add_argument(func_id, b.i64_type, 'needle')
alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
zero := b.m.get_or_add_const(b.i64_type, '0')
one := b.m.get_or_add_const(b.i64_type, '1')
b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 2)
elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
blk_loop := b.m.add_block(func_id, 'array_index_int_loop')
blk_body := b.m.add_block(func_id, 'array_index_int_body')
blk_next := b.m.add_block(func_id, 'array_index_int_next')
blk_found := b.m.add_block(func_id, 'array_index_int_found')
blk_not_found := b.m.add_block(func_id, 'array_index_int_not_found')
b.block_instr1(.jmp, entry, b.void_type, ValueID(blk_loop))
i := b.block_instr1(.load, blk_loop, b.i64_type, alloca_i)
len32 := b.block_instr1(.load, blk_loop, b.i32_type, len_ptr)
len := b.block_instr1(.zext, blk_loop, b.i64_type, len32)
in_range := b.block_instr2(.lt, blk_loop, b.i1_type, i, len)
b.block_instr3(.br, blk_loop, b.void_type, in_range, ValueID(blk_body), ValueID(blk_not_found))
data := b.block_instr1(.load, blk_body, ptr_i8, data_ptr)
elem_size32 := b.block_instr1(.load, blk_body, b.i32_type, elem_size_ptr)
elem_size := b.block_instr1(.zext, blk_body, b.i64_type, elem_size32)
offset := b.block_instr2(.mul, blk_body, b.i64_type, i, elem_size)
slot := b.block_instr2(.add, blk_body, ptr_i8, data, offset)
slot_i32_ptr := b.block_instr1(.bitcast, blk_body, ptr_i32, slot)
slot_i32 := b.block_instr1(.load, blk_body, b.i32_type, slot_i32_ptr)
slot_i64 := b.block_instr1(.sext, blk_body, b.i64_type, slot_i32)
is_eq := b.block_instr2(.eq, blk_body, b.i1_type, slot_i64, needle)
b.block_instr3(.br, blk_body, b.void_type, is_eq, ValueID(blk_found), ValueID(blk_next))
next_i := b.block_instr2(.add, blk_next, b.i64_type, i, one)
b.block_instr2(.store, blk_next, b.void_type, next_i, alloca_i)
b.block_instr1(.jmp, blk_next, b.void_type, ValueID(blk_loop))
b.block_instr1(.ret, blk_found, b.void_type, i)
not_found := b.m.get_or_add_const(b.i64_type, '-1')
b.block_instr1(.ret, blk_not_found, b.void_type, not_found)
}
fn (mut b Builder) emit_map_state_alloc(block_id BlockID, key_size ValueID, val_size ValueID) ValueID {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_state := b.m.type_store.get_ptr(b.map_state_type)
cap := b.m.get_or_add_const(b.i64_type, '8')
one := b.m.get_or_add_const(b.i64_type, '1')
zero := b.m.get_or_add_const(b.i64_type, '0')
state_size := b.m.get_or_add_const(b.i64_type, '48')
calloc_ref := b.m.add_value(.func_ref, b.void_type, 'calloc', b.fn_ids['calloc'])
state_raw := b.block_instr3(.call, block_id, ptr_i8, calloc_ref, one, state_size)
state := b.block_instr1(.bitcast, block_id, ptr_state, state_raw)
keys := b.block_instr3(.call, block_id, ptr_i8, calloc_ref, cap, key_size)
vals := b.block_instr3(.call, block_id, ptr_i8, calloc_ref, cap, val_size)
keys_ptr := b.map_state_field_ptr(block_id, state, 0)
vals_ptr := b.map_state_field_ptr(block_id, state, 1)
cap_ptr := b.map_state_field_ptr(block_id, state, 2)
len_ptr := b.map_state_field_ptr(block_id, state, 3)
key_size_ptr := b.map_state_field_ptr(block_id, state, 4)
val_size_ptr := b.map_state_field_ptr(block_id, state, 5)
b.block_instr2(.store, block_id, b.void_type, keys, keys_ptr)
b.block_instr2(.store, block_id, b.void_type, vals, vals_ptr)
b.block_instr2(.store, block_id, b.void_type, cap, cap_ptr)
b.block_instr2(.store, block_id, b.void_type, zero, len_ptr)
b.block_instr2(.store, block_id, b.void_type, key_size, key_size_ptr)
b.block_instr2(.store, block_id, b.void_type, val_size, val_size_ptr)
return state
}
fn (mut b Builder) map_state_ptr(block_id BlockID, map_ptr ValueID) ValueID {
ptr_state := b.m.type_store.get_ptr(b.map_state_type)
state_field_ptr := b.block_struct_field_ptr(block_id, map_ptr, b.map_type, 0)
return b.block_instr1(.load, block_id, ptr_state, state_field_ptr)
}
fn (mut b Builder) map_state_field_ptr(block_id BlockID, state_ptr ValueID, field_idx int) ValueID {
return b.block_struct_field_ptr(block_id, state_ptr, b.map_state_type, field_idx)
}
fn (mut b Builder) generate_new_map_body(func_id int) {
ptr_map := b.m.type_store.get_ptr(b.map_type)
entry := b.m.add_block(func_id, 'entry')
key_size := b.func_add_argument(func_id, b.i64_type, 'key_size')
val_size := b.func_add_argument(func_id, b.i64_type, 'val_size')
hash_fn := b.func_add_argument(func_id, b.i64_type, 'hash_fn')
eq_fn := b.func_add_argument(func_id, b.i64_type, 'eq_fn')
clone_fn := b.func_add_argument(func_id, b.i64_type, 'clone_fn')
free_fn := b.func_add_argument(func_id, b.i64_type, 'free_fn')
_ = hash_fn
_ = eq_fn
_ = clone_fn
_ = free_fn
alloca_m := b.block_instr0(.alloca, entry, ptr_map)
state := b.emit_map_state_alloc(entry, key_size, val_size)
state_ptr := b.block_struct_field_ptr(entry, alloca_m, b.map_type, 0)
b.block_instr2(.store, entry, b.void_type, state, state_ptr)
result := b.block_instr1(.load, entry, b.map_type, alloca_m)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_map_find_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_map := b.m.type_store.get_ptr(b.map_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
ptr_state := b.m.type_store.get_ptr(b.map_state_type)
entry := b.m.add_block(func_id, 'entry')
map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
zero := b.m.get_or_add_const(b.i64_type, '0')
b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
blk_has_map := b.m.add_block(func_id, 'map_find_has_map')
blk_loop := b.m.add_block(func_id, 'map_find_loop')
blk_body := b.m.add_block(func_id, 'map_find_body')
blk_string_cmp := b.m.add_block(func_id, 'map_find_string_cmp')
blk_mem_cmp := b.m.add_block(func_id, 'map_find_mem_cmp')
blk_found := b.m.add_block(func_id, 'map_find_found')
blk_next := b.m.add_block(func_id, 'map_find_next')
blk_not_found := b.m.add_block(func_id, 'map_find_not_found')
zero_map := b.m.get_or_add_const(ptr_map, '0')
has_map := b.block_instr2(.ne, entry, b.i1_type, map_ptr, zero_map)
b.block_instr3(.br, entry, b.void_type, has_map, ValueID(blk_has_map), ValueID(blk_not_found))
state := b.map_state_ptr(blk_has_map, map_ptr)
zero_state := b.m.get_or_add_const(ptr_state, '0')
has_state := b.block_instr2(.ne, blk_has_map, b.i1_type, state, zero_state)
keys_ptr := b.map_state_field_ptr(blk_has_map, state, 0)
len_ptr := b.map_state_field_ptr(blk_has_map, state, 3)
key_size_ptr := b.map_state_field_ptr(blk_has_map, state, 4)
b.block_instr3(.br, blk_has_map, b.void_type, has_state, ValueID(blk_loop),
ValueID(blk_not_found))
i := b.block_instr1(.load, blk_loop, b.i64_type, alloca_i)
len := b.block_instr1(.load, blk_loop, b.i64_type, len_ptr)
in_range := b.block_instr2(.lt, blk_loop, b.i1_type, i, len)
b.block_instr3(.br, blk_loop, b.void_type, in_range, ValueID(blk_body), ValueID(blk_not_found))
keys := b.block_instr1(.load, blk_body, ptr_i8, keys_ptr)
key_size := b.block_instr1(.load, blk_body, b.i64_type, key_size_ptr)
offset := b.block_instr2(.mul, blk_body, b.i64_type, i, key_size)
slot_key := b.block_instr2(.add, blk_body, ptr_i8, keys, offset)
string_key_size := b.m.get_or_add_const(b.i64_type, '16')
is_string_key := b.block_instr2(.eq, blk_body, b.i1_type, key_size, string_key_size)
b.block_instr3(.br, blk_body, b.void_type, is_string_key, ValueID(blk_string_cmp),
ValueID(blk_mem_cmp))
slot_string_ptr := b.block_instr1(.bitcast, blk_string_cmp, ptr_string, slot_key)
key_string_ptr := b.block_instr1(.bitcast, blk_string_cmp, ptr_string, key_ptr)
slot_string := b.block_instr1(.load, blk_string_cmp, b.str_type, slot_string_ptr)
key_string := b.block_instr1(.load, blk_string_cmp, b.str_type, key_string_ptr)
eq_ref := b.m.add_value(.func_ref, b.void_type, 'fast_string_eq', b.fn_ids['fast_string_eq'])
string_eq := b.block_instr3(.call, blk_string_cmp, b.i1_type, eq_ref, slot_string, key_string)
b.block_instr3(.br, blk_string_cmp, b.void_type, string_eq, ValueID(blk_found),
ValueID(blk_next))
memcmp_ref := b.m.add_value(.func_ref, b.void_type, 'memcmp', b.fn_ids['memcmp'])
cmp := b.block_instr4(.call, blk_mem_cmp, b.i64_type, memcmp_ref, slot_key, key_ptr, key_size)
mem_eq := b.block_instr2(.eq, blk_mem_cmp, b.i1_type, cmp, zero)
b.block_instr3(.br, blk_mem_cmp, b.void_type, mem_eq, ValueID(blk_found), ValueID(blk_next))
b.block_instr1(.ret, blk_found, b.void_type, i)
one := b.m.get_or_add_const(b.i64_type, '1')
next_i := b.block_instr2(.add, blk_next, b.i64_type, i, one)
b.block_instr2(.store, blk_next, b.void_type, next_i, alloca_i)
b.block_instr1(.jmp, blk_next, b.void_type, ValueID(blk_loop))
not_found := b.m.get_or_add_const(b.i64_type, '-1')
b.block_instr1(.ret, blk_not_found, b.void_type, not_found)
}
fn (mut b Builder) generate_map_exists_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_map := b.m.type_store.get_ptr(b.map_type)
entry := b.m.add_block(func_id, 'entry')
map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
find_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_find', b.fn_ids['v3_map_find'])
idx := b.block_instr3(.call, entry, b.i64_type, find_ref, map_ptr, key_ptr)
zero := b.m.get_or_add_const(b.i64_type, '0')
found := b.block_instr2(.ge, entry, b.i1_type, idx, zero)
b.block_instr1(.ret, entry, b.void_type, found)
}
fn (mut b Builder) generate_map_get_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_map := b.m.type_store.get_ptr(b.map_type)
entry := b.m.add_block(func_id, 'entry')
map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
zero_ptr := b.func_add_argument(func_id, ptr_i8, 'zero')
find_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_find', b.fn_ids['v3_map_find'])
idx := b.block_instr3(.call, entry, b.i64_type, find_ref, map_ptr, key_ptr)
zero := b.m.get_or_add_const(b.i64_type, '0')
found := b.block_instr2(.ge, entry, b.i1_type, idx, zero)
blk_found := b.m.add_block(func_id, 'map_get_found')
blk_missing := b.m.add_block(func_id, 'map_get_missing')
b.block_instr3(.br, entry, b.void_type, found, ValueID(blk_found), ValueID(blk_missing))
state := b.map_state_ptr(blk_found, map_ptr)
vals_ptr := b.map_state_field_ptr(blk_found, state, 1)
val_size_ptr := b.map_state_field_ptr(blk_found, state, 5)
vals := b.block_instr1(.load, blk_found, ptr_i8, vals_ptr)
val_size := b.block_instr1(.load, blk_found, b.i64_type, val_size_ptr)
offset := b.block_instr2(.mul, blk_found, b.i64_type, idx, val_size)
result := b.block_instr2(.add, blk_found, ptr_i8, vals, offset)
b.block_instr1(.ret, blk_found, b.void_type, result)
b.block_instr1(.ret, blk_missing, b.void_type, zero_ptr)
}
fn (mut b Builder) generate_map_get_check_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_map := b.m.type_store.get_ptr(b.map_type)
entry := b.m.add_block(func_id, 'entry')
map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
find_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_find', b.fn_ids['v3_map_find'])
idx := b.block_instr3(.call, entry, b.i64_type, find_ref, map_ptr, key_ptr)
zero := b.m.get_or_add_const(b.i64_type, '0')
found := b.block_instr2(.ge, entry, b.i1_type, idx, zero)
blk_found := b.m.add_block(func_id, 'map_get_check_found')
blk_missing := b.m.add_block(func_id, 'map_get_check_missing')
b.block_instr3(.br, entry, b.void_type, found, ValueID(blk_found), ValueID(blk_missing))
state := b.map_state_ptr(blk_found, map_ptr)
vals_ptr := b.map_state_field_ptr(blk_found, state, 1)
val_size_ptr := b.map_state_field_ptr(blk_found, state, 5)
vals := b.block_instr1(.load, blk_found, ptr_i8, vals_ptr)
val_size := b.block_instr1(.load, blk_found, b.i64_type, val_size_ptr)
offset := b.block_instr2(.mul, blk_found, b.i64_type, idx, val_size)
result := b.block_instr2(.add, blk_found, ptr_i8, vals, offset)
b.block_instr1(.ret, blk_found, b.void_type, result)
zero_ptr := b.m.get_or_add_const(ptr_i8, '0')
b.block_instr1(.ret, blk_missing, b.void_type, zero_ptr)
}
fn (mut b Builder) generate_map_set_default_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_map := b.m.type_store.get_ptr(b.map_type)
entry := b.m.add_block(func_id, 'entry')
map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
val_ptr := b.func_add_argument(func_id, ptr_i8, 'val')
key_size := b.m.get_or_add_const(b.i64_type, '16')
val_size := b.m.get_or_add_const(b.i64_type, '8')
fn_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_set_sized',
b.fn_ids['v3_map_set_sized'])
mut args := []ValueID{}
args << fn_ref
args << map_ptr
args << key_ptr
args << val_ptr
args << key_size
args << val_size
b.m.add_instr(.call, entry, b.void_type, args)
b.block_instr0(.ret, entry, b.void_type)
}
fn (mut b Builder) generate_map_set_sized_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_map := b.m.type_store.get_ptr(b.map_type)
ptr_state := b.m.type_store.get_ptr(b.map_state_type)
entry := b.m.add_block(func_id, 'entry')
map_ptr := b.func_add_argument(func_id, ptr_map, 'map')
key_ptr := b.func_add_argument(func_id, ptr_i8, 'key')
val_ptr := b.func_add_argument(func_id, ptr_i8, 'val')
key_size_arg := b.func_add_argument(func_id, b.i64_type, 'key_size')
val_size_arg := b.func_add_argument(func_id, b.i64_type, 'val_size')
zero := b.m.get_or_add_const(b.i64_type, '0')
state_slot := b.block_instr0(.alloca, entry, b.m.type_store.get_ptr(ptr_state))
state_field_ptr := b.block_struct_field_ptr(entry, map_ptr, b.map_type, 0)
old_state := b.block_instr1(.load, entry, ptr_state, state_field_ptr)
b.block_instr2(.store, entry, b.void_type, old_state, state_slot)
zero_state := b.m.get_or_add_const(ptr_state, '0')
has_state := b.block_instr2(.ne, entry, b.i1_type, old_state, zero_state)
blk_init := b.m.add_block(func_id, 'map_set_init')
blk_ready := b.m.add_block(func_id, 'map_set_ready')
blk_update := b.m.add_block(func_id, 'map_set_update')
blk_insert := b.m.add_block(func_id, 'map_set_insert')
b.block_instr3(.br, entry, b.void_type, has_state, ValueID(blk_ready), ValueID(blk_init))
new_state := b.emit_map_state_alloc(blk_init, key_size_arg, val_size_arg)
b.block_instr2(.store, blk_init, b.void_type, new_state, state_field_ptr)
b.block_instr2(.store, blk_init, b.void_type, new_state, state_slot)
b.block_instr1(.jmp, blk_init, b.void_type, ValueID(blk_ready))
state := b.block_instr1(.load, blk_ready, ptr_state, state_slot)
keys_ptr := b.map_state_field_ptr(blk_ready, state, 0)
vals_ptr := b.map_state_field_ptr(blk_ready, state, 1)
cap_ptr := b.map_state_field_ptr(blk_ready, state, 2)
len_ptr := b.map_state_field_ptr(blk_ready, state, 3)
key_size_ptr := b.map_state_field_ptr(blk_ready, state, 4)
val_size_ptr := b.map_state_field_ptr(blk_ready, state, 5)
find_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_find', b.fn_ids['v3_map_find'])
idx := b.block_instr3(.call, blk_ready, b.i64_type, find_ref, map_ptr, key_ptr)
found := b.block_instr2(.ge, blk_ready, b.i1_type, idx, zero)
b.block_instr3(.br, blk_ready, b.void_type, found, ValueID(blk_update), ValueID(blk_insert))
vals_update := b.block_instr1(.load, blk_update, ptr_i8, vals_ptr)
val_size_update := b.block_instr1(.load, blk_update, b.i64_type, val_size_ptr)
update_off := b.block_instr2(.mul, blk_update, b.i64_type, idx, val_size_update)
update_dest := b.block_instr2(.add, blk_update, ptr_i8, vals_update, update_off)
memcpy_ref_update := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, blk_update, ptr_i8, memcpy_ref_update, update_dest, val_ptr,
val_size_update)
b.block_instr0(.ret, blk_update, b.void_type)
len := b.block_instr1(.load, blk_insert, b.i64_type, len_ptr)
cap := b.block_instr1(.load, blk_insert, b.i64_type, cap_ptr)
needs_grow := b.block_instr2(.ge, blk_insert, b.i1_type, len, cap)
blk_grow := b.m.add_block(func_id, 'map_set_grow')
blk_store := b.m.add_block(func_id, 'map_set_store')
b.block_instr3(.br, blk_insert, b.void_type, needs_grow, ValueID(blk_grow), ValueID(blk_store))
two := b.m.get_or_add_const(b.i64_type, '2')
eight := b.m.get_or_add_const(b.i64_type, '8')
key_size_grow := b.block_instr1(.load, blk_grow, b.i64_type, key_size_ptr)
val_size_grow := b.block_instr1(.load, blk_grow, b.i64_type, val_size_ptr)
keys_old := b.block_instr1(.load, blk_grow, ptr_i8, keys_ptr)
vals_old := b.block_instr1(.load, blk_grow, ptr_i8, vals_ptr)
double_cap := b.block_instr2(.mul, blk_grow, b.i64_type, cap, two)
new_cap := b.block_instr2(.add, blk_grow, b.i64_type, double_cap, eight)
key_bytes := b.block_instr2(.mul, blk_grow, b.i64_type, new_cap, key_size_grow)
val_bytes := b.block_instr2(.mul, blk_grow, b.i64_type, new_cap, val_size_grow)
realloc_ref_keys := b.m.add_value(.func_ref, b.void_type, 'realloc', b.fn_ids['realloc'])
realloc_ref_vals := b.m.add_value(.func_ref, b.void_type, 'realloc', b.fn_ids['realloc'])
keys_new := b.block_instr3(.call, blk_grow, ptr_i8, realloc_ref_keys, keys_old, key_bytes)
vals_new := b.block_instr3(.call, blk_grow, ptr_i8, realloc_ref_vals, vals_old, val_bytes)
b.block_instr2(.store, blk_grow, b.void_type, keys_new, keys_ptr)
b.block_instr2(.store, blk_grow, b.void_type, vals_new, vals_ptr)
b.block_instr2(.store, blk_grow, b.void_type, new_cap, cap_ptr)
b.block_instr1(.jmp, blk_grow, b.void_type, ValueID(blk_store))
keys := b.block_instr1(.load, blk_store, ptr_i8, keys_ptr)
vals := b.block_instr1(.load, blk_store, ptr_i8, vals_ptr)
key_size := b.block_instr1(.load, blk_store, b.i64_type, key_size_ptr)
val_size := b.block_instr1(.load, blk_store, b.i64_type, val_size_ptr)
key_off := b.block_instr2(.mul, blk_store, b.i64_type, len, key_size)
val_off := b.block_instr2(.mul, blk_store, b.i64_type, len, val_size)
key_dest := b.block_instr2(.add, blk_store, ptr_i8, keys, key_off)
val_dest := b.block_instr2(.add, blk_store, ptr_i8, vals, val_off)
memcpy_ref_key := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
memcpy_ref_val := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, blk_store, ptr_i8, memcpy_ref_key, key_dest, key_ptr, key_size)
b.block_instr4(.call, blk_store, ptr_i8, memcpy_ref_val, val_dest, val_ptr, val_size)
one := b.m.get_or_add_const(b.i64_type, '1')
new_len := b.block_instr2(.add, blk_store, b.i64_type, len, one)
b.block_instr2(.store, blk_store, b.void_type, new_len, len_ptr)
b.block_instr0(.ret, blk_store, b.void_type)
}
fn (mut b Builder) generate_array_new_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_ptr_i8 := b.m.type_store.get_ptr(ptr_i8)
entry := b.m.add_block(func_id, 'entry')
elem_size := b.func_add_argument(func_id, b.i64_type, 'elem_size')
len := b.func_add_argument(func_id, b.i64_type, 'len')
cap := b.func_add_argument(func_id, b.i64_type, 'cap')
alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
alloca_cap := b.block_instr0(.alloca, entry, ptr_i64)
alloca_data := b.block_instr0(.alloca, entry, ptr_ptr_i8)
cap_lt_len := b.block_instr2(.lt, entry, b.i1_type, cap, len)
blk_use_len := b.m.add_block(func_id, 'array_new_use_len')
blk_use_cap := b.m.add_block(func_id, 'array_new_use_cap')
blk_init := b.m.add_block(func_id, 'array_new_init')
b.block_instr3(.br, entry, b.void_type, cap_lt_len, ValueID(blk_use_len), ValueID(blk_use_cap))
b.block_instr2(.store, blk_use_len, b.void_type, len, alloca_cap)
b.block_instr1(.jmp, blk_use_len, b.void_type, ValueID(blk_init))
b.block_instr2(.store, blk_use_cap, b.void_type, cap, alloca_cap)
b.block_instr1(.jmp, blk_use_cap, b.void_type, ValueID(blk_init))
final_cap := b.block_instr1(.load, blk_init, b.i64_type, alloca_cap)
zero := b.m.get_or_add_const(b.i64_type, '0')
has_cap := b.block_instr2(.gt, blk_init, b.i1_type, final_cap, zero)
blk_alloc := b.m.add_block(func_id, 'array_new_alloc')
blk_empty := b.m.add_block(func_id, 'array_new_empty')
blk_fields := b.m.add_block(func_id, 'array_new_fields')
b.block_instr3(.br, blk_init, b.void_type, has_cap, ValueID(blk_alloc), ValueID(blk_empty))
fn_ref := b.m.add_value(.func_ref, b.void_type, 'calloc', b.fn_ids['calloc'])
allocated_data := b.block_instr3(.call, blk_alloc, ptr_i8, fn_ref, final_cap, elem_size)
b.block_instr2(.store, blk_alloc, b.void_type, allocated_data, alloca_data)
b.block_instr1(.jmp, blk_alloc, b.void_type, ValueID(blk_fields))
null_data := b.m.get_or_add_const(ptr_i8, '0')
b.block_instr2(.store, blk_empty, b.void_type, null_data, alloca_data)
b.block_instr1(.jmp, blk_empty, b.void_type, ValueID(blk_fields))
data := b.block_instr1(.load, blk_fields, ptr_i8, alloca_data)
data_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 0)
offset_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 1)
len_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 2)
cap_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 3)
flags_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 4)
elem_size_ptr := b.block_struct_field_ptr(blk_fields, alloca_arr, b.array_type, 5)
b.block_instr2(.store, blk_fields, b.void_type, data, data_ptr)
b.block_instr2(.store, blk_fields, b.void_type, zero, offset_ptr)
b.block_instr2(.store, blk_fields, b.void_type, len, len_ptr)
b.block_instr2(.store, blk_fields, b.void_type, final_cap, cap_ptr)
b.block_instr2(.store, blk_fields, b.void_type, zero, flags_ptr)
b.block_instr2(.store, blk_fields, b.void_type, elem_size, elem_size_ptr)
arr := b.block_instr1(.load, blk_fields, b.array_type, alloca_arr)
b.block_instr1(.ret, blk_fields, b.void_type, arr)
}
fn (mut b Builder) generate_array_get_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
arr := b.func_add_argument(func_id, b.array_type, 'arr')
idx := b.func_add_argument(func_id, b.i64_type, 'idx')
alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
elem_size32 := b.block_instr1(.load, entry, b.i32_type, elem_size_ptr)
elem_size := b.block_instr1(.zext, entry, b.i64_type, elem_size32)
offset := b.block_instr2(.mul, entry, b.i64_type, idx, elem_size)
result := b.block_instr2(.add, entry, ptr_i8, data, offset)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_array_slice_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
arr := b.func_add_argument(func_id, b.array_type, 'arr')
start := b.func_add_argument(func_id, b.i64_type, 'start')
end := b.func_add_argument(func_id, b.i64_type, 'end')
alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
alloca_out := b.block_instr0(.alloca, entry, ptr_array)
b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
elem_size32 := b.block_instr1(.load, entry, b.i32_type, elem_size_ptr)
elem_size := b.block_instr1(.zext, entry, b.i64_type, elem_size32)
offset := b.block_instr2(.mul, entry, b.i64_type, start, elem_size)
slice_data := b.block_instr2(.add, entry, ptr_i8, data, offset)
slice_len := b.block_instr2(.sub, entry, b.i64_type, end, start)
zero := b.m.get_or_add_const(b.i64_type, '0')
out_data_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 0)
out_offset_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 1)
out_len_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 2)
out_cap_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 3)
out_flags_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 4)
out_elem_size_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 5)
b.block_instr2(.store, entry, b.void_type, slice_data, out_data_ptr)
b.block_instr2(.store, entry, b.void_type, zero, out_offset_ptr)
b.block_instr2(.store, entry, b.void_type, slice_len, out_len_ptr)
b.block_instr2(.store, entry, b.void_type, slice_len, out_cap_ptr)
b.block_instr2(.store, entry, b.void_type, zero, out_flags_ptr)
b.block_instr2(.store, entry, b.void_type, elem_size, out_elem_size_ptr)
result := b.block_instr1(.load, entry, b.array_type, alloca_out)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) register_arguments_stub() {
arguments_id := b.register_synthetic_function('arguments', b.array_type, []TypeID{})
b.generate_arguments_body(arguments_id)
}
fn (mut b Builder) generate_arguments_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_ptr_i8 := b.m.type_store.get_ptr(ptr_i8)
ptr_array := b.m.type_store.get_ptr(b.array_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
entry := b.m.add_block(func_id, 'entry')
argc_global := b.vars['g_main_argc']
argv_global := b.vars['g_main_argv']
argc := b.block_instr1(.load, entry, b.i64_type, argc_global)
argv := b.block_instr1(.load, entry, ptr_ptr_i8, argv_global)
elem_size := b.m.get_or_add_const(b.i64_type, '16')
zero := b.m.get_or_add_const(b.i64_type, '0')
one := b.m.get_or_add_const(b.i64_type, '1')
ptr_size := b.m.get_or_add_const(b.i64_type, '8')
new_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', b.fn_ids['array_new'])
arr := b.block_instr4(.call, entry, b.array_type, new_ref, elem_size, zero, argc)
alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
loop := b.m.add_block(func_id, 'arguments_loop')
body := b.m.add_block(func_id, 'arguments_body')
done := b.m.add_block(func_id, 'arguments_done')
b.block_instr1(.jmp, entry, b.void_type, ValueID(loop))
i := b.block_instr1(.load, loop, b.i64_type, alloca_i)
more := b.block_instr2(.lt, loop, b.i1_type, i, argc)
b.block_instr3(.br, loop, b.void_type, more, ValueID(body), ValueID(done))
argv_off := b.block_instr2(.mul, body, b.i64_type, i, ptr_size)
argv_slot := b.block_instr2(.add, body, ptr_ptr_i8, argv, argv_off)
cstr := b.block_instr1(.load, body, ptr_i8, argv_slot)
tos_clone_ref := b.m.add_value(.func_ref, b.str_type, 'tos_clone', b.fn_ids['tos_clone'])
arg_string := b.block_instr2(.call, body, b.str_type, tos_clone_ref, cstr)
data_ptr := b.block_struct_field_ptr(body, alloca_arr, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(body, alloca_arr, b.array_type, 2)
data := b.block_instr1(.load, body, ptr_i8, data_ptr)
elem_off := b.block_instr2(.mul, body, b.i64_type, i, elem_size)
dest := b.block_instr2(.add, body, ptr_i8, data, elem_off)
dest_string := b.block_instr1(.bitcast, body, ptr_string, dest)
b.block_instr2(.store, body, b.void_type, arg_string, dest_string)
next_i := b.block_instr2(.add, body, b.i64_type, i, one)
b.block_instr2(.store, body, b.void_type, next_i, len_ptr)
b.block_instr2(.store, body, b.void_type, next_i, alloca_i)
b.block_instr1(.jmp, body, b.void_type, ValueID(loop))
result := b.block_instr1(.load, done, b.array_type, alloca_arr)
b.block_instr1(.ret, done, b.void_type, result)
}
fn (mut b Builder) generate_array_clone_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
arr := b.func_add_argument(func_id, b.array_type, 'arr')
alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
alloca_clone := b.block_instr0(.alloca, entry, ptr_array)
b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 2)
cap_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 3)
elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
len := b.block_instr1(.zext, entry, b.i64_type, len32)
cap32 := b.block_instr1(.load, entry, b.i32_type, cap_ptr)
cap := b.block_instr1(.zext, entry, b.i64_type, cap32)
elem_size32 := b.block_instr1(.load, entry, b.i32_type, elem_size_ptr)
elem_size := b.block_instr1(.zext, entry, b.i64_type, elem_size32)
new_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', b.fn_ids['array_new'])
clone := b.block_instr4(.call, entry, b.array_type, new_ref, elem_size, len, cap)
b.block_instr2(.store, entry, b.void_type, clone, alloca_clone)
clone_data_ptr := b.block_struct_field_ptr(entry, alloca_clone, b.array_type, 0)
clone_data := b.block_instr1(.load, entry, ptr_i8, clone_data_ptr)
copy_size := b.block_instr2(.mul, entry, b.i64_type, len, elem_size)
memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, entry, ptr_i8, memcpy_ref, clone_data, data, copy_size)
result := b.block_instr1(.load, entry, b.array_type, alloca_clone)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_array_repeat_to_depth_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
entry := b.m.add_block(func_id, 'entry')
arr := b.func_add_argument(func_id, b.array_type, 'arr')
count := b.func_add_argument(func_id, b.i64_type, 'count')
_ := b.func_add_argument(func_id, b.i64_type, 'depth')
alloca_arr := b.block_instr0(.alloca, entry, ptr_array)
alloca_out := b.block_instr0(.alloca, entry, ptr_array)
alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
b.block_instr2(.store, entry, b.void_type, arr, alloca_arr)
data_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 2)
elem_size_ptr := b.block_struct_field_ptr(entry, alloca_arr, b.array_type, 5)
data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
len := b.block_instr1(.zext, entry, b.i64_type, len32)
elem_size32 := b.block_instr1(.load, entry, b.i32_type, elem_size_ptr)
elem_size := b.block_instr1(.zext, entry, b.i64_type, elem_size32)
total_len := b.block_instr2(.mul, entry, b.i64_type, len, count)
new_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', b.fn_ids['array_new'])
out := b.block_instr4(.call, entry, b.array_type, new_ref, elem_size, total_len, total_len)
b.block_instr2(.store, entry, b.void_type, out, alloca_out)
out_data_ptr := b.block_struct_field_ptr(entry, alloca_out, b.array_type, 0)
out_data := b.block_instr1(.load, entry, ptr_i8, out_data_ptr)
chunk_size := b.block_instr2(.mul, entry, b.i64_type, len, elem_size)
zero := b.m.get_or_add_const(b.i64_type, '0')
one := b.m.get_or_add_const(b.i64_type, '1')
b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
loop := b.m.add_block(func_id, 'array_repeat_loop')
body := b.m.add_block(func_id, 'array_repeat_body')
done := b.m.add_block(func_id, 'array_repeat_done')
b.block_instr1(.jmp, entry, b.void_type, ValueID(loop))
i := b.block_instr1(.load, loop, b.i64_type, alloca_i)
more := b.block_instr2(.lt, loop, b.i1_type, i, count)
b.block_instr3(.br, loop, b.void_type, more, ValueID(body), ValueID(done))
dest_off := b.block_instr2(.mul, body, b.i64_type, i, chunk_size)
dest := b.block_instr2(.add, body, ptr_i8, out_data, dest_off)
memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, body, ptr_i8, memcpy_ref, dest, data, chunk_size)
next_i := b.block_instr2(.add, body, b.i64_type, i, one)
b.block_instr2(.store, body, b.void_type, next_i, alloca_i)
b.block_instr1(.jmp, body, b.void_type, ValueID(loop))
result := b.block_instr1(.load, done, b.array_type, alloca_out)
b.block_instr1(.ret, done, b.void_type, result)
}
fn (mut b Builder) generate_array_push_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
arr_ptr := b.func_add_argument(func_id, ptr_array, 'arr')
elem_ptr := b.func_add_argument(func_id, ptr_i8, 'elem')
data_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 2)
cap_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 3)
elem_size_ptr := b.block_struct_field_ptr(entry, arr_ptr, b.array_type, 5)
len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
len := b.block_instr1(.zext, entry, b.i64_type, len32)
cap32 := b.block_instr1(.load, entry, b.i32_type, cap_ptr)
cap := b.block_instr1(.zext, entry, b.i64_type, cap32)
needs_grow := b.block_instr2(.ge, entry, b.i1_type, len, cap)
blk_grow := b.m.add_block(func_id, 'array_push_grow')
blk_store := b.m.add_block(func_id, 'array_push_store')
b.block_instr3(.br, entry, b.void_type, needs_grow, ValueID(blk_grow), ValueID(blk_store))
two := b.m.get_or_add_const(b.i64_type, '2')
old_data := b.block_instr1(.load, blk_grow, ptr_i8, data_ptr)
elem_size_grow32 := b.block_instr1(.load, blk_grow, b.i32_type, elem_size_ptr)
elem_size_grow := b.block_instr1(.zext, blk_grow, b.i64_type, elem_size_grow32)
double_cap := b.block_instr2(.mul, blk_grow, b.i64_type, cap, two)
new_cap := b.block_instr2(.add, blk_grow, b.i64_type, double_cap, two)
new_size := b.block_instr2(.mul, blk_grow, b.i64_type, new_cap, elem_size_grow)
realloc_ref := b.m.add_value(.func_ref, b.void_type, 'realloc', b.fn_ids['realloc'])
new_data := b.block_instr3(.call, blk_grow, ptr_i8, realloc_ref, old_data, new_size)
b.block_instr2(.store, blk_grow, b.void_type, new_data, data_ptr)
b.block_instr2(.store, blk_grow, b.void_type, new_cap, cap_ptr)
b.block_instr1(.jmp, blk_grow, b.void_type, ValueID(blk_store))
data := b.block_instr1(.load, blk_store, ptr_i8, data_ptr)
elem_size32 := b.block_instr1(.load, blk_store, b.i32_type, elem_size_ptr)
elem_size := b.block_instr1(.zext, blk_store, b.i64_type, elem_size32)
offset := b.block_instr2(.mul, blk_store, b.i64_type, len, elem_size)
dest := b.block_instr2(.add, blk_store, ptr_i8, data, offset)
memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, blk_store, ptr_i8, memcpy_ref, dest, elem_ptr, elem_size)
one := b.m.get_or_add_const(b.i64_type, '1')
new_len := b.block_instr2(.add, blk_store, b.i64_type, len, one)
b.block_instr2(.store, blk_store, b.void_type, new_len, len_ptr)
b.block_instr0(.ret, blk_store, b.void_type)
}
fn (mut b Builder) generate_array_push_many_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
arr_ptr := b.func_add_argument(func_id, ptr_array, 'arr')
src_ptr := b.func_add_argument(func_id, ptr_i8, 'src')
count := b.func_add_argument(func_id, b.i64_type, 'count')
zero := b.m.get_or_add_const(b.i64_type, '0')
has_items := b.block_instr2(.gt, entry, b.i1_type, count, zero)
blk_check_cap := b.m.add_block(func_id, 'array_push_many_check_cap')
blk_done := b.m.add_block(func_id, 'array_push_many_done')
b.block_instr3(.br, entry, b.void_type, has_items, ValueID(blk_check_cap), ValueID(blk_done))
data_ptr := b.block_struct_field_ptr(blk_check_cap, arr_ptr, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(blk_check_cap, arr_ptr, b.array_type, 2)
cap_ptr := b.block_struct_field_ptr(blk_check_cap, arr_ptr, b.array_type, 3)
elem_size_ptr := b.block_struct_field_ptr(blk_check_cap, arr_ptr, b.array_type, 5)
len32 := b.block_instr1(.load, blk_check_cap, b.i32_type, len_ptr)
len := b.block_instr1(.zext, blk_check_cap, b.i64_type, len32)
cap32 := b.block_instr1(.load, blk_check_cap, b.i32_type, cap_ptr)
cap := b.block_instr1(.zext, blk_check_cap, b.i64_type, cap32)
new_len := b.block_instr2(.add, blk_check_cap, b.i64_type, len, count)
needs_grow := b.block_instr2(.gt, blk_check_cap, b.i1_type, new_len, cap)
blk_grow := b.m.add_block(func_id, 'array_push_many_grow')
blk_copy := b.m.add_block(func_id, 'array_push_many_copy')
b.block_instr3(.br, blk_check_cap, b.void_type, needs_grow, ValueID(blk_grow),
ValueID(blk_copy))
old_data := b.block_instr1(.load, blk_grow, ptr_i8, data_ptr)
elem_size_grow32 := b.block_instr1(.load, blk_grow, b.i32_type, elem_size_ptr)
elem_size_grow := b.block_instr1(.zext, blk_grow, b.i64_type, elem_size_grow32)
two := b.m.get_or_add_const(b.i64_type, '2')
new_cap_base := b.block_instr2(.mul, blk_grow, b.i64_type, new_len, two)
new_cap := b.block_instr2(.add, blk_grow, b.i64_type, new_cap_base, two)
new_size := b.block_instr2(.mul, blk_grow, b.i64_type, new_cap, elem_size_grow)
realloc_ref := b.m.add_value(.func_ref, b.void_type, 'realloc', b.fn_ids['realloc'])
new_data := b.block_instr3(.call, blk_grow, ptr_i8, realloc_ref, old_data, new_size)
b.block_instr2(.store, blk_grow, b.void_type, new_data, data_ptr)
b.block_instr2(.store, blk_grow, b.void_type, new_cap, cap_ptr)
b.block_instr1(.jmp, blk_grow, b.void_type, ValueID(blk_copy))
data := b.block_instr1(.load, blk_copy, ptr_i8, data_ptr)
elem_size32 := b.block_instr1(.load, blk_copy, b.i32_type, elem_size_ptr)
elem_size := b.block_instr1(.zext, blk_copy, b.i64_type, elem_size32)
offset := b.block_instr2(.mul, blk_copy, b.i64_type, len, elem_size)
dest := b.block_instr2(.add, blk_copy, ptr_i8, data, offset)
copy_size := b.block_instr2(.mul, blk_copy, b.i64_type, count, elem_size)
memcpy_ref := b.m.add_value(.func_ref, b.void_type, 'memcpy', b.fn_ids['memcpy'])
b.block_instr4(.call, blk_copy, ptr_i8, memcpy_ref, dest, src_ptr, copy_size)
b.block_instr2(.store, blk_copy, b.void_type, new_len, len_ptr)
b.block_instr0(.ret, blk_copy, b.void_type)
b.block_instr0(.ret, blk_done, b.void_type)
}
fn (mut b Builder) generate_array_push_many_array_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
entry := b.m.add_block(func_id, 'entry')
arr_ptr := b.func_add_argument(func_id, ptr_array, 'arr')
src := b.func_add_argument(func_id, b.array_type, 'src')
src_alloca := b.block_instr0(.alloca, entry, ptr_array)
b.block_instr2(.store, entry, b.void_type, src, src_alloca)
data_ptr := b.block_struct_field_ptr(entry, src_alloca, b.array_type, 0)
len_ptr := b.block_struct_field_ptr(entry, src_alloca, b.array_type, 2)
data := b.block_instr1(.load, entry, ptr_i8, data_ptr)
len32 := b.block_instr1(.load, entry, b.i32_type, len_ptr)
len := b.block_instr1(.zext, entry, b.i64_type, len32)
push_many_ref := b.m.add_value(.func_ref, b.void_type, 'array.push_many',
b.fn_ids['array.push_many'])
b.block_instr4(.call, entry, b.void_type, push_many_ref, arr_ptr, data, len)
b.block_instr0(.ret, entry, b.void_type)
}
fn (mut b Builder) register_string_eq_stub() {
mut p2 := []TypeID{}
p2 << b.str_type
p2 << b.str_type
func_id := b.register_synthetic_function('string__eq', b.i1_type, p2)
b.generate_string_eq_body(func_id)
}
fn (mut b Builder) register_fast_string_eq_stub() {
mut p2 := []TypeID{}
p2 << b.str_type
p2 << b.str_type
func_id := b.register_synthetic_function('fast_string_eq', b.i1_type, p2)
b.generate_string_eq_body(func_id)
}
fn (mut b Builder) register_string_lt_stub() {
mut p2 := []TypeID{}
p2 << b.str_type
p2 << b.str_type
func_id := b.register_synthetic_function('string__lt', b.i1_type, p2)
b.generate_string_lt_body(func_id)
}
fn (mut b Builder) register_string_trim_stubs() {
mut p2 := []TypeID{}
p2 << b.str_type
p2 << b.str_type
trim_right_id := b.register_synthetic_function('string.trim_right', b.str_type, p2)
b.generate_string_trim_right_body(trim_right_id)
}
fn (mut b Builder) generate_string_trim_right_body(func_id int) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_i1 := b.m.type_store.get_ptr(b.i1_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
entry := b.m.add_block(func_id, 'entry')
s := b.func_add_argument(func_id, b.str_type, 's')
cutset := b.func_add_argument(func_id, b.str_type, 'cutset')
alloca_s := b.block_instr0(.alloca, entry, ptr_string)
alloca_cutset := b.block_instr0(.alloca, entry, ptr_string)
alloca_end := b.block_instr0(.alloca, entry, ptr_i64)
alloca_j := b.block_instr0(.alloca, entry, ptr_i64)
alloca_found := b.block_instr0(.alloca, entry, ptr_i1)
b.block_instr2(.store, entry, b.void_type, s, alloca_s)
b.block_instr2(.store, entry, b.void_type, cutset, alloca_cutset)
s_data_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 0)
s_len_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 1)
cut_data_ptr := b.block_struct_field_ptr(entry, alloca_cutset, b.str_type, 0)
cut_len_ptr := b.block_struct_field_ptr(entry, alloca_cutset, b.str_type, 1)
s_data := b.block_instr1(.load, entry, ptr_i8, s_data_ptr)
s_len32 := b.block_instr1(.load, entry, b.i32_type, s_len_ptr)
s_len := b.block_instr1(.zext, entry, b.i64_type, s_len32)
cut_data := b.block_instr1(.load, entry, ptr_i8, cut_data_ptr)
cut_len32 := b.block_instr1(.load, entry, b.i32_type, cut_len_ptr)
cut_len := b.block_instr1(.zext, entry, b.i64_type, cut_len32)
zero64 := b.m.get_or_add_const(b.i64_type, '0')
one64 := b.m.get_or_add_const(b.i64_type, '1')
false_val := b.m.get_or_add_const(b.i1_type, '0')
true_val := b.m.get_or_add_const(b.i1_type, '1')
b.block_instr2(.store, entry, b.void_type, s_len, alloca_end)
loop := b.m.add_block(func_id, 'trim_right_loop')
body := b.m.add_block(func_id, 'trim_right_body')
cut_loop := b.m.add_block(func_id, 'trim_right_cut_loop')
cut_body := b.m.add_block(func_id, 'trim_right_cut_body')
cut_found := b.m.add_block(func_id, 'trim_right_cut_found')
cut_next := b.m.add_block(func_id, 'trim_right_cut_next')
after_cut := b.m.add_block(func_id, 'trim_right_after_cut')
trim_one := b.m.add_block(func_id, 'trim_right_trim_one')
done := b.m.add_block(func_id, 'trim_right_done')
b.block_instr1(.jmp, entry, b.void_type, ValueID(loop))
end_val := b.block_instr1(.load, loop, b.i64_type, alloca_end)
has_chars := b.block_instr2(.gt, loop, b.i1_type, end_val, zero64)
b.block_instr3(.br, loop, b.void_type, has_chars, ValueID(body), ValueID(done))
last_idx := b.block_instr2(.sub, body, b.i64_type, end_val, one64)
ch_ptr := b.block_instr2(.add, body, ptr_i8, s_data, last_idx)
ch := b.block_instr1(.load, body, b.i8_type, ch_ptr)
b.block_instr2(.store, body, b.void_type, false_val, alloca_found)
b.block_instr2(.store, body, b.void_type, zero64, alloca_j)
b.block_instr1(.jmp, body, b.void_type, ValueID(cut_loop))
j := b.block_instr1(.load, cut_loop, b.i64_type, alloca_j)
more_cut := b.block_instr2(.lt, cut_loop, b.i1_type, j, cut_len)
b.block_instr3(.br, cut_loop, b.void_type, more_cut, ValueID(cut_body), ValueID(after_cut))
cut_ch_ptr := b.block_instr2(.add, cut_body, ptr_i8, cut_data, j)
cut_ch := b.block_instr1(.load, cut_body, b.i8_type, cut_ch_ptr)
is_match := b.block_instr2(.eq, cut_body, b.i1_type, ch, cut_ch)
b.block_instr3(.br, cut_body, b.void_type, is_match, ValueID(cut_found), ValueID(cut_next))
b.block_instr2(.store, cut_found, b.void_type, true_val, alloca_found)
b.block_instr1(.jmp, cut_found, b.void_type, ValueID(after_cut))
next_j := b.block_instr2(.add, cut_next, b.i64_type, j, one64)
b.block_instr2(.store, cut_next, b.void_type, next_j, alloca_j)
b.block_instr1(.jmp, cut_next, b.void_type, ValueID(cut_loop))
found := b.block_instr1(.load, after_cut, b.i1_type, alloca_found)
b.block_instr3(.br, after_cut, b.void_type, found, ValueID(trim_one), ValueID(done))
cur_end := b.block_instr1(.load, trim_one, b.i64_type, alloca_end)
new_end := b.block_instr2(.sub, trim_one, b.i64_type, cur_end, one64)
b.block_instr2(.store, trim_one, b.void_type, new_end, alloca_end)
b.block_instr1(.jmp, trim_one, b.void_type, ValueID(loop))
final_len := b.block_instr1(.load, done, b.i64_type, alloca_end)
mut result := ValueID(0)
for substr_name in ['string.substr', 'string__substr', 'substr'] {
if substr_id := b.fn_ids[substr_name] {
substr_ref := b.m.add_value(.func_ref, b.str_type, substr_name, substr_id)
result = b.block_instr4(.call, done, b.str_type, substr_ref, s, zero64, final_len)
break
}
}
if result == ValueID(0) {
result = b.emit_make_string(done, s_data, final_len)
}
b.block_instr1(.ret, done, b.void_type, result)
}
fn (mut b Builder) register_string_last_part_stubs() {
mut p2 := []TypeID{}
p2 << b.str_type
p2 << b.str_type
for name in ['string.all_before_last', 'string__all_before_last', 'all_before_last'] {
func_id := b.register_synthetic_function(name, b.str_type, p2)
b.generate_string_all_last_body(func_id, true)
}
for name in ['string.all_after_last', 'string__all_after_last', 'all_after_last'] {
func_id := b.register_synthetic_function(name, b.str_type, p2)
b.generate_string_all_last_body(func_id, false)
}
}
fn (mut b Builder) generate_string_all_last_body(func_id int, before bool) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_string := b.m.type_store.get_ptr(b.str_type)
entry := b.m.add_block(func_id, 'entry')
s := b.func_add_argument(func_id, b.str_type, 's')
sub := b.func_add_argument(func_id, b.str_type, 'sub')
alloca_s := b.block_instr0(.alloca, entry, ptr_string)
alloca_sub := b.block_instr0(.alloca, entry, ptr_string)
alloca_i := b.block_instr0(.alloca, entry, ptr_i64)
alloca_last := b.block_instr0(.alloca, entry, ptr_i64)
b.block_instr2(.store, entry, b.void_type, s, alloca_s)
b.block_instr2(.store, entry, b.void_type, sub, alloca_sub)
s_data_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 0)
s_len_ptr := b.block_struct_field_ptr(entry, alloca_s, b.str_type, 1)
sub_data_ptr := b.block_struct_field_ptr(entry, alloca_sub, b.str_type, 0)
sub_len_ptr := b.block_struct_field_ptr(entry, alloca_sub, b.str_type, 1)
s_data := b.block_instr1(.load, entry, ptr_i8, s_data_ptr)
s_len32 := b.block_instr1(.load, entry, b.i32_type, s_len_ptr)
s_len := b.block_instr1(.zext, entry, b.i64_type, s_len32)
sub_data := b.block_instr1(.load, entry, ptr_i8, sub_data_ptr)
sub_len32 := b.block_instr1(.load, entry, b.i32_type, sub_len_ptr)
sub_len := b.block_instr1(.zext, entry, b.i64_type, sub_len32)
zero := b.m.get_or_add_const(b.i64_type, '0')
one := b.m.get_or_add_const(b.i64_type, '1')
minus_one := b.m.get_or_add_const(b.i64_type, '-1')
b.block_instr2(.store, entry, b.void_type, zero, alloca_i)
b.block_instr2(.store, entry, b.void_type, minus_one, alloca_last)
check_size := b.m.add_block(func_id, 'all_last_check_size')
loop := b.m.add_block(func_id, 'all_last_loop')
body := b.m.add_block(func_id, 'all_last_body')
match_block := b.m.add_block(func_id, 'all_last_match')
next := b.m.add_block(func_id, 'all_last_next')
done := b.m.add_block(func_id, 'all_last_done')
return_match := b.m.add_block(func_id, 'all_last_return_match')
return_original := b.m.add_block(func_id, 'all_last_return_original')
has_sub := b.block_instr2(.gt, entry, b.i1_type, sub_len, zero)
b.block_instr3(.br, entry, b.void_type, has_sub, ValueID(check_size), ValueID(return_original))
sub_fits := b.block_instr2(.ge, check_size, b.i1_type, s_len, sub_len)
max_start := b.block_instr2(.sub, check_size, b.i64_type, s_len, sub_len)
b.block_instr3(.br, check_size, b.void_type, sub_fits, ValueID(loop), ValueID(return_original))
i := b.block_instr1(.load, loop, b.i64_type, alloca_i)
in_range := b.block_instr2(.le, loop, b.i1_type, i, max_start)
b.block_instr3(.br, loop, b.void_type, in_range, ValueID(body), ValueID(done))
slot := b.block_instr2(.add, body, ptr_i8, s_data, i)
memcmp_ref := b.m.add_value(.func_ref, b.void_type, 'memcmp', b.fn_ids['memcmp'])
cmp := b.block_instr4(.call, body, b.i64_type, memcmp_ref, slot, sub_data, sub_len)
is_match := b.block_instr2(.eq, body, b.i1_type, cmp, zero)
b.block_instr3(.br, body, b.void_type, is_match, ValueID(match_block), ValueID(next))
b.block_instr2(.store, match_block, b.void_type, i, alloca_last)
b.block_instr1(.jmp, match_block, b.void_type, ValueID(next))
next_i := b.block_instr2(.add, next, b.i64_type, i, one)
b.block_instr2(.store, next, b.void_type, next_i, alloca_i)
b.block_instr1(.jmp, next, b.void_type, ValueID(loop))
last := b.block_instr1(.load, done, b.i64_type, alloca_last)
found := b.block_instr2(.ge, done, b.i1_type, last, zero)
b.block_instr3(.br, done, b.void_type, found, ValueID(return_match), ValueID(return_original))
result := if before {
b.emit_make_string(return_match, s_data, last)
} else {
start := b.block_instr2(.add, return_match, b.i64_type, last, sub_len)
result_data := b.block_instr2(.add, return_match, ptr_i8, s_data, start)
result_len := b.block_instr2(.sub, return_match, b.i64_type, s_len, start)
b.emit_make_string(return_match, result_data, result_len)
}
b.block_instr1(.ret, return_match, b.void_type, result)
b.block_instr1(.ret, return_original, b.void_type, s)
}
fn (mut b Builder) generate_string_eq_body(func_id int) {
ptr_string := b.m.type_store.get_ptr(b.str_type)
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i32 := b.m.type_store.get_ptr(b.i32_type)
entry := b.m.add_block(func_id, 'entry')
param_a := b.func_add_argument(func_id, b.str_type, 'a')
param_b := b.func_add_argument(func_id, b.str_type, 'b')
alloca_a := b.block_instr0(.alloca, entry, ptr_string)
alloca_b := b.block_instr0(.alloca, entry, ptr_string)
b.block_instr2(.store, entry, b.void_type, param_a, alloca_a)
b.block_instr2(.store, entry, b.void_type, param_b, alloca_b)
zero_64 := b.m.get_or_add_const(b.i64_type, '0')
len_off := b.m.get_or_add_const(b.i64_type, '8')
a_str_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i8, alloca_a, zero_64)
b_str_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i8, alloca_b, zero_64)
a_len_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i32, alloca_a, len_off)
b_len_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i32, alloca_b, len_off)
a_str := b.block_instr1(.load, entry, ptr_i8, a_str_ptr)
b_str := b.block_instr1(.load, entry, ptr_i8, b_str_ptr)
a_len := b.block_instr1(.load, entry, b.i32_type, a_len_ptr)
b_len := b.block_instr1(.load, entry, b.i32_type, b_len_ptr)
len_eq := b.block_instr2(.eq, entry, b.i1_type, a_len, b_len)
blk_cmp := b.m.add_block(func_id, 'string_eq_cmp')
blk_false := b.m.add_block(func_id, 'string_eq_false')
b.block_instr3(.br, entry, b.void_type, len_eq, ValueID(blk_cmp), ValueID(blk_false))
false_val := b.m.get_or_add_const(b.i1_type, '0')
b.block_instr1(.ret, blk_false, b.void_type, false_val)
a_len64 := b.block_instr1(.zext, blk_cmp, b.i64_type, a_len)
fn_ref := b.m.add_value(.func_ref, b.void_type, 'memcmp', b.fn_ids['memcmp'])
cmp := b.block_instr4(.call, blk_cmp, b.i64_type, fn_ref, a_str, b_str, a_len64)
is_eq := b.block_instr2(.eq, blk_cmp, b.i1_type, cmp, zero_64)
b.block_instr1(.ret, blk_cmp, b.void_type, is_eq)
}
fn (mut b Builder) generate_string_lt_body(func_id int) {
ptr_string := b.m.type_store.get_ptr(b.str_type)
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i32 := b.m.type_store.get_ptr(b.i32_type)
entry := b.m.add_block(func_id, 'entry')
param_a := b.func_add_argument(func_id, b.str_type, 'a')
param_b := b.func_add_argument(func_id, b.str_type, 'b')
alloca_a := b.block_instr0(.alloca, entry, ptr_string)
alloca_b := b.block_instr0(.alloca, entry, ptr_string)
alloca_min := b.block_instr0(.alloca, entry, ptr_i32)
b.block_instr2(.store, entry, b.void_type, param_a, alloca_a)
b.block_instr2(.store, entry, b.void_type, param_b, alloca_b)
zero_64 := b.m.get_or_add_const(b.i64_type, '0')
len_off := b.m.get_or_add_const(b.i64_type, '8')
a_str_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i8, alloca_a, zero_64)
b_str_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i8, alloca_b, zero_64)
a_len_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i32, alloca_a, len_off)
b_len_ptr := b.block_instr2(.get_element_ptr, entry, ptr_i32, alloca_b, len_off)
a_str := b.block_instr1(.load, entry, ptr_i8, a_str_ptr)
b_str := b.block_instr1(.load, entry, ptr_i8, b_str_ptr)
a_len := b.block_instr1(.load, entry, b.i32_type, a_len_ptr)
b_len := b.block_instr1(.load, entry, b.i32_type, b_len_ptr)
a_shorter := b.block_instr2(.lt, entry, b.i1_type, a_len, b_len)
blk_a_min := b.m.add_block(func_id, 'string_lt_a_min')
blk_b_min := b.m.add_block(func_id, 'string_lt_b_min')
blk_cmp := b.m.add_block(func_id, 'string_lt_cmp')
blk_true := b.m.add_block(func_id, 'string_lt_true')
blk_false := b.m.add_block(func_id, 'string_lt_false')
blk_cmp_gt := b.m.add_block(func_id, 'string_lt_cmp_gt')
blk_len := b.m.add_block(func_id, 'string_lt_len')
b.block_instr3(.br, entry, b.void_type, a_shorter, ValueID(blk_a_min), ValueID(blk_b_min))
b.block_instr2(.store, blk_a_min, b.void_type, a_len, alloca_min)
b.block_instr1(.jmp, blk_a_min, b.void_type, ValueID(blk_cmp))
b.block_instr2(.store, blk_b_min, b.void_type, b_len, alloca_min)
b.block_instr1(.jmp, blk_b_min, b.void_type, ValueID(blk_cmp))
min_len := b.block_instr1(.load, blk_cmp, b.i32_type, alloca_min)
min_len64 := b.block_instr1(.zext, blk_cmp, b.i64_type, min_len)
fn_ref := b.m.add_value(.func_ref, b.void_type, 'memcmp', b.fn_ids['memcmp'])
cmp := b.block_instr4(.call, blk_cmp, b.i64_type, fn_ref, a_str, b_str, min_len64)
cmp_lt_zero := b.block_instr2(.lt, blk_cmp, b.i1_type, cmp, zero_64)
b.block_instr3(.br, blk_cmp, b.void_type, cmp_lt_zero, ValueID(blk_true), ValueID(blk_cmp_gt))
cmp_gt_zero := b.block_instr2(.gt, blk_cmp_gt, b.i1_type, cmp, zero_64)
b.block_instr3(.br, blk_cmp_gt, b.void_type, cmp_gt_zero, ValueID(blk_false), ValueID(blk_len))
true_val := b.m.get_or_add_const(b.i1_type, '1')
false_val := b.m.get_or_add_const(b.i1_type, '0')
b.block_instr1(.ret, blk_true, b.void_type, true_val)
b.block_instr1(.ret, blk_false, b.void_type, false_val)
len_lt := b.block_instr2(.lt, blk_len, b.i1_type, a_len, b_len)
b.block_instr1(.ret, blk_len, b.void_type, len_lt)
}
fn (mut b Builder) generate_wymix_body(func_id int) {
entry := b.m.add_block(func_id, 'entry')
param_a := b.func_add_argument(func_id, b.i64_type, 'a')
param_b := b.func_add_argument(func_id, b.i64_type, 'b')
result := b.wymix_inline(entry, param_a, param_b)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) generate_wyhash64_body(func_id int) {
entry := b.m.add_block(func_id, 'entry')
param_a := b.func_add_argument(func_id, b.i64_type, 'a')
param_b := b.func_add_argument(func_id, b.i64_type, 'b')
wyp0 := b.m.get_or_add_const(b.i64_type, '3257665815644502181')
wyp1 := b.m.get_or_add_const(b.i64_type, '10067880064238660809')
x := b.block_instr2(.xor, entry, b.i64_type, param_a, wyp0)
y := b.block_instr2(.xor, entry, b.i64_type, param_b, wyp1)
result := b.wymix_inline(entry, x, y)
b.block_instr1(.ret, entry, b.void_type, result)
}
fn (mut b Builder) wymum_pair_inline(block_id BlockID, a ValueID, b_val ValueID) (ValueID, ValueID) {
mask32 := b.m.get_or_add_const(b.i64_type, '4294967295')
c32 := b.m.get_or_add_const(b.i64_type, '32')
lo := b.block_instr2(.mul, block_id, b.i64_type, a, b_val)
x0 := b.block_instr2(.and_, block_id, b.i64_type, a, mask32)
x1 := b.block_instr2(.lshr, block_id, b.i64_type, a, c32)
y0 := b.block_instr2(.and_, block_id, b.i64_type, b_val, mask32)
y1 := b.block_instr2(.lshr, block_id, b.i64_type, b_val, c32)
w0 := b.block_instr2(.mul, block_id, b.i64_type, x0, y0)
x1y0 := b.block_instr2(.mul, block_id, b.i64_type, x1, y0)
w0_hi := b.block_instr2(.lshr, block_id, b.i64_type, w0, c32)
t := b.block_instr2(.add, block_id, b.i64_type, x1y0, w0_hi)
t_lo := b.block_instr2(.and_, block_id, b.i64_type, t, mask32)
x0y1 := b.block_instr2(.mul, block_id, b.i64_type, x0, y1)
w1 := b.block_instr2(.add, block_id, b.i64_type, t_lo, x0y1)
w2 := b.block_instr2(.lshr, block_id, b.i64_type, t, c32)
x1y1 := b.block_instr2(.mul, block_id, b.i64_type, x1, y1)
w1_hi := b.block_instr2(.lshr, block_id, b.i64_type, w1, c32)
hi_tmp := b.block_instr2(.add, block_id, b.i64_type, x1y1, w2)
hi := b.block_instr2(.add, block_id, b.i64_type, hi_tmp, w1_hi)
return lo, hi
}
fn (mut b Builder) wymix_inline(block_id BlockID, a ValueID, b_val ValueID) ValueID {
lo, hi := b.wymum_pair_inline(block_id, a, b_val)
return b.block_instr2(.xor, block_id, b.i64_type, hi, lo)
}
fn (mut b Builder) generate_wyhash_body(func_id int) {
ptr_u8 := b.m.type_store.get_ptr(b.i8_type)
ptr_u32 := b.m.type_store.get_ptr(b.u32_type)
ptr_u64 := b.m.type_store.get_ptr(b.u64_type)
entry := b.m.add_block(func_id, 'entry')
param_key := b.func_add_argument(func_id, ptr_u8, 'key')
param_len := b.func_add_argument(func_id, b.i64_type, 'len')
param_seed := b.func_add_argument(func_id, b.i64_type, 'seed')
_ := b.func_add_argument(func_id, ptr_u64, 'secret')
wyp0 := b.m.get_or_add_const(b.i64_type, '3257665815644502181')
wyp1 := b.m.get_or_add_const(b.i64_type, '10067880064238660809')
seed_xor_s0 := b.block_instr2(.xor, entry, b.i64_type, param_seed, wyp0)
seed_xor_s0_len := b.block_instr2(.xor, entry, b.i64_type, seed_xor_s0, param_len)
seed_mix := b.wymix_inline(entry, seed_xor_s0_len, wyp1)
seed_init := b.block_instr2(.xor, entry, b.i64_type, param_seed, seed_mix)
alloca_a := b.block_instr0(.alloca, entry, ptr_u64)
alloca_b := b.block_instr0(.alloca, entry, ptr_u64)
alloca_seed := b.block_instr0(.alloca, entry, ptr_u64)
zero_64 := b.m.get_or_add_const(b.i64_type, '0')
one_64 := b.m.get_or_add_const(b.i64_type, '1')
two_64 := b.m.get_or_add_const(b.i64_type, '2')
three_64 := b.m.get_or_add_const(b.i64_type, '3')
four_64 := b.m.get_or_add_const(b.i64_type, '4')
eight_64 := b.m.get_or_add_const(b.i64_type, '8')
sixteen_64 := b.m.get_or_add_const(b.i64_type, '16')
c32 := b.m.get_or_add_const(b.i64_type, '32')
b.block_instr2(.store, entry, b.void_type, zero_64, alloca_a)
b.block_instr2(.store, entry, b.void_type, zero_64, alloca_b)
b.block_instr2(.store, entry, b.void_type, seed_init, alloca_seed)
blk_short := b.m.add_block(func_id, 'wyhash_short')
blk_short_4_16 := b.m.add_block(func_id, 'wyhash_short_4_16')
blk_short_0_3 := b.m.add_block(func_id, 'wyhash_short_0_3')
blk_wyr3 := b.m.add_block(func_id, 'wyhash_wyr3')
blk_long := b.m.add_block(func_id, 'wyhash_long')
blk_final := b.m.add_block(func_id, 'wyhash_final')
len_le_16 := b.block_instr2(.le, entry, b.i1_type, param_len, sixteen_64)
b.block_instr3(.br, entry, b.void_type, len_le_16, ValueID(blk_short), ValueID(blk_long))
len_ge_4 := b.block_instr2(.ge, blk_short, b.i1_type, param_len, four_64)
b.block_instr3(.br, blk_short, b.void_type, len_ge_4, ValueID(blk_short_4_16),
ValueID(blk_short_0_3))
p_as_u32 := b.block_instr1(.bitcast, blk_short_4_16, ptr_u32, param_key)
wyr4_0 := b.block_instr1(.load, blk_short_4_16, b.u32_type, p_as_u32)
wyr4_0_64 := b.block_instr1(.zext, blk_short_4_16, b.i64_type, wyr4_0)
len_shr3 := b.block_instr2(.lshr, blk_short_4_16, b.i64_type, param_len, three_64)
off1 := b.block_instr2(.shl, blk_short_4_16, b.i64_type, len_shr3, two_64)
p_off1 := b.block_instr2(.add, blk_short_4_16, ptr_u8, param_key, off1)
p_off1_u32 := b.block_instr1(.bitcast, blk_short_4_16, ptr_u32, p_off1)
wyr4_1 := b.block_instr1(.load, blk_short_4_16, b.u32_type, p_off1_u32)
wyr4_1_64 := b.block_instr1(.zext, blk_short_4_16, b.i64_type, wyr4_1)
wyr4_0_shifted := b.block_instr2(.shl, blk_short_4_16, b.i64_type, wyr4_0_64, c32)
a_val := b.block_instr2(.or_, blk_short_4_16, b.i64_type, wyr4_0_shifted, wyr4_1_64)
len_m4 := b.block_instr2(.sub, blk_short_4_16, b.i64_type, param_len, four_64)
p_lm4 := b.block_instr2(.add, blk_short_4_16, ptr_u8, param_key, len_m4)
p_lm4_u32 := b.block_instr1(.bitcast, blk_short_4_16, ptr_u32, p_lm4)
wyr4_2 := b.block_instr1(.load, blk_short_4_16, b.u32_type, p_lm4_u32)
wyr4_2_64 := b.block_instr1(.zext, blk_short_4_16, b.i64_type, wyr4_2)
lm4_moff1 := b.block_instr2(.sub, blk_short_4_16, b.i64_type, len_m4, off1)
p_lm4_moff1 := b.block_instr2(.add, blk_short_4_16, ptr_u8, param_key, lm4_moff1)
p_lm4_moff1_u32 := b.block_instr1(.bitcast, blk_short_4_16, ptr_u32, p_lm4_moff1)
wyr4_3 := b.block_instr1(.load, blk_short_4_16, b.u32_type, p_lm4_moff1_u32)
wyr4_3_64 := b.block_instr1(.zext, blk_short_4_16, b.i64_type, wyr4_3)
wyr4_2_shifted := b.block_instr2(.shl, blk_short_4_16, b.i64_type, wyr4_2_64, c32)
b_val := b.block_instr2(.or_, blk_short_4_16, b.i64_type, wyr4_2_shifted, wyr4_3_64)
b.block_instr2(.store, blk_short_4_16, b.void_type, a_val, alloca_a)
b.block_instr2(.store, blk_short_4_16, b.void_type, b_val, alloca_b)
b.block_instr1(.jmp, blk_short_4_16, b.void_type, ValueID(blk_final))
len_gt_0 := b.block_instr2(.gt, blk_short_0_3, b.i1_type, param_len, zero_64)
b.block_instr3(.br, blk_short_0_3, b.void_type, len_gt_0, ValueID(blk_wyr3), ValueID(blk_final))
byte_0 := b.block_instr1(.load, blk_wyr3, b.i8_type, param_key)
byte_0_64 := b.block_instr1(.zext, blk_wyr3, b.i64_type, byte_0)
byte_0_shifted := b.block_instr2(.shl, blk_wyr3, b.i64_type, byte_0_64, sixteen_64)
len_shr1 := b.block_instr2(.lshr, blk_wyr3, b.i64_type, param_len, one_64)
p_mid := b.block_instr2(.add, blk_wyr3, ptr_u8, param_key, len_shr1)
byte_mid := b.block_instr1(.load, blk_wyr3, b.i8_type, p_mid)
byte_mid_64 := b.block_instr1(.zext, blk_wyr3, b.i64_type, byte_mid)
byte_mid_shifted := b.block_instr2(.shl, blk_wyr3, b.i64_type, byte_mid_64, eight_64)
len_m1 := b.block_instr2(.sub, blk_wyr3, b.i64_type, param_len, one_64)
p_last := b.block_instr2(.add, blk_wyr3, ptr_u8, param_key, len_m1)
byte_last := b.block_instr1(.load, blk_wyr3, b.i8_type, p_last)
byte_last_64 := b.block_instr1(.zext, blk_wyr3, b.i64_type, byte_last)
a_tmp := b.block_instr2(.or_, blk_wyr3, b.i64_type, byte_0_shifted, byte_mid_shifted)
a_wyr3 := b.block_instr2(.or_, blk_wyr3, b.i64_type, a_tmp, byte_last_64)
b.block_instr2(.store, blk_wyr3, b.void_type, a_wyr3, alloca_a)
b.block_instr1(.jmp, blk_wyr3, b.void_type, ValueID(blk_final))
p_as_u64 := b.block_instr1(.bitcast, blk_long, ptr_u64, param_key)
wyr8_first := b.block_instr1(.load, blk_long, b.i64_type, p_as_u64)
p_plus_8 := b.block_instr2(.add, blk_long, ptr_u8, param_key, eight_64)
p_plus_8_u64 := b.block_instr1(.bitcast, blk_long, ptr_u64, p_plus_8)
wyr8_second := b.block_instr1(.load, blk_long, b.i64_type, p_plus_8_u64)
len_m16 := b.block_instr2(.sub, blk_long, b.i64_type, param_len, sixteen_64)
p_end_16 := b.block_instr2(.add, blk_long, ptr_u8, param_key, len_m16)
p_end_16_u64 := b.block_instr1(.bitcast, blk_long, ptr_u64, p_end_16)
wyr8_end_16 := b.block_instr1(.load, blk_long, b.i64_type, p_end_16_u64)
len_m8 := b.block_instr2(.sub, blk_long, b.i64_type, param_len, eight_64)
p_end_8 := b.block_instr2(.add, blk_long, ptr_u8, param_key, len_m8)
p_end_8_u64 := b.block_instr1(.bitcast, blk_long, ptr_u64, p_end_8)
wyr8_end_8 := b.block_instr1(.load, blk_long, b.i64_type, p_end_8_u64)
mix_a := b.block_instr2(.xor, blk_long, b.i64_type, wyr8_first, wyp1)
seed_cur := b.block_instr1(.load, blk_long, b.i64_type, alloca_seed)
mix_b := b.block_instr2(.xor, blk_long, b.i64_type, wyr8_second, seed_cur)
seed_long := b.wymix_inline(blk_long, mix_a, mix_b)
b.block_instr2(.store, blk_long, b.void_type, seed_long, alloca_seed)
b.block_instr2(.store, blk_long, b.void_type, wyr8_end_16, alloca_a)
b.block_instr2(.store, blk_long, b.void_type, wyr8_end_8, alloca_b)
b.block_instr1(.jmp, blk_long, b.void_type, ValueID(blk_final))
final_a := b.block_instr1(.load, blk_final, b.i64_type, alloca_a)
final_b := b.block_instr1(.load, blk_final, b.i64_type, alloca_b)
final_seed := b.block_instr1(.load, blk_final, b.i64_type, alloca_seed)
final_a_xor := b.block_instr2(.xor, blk_final, b.i64_type, final_a, wyp1)
final_b_xor := b.block_instr2(.xor, blk_final, b.i64_type, final_b, final_seed)
mum_lo, mum_hi := b.wymum_pair_inline(blk_final, final_a_xor, final_b_xor)
lo_xor_s0 := b.block_instr2(.xor, blk_final, b.i64_type, mum_lo, wyp0)
lo_xor_s0_len := b.block_instr2(.xor, blk_final, b.i64_type, lo_xor_s0, param_len)
hi_xor_s1 := b.block_instr2(.xor, blk_final, b.i64_type, mum_hi, wyp1)
final_result := b.wymix_inline(blk_final, lo_xor_s0_len, hi_xor_s1)
b.block_instr1(.ret, blk_final, b.void_type, final_result)
}
fn (mut b Builder) build_functions() {
mut cur_module := ''
for node in b.a.nodes {
if node.kind == .module_decl {
cur_module = node.value
continue
}
if node.kind == .fn_decl {
if b.skip_source_fn_in_module(node.value, cur_module) {
continue
}
fn_name := ssa_fn_name_in_module(cur_module, node.value)
// Dead-module elimination: skip every function from a skipped module.
if b.skip_modules.len > 0 && cur_module in b.skip_modules {
b.mark_fn_prototype(fn_name)
continue
}
if b.used_fns.len > 0 && !b.fn_is_used(node.value) && !b.fn_is_used(fn_name) {
continue
}
// Hot reload: only the named function's body is materialized.
if b.hot_fn.len > 0 && node.value != b.hot_fn && fn_name != b.hot_fn {
b.mark_fn_prototype(fn_name)
continue
}
// Signature-only build: register the prototype, skip the body.
if b.skip_fn_bodies {
b.mark_fn_prototype(fn_name)
continue
}
b.build_function(node, cur_module)
}
}
if b.top_level_main && !b.skip_fn_bodies && b.hot_fn.len == 0 {
b.build_top_level_main()
}
}
// mark_fn_prototype flags an already-registered function as a declaration whose
// body was intentionally not materialized (hot reload / skip-bodies / skip-module).
fn (mut b Builder) mark_fn_prototype(fn_name string) {
if fn_id := b.fn_ids[fn_name] {
b.m.func_set_prototype(fn_id, true)
}
}
// main_module_name returns the program's primary module name (the first
// non-builtin module_decl), defaulting to 'main'.
fn (b &Builder) main_module_name() string {
for node in b.a.nodes {
if node.kind == .module_decl && node.value.len > 0 && node.value != 'builtin' {
return node.value
}
}
return 'main'
}
// register_type_aliases records `type Foo = Bar` aliases as SSA base TypeIDs so
// resolve_type can resolve them even when no checker is attached.
fn (mut b Builder) register_type_aliases() {
mut cur_module := ''
for node in b.a.nodes {
if node.kind == .module_decl {
cur_module = node.value
continue
}
// A type_decl with no children and a non-empty `typ` is an alias
// (`type Foo = Bar`); a type_decl with children is a sum type.
if node.kind == .type_decl && node.children_count == 0 && node.typ.len > 0 {
base := b.resolve_type_in_module(node.typ, cur_module)
if base > 0 {
b.type_aliases[node.value] = base
qualified := qualify_type_name(node.value, cur_module)
b.type_aliases[qualified] = base
}
}
}
}
fn (b &Builder) checker_param_type_name(fn_name string, module_name string, idx int) ?string {
if b.tc == unsafe { nil } {
return none
}
candidates := checker_fn_name_candidates(fn_name, module_name)
for candidate in candidates {
if params := b.tc.fn_param_types[candidate] {
if idx >= 0 && idx < params.len {
return params[idx].name()
}
}
}
return none
}
fn checker_fn_name_candidates(fn_name string, module_name string) []string {
mut candidates := []string{}
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
candidates << module_name + '.' + fn_name
if fn_name.contains('__') {
candidates << module_name + '.' + fn_name.replace('__', '.')
}
if fn_name.contains('.') {
candidates << module_name + '.' + fn_name.replace('.', '__')
short_name := fn_name.all_after('.')
candidates << module_name + '.' + short_name
}
}
candidates << fn_name
if fn_name.contains('__') {
candidates << fn_name.replace('__', '.')
}
if fn_name.contains('.') {
candidates << fn_name.replace('.', '__')
}
return candidates
}
fn (mut b Builder) checker_return_type(fn_name string, module_name string) ?TypeID {
if b.tc == unsafe { nil } {
return none
}
candidates := checker_fn_name_candidates(fn_name, module_name)
for candidate in candidates {
if ret := b.tc.fn_ret_types[candidate] {
return b.ssa_type_from_checker_type(ret)
}
}
return none
}
fn (b &Builder) fn_is_used(name string) bool {
if name in b.used_fns {
return true
}
if name == 'bench.new' || name == 'bench__new' || name.ends_with('.bench.new')
|| name.ends_with('__bench__new') {
return true
}
if name in ['error_file_not_opened', 'error_size_of_type_0', 'vpopen', 'vpclose'] {
return true
}
if name.contains('__') && name.replace('__', '.') in b.used_fns {
return true
}
if !name.contains('.') {
for used_name, _ in b.used_fns {
normalized_used := used_name.replace('__', '.')
if used_name.ends_with('.${name}') || normalized_used.ends_with('.${name}') {
return true
}
}
}
if name.contains('.') {
normalized_name := name.replace('__', '.')
short_name := normalized_name.all_after_last('.')
if short_name in b.used_fns {
return true
}
for used_name, _ in b.used_fns {
normalized_used := used_name.replace('__', '.')
if used_name.ends_with('.${name}') || normalized_used.ends_with('.${name}')
|| normalized_name.ends_with('.${normalized_used}') {
return true
}
}
}
if name.starts_with('array_') || name.starts_with('string__') || name.starts_with('strings__')
|| name.starts_with('strconv__') || name.starts_with('IError.') {
return true
}
if name in ['new_map', 'memdup', 'int_str', 'bool_str', 'print', 'println', 'eprint', 'eprintln',
'exit', 'arguments', 'tos', 'tos2', 'tos3', 'tos_clone', 'cstring_to_vstring',
'malloc_noscan', 'isnil', 'error', 'error_with_code', 'join_path_single'] {
return true
}
return false
}
fn (mut b Builder) build_function(node flat.Node, module_name string) {
fn_name := ssa_fn_name_in_module(module_name, node.value)
func_id := b.fn_ids[fn_name]
b.cur_module = module_name
b.cur_func = func_id
b.vars = map[string]ValueID{}
b.var_type_names = map[string]string{}
b.label_blocks = map[string]BlockID{}
b.defer_body_ids = []flat.NodeId{}
for v in b.m.values {
if v.kind == .global {
b.vars[v.name] = v.id
}
}
entry := b.m.add_block(func_id, 'entry')
b.cur_block = entry
for i in 0 .. node.children_count {
child := b.a.child_node(&node, i)
if child.kind == .param {
typ_name := if child.typ.starts_with('&') {
child.typ
} else {
b.checker_param_type_name(node.value, module_name, b.m.funcs[func_id].params.len) or {
child.typ
}
}
typ := b.resolve_type_in_module(typ_name, module_name)
param_val := b.m.add_value(.argument, typ, child.value, b.m.funcs[func_id].params.len)
mut f := b.m.funcs[func_id]
f.params << param_val
b.m.funcs[func_id] = f
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(typ))
b.emit2(.store, b.void_type, param_val, alloca)
b.vars[child.value] = alloca
b.var_type_names[child.value] = typ_name
}
}
body_ids := b.fn_body_ids(node)
for id in body_ids {
b.build_stmt(id)
}
blk := b.m.blocks[b.cur_block]
if blk.instrs.len == 0 || !b.is_terminator(blk.instrs.last()) {
ret_type := b.m.funcs[func_id].typ
if ret_type == b.void_type {
b.emit_deferred_stmts()
b.emit0(.ret, b.void_type)
}
}
}
fn (mut b Builder) build_top_level_main() {
func_id := b.fn_ids['main'] or { return }
b.cur_module = 'main'
b.cur_func = func_id
b.vars = map[string]ValueID{}
b.var_type_names = map[string]string{}
b.label_blocks = map[string]BlockID{}
b.defer_body_ids = []flat.NodeId{}
for v in b.m.values {
if v.kind == .global {
b.vars[v.name] = v.id
}
}
entry := b.m.add_block(func_id, 'entry')
b.cur_block = entry
for id in b.top_level_stmt_ids() {
b.build_stmt(id)
}
blk := b.m.blocks[b.cur_block]
if blk.instrs.len == 0 || !b.is_terminator(blk.instrs.last()) {
b.emit_deferred_stmts()
b.emit0(.ret, b.void_type)
}
}
fn (b &Builder) fn_body_ids(node flat.Node) []flat.NodeId {
mut ids := []flat.NodeId{}
for i in 0 .. node.children_count {
child_id := b.a.child(&node, i)
child := b.a.nodes[int(child_id)]
if child.kind != .param && !b.is_declaration_node(child) {
ids << child_id
}
}
return ids
}
fn (b &Builder) is_declaration_node(node flat.Node) bool {
return match node.kind {
.fn_decl, .c_fn_decl, .struct_decl, .field_decl, .global_decl, .const_decl, .const_field,
.enum_decl, .enum_field, .type_decl, .interface_decl, .interface_field, .import_decl,
.module_decl, .directive {
true
}
else {
false
}
}
}
fn (b &Builder) is_terminator(val_id ValueID) bool {
if val_id <= 0 || val_id >= b.m.values.len {
return false
}
v := b.m.values[val_id]
if v.kind != .instruction {
return false
}
instr := b.m.instrs[v.index]
return instr.op == .ret || instr.op == .br || instr.op == .jmp || instr.op == .unreachable
}
fn (b &Builder) valid_node_id(id flat.NodeId) bool {
return b.a != unsafe { nil } && int(id) >= 0 && int(id) < b.a.nodes.len
}
fn (b &Builder) ident_name(id flat.NodeId) string {
if !b.valid_node_id(id) {
return ''
}
node := b.a.nodes[int(id)]
if node.kind != .ident {
return ''
}
return node.value
}
fn (b &Builder) save_var_binding(name string) VarBinding {
if name.len == 0 {
return VarBinding{}
}
if addr := b.vars[name] {
return VarBinding{
exists: true
addr: addr
typ: b.var_type_names[name] or { '' }
}
}
return VarBinding{}
}
fn (mut b Builder) restore_var_binding(name string, binding VarBinding) {
if name.len == 0 {
return
}
if binding.exists {
b.vars[name] = binding.addr
if binding.typ.len > 0 {
b.var_type_names[name] = binding.typ
} else {
b.var_type_names.delete(name)
}
} else {
b.vars.delete(name)
b.var_type_names.delete(name)
}
}
fn (mut b Builder) bind_loop_var(name string, typ TypeID, typ_name string) ValueID {
if name.len == 0 {
return ValueID(0)
}
slot := b.emit0(.alloca, b.m.type_store.get_ptr(typ))
b.vars[name] = slot
if typ_name.len > 0 {
b.var_type_names[name] = typ_name
}
return slot
}
fn (b &Builder) for_in_container_type_name(container_id flat.NodeId) string {
checked := b.checked_expr_type_name(container_id)
if checked.len > 0 && checked != 'unknown' {
return checked.trim_left('&')
}
if !b.valid_node_id(container_id) {
return ''
}
node := b.a.nodes[int(container_id)]
if node.typ.len > 0 {
return node.typ.trim_left('&')
}
if node.kind == .ident {
return (b.var_type_names[node.value] or { '' }).trim_left('&')
}
if node.kind == .selector && node.children_count > 0 {
base_type := b.for_in_container_type_name(b.a.child(&node, 0)).trim_left('&')
field_key := base_type + '.' + node.value
if field_type := b.struct_field_types[field_key] {
return field_type.trim_left('&')
}
short_type := base_type.all_after('.')
short_key := short_type + '.' + node.value
if field_type := b.struct_field_types[short_key] {
return field_type.trim_left('&')
}
}
return ''
}
fn (b &Builder) for_in_array_elem_type_name(container_id flat.NodeId, container_type string) string {
clean := container_type.trim_left('&')
if clean.starts_with('[]') {
return clean[2..]
}
if clean.starts_with('[') {
idx := clean.index_u8(`]`)
if idx > 0 && idx + 1 < clean.len {
return clean[idx + 1..]
}
}
if clean == 'string' {
return 'u8'
}
if b.valid_node_id(container_id) {
node := b.a.nodes[int(container_id)]
if node.kind == .array_literal && node.children_count > 0 {
first_id := b.a.child(&node, 0)
first_type := b.checked_expr_type_name(first_id)
if first_type.len > 0 && first_type != 'unknown' {
return first_type
}
}
}
return 'int'
}
fn (mut b Builder) build_stmt(id flat.NodeId) {
if int(id) < 0 {
return
}
node := b.a.nodes[int(id)]
match node.kind {
.expr_stmt {
expr_id := b.a.child(&node, 0)
expr := b.a.nodes[int(expr_id)]
if expr.kind == .infix && expr.op == .left_shift && expr.value == 'push' {
b.build_array_push_expr(expr)
return
}
b.build_expr(expr_id)
}
.decl_assign {
b.build_decl_assign(node)
}
.assign {
b.build_assign(node)
}
.selector_assign {
b.build_selector_assign(node)
}
.index_assign {
b.build_index_assign(node)
}
.return_stmt {
if node.children_count > 0 {
expr_id := b.a.child(&node, 0)
ret_type := b.m.funcs[b.cur_func].typ
mut val := if node.children_count > 1 && b.is_struct_type(ret_type) {
b.build_multi_return_value(ret_type, node)
} else {
b.build_expr(expr_id)
}
if b.is_option_type(ret_type) && b.value_type(val) != ret_type {
expr_node := b.a.nodes[int(expr_id)]
val = b.build_option_value(ret_type, expr_node.kind != .none_expr, val)
}
b.emit_deferred_stmts()
b.emit1(.ret, b.void_type, val)
} else {
b.emit_deferred_stmts()
b.emit0(.ret, b.void_type)
}
}
.defer_stmt {
if node.children_count > 0 {
b.defer_body_ids << b.a.child(&node, 0)
}
}
.for_stmt {
b.build_for(node)
}
.for_in_stmt {
b.build_for_in(node)
}
.break_stmt {
if b.break_targets.len > 0 {
target := b.break_targets.last()
b.emit1(.jmp, b.void_type, ValueID(target))
}
}
.continue_stmt {
if b.continue_targets.len > 0 {
target := b.continue_targets.last()
b.emit1(.jmp, b.void_type, ValueID(target))
}
}
.goto_stmt {
target := b.label_block(node.value)
b.emit1(.jmp, b.void_type, ValueID(target))
}
.label_stmt {
target := b.label_block(node.value)
if !b.current_block_terminated() {
b.emit1(.jmp, b.void_type, ValueID(target))
}
b.cur_block = target
}
.if_expr {
b.build_if(node)
}
.match_stmt {
b.build_match_stmt(node)
}
.block {
for i in 0 .. node.children_count {
b.build_stmt(b.a.child(&node, i))
}
}
.assert_stmt {
b.build_assert(node)
}
.fn_decl, .c_fn_decl, .struct_decl, .field_decl, .global_decl, .const_decl, .const_field,
.enum_decl, .enum_field, .type_decl, .interface_decl, .interface_field, .import_decl,
.module_decl, .directive, .param {}
.empty {}
else {
eprintln('build_stmt: unsupported node kind: ${node.kind}')
}
}
}
fn (mut b Builder) build_multi_return_value(ret_type TypeID, node flat.Node) ValueID {
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(ret_type))
typ := b.m.type_store.types[ret_type]
for i in 0 .. node.children_count {
if i >= typ.fields.len {
break
}
field_ptr := b.block_struct_field_ptr(b.cur_block, alloca, ret_type, i)
mut value := b.build_expr(b.a.child(&node, i))
field_type := typ.fields[i]
if b.is_int_type(b.value_type(value)) && b.is_int_type(field_type) {
value = b.coerce_int_value(value, field_type)
}
b.emit2(.store, b.void_type, value, field_ptr)
}
return b.emit1(.load, ret_type, alloca)
}
fn (mut b Builder) emit_deferred_stmts() {
for i := b.defer_body_ids.len; i > 0; i-- {
body_id := b.defer_body_ids[i - 1]
if !b.valid_node_id(body_id) {
continue
}
body := b.a.nodes[int(body_id)]
if body.kind == .block {
for j in 0 .. body.children_count {
b.build_stmt(b.a.child(&body, j))
if b.current_block_terminated() {
return
}
}
} else {
b.build_stmt(body_id)
if b.current_block_terminated() {
return
}
}
}
}
fn (mut b Builder) label_block(name string) BlockID {
if block := b.label_blocks[name] {
return block
}
block := b.m.add_block(b.cur_func, 'label_${name}')
b.label_blocks[name] = block
return block
}
fn (mut b Builder) build_decl_assign(node flat.Node) {
mut i := 0
for i < node.children_count {
lhs_id := b.a.child(&node, i)
rhs_id := b.a.child(&node, i + 1)
lhs := b.a.nodes[int(lhs_id)]
rhs_val := b.build_expr(rhs_id)
rhs_type := b.value_type(rhs_val)
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(rhs_type))
b.emit2(.store, b.void_type, rhs_val, alloca)
if lhs.kind == .ident {
b.vars[lhs.value] = alloca
declared_type := b.declared_v_type_name(lhs_id, rhs_id)
rhs_type_name := b.checked_expr_type_name(rhs_id)
b.var_type_names[lhs.value] = if declared_type.len > 0 {
declared_type
} else if rhs_type_name.len > 0 && rhs_type_name != 'unknown' {
rhs_type_name
} else {
node.typ
}
}
i += 2
}
}
fn (mut b Builder) build_assign(node flat.Node) {
mut i := 0
for i < node.children_count {
lhs_id := b.a.child(&node, i)
rhs_id := b.a.child(&node, i + 1)
lhs := b.a.nodes[int(lhs_id)]
if lhs.kind == .ident && lhs.value == '_' {
b.build_expr(rhs_id)
i += 2
continue
}
if lhs.kind == .ident {
if addr := b.vars[lhs.value] {
if node.op == .assign {
rhs_val := b.build_expr(rhs_id)
b.emit2(.store, b.void_type, rhs_val, addr)
} else {
cur := b.emit1(.load, b.deref_type(addr), addr)
rhs_val := b.build_expr(rhs_id)
op := b.compound_to_op(node.op)
result := b.emit2(op, b.value_type(cur), cur, rhs_val)
b.emit2(.store, b.void_type, result, addr)
}
}
} else if lhs.kind == .selector || lhs.kind == .index {
addr := if lhs.kind == .selector {
b.build_selector_addr(lhs)
} else {
b.build_lvalue_addr(lhs_id)
}
if node.op == .assign {
rhs_val := b.build_expr(rhs_id)
b.emit2(.store, b.void_type, rhs_val, addr)
} else {
field_type := b.deref_type(addr)
cur := b.emit1(.load, field_type, addr)
rhs_val := b.build_expr(rhs_id)
op := b.compound_to_op(node.op)
result := b.emit2(op, field_type, cur, rhs_val)
b.emit2(.store, b.void_type, result, addr)
}
}
i += 2
}
}
fn (mut b Builder) build_selector_assign(node flat.Node) {
mut i := 0
for i < node.children_count {
lhs_id := b.a.child(&node, i)
rhs_id := b.a.child(&node, i + 1)
lhs := b.a.nodes[int(lhs_id)]
if lhs.kind == .selector {
field_ptr := b.build_selector_addr(lhs)
if node.op == .assign {
rhs_val := b.build_expr(rhs_id)
b.emit2(.store, b.void_type, rhs_val, field_ptr)
} else {
field_type := b.deref_type(field_ptr)
cur := b.emit1(.load, field_type, field_ptr)
rhs_val := b.build_expr(rhs_id)
op := b.compound_to_op(node.op)
result := b.emit2(op, field_type, cur, rhs_val)
b.emit2(.store, b.void_type, result, field_ptr)
}
}
i += 2
}
}
fn (mut b Builder) build_index_assign(node flat.Node) {
mut i := 0
for i < node.children_count {
lhs_id := b.a.child(&node, i)
rhs_id := b.a.child(&node, i + 1)
addr := b.build_lvalue_addr(lhs_id)
if node.op == .assign {
rhs_val := b.build_expr(rhs_id)
b.emit2(.store, b.void_type, rhs_val, addr)
} else {
field_type := b.deref_type(addr)
cur := b.emit1(.load, field_type, addr)
rhs_val := b.build_expr(rhs_id)
op := b.compound_to_op(node.op)
result := b.emit2(op, field_type, cur, rhs_val)
b.emit2(.store, b.void_type, result, addr)
}
i += 2
}
}
fn (mut b Builder) build_for(node flat.Node) {
init_id := b.a.child(&node, 0)
cond_id := b.a.child(&node, 1)
post_id := b.a.child(&node, 2)
init_node := b.a.nodes[int(init_id)]
cond_node := b.a.nodes[int(cond_id)]
post_node := b.a.nodes[int(post_id)]
if init_node.kind != .empty {
b.build_stmt(init_id)
}
cond_block := b.m.add_block(b.cur_func, 'for_cond')
body_block := b.m.add_block(b.cur_func, 'for_body')
post_block := b.m.add_block(b.cur_func, 'for_post')
exit_block := b.m.add_block(b.cur_func, 'for_exit')
b.emit1(.jmp, b.void_type, ValueID(cond_block))
b.cur_block = cond_block
if cond_node.kind != .empty {
cond_val := b.build_expr(cond_id)
b.emit3(.br, b.void_type, cond_val, ValueID(body_block), ValueID(exit_block))
} else {
b.emit1(.jmp, b.void_type, ValueID(body_block))
}
b.cur_block = body_block
b.break_targets << exit_block
b.continue_targets << if post_node.kind != .empty { post_block } else { cond_block }
for i in 3 .. node.children_count {
b.build_stmt(b.a.child(&node, i))
}
b.break_targets.delete_last()
b.continue_targets.delete_last()
blk := b.m.blocks[b.cur_block]
if blk.instrs.len == 0 || !b.is_terminator(blk.instrs.last()) {
if post_node.kind != .empty {
b.emit1(.jmp, b.void_type, ValueID(post_block))
} else {
b.emit1(.jmp, b.void_type, ValueID(cond_block))
}
}
if post_node.kind != .empty {
b.cur_block = post_block
b.build_stmt(post_id)
post_blk := b.m.blocks[b.cur_block]
if post_blk.instrs.len == 0 || !b.is_terminator(post_blk.instrs.last()) {
b.emit1(.jmp, b.void_type, ValueID(cond_block))
}
}
b.cur_block = exit_block
}
fn (mut b Builder) build_for_in(node flat.Node) {
if node.children_count < 3 {
return
}
key_id := b.a.child(&node, 0)
val_id := b.a.child(&node, 1)
container_id := b.a.child(&node, 2)
body_start := if node.value == '4' { 4 } else { 3 }
if body_start == 4 {
end_id := b.a.child(&node, 3)
b.build_range_for_in(node, key_id, container_id, end_id, body_start)
return
}
container_type := b.for_in_container_type_name(container_id)
if container_type == 'string' {
b.build_string_for_in(node, key_id, val_id, container_id, body_start)
return
}
if container_type.starts_with('map[') || container_type == 'map' || container_type == 'Map' {
b.build_map_for_in(node, key_id, val_id, container_id, body_start, container_type)
return
}
b.build_array_for_in(node, key_id, val_id, container_id, body_start, container_type)
}
fn (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) {
start_val := b.build_expr(start_id)
end_val := b.build_expr(end_id)
idx_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.i64_type))
b.emit2(.store, b.void_type, start_val, idx_alloca)
key_name := b.ident_name(key_id)
old_key := b.save_var_binding(key_name)
key_alloca := b.bind_loop_var(key_name, b.i64_type, 'int')
cond_block := b.m.add_block(b.cur_func, 'for_in_range_cond')
body_block := b.m.add_block(b.cur_func, 'for_in_range_body')
post_block := b.m.add_block(b.cur_func, 'for_in_range_post')
exit_block := b.m.add_block(b.cur_func, 'for_in_range_exit')
b.emit1(.jmp, b.void_type, ValueID(cond_block))
b.cur_block = cond_block
idx := b.emit1(.load, b.i64_type, idx_alloca)
in_range := b.emit2(.lt, b.i1_type, idx, end_val)
b.emit3(.br, b.void_type, in_range, ValueID(body_block), ValueID(exit_block))
b.cur_block = body_block
if key_alloca > 0 {
b.emit2(.store, b.void_type, idx, key_alloca)
}
b.break_targets << exit_block
b.continue_targets << post_block
b.build_for_in_body(node, body_start)
b.break_targets.delete_last()
b.continue_targets.delete_last()
if !b.current_block_terminated() {
b.emit1(.jmp, b.void_type, ValueID(post_block))
}
b.cur_block = post_block
one := b.m.get_or_add_const(b.i64_type, '1')
cur_idx := b.emit1(.load, b.i64_type, idx_alloca)
next_idx := b.emit2(.add, b.i64_type, cur_idx, one)
b.emit2(.store, b.void_type, next_idx, idx_alloca)
b.emit1(.jmp, b.void_type, ValueID(cond_block))
b.restore_var_binding(key_name, old_key)
b.cur_block = exit_block
}
fn (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) {
arr_val := b.build_expr(container_id)
ptr_array := b.m.type_store.get_ptr(b.array_type)
arr_alloca := b.emit0(.alloca, ptr_array)
b.emit2(.store, b.void_type, arr_val, arr_alloca)
idx_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.i64_type))
zero := b.m.get_or_add_const(b.i64_type, '0')
b.emit2(.store, b.void_type, zero, idx_alloca)
elem_type_name := b.for_in_array_elem_type_name(container_id, container_type)
elem_type := b.resolve_type(elem_type_name)
has_value_var := b.valid_node_id(val_id)
key_name := b.ident_name(key_id)
val_name := if has_value_var { b.ident_name(val_id) } else { key_name }
old_key := b.save_var_binding(key_name)
old_val := if has_value_var { b.save_var_binding(val_name) } else { VarBinding{} }
key_alloca := if has_value_var {
b.bind_loop_var(key_name, b.i64_type, 'int')
} else {
ValueID(0)
}
val_alloca := b.bind_loop_var(val_name, elem_type, elem_type_name)
cond_block := b.m.add_block(b.cur_func, 'for_in_array_cond')
body_block := b.m.add_block(b.cur_func, 'for_in_array_body')
post_block := b.m.add_block(b.cur_func, 'for_in_array_post')
exit_block := b.m.add_block(b.cur_func, 'for_in_array_exit')
b.emit1(.jmp, b.void_type, ValueID(cond_block))
b.cur_block = cond_block
idx := b.emit1(.load, b.i64_type, idx_alloca)
len := b.block_load_array_int_field(cond_block, arr_alloca, 2)
in_range := b.emit2(.lt, b.i1_type, idx, len)
b.emit3(.br, b.void_type, in_range, ValueID(body_block), ValueID(exit_block))
b.cur_block = body_block
body_idx := b.emit1(.load, b.i64_type, idx_alloca)
if key_alloca > 0 {
b.emit2(.store, b.void_type, body_idx, key_alloca)
}
if val_alloca > 0 {
elem_ptr := b.build_array_data_index_addr(arr_alloca, body_idx, elem_type)
elem_val := b.emit1(.load, elem_type, elem_ptr)
b.emit2(.store, b.void_type, elem_val, val_alloca)
}
b.break_targets << exit_block
b.continue_targets << post_block
b.build_for_in_body(node, body_start)
b.break_targets.delete_last()
b.continue_targets.delete_last()
if !b.current_block_terminated() {
b.emit1(.jmp, b.void_type, ValueID(post_block))
}
b.cur_block = post_block
one := b.m.get_or_add_const(b.i64_type, '1')
cur_idx := b.emit1(.load, b.i64_type, idx_alloca)
next_idx := b.emit2(.add, b.i64_type, cur_idx, one)
b.emit2(.store, b.void_type, next_idx, idx_alloca)
b.emit1(.jmp, b.void_type, ValueID(cond_block))
b.restore_var_binding(key_name, old_key)
if has_value_var {
b.restore_var_binding(val_name, old_val)
}
b.cur_block = exit_block
}
fn (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) {
str_val := b.build_expr(container_id)
ptr_string := b.m.type_store.get_ptr(b.str_type)
str_alloca := b.emit0(.alloca, ptr_string)
b.emit2(.store, b.void_type, str_val, str_alloca)
idx_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.i64_type))
zero := b.m.get_or_add_const(b.i64_type, '0')
b.emit2(.store, b.void_type, zero, idx_alloca)
has_value_var := b.valid_node_id(val_id)
key_name := b.ident_name(key_id)
val_name := if has_value_var { b.ident_name(val_id) } else { key_name }
old_key := b.save_var_binding(key_name)
old_val := if has_value_var { b.save_var_binding(val_name) } else { VarBinding{} }
key_alloca := if has_value_var {
b.bind_loop_var(key_name, b.i64_type, 'int')
} else {
ValueID(0)
}
val_alloca := b.bind_loop_var(val_name, b.i8_type, 'u8')
cond_block := b.m.add_block(b.cur_func, 'for_in_string_cond')
body_block := b.m.add_block(b.cur_func, 'for_in_string_body')
post_block := b.m.add_block(b.cur_func, 'for_in_string_post')
exit_block := b.m.add_block(b.cur_func, 'for_in_string_exit')
b.emit1(.jmp, b.void_type, ValueID(cond_block))
b.cur_block = cond_block
idx := b.emit1(.load, b.i64_type, idx_alloca)
len_ptr := b.get_field_ptr(str_alloca, 'len')
len32 := b.emit1(.load, b.i32_type, len_ptr)
len := b.emit1(.zext, b.i64_type, len32)
in_range := b.emit2(.lt, b.i1_type, idx, len)
b.emit3(.br, b.void_type, in_range, ValueID(body_block), ValueID(exit_block))
b.cur_block = body_block
body_idx := b.emit1(.load, b.i64_type, idx_alloca)
if key_alloca > 0 {
b.emit2(.store, b.void_type, body_idx, key_alloca)
}
data_ptr := b.get_field_ptr(str_alloca, 'str')
data := b.emit1(.load, b.m.type_store.get_ptr(b.i8_type), data_ptr)
ch_ptr := b.emit2(.add, b.m.type_store.get_ptr(b.i8_type), data, body_idx)
ch := b.emit1(.load, b.i8_type, ch_ptr)
if val_alloca > 0 {
b.emit2(.store, b.void_type, ch, val_alloca)
}
b.break_targets << exit_block
b.continue_targets << post_block
b.build_for_in_body(node, body_start)
b.break_targets.delete_last()
b.continue_targets.delete_last()
if !b.current_block_terminated() {
b.emit1(.jmp, b.void_type, ValueID(post_block))
}
b.cur_block = post_block
one := b.m.get_or_add_const(b.i64_type, '1')
cur_idx := b.emit1(.load, b.i64_type, idx_alloca)
next_idx := b.emit2(.add, b.i64_type, cur_idx, one)
b.emit2(.store, b.void_type, next_idx, idx_alloca)
b.emit1(.jmp, b.void_type, ValueID(cond_block))
b.restore_var_binding(key_name, old_key)
if has_value_var {
b.restore_var_binding(val_name, old_val)
}
b.cur_block = exit_block
}
fn (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) {
key_type_name, val_type_name := map_type_parts(container_type)
key_type := if key_type_name.len > 0 { b.resolve_type(key_type_name) } else { b.str_type }
val_type := if val_type_name.len > 0 { b.resolve_type(val_type_name) } else { b.i64_type }
map_val := b.build_expr(container_id)
ptr_map := b.m.type_store.get_ptr(b.map_type)
ptr_state := b.m.type_store.get_ptr(b.map_state_type)
map_alloca := b.emit0(.alloca, ptr_map)
b.emit2(.store, b.void_type, map_val, map_alloca)
state := b.map_state_ptr(b.cur_block, map_alloca)
zero_state := b.m.get_or_add_const(ptr_state, '0')
has_state := b.emit2(.ne, b.i1_type, state, zero_state)
idx_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.i64_type))
zero := b.m.get_or_add_const(b.i64_type, '0')
b.emit2(.store, b.void_type, zero, idx_alloca)
has_value_var := b.valid_node_id(val_id)
key_name := b.ident_name(key_id)
val_name := if has_value_var { b.ident_name(val_id) } else { '' }
old_key := b.save_var_binding(key_name)
old_val := if has_value_var { b.save_var_binding(val_name) } else { VarBinding{} }
key_alloca := b.bind_loop_var(key_name, key_type, key_type_name)
val_alloca := if has_value_var {
b.bind_loop_var(val_name, val_type, val_type_name)
} else {
ValueID(0)
}
cond_block := b.m.add_block(b.cur_func, 'for_in_map_cond')
body_block := b.m.add_block(b.cur_func, 'for_in_map_body')
post_block := b.m.add_block(b.cur_func, 'for_in_map_post')
exit_block := b.m.add_block(b.cur_func, 'for_in_map_exit')
b.emit3(.br, b.void_type, has_state, ValueID(cond_block), ValueID(exit_block))
b.cur_block = cond_block
idx := b.emit1(.load, b.i64_type, idx_alloca)
len_ptr := b.map_state_field_ptr(cond_block, state, 3)
len := b.emit1(.load, b.i64_type, len_ptr)
in_range := b.emit2(.lt, b.i1_type, idx, len)
b.emit3(.br, b.void_type, in_range, ValueID(body_block), ValueID(exit_block))
b.cur_block = body_block
body_idx := b.emit1(.load, b.i64_type, idx_alloca)
keys_ptr := b.map_state_field_ptr(body_block, state, 0)
vals_ptr := b.map_state_field_ptr(body_block, state, 1)
key_size_ptr := b.map_state_field_ptr(body_block, state, 4)
val_size_ptr := b.map_state_field_ptr(body_block, state, 5)
keys := b.emit1(.load, b.m.type_store.get_ptr(b.i8_type), keys_ptr)
vals := b.emit1(.load, b.m.type_store.get_ptr(b.i8_type), vals_ptr)
key_size := b.emit1(.load, b.i64_type, key_size_ptr)
val_size := b.emit1(.load, b.i64_type, val_size_ptr)
key_off := b.emit2(.mul, b.i64_type, body_idx, key_size)
val_off := b.emit2(.mul, b.i64_type, body_idx, val_size)
key_raw := b.emit2(.add, b.m.type_store.get_ptr(b.i8_type), keys, key_off)
val_raw := b.emit2(.add, b.m.type_store.get_ptr(b.i8_type), vals, val_off)
if key_alloca > 0 {
key_ptr := b.emit1(.bitcast, b.m.type_store.get_ptr(key_type), key_raw)
key_val := b.emit1(.load, key_type, key_ptr)
b.emit2(.store, b.void_type, key_val, key_alloca)
}
if val_alloca > 0 {
val_ptr := b.emit1(.bitcast, b.m.type_store.get_ptr(val_type), val_raw)
val_val := b.emit1(.load, val_type, val_ptr)
b.emit2(.store, b.void_type, val_val, val_alloca)
}
b.break_targets << exit_block
b.continue_targets << post_block
b.build_for_in_body(node, body_start)
b.break_targets.delete_last()
b.continue_targets.delete_last()
if !b.current_block_terminated() {
b.emit1(.jmp, b.void_type, ValueID(post_block))
}
b.cur_block = post_block
one := b.m.get_or_add_const(b.i64_type, '1')
cur_idx := b.emit1(.load, b.i64_type, idx_alloca)
next_idx := b.emit2(.add, b.i64_type, cur_idx, one)
b.emit2(.store, b.void_type, next_idx, idx_alloca)
b.emit1(.jmp, b.void_type, ValueID(cond_block))
b.restore_var_binding(key_name, old_key)
if has_value_var {
b.restore_var_binding(val_name, old_val)
}
b.cur_block = exit_block
}
fn (mut b Builder) build_for_in_body(node flat.Node, body_start int) {
for i in body_start .. node.children_count {
b.build_stmt(b.a.child(&node, i))
}
}
fn (mut b Builder) build_if(node flat.Node) {
cond_node := b.a.child_node(&node, 0)
then_block := b.m.add_block(b.cur_func, 'if_then')
merge_block := b.m.add_block(b.cur_func, 'if_merge')
mut guard := IfGuardState{}
if cond_node.kind == .empty {
b.emit1(.jmp, b.void_type, ValueID(then_block))
} else {
mut cond_val := ValueID(0)
if cond_node.kind == .decl_assign {
cond_val, guard = b.build_if_guard_condition(*cond_node)
} else {
cond_val = b.build_expr(b.a.child(&node, 0))
}
if node.children_count > 2 {
else_block := b.m.add_block(b.cur_func, 'if_else')
b.emit3(.br, b.void_type, cond_val, ValueID(then_block), ValueID(else_block))
b.cur_block = else_block
else_node := b.a.child_node(&node, 2)
if else_node.kind == .if_expr {
b.build_if(*else_node)
} else if else_node.kind == .block {
for i in 0 .. else_node.children_count {
b.build_stmt(b.a.child(else_node, i))
}
}
eblk := b.m.blocks[b.cur_block]
if eblk.instrs.len == 0 || !b.is_terminator(eblk.instrs.last()) {
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
} else {
b.emit3(.br, b.void_type, cond_val, ValueID(then_block), ValueID(merge_block))
}
}
b.cur_block = then_block
if guard.active {
b.activate_if_guard(guard)
}
then_node := b.a.child_node(&node, 1)
for i in 0 .. then_node.children_count {
b.build_stmt(b.a.child(then_node, i))
}
tblk := b.m.blocks[b.cur_block]
if tblk.instrs.len == 0 || !b.is_terminator(tblk.instrs.last()) {
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
if guard.active {
b.restore_var_binding(guard.name, guard.old)
}
b.cur_block = merge_block
}
fn (mut b Builder) build_if_guard_condition(node flat.Node) (ValueID, IfGuardState) {
if node.children_count < 2 {
return b.m.get_or_add_const(b.i1_type, '1'), IfGuardState{}
}
lhs_id := b.a.child(&node, 0)
rhs_id := b.a.child(&node, 1)
lhs := b.a.nodes[int(lhs_id)]
rhs := b.a.nodes[int(rhs_id)]
if lhs.kind == .ident && rhs.kind == .index && rhs.children_count >= 2 && rhs.value != 'range' {
ok, cond, guard := b.build_map_index_if_guard(lhs.value, rhs)
if ok {
return cond, guard
}
}
if lhs.kind == .ident {
ok, cond, guard := b.build_option_if_guard(lhs.value, rhs_id)
if ok {
return cond, guard
}
}
b.build_decl_assign(node)
return b.m.get_or_add_const(b.i1_type, '1'), IfGuardState{}
}
fn (mut b Builder) build_option_if_guard(name string, rhs_id flat.NodeId) (bool, ValueID, IfGuardState) {
if name.len == 0 {
return false, b.m.get_or_add_const(b.i1_type, '0'), IfGuardState{}
}
opt_val := b.build_expr(rhs_id)
opt_typ := b.value_type(opt_val)
value_typ := b.option_value_type(opt_typ)
if value_typ == TypeID(0) || value_typ == b.void_type {
return false, b.m.get_or_add_const(b.i1_type, '0'), IfGuardState{}
}
opt_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(opt_typ))
b.emit2(.store, b.void_type, opt_val, opt_alloca)
ok_ptr := b.get_field_ptr(opt_alloca, 'ok')
cond := b.emit1(.load, b.i1_type, ok_ptr)
value_ptr := b.get_field_ptr(opt_alloca, 'value')
guard := IfGuardState{
active: true
name: name
old: b.save_var_binding(name)
slot: b.emit0(.alloca, b.m.type_store.get_ptr(value_typ))
value_ptr: value_ptr
typ: value_typ
typ_name: b.option_payload_type_name(rhs_id)
}
return true, cond, guard
}
fn (mut b Builder) build_map_index_if_guard(name string, index_node flat.Node) (bool, ValueID, IfGuardState) {
base_id := b.a.child(&index_node, 0)
key_id := b.a.child(&index_node, 1)
map_type_name := b.expr_type_name_for_map(base_id)
key_type_name, val_type_name := map_type_parts(map_type_name)
if name.len == 0 || key_type_name.len == 0 || val_type_name.len == 0 {
return false, b.m.get_or_add_const(b.i1_type, '0'), IfGuardState{}
}
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
val_type := b.resolve_type(val_type_name)
ptr_val_type := b.m.type_store.get_ptr(val_type)
map_ptr := b.map_expr_ptr(base_id)
key_val := b.build_expr(key_id)
key_type := b.resolve_type(key_type_name)
key_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(key_type))
b.emit2(.store, b.void_type, key_val, key_alloca)
key_ptr := if b.value_type(key_alloca) == ptr_i8 {
key_alloca
} else {
b.emit1(.bitcast, ptr_i8, key_alloca)
}
get_ref := b.m.add_value(.func_ref, ptr_i8, 'map__get_check', b.fn_ids['map__get_check'])
value_ptr := b.emit3(.call, ptr_i8, get_ref, map_ptr, key_ptr)
zero_ptr := b.m.get_or_add_const(ptr_i8, '0')
found := b.emit2(.ne, b.i1_type, value_ptr, zero_ptr)
slot := b.emit0(.alloca, ptr_val_type)
guard := IfGuardState{
active: true
name: name
old: b.save_var_binding(name)
slot: slot
value_ptr: value_ptr
typ: val_type
typ_name: val_type_name
}
return true, found, guard
}
fn (mut b Builder) activate_if_guard(guard IfGuardState) {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_val_type := b.m.type_store.get_ptr(guard.typ)
typed_value_ptr := if ptr_val_type == ptr_i8 {
guard.value_ptr
} else {
b.emit1(.bitcast, ptr_val_type, guard.value_ptr)
}
value := b.emit1(.load, guard.typ, typed_value_ptr)
b.emit2(.store, b.void_type, value, guard.slot)
b.vars[guard.name] = guard.slot
if guard.typ_name.len > 0 {
b.var_type_names[guard.name] = guard.typ_name
}
}
fn (mut b Builder) build_if_value(id flat.NodeId, node flat.Node) ValueID {
if node.children_count < 3 {
b.build_if(node)
return b.default_value_for_type(b.if_value_result_type(id, []ValueID{}))
}
cond_node := b.a.child_node(&node, 0)
then_block := b.m.add_block(b.cur_func, 'if_value_then')
else_block := b.m.add_block(b.cur_func, 'if_value_else')
merge_block := b.m.add_block(b.cur_func, 'if_value_merge')
if cond_node.kind == .empty {
b.emit1(.jmp, b.void_type, ValueID(then_block))
} else {
cond_val := b.build_expr(b.a.child(&node, 0))
b.emit3(.br, b.void_type, cond_val, ValueID(then_block), ValueID(else_block))
}
mut incoming_values := []ValueID{}
mut incoming_blocks := []BlockID{}
b.cur_block = then_block
if then_value := b.build_branch_value(b.a.child(&node, 1), b.a.child_node(&node, 1)) {
then_end := b.cur_block
if !b.current_block_terminated() {
incoming_values << then_value
incoming_blocks << then_end
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
} else if !b.current_block_terminated() {
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
b.cur_block = else_block
if else_value := b.build_branch_value(b.a.child(&node, 2), b.a.child_node(&node, 2)) {
else_end := b.cur_block
if !b.current_block_terminated() {
incoming_values << else_value
incoming_blocks << else_end
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
} else if !b.current_block_terminated() {
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
b.cur_block = merge_block
result_type := b.if_value_result_type(id, incoming_values)
if incoming_values.len == 0 {
return b.default_value_for_type(result_type)
}
if incoming_values.len == 1 {
return incoming_values[0]
}
mut operands := []ValueID{}
for i, value in incoming_values {
operands << value
operands << ValueID(incoming_blocks[i])
}
return b.m.add_instr(.phi, b.cur_block, result_type, operands)
}
fn (mut b Builder) build_branch_value(id flat.NodeId, node &flat.Node) ?ValueID {
if int(id) < 0 {
return none
}
if node.kind == .block {
if node.children_count == 0 {
return b.m.get_or_add_const(b.i64_type, '0')
}
for i in 0 .. node.children_count - 1 {
b.build_stmt(b.a.child(node, i))
if b.current_block_terminated() {
return none
}
}
last_id := b.a.child(node, node.children_count - 1)
last := b.a.nodes[int(last_id)]
if last.kind == .expr_stmt && last.children_count > 0 {
return b.build_expr(b.a.child(&last, 0))
}
if last.kind == .match_stmt {
return b.build_match_value(last_id, last)
}
if last.kind == .if_expr {
return b.build_if_value(last_id, last)
}
if b.is_stmt_kind(last.kind) {
b.build_stmt(last_id)
return none
}
return b.build_expr(last_id)
}
if node.kind == .if_expr {
return b.build_if_value(id, *node)
}
if node.kind == .match_stmt {
return b.build_match_value(id, *node)
}
if b.is_stmt_kind(node.kind) {
b.build_stmt(id)
return none
}
return b.build_expr(id)
}
fn (mut b Builder) build_match_stmt(node flat.Node) {
if node.children_count < 2 {
return
}
subject := b.build_expr(b.a.child(&node, 0))
exit_block := b.m.add_block(b.cur_func, 'match_exit')
for i in 1 .. node.children_count {
branch := b.a.child_node(&node, i)
if branch.kind != .match_branch {
continue
}
is_else := branch.value == 'else'
body_block := b.m.add_block(b.cur_func, 'match_body')
next_block := if i + 1 < node.children_count {
b.m.add_block(b.cur_func, 'match_next')
} else {
exit_block
}
if is_else {
b.emit1(.jmp, b.void_type, ValueID(body_block))
} else {
cond := b.build_match_condition(subject, *branch)
b.emit3(.br, b.void_type, cond, ValueID(body_block), ValueID(next_block))
}
b.cur_block = body_block
body_start := b.match_branch_body_start(*branch)
for j in body_start .. branch.children_count {
b.build_stmt(b.a.child(branch, j))
if b.current_block_terminated() {
break
}
}
if !b.current_block_terminated() {
b.emit1(.jmp, b.void_type, ValueID(exit_block))
}
b.cur_block = next_block
}
}
fn (mut b Builder) build_match_value(id flat.NodeId, node flat.Node) ValueID {
if node.children_count < 2 {
return b.default_value_for_type(b.if_value_result_type(id, []ValueID{}))
}
subject := b.build_expr(b.a.child(&node, 0))
merge_block := b.m.add_block(b.cur_func, 'match_merge')
mut incoming_values := []ValueID{}
mut incoming_blocks := []BlockID{}
for i in 1 .. node.children_count {
branch := b.a.child_node(&node, i)
if branch.kind != .match_branch {
continue
}
is_else := branch.value == 'else'
body_block := b.m.add_block(b.cur_func, 'match_value_body')
next_block := if i + 1 < node.children_count {
b.m.add_block(b.cur_func, 'match_value_next')
} else {
merge_block
}
if is_else {
b.emit1(.jmp, b.void_type, ValueID(body_block))
} else {
cond := b.build_match_condition(subject, *branch)
b.emit3(.br, b.void_type, cond, ValueID(body_block), ValueID(next_block))
}
b.cur_block = body_block
if value := b.build_match_branch_value(*branch) {
end_block := b.cur_block
if !b.current_block_terminated() {
incoming_values << value
incoming_blocks << end_block
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
} else if !b.current_block_terminated() {
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
b.cur_block = next_block
}
b.cur_block = merge_block
result_type := b.if_value_result_type(id, incoming_values)
if incoming_values.len == 0 {
return b.default_value_for_type(result_type)
}
if incoming_values.len == 1 {
return incoming_values[0]
}
mut operands := []ValueID{}
for i, value in incoming_values {
operands << value
operands << ValueID(incoming_blocks[i])
}
return b.m.add_instr(.phi, b.cur_block, result_type, operands)
}
fn (mut b Builder) build_match_branch_value(branch flat.Node) ?ValueID {
body_start := b.match_branch_body_start(branch)
if body_start >= branch.children_count {
return none
}
for i in body_start .. branch.children_count - 1 {
b.build_stmt(b.a.child(&branch, i))
if b.current_block_terminated() {
return none
}
}
last_id := b.a.child(&branch, branch.children_count - 1)
last := b.a.nodes[int(last_id)]
if last.kind == .expr_stmt && last.children_count > 0 {
return b.build_expr(b.a.child(&last, 0))
}
if last.kind == .match_stmt {
return b.build_match_value(last_id, last)
}
if last.kind == .if_expr {
return b.build_if_value(last_id, last)
}
if b.is_stmt_kind(last.kind) {
b.build_stmt(last_id)
return none
}
return b.build_expr(last_id)
}
fn (b &Builder) match_branch_body_start(branch flat.Node) int {
if branch.value == 'else' {
return 0
}
return branch.value.int()
}
fn (mut b Builder) build_match_condition(subject ValueID, branch flat.Node) ValueID {
n_conds := b.match_branch_body_start(branch)
mut result := ValueID(0)
for i in 0 .. n_conds {
cond_value := b.build_expr(b.a.child(&branch, i))
cmp := b.emit2(.eq, b.i1_type, subject, cond_value)
if result == 0 {
result = cmp
} else {
result = b.emit2(.or_, b.i1_type, result, cmp)
}
}
if result == 0 {
return b.m.get_or_add_const(b.i1_type, '1')
}
return result
}
fn (mut b Builder) if_value_result_type(id flat.NodeId, values []ValueID) TypeID {
if b.tc != unsafe { nil } {
if typ := b.tc.expr_type(id) {
name := typ.name()
if name.len > 0 && name != 'unknown' && name != 'void' {
return b.resolve_type(name)
}
}
}
for value in values {
typ := b.value_type(value)
if typ != b.void_type {
return typ
}
}
return b.i64_type
}
fn (mut b Builder) build_assert(node flat.Node) {
cond_val := b.build_expr(b.a.child(&node, 0))
fail_block := b.m.add_block(b.cur_func, 'assert_fail')
ok_block := b.m.add_block(b.cur_func, 'assert_ok')
b.emit3(.br, b.void_type, cond_val, ValueID(ok_block), ValueID(fail_block))
b.cur_block = fail_block
if exit_fn := b.fn_ids['exit'] {
one := b.m.get_or_add_const(b.i64_type, '1')
fn_ref := b.m.add_value(.func_ref, b.void_type, 'exit', exit_fn)
b.emit2(.call, b.void_type, fn_ref, one)
}
b.emit0(.unreachable, b.void_type)
b.cur_block = ok_block
}
fn (mut b Builder) build_expr(id flat.NodeId) ValueID {
if int(id) < 0 {
return b.m.get_or_add_const(b.i64_type, '0')
}
node := b.a.nodes[int(id)]
match node.kind {
.empty {
return b.m.get_or_add_const(b.i64_type, '0')
}
.int_literal {
return b.m.get_or_add_const(b.i64_type, node.value)
}
.float_literal {
return b.m.get_or_add_const(b.float_literal_type(id, node), node.value)
}
.bool_literal {
val := if node.value == 'true' { '1' } else { '0' }
return b.m.get_or_add_const(b.i1_type, val)
}
.string_literal {
return b.m.add_value(.string_literal, b.str_type, node.value, 0)
}
.char_literal {
return b.m.get_or_add_const(b.i8_type, '${char_literal_value(node.value)}')
}
.string_interp {
return b.build_string_interp(node)
}
.enum_val {
return b.build_enum_val(id, node)
}
.sizeof_expr {
size := b.m.type_size(b.resolve_type(node.value))
return b.m.get_or_add_const(b.i64_type, '${size}')
}
.ident {
if addr := b.vars[node.value] {
addr_val := b.m.values[addr]
if addr_val.kind == .argument {
return addr
}
if smart_val := b.load_smartcast_sum_value(addr, id) {
return smart_val
}
return b.emit1(.load, b.deref_type(addr), addr)
}
if node.value == 'path_separator' {
return b.m.add_value(.string_literal, b.str_type, '/', 0)
}
if expr_id := b.lookup_const_expr(node.value) {
return b.build_expr(expr_id)
}
if !b.enum_member_dupes[node.value] {
if enum_value := b.enum_member_values[node.value] {
return b.m.get_or_add_const(b.i64_type, enum_value.str())
}
}
if fn_idx := b.fn_ids[node.value] {
return b.m.add_value(.func_ref, b.i64_type, node.value, fn_idx)
}
qualified_fn_name := ssa_fn_name_in_module(b.cur_module, node.value)
if qualified_fn_name != node.value {
if fn_idx := b.fn_ids[qualified_fn_name] {
return b.m.add_value(.func_ref, b.i64_type, qualified_fn_name, fn_idx)
}
}
match node.value {
'min_i8' {
return b.m.get_or_add_const(b.i64_type, '-128')
}
'max_i8' {
return b.m.get_or_add_const(b.i64_type, '127')
}
'min_i16' {
return b.m.get_or_add_const(b.i64_type, '-32768')
}
'max_i16' {
return b.m.get_or_add_const(b.i64_type, '32767')
}
'min_i32', 'min_int' {
return b.m.get_or_add_const(b.i64_type, '-2147483648')
}
'max_i32', 'max_int' {
return b.m.get_or_add_const(b.i64_type, '2147483647')
}
'min_i64' {
return b.m.get_or_add_const(b.i64_type, '-9223372036854775808')
}
'max_i64' {
return b.m.get_or_add_const(b.i64_type, '9223372036854775807')
}
'min_u8', 'min_u16', 'min_u32', 'min_u64' {
return b.m.get_or_add_const(b.i64_type, '0')
}
'max_u8' {
return b.m.get_or_add_const(b.i64_type, '255')
}
'max_u16' {
return b.m.get_or_add_const(b.i64_type, '65535')
}
'max_u32' {
return b.m.get_or_add_const(b.i64_type, '4294967295')
}
'max_u64' {
return b.m.get_or_add_const(b.i64_type, '18446744073709551615')
}
else {}
}
if expr_id := b.lookup_const_expr(node.value) {
return b.build_expr(expr_id)
}
return b.m.get_or_add_const(b.i64_type, '0')
}
.infix {
return b.build_infix(node)
}
.prefix {
return b.build_prefix(node, id)
}
.postfix {
return b.build_postfix(node)
}
.paren {
return b.build_expr(b.a.child(&node, 0))
}
.call {
return b.build_call(id, node)
}
.selector {
return b.build_selector(node)
}
.index {
return b.build_index(id, node)
}
.struct_init {
return b.build_struct_init(node)
}
.array_literal {
return b.build_array_literal(node)
}
.array_init {
return b.build_array_init(node)
}
.map_init {
return b.build_map_init(node)
}
.cast_expr {
return b.build_cast_expr(node)
}
.as_expr {
return b.build_as_expr(node)
}
.is_expr {
return b.build_is_expr(node)
}
.nil_literal {
return b.m.get_or_add_const(b.m.type_store.get_ptr(b.i8_type), '0')
}
.none_expr {
return b.default_value_for_type(b.call_expr_result_type(id, node))
}
.if_expr {
return b.build_if_value(id, node)
}
.match_stmt {
return b.build_match_value(id, node)
}
.or_expr {
return b.build_or_expr(id, node)
}
.field_init {
if node.children_count > 0 {
return b.build_expr(b.a.child(&node, 0))
}
return b.m.get_or_add_const(b.i64_type, '0')
}
.decl_assign {
b.build_decl_assign(node)
return b.m.get_or_add_const(b.i1_type, '1')
}
.assign, .selector_assign, .index_assign {
b.build_stmt(id)
return b.m.get_or_add_const(b.i64_type, '0')
}
.dump_expr {
if node.children_count > 0 {
return b.build_expr(b.a.child(&node, 0))
}
return b.m.get_or_add_const(b.i64_type, '0')
}
.in_expr {
return b.build_in_expr(node)
}
.range {
return b.build_range_expr(node)
}
.block {
return b.build_block_expr(node)
}
else {
eprintln('build_expr: unsupported expr kind: ${node.kind}')
return b.m.get_or_add_const(b.i64_type, '0')
}
}
}
fn (mut b Builder) build_range_expr(node flat.Node) ValueID {
if node.children_count > 0 {
return b.build_expr(b.a.child(&node, 0))
}
return b.m.get_or_add_const(b.i64_type, '0')
}
fn (mut b Builder) build_in_expr(node flat.Node) ValueID {
if node.children_count < 2 {
return b.m.get_or_add_const(b.i1_type, '0')
}
lhs_id := b.a.child(&node, 0)
rhs_id := b.a.child(&node, 1)
rhs := b.a.nodes[int(rhs_id)]
if rhs.kind == .range && rhs.children_count >= 2 {
lhs := b.build_expr(lhs_id)
low := b.build_expr(b.a.child(&rhs, 0))
high := b.build_expr(b.a.child(&rhs, 1))
ge_low := b.emit2(.ge, b.i1_type, lhs, low)
lt_high := b.emit2(.lt, b.i1_type, lhs, high)
return b.emit2(.and_, b.i1_type, ge_low, lt_high)
}
map_type_name := b.expr_type_name_for_map(rhs_id)
key_type_name, _ := map_type_parts(map_type_name)
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
map_ptr := b.map_expr_ptr(rhs_id)
key_val := b.build_expr(lhs_id)
mut key_type := if key_type_name.len > 0 {
b.resolve_type(key_type_name)
} else {
b.value_type(key_val)
}
if key_type == b.void_type {
key_type = b.value_type(key_val)
}
key_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(key_type))
b.emit2(.store, b.void_type, key_val, key_alloca)
key_ptr := if b.value_type(key_alloca) == ptr_i8 {
key_alloca
} else {
b.emit1(.bitcast, ptr_i8, key_alloca)
}
exists_ref := b.m.add_value(.func_ref, b.i1_type, 'map__exists', b.fn_ids['map__exists'])
return b.emit3(.call, b.i1_type, exists_ref, map_ptr, key_ptr)
}
fn (mut b Builder) build_enum_val(id flat.NodeId, node flat.Node) ValueID {
if node.typ.len > 0 {
if value := b.enum_value_for_type(node.typ, node.value) {
return b.m.get_or_add_const(b.i64_type, value.str())
}
}
clean_member0 := node.value.trim_left('.')
if value := b.enum_values[clean_member0] {
return b.m.get_or_add_const(b.i64_type, value.str())
}
if b.tc != unsafe { nil } {
if typ := b.tc.expr_type(id) {
type_name := typ.name()
if value := b.enum_value_for_type(type_name, node.value) {
return b.m.get_or_add_const(b.i64_type, value.str())
}
}
}
if !b.enum_member_dupes[node.value] {
if value := b.enum_member_values[node.value] {
return b.m.get_or_add_const(b.i64_type, value.str())
}
}
return b.m.get_or_add_const(b.i64_type, '0')
}
fn (b &Builder) enum_value_for_type(type_name string, member string) ?int {
if type_name.len == 0 || type_name in ['int', 'unknown'] {
return none
}
clean_member0 := member.trim_left('.')
if value := b.enum_values[clean_member0] {
enum_name := clean_member0.all_before_last('.')
return if b.is_flag_enum_type_name(enum_name) { 1 << value } else { value }
}
clean_member := clean_member0.all_after_last('.')
mut names := []string{}
names << type_name
short_type := type_name.all_after('.')
if short_type != type_name {
names << short_type
}
for name in names {
key := name + '.' + clean_member
if value := b.enum_values[key] {
return if b.is_flag_enum_type_name(name) { 1 << value } else { value }
}
}
return none
}
fn (b &Builder) is_flag_enum_type_name(type_name string) bool {
clean := type_name.trim_left('&')
if clean in b.flag_enum_types {
return true
}
short_name := clean.all_after('.')
if short_name in b.flag_enum_types {
return true
}
if b.tc != unsafe { nil } {
if clean in b.tc.flag_enums {
return true
}
if short_name in b.tc.flag_enums {
return true
}
if !clean.contains('.') && 'builtin.${clean}' in b.tc.flag_enums {
return true
}
}
return clean == 'ArrayFlags'
}
fn (b &Builder) checker_has_flag_enum(type_name string) bool {
if b.tc == unsafe { nil } {
return false
}
if type_name in b.tc.flag_enums {
return true
}
short_name := type_name.all_after('.')
return short_name in b.tc.flag_enums
}
fn (mut b Builder) build_enum_val_with_type(node flat.Node, type_name string) ?ValueID {
if value := b.enum_value_for_type(type_name, node.value) {
return b.m.get_or_add_const(b.i64_type, value.str())
}
return none
}
fn char_literal_value(value string) int {
if value.len == 0 {
return 0
}
if value[0] == `\\` && value.len > 1 {
return match value[1] {
`n` { 10 }
`r` { 13 }
`t` { 9 }
`0` { 0 }
`\\` { 92 }
`'` { 39 }
else { int(value[1]) }
}
}
return int(value[0])
}
fn parse_int_literal(value string) int {
clean := value.replace('_', '')
if clean.len > 2 && clean[0] == `0` && (clean[1] == `x` || clean[1] == `X`) {
mut n := 0
for ch in clean[2..] {
digit := if ch >= `0` && ch <= `9` {
int(ch - `0`)
} else if ch >= `a` && ch <= `f` {
int(ch - `a`) + 10
} else if ch >= `A` && ch <= `F` {
int(ch - `A`) + 10
} else {
0
}
n = n * 16 + digit
}
return n
}
return clean.int()
}
fn (mut b Builder) build_block_expr(node flat.Node) ValueID {
if node.children_count == 0 {
return b.m.get_or_add_const(b.i64_type, '0')
}
for i in 0 .. node.children_count - 1 {
b.build_stmt(b.a.child(&node, i))
}
last_id := b.a.child(&node, node.children_count - 1)
last := b.a.nodes[int(last_id)]
if last.kind == .expr_stmt && last.children_count > 0 {
return b.build_expr(b.a.child(&last, 0))
}
return b.build_expr(last_id)
}
fn (mut b Builder) build_array_literal(node flat.Node) ValueID {
if b.is_fixed_array_type_name(node.typ) {
mut fixed_node := node
fixed_node.value = node.typ
return b.build_fixed_array_init(fixed_node)
}
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
mut values := []ValueID{}
mut elem_type := b.i64_type
for i in 0 .. node.children_count {
value := b.build_expr(b.a.child(&node, i))
if i == 0 {
elem_type = b.value_type(value)
}
values << value
}
elem_size := b.m.type_size(elem_type)
actual_elem_size := if elem_size > 0 { elem_size } else { 8 }
elem_size_const := b.m.get_or_add_const(b.i64_type, '${actual_elem_size}')
zero := b.m.get_or_add_const(b.i64_type, '0')
cap_const := b.m.get_or_add_const(b.i64_type, '${node.children_count}')
new_ref := b.m.add_value(.func_ref, b.array_type, 'array_new', b.fn_ids['array_new'])
arr := b.emit4(.call, b.array_type, new_ref, elem_size_const, zero, cap_const)
arr_alloca := b.emit0(.alloca, ptr_array)
b.emit2(.store, b.void_type, arr, arr_alloca)
push_ref := b.m.add_value(.func_ref, b.void_type, 'array_push', b.fn_ids['array_push'])
for value in values {
value_type := b.value_type(value)
value_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(value_type))
b.emit2(.store, b.void_type, value, value_alloca)
value_arg := if b.value_type(value_alloca) == ptr_i8 {
value_alloca
} else {
b.emit1(.bitcast, ptr_i8, value_alloca)
}
b.emit3(.call, b.void_type, push_ref, arr_alloca, value_arg)
}
return b.emit1(.load, b.array_type, arr_alloca)
}
fn (mut b Builder) build_array_init(node flat.Node) ValueID {
if b.is_fixed_array_type_name(node.value) {
return b.build_fixed_array_init(node)
}
mut elem_type_name := node.value
mut len_val := b.m.get_or_add_const(b.i64_type, '0')
mut cap_val := b.m.get_or_add_const(b.i64_type, '0')
mut init_val := ValueID(0)
if node.value.starts_with('[') {
len_text := node.value.all_after('[').all_before(']')
len_val = b.fixed_array_len_value(len_text)
cap_val = len_val
elem_type_name = node.value.all_after(']')
} else {
mut has_cap := false
for i in 0 .. node.children_count {
child_id := b.a.child(&node, i)
child := b.a.nodes[int(child_id)]
if child.kind == .field_init && child.children_count > 0 {
value := b.build_expr(b.a.child(&child, 0))
if child.value == 'len' {
len_val = value
if !has_cap {
cap_val = value
}
} else if child.value == 'cap' {
cap_val = value
has_cap = true
} else if child.value == 'init' {
init_val = value
}
}
}
}
elem_type := b.resolve_type(elem_type_name)
elem_size := b.m.type_size(elem_type)
actual_elem_size := if elem_size > 0 { elem_size } else { 8 }
elem_size_const := b.m.get_or_add_const(b.i64_type, '${actual_elem_size}')
new_ref := b.m.add_value(.func_ref, b.array_type, 'array_new', b.fn_ids['array_new'])
arr := b.emit4(.call, b.array_type, new_ref, elem_size_const, len_val, cap_val)
if init_val == ValueID(0) {
return arr
}
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
arr_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.array_type))
i_alloca := b.emit0(.alloca, ptr_i64)
b.emit2(.store, b.void_type, arr, arr_alloca)
zero := b.m.get_or_add_const(b.i64_type, '0')
one := b.m.get_or_add_const(b.i64_type, '1')
b.emit2(.store, b.void_type, zero, i_alloca)
loop := b.m.add_block(b.cur_func, 'array_init_fill_loop')
body := b.m.add_block(b.cur_func, 'array_init_fill_body')
done := b.m.add_block(b.cur_func, 'array_init_fill_done')
b.emit1(.jmp, b.void_type, ValueID(loop))
b.cur_block = loop
i_val := b.emit1(.load, b.i64_type, i_alloca)
more := b.emit2(.lt, b.i1_type, i_val, len_val)
b.emit3(.br, b.void_type, more, ValueID(body), ValueID(done))
b.cur_block = body
data_ptr := b.get_field_ptr(arr_alloca, 'data')
data := b.emit1(.load, ptr_i8, data_ptr)
offset := b.emit2(.mul, b.i64_type, i_val, elem_size_const)
elem_ptr_raw := b.emit2(.add, ptr_i8, data, offset)
elem_ptr := b.emit1(.bitcast, b.m.type_store.get_ptr(elem_type), elem_ptr_raw)
elem_value := if b.is_int_type(b.value_type(init_val)) && b.is_int_type(elem_type) {
b.coerce_int_value(init_val, elem_type)
} else {
init_val
}
b.emit2(.store, b.void_type, elem_value, elem_ptr)
next_i := b.emit2(.add, b.i64_type, i_val, one)
b.emit2(.store, b.void_type, next_i, i_alloca)
b.emit1(.jmp, b.void_type, ValueID(loop))
b.cur_block = done
return b.emit1(.load, b.array_type, arr_alloca)
}
fn (mut b Builder) fixed_array_len_value(len_text string) ValueID {
if len_text.len == 0 {
return b.m.get_or_add_const(b.i64_type, '0')
}
if expr_id := b.lookup_const_expr(len_text) {
if literal := b.const_int_literal_value(expr_id) {
return b.m.get_or_add_const(b.i64_type, literal)
}
}
return b.m.get_or_add_const(b.i64_type, len_text)
}
fn (mut b Builder) build_as_expr(node flat.Node) ValueID {
if node.children_count == 0 {
return b.default_value_for_type(b.resolve_type(node.value))
}
child_id := b.a.child(&node, 0)
child_type_name := b.checked_expr_type_name(child_id)
sum_name := b.canonical_sum_type_name(child_type_name) or { return b.build_expr(child_id) }
field_name := sum_variant_field_name(node.value)
sum_val := b.build_expr(child_id)
sum_type := b.resolve_type(sum_name)
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(sum_type))
b.emit2(.store, b.void_type, sum_val, alloca)
field_ptr := b.get_field_ptr(alloca, field_name)
return b.emit1(.load, b.deref_type(field_ptr), field_ptr)
}
fn (mut b Builder) build_is_expr(node flat.Node) ValueID {
if node.children_count == 0 {
return b.m.get_or_add_const(b.i1_type, '0')
}
child_id := b.a.child(&node, 0)
child_type_name := b.checked_expr_type_name(child_id)
sum_name := b.canonical_sum_type_name(child_type_name) or {
return b.m.get_or_add_const(b.i1_type, '1')
}
tag := b.sum_variant_index(sum_name, node.value)
if tag <= 0 {
return b.m.get_or_add_const(b.i1_type, '0')
}
sum_val := b.build_expr(child_id)
sum_type := b.resolve_type(sum_name)
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(sum_type))
b.emit2(.store, b.void_type, sum_val, alloca)
tag_ptr := b.get_field_ptr(alloca, 'typ')
tag_val := b.emit1(.load, b.deref_type(tag_ptr), tag_ptr)
tag_const := b.m.get_or_add_const(b.value_type(tag_val), '${tag}')
return b.emit2(.eq, b.i1_type, tag_val, tag_const)
}
fn (mut b Builder) build_map_init(node flat.Node) ValueID {
key_type_name, val_type_name := map_type_parts(node.value)
if key_type_name.len == 0 || val_type_name.len == 0 {
return b.default_value_for_type(b.map_type)
}
key_type := b.resolve_type(key_type_name)
val_type := b.resolve_type(val_type_name)
key_size := b.m.get_or_add_const(b.i64_type, '${b.m.type_size(key_type)}')
val_size := b.m.get_or_add_const(b.i64_type, '${b.m.type_size(val_type)}')
zero := b.m.get_or_add_const(b.i64_type, '0')
mut args := []ValueID{}
if fn_id := b.fn_ids['new_map'] {
fn_ref := b.m.add_value(.func_ref, b.void_type, 'new_map', fn_id)
args << fn_ref
args << key_size
args << val_size
args << zero
args << zero
args << zero
args << zero
} else {
return b.default_value_for_type(b.map_type)
}
map_val := b.m.add_instr(.call, b.cur_block, b.map_type, args)
if node.children_count == 0 {
return map_val
}
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_map := b.m.type_store.get_ptr(b.map_type)
map_alloca := b.emit0(.alloca, ptr_map)
b.emit2(.store, b.void_type, map_val, map_alloca)
mut i := 0
for i < node.children_count {
key_id := b.a.child(&node, i)
val_id := b.a.child(&node, i + 1)
key_val := b.build_expr(key_id)
val_val := b.build_expr(val_id)
key_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(key_type))
val_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(val_type))
b.emit2(.store, b.void_type, key_val, key_alloca)
b.emit2(.store, b.void_type, val_val, val_alloca)
key_ptr := if b.value_type(key_alloca) == ptr_i8 {
key_alloca
} else {
b.emit1(.bitcast, ptr_i8, key_alloca)
}
val_ptr := if b.value_type(val_alloca) == ptr_i8 {
val_alloca
} else {
b.emit1(.bitcast, ptr_i8, val_alloca)
}
if set_id := b.fn_ids['v3_map_set_sized'] {
set_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_set_sized', set_id)
mut set_args := []ValueID{}
set_args << set_ref
set_args << map_alloca
set_args << key_ptr
set_args << val_ptr
set_args << key_size
set_args << val_size
b.m.add_instr(.call, b.cur_block, b.void_type, set_args)
}
i += 2
}
return b.emit1(.load, b.map_type, map_alloca)
}
fn (b &Builder) is_fixed_array_type_name(type_name string) bool {
if type_name.starts_with('[') {
idx := type_name.index_u8(`]`)
return idx > 1 && idx + 1 < type_name.len
}
if type_name.contains('[') && type_name.ends_with(']') {
idx := type_name.index_u8(`[`)
return idx > 0 && idx < type_name.len - 1
}
return false
}
fn (b &Builder) fixed_array_elem_type_name(type_name string) string {
if type_name.starts_with('[') {
idx := type_name.index_u8(`]`)
if idx > 0 && idx + 1 < type_name.len {
return type_name[idx + 1..]
}
} else if type_name.contains('[') && type_name.ends_with(']') {
idx := type_name.index_u8(`[`)
if idx > 0 {
return type_name[..idx]
}
}
return 'u8'
}
fn (b &Builder) fixed_array_len_text(type_name string) string {
mut raw_len := ''
if type_name.starts_with('[') {
idx := type_name.index_u8(`]`)
if idx > 1 {
raw_len = type_name[1..idx]
}
} else if type_name.contains('[') && type_name.ends_with(']') {
idx := type_name.index_u8(`[`)
if idx > 0 && idx < type_name.len - 1 {
raw_len = type_name[idx + 1..type_name.len - 1]
}
}
if raw_len.len == 0 {
return '1'
}
clean_len := raw_len.replace('_', '')
if b.is_decimal_int_text(clean_len) {
return clean_len
}
if expr_id := b.lookup_const_expr(raw_len) {
if value := b.const_int_literal_value(expr_id) {
return value.replace('_', '')
}
}
return '1'
}
fn (b &Builder) is_decimal_int_text(text string) bool {
if text.len == 0 {
return false
}
for ch in text {
if ch < `0` || ch > `9` {
return false
}
}
return true
}
fn (mut b Builder) build_fixed_array_init(node flat.Node) ValueID {
elem_type_name := b.fixed_array_elem_type_name(node.value)
elem_type := b.resolve_type(elem_type_name)
ptr_elem := b.m.type_store.get_ptr(elem_type)
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
len_text := b.fixed_array_len_text(node.value)
len_val := b.m.get_or_add_const(b.i64_type, len_text)
alloca := b.emit1(.alloca, ptr_elem, len_val)
data := if ptr_elem == ptr_i8 {
alloca
} else {
b.emit1(.bitcast, ptr_i8, alloca)
}
elem_size := b.m.type_size(elem_type)
actual_elem_size := if elem_size > 0 { elem_size } else { 8 }
byte_count := b.m.get_or_add_const(b.i64_type, '${len_text.int() * actual_elem_size}')
zero := b.m.get_or_add_const(b.i64_type, '0')
memset_ref := b.m.add_value(.func_ref, b.void_type, 'memset', b.fn_ids['memset'])
b.emit4(.call, ptr_i8, memset_ref, data, zero, byte_count)
mut elem_index := 0
for i in 0 .. node.children_count {
child_id := b.a.child(&node, i)
child := b.a.nodes[int(child_id)]
if child.kind == .field_init {
continue
}
value := b.build_expr(child_id)
offset := b.m.get_or_add_const(b.i64_type, '${elem_index * actual_elem_size}')
slot_i8 := b.emit2(.get_element_ptr, ptr_i8, data, offset)
slot := if ptr_elem == ptr_i8 {
slot_i8
} else {
b.emit1(.bitcast, ptr_elem, slot_i8)
}
b.emit2(.store, b.void_type, value, slot)
elem_index++
}
return alloca
}
fn (mut b Builder) build_or_expr(id flat.NodeId, node flat.Node) ValueID {
if node.children_count < 2 {
return b.m.get_or_add_const(b.i64_type, '0')
}
expr_id := b.a.child(&node, 0)
expr := b.a.nodes[int(expr_id)]
if expr.kind == .index && expr.children_count >= 2 && expr.value != 'range' {
if result := b.build_map_index_or_expr(id, expr, b.a.child(&node, 1)) {
return result
}
}
if result := b.build_option_or_expr(id, expr_id, b.a.child(&node, 1)) {
return result
}
return b.default_value_for_type(b.or_result_type(id, node.typ))
}
fn (mut b Builder) build_option_or_expr(id flat.NodeId, expr_id flat.NodeId, fallback_id flat.NodeId) ?ValueID {
opt_val := b.build_expr(expr_id)
opt_typ := b.value_type(opt_val)
value_typ := b.option_value_type(opt_typ)
if value_typ == TypeID(0) {
return none
}
if value_typ == b.void_type {
return none
}
result_type := b.or_result_type(id, b.option_payload_type_name(expr_id))
result_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(result_type))
opt_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(opt_typ))
b.emit2(.store, b.void_type, opt_val, opt_alloca)
ok_ptr := b.get_field_ptr(opt_alloca, 'ok')
ok := b.emit1(.load, b.i1_type, ok_ptr)
then_block := b.m.add_block(b.cur_func, 'or_option_some')
else_block := b.m.add_block(b.cur_func, 'or_option_else')
merge_block := b.m.add_block(b.cur_func, 'or_option_merge')
b.emit3(.br, b.void_type, ok, ValueID(then_block), ValueID(else_block))
b.cur_block = then_block
value_ptr := b.get_field_ptr(opt_alloca, 'value')
mut value := b.emit1(.load, value_typ, value_ptr)
if value_typ != result_type {
value = b.coerce_int_value(value, result_type)
}
b.emit2(.store, b.void_type, value, result_alloca)
if !b.current_block_terminated() {
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
b.cur_block = else_block
fallback_value := b.build_or_fallback_value(fallback_id, result_type)
if !b.current_block_terminated() {
b.emit2(.store, b.void_type, fallback_value, result_alloca)
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
b.cur_block = merge_block
return b.emit1(.load, result_type, result_alloca)
}
fn (mut b Builder) build_map_index_or_expr(id flat.NodeId, index_node flat.Node, fallback_id flat.NodeId) ?ValueID {
base_id := b.a.child(&index_node, 0)
key_id := b.a.child(&index_node, 1)
map_type_name := b.expr_type_name_for_map(base_id)
key_type_name, val_type_name := map_type_parts(map_type_name)
if key_type_name.len == 0 || val_type_name.len == 0 {
return none
}
result_type := b.or_result_type(id, val_type_name)
ptr_result := b.m.type_store.get_ptr(result_type)
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
result_alloca := b.emit0(.alloca, ptr_result)
map_ptr := b.map_expr_ptr(base_id)
key_val := b.build_expr(key_id)
key_type := b.resolve_type(key_type_name)
key_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(key_type))
b.emit2(.store, b.void_type, key_val, key_alloca)
key_ptr := if b.value_type(key_alloca) == ptr_i8 {
key_alloca
} else {
b.emit1(.bitcast, ptr_i8, key_alloca)
}
get_ref := b.m.add_value(.func_ref, ptr_i8, 'map__get_check', b.fn_ids['map__get_check'])
value_ptr := b.emit3(.call, ptr_i8, get_ref, map_ptr, key_ptr)
zero_ptr := b.m.get_or_add_const(ptr_i8, '0')
found := b.emit2(.ne, b.i1_type, value_ptr, zero_ptr)
then_block := b.m.add_block(b.cur_func, 'or_map_found')
else_block := b.m.add_block(b.cur_func, 'or_map_else')
merge_block := b.m.add_block(b.cur_func, 'or_map_merge')
b.emit3(.br, b.void_type, found, ValueID(then_block), ValueID(else_block))
b.cur_block = then_block
typed_value_ptr := if ptr_result == ptr_i8 {
value_ptr
} else {
b.emit1(.bitcast, ptr_result, value_ptr)
}
found_value := b.emit1(.load, result_type, typed_value_ptr)
b.emit2(.store, b.void_type, found_value, result_alloca)
if !b.current_block_terminated() {
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
b.cur_block = else_block
fallback_value := b.build_or_fallback_value(fallback_id, result_type)
if !b.current_block_terminated() {
b.emit2(.store, b.void_type, fallback_value, result_alloca)
b.emit1(.jmp, b.void_type, ValueID(merge_block))
}
b.cur_block = merge_block
return b.emit1(.load, result_type, result_alloca)
}
fn (mut b Builder) map_expr_ptr(id flat.NodeId) ValueID {
node := b.a.nodes[int(id)]
if node.kind == .ident || node.kind == .selector {
addr := b.build_lvalue_addr(id)
if b.value_type(addr) == b.m.type_store.get_ptr(b.map_type) {
return addr
}
}
val := b.build_expr(id)
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.value_type(val)))
b.emit2(.store, b.void_type, val, alloca)
return alloca
}
fn (mut b Builder) build_or_fallback_value(id flat.NodeId, result_type TypeID) ValueID {
if int(id) < 0 {
return b.default_value_for_type(result_type)
}
node := b.a.nodes[int(id)]
if node.kind != .block {
return b.build_expr(id)
}
if node.children_count == 0 {
return b.default_value_for_type(result_type)
}
for i in 0 .. node.children_count {
child_id := b.a.child(&node, i)
child := b.a.nodes[int(child_id)]
is_last := i == node.children_count - 1
if is_last {
if child.kind == .expr_stmt && child.children_count > 0 {
return b.build_expr(b.a.child(&child, 0))
}
if b.is_stmt_kind(child.kind) {
b.build_stmt(child_id)
return b.default_value_for_type(result_type)
}
return b.build_expr(child_id)
}
b.build_stmt(child_id)
if b.current_block_terminated() {
return b.default_value_for_type(result_type)
}
}
return b.default_value_for_type(result_type)
}
fn (mut b Builder) or_result_type(id flat.NodeId, fallback_type string) TypeID {
if b.tc != unsafe { nil } {
if typ := b.tc.expr_type(id) {
name := typ.name()
if name.len > 0 && name != 'unknown' {
return b.resolve_type(name)
}
}
}
return b.resolve_type(fallback_type)
}
fn (mut b Builder) default_value_for_type(typ TypeID) ValueID {
if b.is_option_type(typ) {
return b.build_option_value(typ, false, ValueID(0))
}
if typ == b.str_type {
return b.m.add_value(.string_literal, b.str_type, '', 0)
}
if typ == b.array_type {
fn_ref := b.m.add_value(.func_ref, b.array_type, 'array_new', b.fn_ids['array_new'])
zero := b.m.get_or_add_const(b.i64_type, '0')
one := b.m.get_or_add_const(b.i64_type, '1')
return b.emit4(.call, b.array_type, fn_ref, one, zero, zero)
}
if typ > 0 && typ < b.m.type_store.types.len && b.m.type_store.types[typ].kind == .ptr_t {
return b.m.get_or_add_const(typ, '0')
}
return b.m.get_or_add_const(typ, '0')
}
fn (mut b Builder) build_option_value(opt_typ TypeID, ok bool, raw_value ValueID) ValueID {
ptr_opt := b.m.type_store.get_ptr(opt_typ)
alloca := b.emit0(.alloca, ptr_opt)
ok_ptr := b.block_struct_field_ptr(b.cur_block, alloca, opt_typ, 0)
ok_val := b.m.get_or_add_const(b.i1_type, if ok { '1' } else { '0' })
b.emit2(.store, b.void_type, ok_val, ok_ptr)
value_typ := b.option_value_type(opt_typ)
if value_typ != b.void_type {
value_ptr := b.block_struct_field_ptr(b.cur_block, alloca, opt_typ, 1)
mut value := raw_value
if !ok || value <= 0 {
value = b.default_value_for_type(value_typ)
}
value_type := b.value_type(value)
if value_type != value_typ {
if b.is_pointer_type(value_typ) && b.is_pointer_type(value_type) {
value = b.emit1(.bitcast, value_typ, value)
} else {
value = b.coerce_int_value(value, value_typ)
if b.value_type(value) != value_typ {
value = b.default_value_for_type(value_typ)
}
}
}
b.emit2(.store, b.void_type, value, value_ptr)
}
return b.emit1(.load, opt_typ, alloca)
}
fn (b &Builder) is_stmt_kind(kind flat.NodeKind) bool {
return kind in [.expr_stmt, .decl_assign, .assign, .selector_assign, .index_assign, .return_stmt,
.defer_stmt, .for_stmt, .for_in_stmt, .break_stmt, .continue_stmt, .if_expr, .match_stmt,
.block, .assert_stmt, .goto_stmt, .label_stmt]
}
fn (b &Builder) current_block_terminated() bool {
if b.cur_block < 0 || b.cur_block >= b.m.blocks.len {
return true
}
blk := b.m.blocks[b.cur_block]
return blk.instrs.len > 0 && b.is_terminator(blk.instrs.last())
}
fn (mut b Builder) build_infix(node flat.Node) ValueID {
if node.op == .logical_and || node.op == .logical_or {
return b.build_short_circuit(node)
}
if node.op == .left_shift && node.value == 'push' {
return b.build_array_push_expr(node)
}
if result := b.build_qualified_infix_call(node) {
return result
}
lhs_id := b.a.child(&node, 0)
rhs_id := b.a.child(&node, 1)
lhs_node := b.a.nodes[int(lhs_id)]
rhs_node := b.a.nodes[int(rhs_id)]
lhs_type_name := b.checked_expr_type_name(lhs_id)
rhs_type_name := b.checked_expr_type_name(rhs_id)
lhs := if lhs_node.kind == .enum_val && rhs_type_name.len > 0 {
b.build_enum_val_with_type(lhs_node, rhs_type_name) or { b.build_expr(lhs_id) }
} else {
b.build_expr(lhs_id)
}
rhs := if rhs_node.kind == .enum_val && lhs_type_name.len > 0 {
b.build_enum_val_with_type(rhs_node, lhs_type_name) or { b.build_expr(rhs_id) }
} else {
b.build_expr(rhs_id)
}
if b.value_type(lhs) == b.str_type || b.value_type(rhs) == b.str_type {
return b.build_string_infix(node.op, lhs, rhs)
}
lhs_type := b.value_type(lhs)
mut op := OpCode.add
if b.is_float_type(lhs_type) {
op = OpCode.fadd
match node.op {
.plus { op = OpCode.fadd }
.minus { op = OpCode.fsub }
.mul { op = OpCode.fmul }
.div { op = OpCode.fdiv }
.mod { op = OpCode.frem }
.eq { op = OpCode.eq }
.ne { op = OpCode.ne }
.lt { op = OpCode.lt }
.gt { op = OpCode.gt }
.le { op = OpCode.le }
.ge { op = OpCode.ge }
else {}
}
} else {
op = OpCode.add
match node.op {
.plus {
op = OpCode.add
}
.minus {
op = OpCode.sub
}
.mul {
op = OpCode.mul
}
.div {
op = if b.is_unsigned_type(lhs_type) { OpCode.udiv } else { OpCode.sdiv }
}
.mod {
op = if b.is_unsigned_type(lhs_type) { OpCode.urem } else { OpCode.srem }
}
.amp {
op = OpCode.and_
}
.pipe {
op = OpCode.or_
}
.xor {
op = OpCode.xor
}
.left_shift {
op = OpCode.shl
}
.right_shift {
op = if b.is_unsigned_type(lhs_type) { OpCode.lshr } else { OpCode.ashr }
}
.eq {
op = OpCode.eq
}
.ne {
op = OpCode.ne
}
.lt {
op = if b.is_unsigned_type(lhs_type) { OpCode.ult } else { OpCode.lt }
}
.gt {
op = if b.is_unsigned_type(lhs_type) { OpCode.ugt } else { OpCode.gt }
}
.le {
op = if b.is_unsigned_type(lhs_type) { OpCode.ule } else { OpCode.le }
}
.ge {
op = if b.is_unsigned_type(lhs_type) { OpCode.uge } else { OpCode.ge }
}
else {
op = OpCode.add
}
}
}
result_type := if node.op in [.eq, .ne, .lt, .gt, .le, .ge] { b.i1_type } else { lhs_type }
return b.emit2(op, result_type, lhs, rhs)
}
fn (mut b Builder) build_qualified_infix_call(node flat.Node) ?ValueID {
if node.children_count < 2 || node.op !in [.dot, .amp] {
return none
}
lhs_id := b.a.child(&node, 0)
rhs_id := b.a.child(&node, 1)
lhs_name := b.qualified_expr_name(lhs_id)
if lhs_name.len == 0 {
return none
}
if _ := b.vars[lhs_name] {
return none
}
rhs := b.a.nodes[int(rhs_id)]
if rhs.kind != .call || rhs.children_count == 0 {
return none
}
fn_node := b.a.child_node(&rhs, 0)
if fn_node.kind != .ident || fn_node.value.len == 0 {
return none
}
for candidate in ['${lhs_name}.${fn_node.value}', '${lhs_name}__${fn_node.value}'] {
if candidate in b.fn_ids {
return b.build_direct_call(candidate, rhs)
}
}
return none
}
fn (mut b Builder) build_direct_call(resolved_name string, node flat.Node) ValueID {
fn_idx := b.fn_ids[resolved_name] or { panic('ssa: unknown function `${resolved_name}`') }
fn_ref := b.m.add_value(.func_ref, b.void_type, resolved_name, fn_idx)
ret_type := b.m.funcs[fn_idx].typ
mut param_types := []TypeID{}
if ft_id := b.fn_types[resolved_name] {
ft := b.m.type_store.types[ft_id]
param_types = ft.params.clone()
}
mut args := []ValueID{}
args << fn_ref
for i in 1 .. node.children_count {
arg_id := b.a.child(&node, i)
param_idx := i - 1
if param_idx < param_types.len {
param_type := param_types[param_idx]
if b.is_option_type(param_type) {
arg_node := b.a.nodes[int(arg_id)]
if arg_node.kind == .none_expr {
args << b.default_value_for_type(param_type)
continue
}
mut arg := b.build_expr(arg_id)
if b.value_type(arg) != param_type {
arg = b.build_option_value(param_type, true, arg)
}
args << arg
continue
}
pt := b.m.type_store.types[param_type]
if pt.kind == .ptr_t {
arg_node := b.a.nodes[int(arg_id)]
if arg_node.kind == .ident {
if addr := b.vars[arg_node.value] {
if b.should_pass_ident_addr_for_ptr_param(addr, param_type) {
args << addr
continue
}
}
} else if arg_node.kind == .selector {
addr := b.build_selector_addr(arg_node)
if b.should_pass_ident_addr_for_ptr_param(addr, param_type) {
args << addr
continue
}
} else if arg_node.kind == .index {
addr := b.build_index_addr(arg_id, arg_node)
if b.should_pass_ident_addr_for_ptr_param(addr, param_type) {
args << addr
continue
}
}
}
}
args << b.build_expr(arg_id)
}
return b.m.add_instr(.call, b.cur_block, ret_type, args)
}
fn (b &Builder) qualified_expr_name(id flat.NodeId) string {
if int(id) < 0 {
return ''
}
node := b.a.nodes[int(id)]
match node.kind {
.ident {
return node.value
}
.selector {
if node.children_count == 0 {
return node.value
}
base := b.qualified_expr_name(b.a.child(&node, 0))
if base.len == 0 {
return node.value
}
return '${base}.${node.value}'
}
else {
return ''
}
}
}
fn (mut b Builder) build_string_infix(op flat.Op, lhs ValueID, rhs ValueID) ValueID {
match op {
.plus {
return b.emit_runtime_call('string__plus', b.str_type, [lhs, rhs])
}
.eq {
return b.emit_runtime_call('string__eq', b.i1_type, [lhs, rhs])
}
.ne {
eq := b.emit_runtime_call('string__eq', b.i1_type, [lhs, rhs])
zero := b.m.get_or_add_const(b.i1_type, '0')
return b.emit2(.eq, b.i1_type, eq, zero)
}
.lt {
return b.emit_runtime_call('string__lt', b.i1_type, [lhs, rhs])
}
.gt {
return b.emit_runtime_call('string__lt', b.i1_type, [rhs, lhs])
}
.le {
gt := b.emit_runtime_call('string__lt', b.i1_type, [rhs, lhs])
zero := b.m.get_or_add_const(b.i1_type, '0')
return b.emit2(.eq, b.i1_type, gt, zero)
}
.ge {
lt := b.emit_runtime_call('string__lt', b.i1_type, [lhs, rhs])
zero := b.m.get_or_add_const(b.i1_type, '0')
return b.emit2(.eq, b.i1_type, lt, zero)
}
else {}
}
return b.m.get_or_add_const(b.i64_type, '0')
}
fn (mut b Builder) emit_runtime_call(name string, ret_type TypeID, values []ValueID) ValueID {
if fn_idx := b.fn_ids[name] {
fn_ref := b.m.add_value(.func_ref, ret_type, name, fn_idx)
mut args := []ValueID{}
args << fn_ref
args << values
return b.m.add_instr(.call, b.cur_block, ret_type, args)
}
return b.m.get_or_add_const(ret_type, '0')
}
fn (mut b Builder) build_cast_expr(node flat.Node) ValueID {
if node.children_count == 0 {
return b.m.get_or_add_const(b.i64_type, '0')
}
target_name := if node.typ.len > 0 { node.typ } else { node.value }
if target_name.len == 0 {
return b.build_expr(b.a.child(&node, 0))
}
if value := b.build_sum_type_cast(target_name, b.a.child(&node, 0)) {
return value
}
value := b.build_expr(b.a.child(&node, 0))
target_type := b.resolve_type(target_name)
from_type := b.value_type(value)
if target_type == from_type {
return value
}
if b.is_pointer_type(target_type) || b.is_pointer_type(from_type) {
return b.emit1(.bitcast, target_type, value)
}
if b.is_int_type(from_type) && b.is_int_type(target_type) {
from_width := b.scalar_type_width(from_type)
to_width := b.scalar_type_width(target_type)
if to_width > 0 && from_width > 0 && to_width < from_width {
return b.emit1(.trunc, target_type, value)
}
if to_width > from_width {
if b.is_unsigned_type(from_type) {
return b.emit1(.zext, target_type, value)
}
return b.emit1(.sext, target_type, value)
}
return b.emit1(.bitcast, target_type, value)
}
if b.is_float_type(from_type) && b.is_int_type(target_type) {
if b.is_unsigned_type(target_type) {
return b.emit1(.fptoui, target_type, value)
}
return b.emit1(.fptosi, target_type, value)
}
if b.is_int_type(from_type) && b.is_float_type(target_type) {
if b.is_unsigned_type(from_type) {
return b.emit1(.uitofp, target_type, value)
}
return b.emit1(.sitofp, target_type, value)
}
return b.emit1(.bitcast, target_type, value)
}
fn (mut b Builder) build_sum_type_cast(target_name string, value_expr flat.NodeId) ?ValueID {
sum_name := b.canonical_sum_type_name(target_name) or { return none }
sum_type := b.resolve_type(sum_name)
if sum_type <= 0 {
return none
}
value := b.build_expr(value_expr)
variant := b.sum_variant_for_expr(sum_name, value_expr, value) or { return none }
tag := b.sum_variant_index(sum_name, variant)
if tag <= 0 {
return none
}
return b.wrap_sum_value(sum_type, variant, tag, value)
}
fn (mut b Builder) wrap_sum_value(sum_type TypeID, variant string, tag int, value ValueID) ValueID {
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(sum_type))
tag_ptr := b.get_field_ptr(alloca, 'typ')
tag_val := b.m.get_or_add_const(b.i32_type, tag.str())
b.emit2(.store, b.void_type, tag_val, tag_ptr)
field_ptr := b.get_field_ptr(alloca, sum_variant_field_name(variant))
field_type := b.deref_type(field_ptr)
field_val := b.coerce_store_value(value, field_type)
b.emit2(.store, b.void_type, field_val, field_ptr)
return b.emit1(.load, sum_type, alloca)
}
fn (mut b Builder) heap_copy_value(value ValueID, value_type TypeID) ValueID {
if b.is_pointer_type(value_type) {
return value
}
ptr_type := b.m.type_store.get_ptr(value_type)
alloca := b.emit0(.alloca, ptr_type)
b.emit2(.store, b.void_type, value, alloca)
size := b.m.type_size(value_type)
size_const := b.m.get_or_add_const(b.i64_type, '${size}')
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
src := b.emit1(.bitcast, ptr_i8, alloca)
memdup_ref := b.m.add_value(.func_ref, ptr_i8, 'memdup', b.fn_ids['memdup'])
copied := b.emit3(.call, ptr_i8, memdup_ref, src, size_const)
return b.emit1(.bitcast, ptr_type, copied)
}
fn (b &Builder) canonical_sum_type_name(name string) ?string {
if !name.contains('.') && b.cur_module.len > 0 && b.cur_module != 'main'
&& b.cur_module != 'builtin' {
qualified := '${b.cur_module}.${name}'
if canonical := b.sum_type_canonical[qualified] {
return canonical
}
}
if canonical := b.sum_type_canonical[name] {
return canonical
}
if name.contains('.') {
return none
}
short_name := name.all_after('.')
if canonical := b.sum_type_canonical[short_name] {
return canonical
}
return none
}
fn (mut b Builder) sum_variant_for_expr(sum_name string, expr_id flat.NodeId, value ValueID) ?string {
mut type_name := ''
if b.tc != unsafe { nil } && int(expr_id) >= 0 && int(expr_id) < b.a.nodes.len {
if typ := b.tc.expr_type(expr_id) {
type_name = typ.name()
}
if type_name.len == 0 || type_name == 'unknown' {
type_name = b.tc.resolve_type(expr_id).name()
}
}
if type_name.len == 0 || type_name == 'unknown' {
type_name = b.infer_v_type(expr_id)
}
if variant := b.find_sum_variant(sum_name, type_name) {
return variant
}
value_type := b.value_type(value)
variants := b.sum_type_variants[sum_name] or { []string{} }
for variant in variants {
variant_type := b.resolve_type(variant)
if variant_type == value_type {
return variant
}
}
return none
}
fn (b &Builder) find_sum_variant(sum_name string, type_name string) ?string {
if type_name.len == 0 {
return none
}
short_type := type_name.all_after('.')
variants := b.sum_type_variants[sum_name] or { []string{} }
for variant in variants {
short_variant := variant.all_after('.')
if variant == type_name || short_variant == short_type {
return variant
}
}
return none
}
fn (b &Builder) sum_variant_index(sum_name string, variant string) int {
short_variant := variant.all_after('.')
variants := b.sum_type_variants[sum_name] or { []string{} }
for i, candidate in variants {
short_candidate := candidate.all_after('.')
if candidate == variant || short_candidate == short_variant {
return i + 1
}
}
return 0
}
fn (b &Builder) sum_variant_references_sum(variant string, sum_name string) bool {
if b.tc == unsafe { nil } {
return false
}
mut visited := map[string]bool{}
return b.sum_variant_references_sum_inner(variant, sum_name, mut visited)
}
fn (b &Builder) sum_variant_references_sum_inner(variant string, sum_name string, mut visited map[string]bool) bool {
if variant in visited {
return false
}
visited[variant] = true
variant_module := if variant.contains('.') { variant.all_before_last('.') } else { '' }
fields := b.tc.structs[variant] or {
short := variant.all_after('.')
b.tc.structs[short] or { []types.StructField{} }
}
for field in fields {
if b.type_name_matches_sum(field.typ.name(), sum_name, variant_module) {
return true
}
field_name := field.typ.name().trim_left('[]&')
if nested := b.find_sum_variant(sum_name, field_name) {
if b.sum_variant_references_sum_inner(nested, sum_name, mut visited) {
return true
}
}
}
return false
}
fn (b &Builder) type_name_matches_sum(type_name string, sum_name string, module_name string) bool {
mut clean := type_name
for clean.starts_with('[]') || clean.starts_with('&') || clean.starts_with('?')
|| clean.starts_with('!') {
if clean.starts_with('[]') {
clean = clean[2..]
} else {
clean = clean[1..]
}
}
if clean == sum_name || clean.all_after('.') == sum_name.all_after('.') {
return true
}
if !clean.contains('.') && module_name.len > 0 {
return '${module_name}.${clean}' == sum_name
}
return false
}
fn (b &Builder) is_sum_type_id(typ_id TypeID) bool {
if typ_id <= 0 {
return false
}
for sum_name, _ in b.sum_type_variants {
if canonical := b.sum_type_canonical[sum_name] {
if sum_typ := b.struct_types[canonical] {
if sum_typ == typ_id {
return true
}
}
}
}
return false
}
fn (b &Builder) sum_type_has_field(typ_id TypeID, field_name string) bool {
if !b.is_sum_type_id(typ_id) {
return false
}
for sum_name, variants in b.sum_type_variants {
if canonical := b.sum_type_canonical[sum_name] {
if sum_typ := b.struct_types[canonical] {
if sum_typ != typ_id {
continue
}
for variant in variants {
if sum_variant_field_name(variant) == field_name {
return true
}
}
}
}
}
return false
}
fn sum_variant_field_name(variant string) string {
if variant.starts_with('[]') {
return '_Array_${ssa_c_name(variant[2..])}'
}
if variant.starts_with('map[') {
return '_Map_${ssa_c_name(variant[4..].replace(']', '_'))}'
}
return match variant {
'int' { '_int' }
'i8' { '_i8' }
'i16' { '_i16' }
'i64' { '_i64' }
'u8', 'byte' { '_u8' }
'u16' { '_u16' }
'u32' { '_u32' }
'u64' { '_u64' }
'f32' { '_f32' }
'f64' { '_f64' }
'bool' { '_bool' }
'string' { '_string' }
else { ssa_c_name(variant) }
}
}
fn ssa_c_name(name string) string {
return name.replace('.', '__').replace('[]', 'Array_').replace('[', '_').replace(']', '_').replace('&',
'ptr_').replace(' ', '_').replace(',', '_')
}
fn (mut b Builder) build_array_push_expr(node flat.Node) ValueID {
if node.children_count < 2 {
return b.m.get_or_add_const(b.i64_type, '0')
}
lhs_id := b.a.child(&node, 0)
rhs_id := b.a.child(&node, 1)
arr_ptr := b.build_lvalue_addr(lhs_id)
rhs_val := b.build_expr(rhs_id)
if node.value == 'push_many' || b.value_type(rhs_val) == b.array_type {
if fn_id := b.fn_ids['array_push_many'] {
fn_ref := b.m.add_value(.func_ref, b.void_type, 'array_push_many', fn_id)
return b.emit3(.call, b.void_type, fn_ref, arr_ptr, rhs_val)
}
}
elem_type := b.value_type(rhs_val)
elem_ptr := b.emit0(.alloca, b.m.type_store.get_ptr(elem_type))
b.emit2(.store, b.void_type, rhs_val, elem_ptr)
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
elem_arg := if b.value_type(elem_ptr) == ptr_i8 {
elem_ptr
} else {
b.emit1(.bitcast, ptr_i8, elem_ptr)
}
if fn_id := b.fn_ids['array_push'] {
fn_ref := b.m.add_value(.func_ref, b.void_type, 'array_push', fn_id)
return b.emit3(.call, b.void_type, fn_ref, arr_ptr, elem_arg)
}
return b.m.get_or_add_const(b.i64_type, '0')
}
fn (mut b Builder) build_short_circuit(node flat.Node) ValueID {
result_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(b.i1_type))
lhs := b.build_expr(b.a.child(&node, 0))
b.emit2(.store, b.void_type, lhs, result_alloca)
rhs_block := b.m.add_block(b.cur_func, 'sc_rhs')
merge_block := b.m.add_block(b.cur_func, 'sc_merge')
if node.op == .logical_and {
b.emit3(.br, b.void_type, lhs, ValueID(rhs_block), ValueID(merge_block))
} else {
b.emit3(.br, b.void_type, lhs, ValueID(merge_block), ValueID(rhs_block))
}
b.cur_block = rhs_block
rhs := b.build_expr(b.a.child(&node, 1))
b.emit2(.store, b.void_type, rhs, result_alloca)
b.emit1(.jmp, b.void_type, ValueID(merge_block))
b.cur_block = merge_block
return b.emit1(.load, b.i1_type, result_alloca)
}
fn (mut b Builder) build_prefix(node flat.Node, _id flat.NodeId) ValueID {
child_id := b.a.child(&node, 0)
child := b.a.nodes[int(child_id)]
if node.op == .amp && child.kind == .struct_init {
return b.build_heap_struct_init(child)
}
if node.op == .amp && child.kind == .ident {
if addr := b.vars[child.value] {
return addr
}
if expr_id := b.lookup_const_expr(child.value) {
if addr := b.const_global_addr(child.value, expr_id) {
return addr
}
}
}
if node.op == .amp && child.kind == .selector {
return b.build_selector_addr(child)
}
if node.op == .amp && child.kind == .index {
return b.build_index_addr(child_id, child)
}
val := b.build_expr(child_id)
match node.op {
.minus {
zero := b.m.get_or_add_const(b.i64_type, '0')
return b.emit2(.sub, b.value_type(val), zero, val)
}
.not {
zero := b.m.get_or_add_const(b.i1_type, '0')
return b.emit2(.eq, b.i1_type, val, zero)
}
.bit_not {
minus_one := b.m.get_or_add_const(b.i64_type, '-1')
return b.emit2(.xor, b.value_type(val), val, minus_one)
}
.amp {
return val
}
.mul {
return b.emit1(.load, b.deref_type(val), val)
}
else {
return val
}
}
}
fn (mut b Builder) const_global_addr(name string, expr_id flat.NodeId) ?ValueID {
if int(expr_id) < 0 || int(expr_id) >= b.a.nodes.len {
return none
}
expr := b.a.nodes[int(expr_id)]
if expr.kind != .struct_init || expr.children_count != 0 {
return none
}
typ_id, _ := b.struct_literal_type(expr.value)
if typ_id <= 0 {
return none
}
qualified_name := if name.contains('.') || b.cur_module.len == 0 || b.cur_module == 'main' {
name
} else {
b.cur_module + '.' + name
}
global_name := '__const_' + ssa_c_name(qualified_name)
for v in b.m.values {
if v.kind == .global && v.name == global_name {
return v.id
}
}
return b.m.add_global(global_name, typ_id)
}
fn (mut b Builder) build_postfix(node flat.Node) ValueID {
if node.children_count == 0 {
return b.m.get_or_add_const(b.i64_type, '0')
}
child_id := b.a.child(&node, 0)
addr := b.build_lvalue_addr(child_id)
value_type := b.deref_type(addr)
if value_type <= 0 {
return b.m.get_or_add_const(b.i64_type, '0')
}
cur := b.emit1(.load, value_type, addr)
one := b.m.get_or_add_const(value_type, '1')
op := if node.op == .inc { OpCode.add } else { OpCode.sub }
result := b.emit2(op, value_type, cur, one)
b.emit2(.store, b.void_type, result, addr)
return cur
}
fn (mut b Builder) build_lvalue_addr(id flat.NodeId) ValueID {
if int(id) < 0 {
return b.m.get_or_add_const(b.m.type_store.get_ptr(b.i8_type), '0')
}
node := b.a.nodes[int(id)]
match node.kind {
.ident {
if addr := b.vars[node.value] {
elem_type := b.deref_type(addr)
if elem_type > 0 && elem_type < b.m.type_store.types.len {
elem := b.m.type_store.types[elem_type]
if elem.kind == .ptr_t {
return b.emit1(.load, elem_type, addr)
}
}
return addr
}
}
.selector {
return b.build_selector_addr(node)
}
.index {
return b.build_index_addr(id, node)
}
else {}
}
val := b.build_expr(id)
val_type := b.value_type(val)
tmp := b.emit0(.alloca, b.m.type_store.get_ptr(val_type))
b.emit2(.store, b.void_type, val, tmp)
return tmp
}
fn (mut b Builder) build_selector_addr(node flat.Node) ValueID {
if node.children_count == 0 {
return b.m.get_or_add_const(b.m.type_store.get_ptr(b.i8_type), '0')
}
base_id := b.a.child(&node, 0)
base := b.a.nodes[int(base_id)]
if node.op == .arrow {
base_ptr := b.build_arrow_selector_base_ptr(base_id, base)
return b.get_field_ptr(base_ptr, node.value)
}
if base.kind == .ident {
if addr := b.vars[base.value] {
if field_ptr := b.smartcast_sum_selector_addr(addr, base_id, node.value) {
return field_ptr
}
return b.get_field_ptr(addr, node.value)
}
}
if base.kind == .selector {
base_addr := b.build_selector_addr(base)
return b.get_field_ptr(base_addr, node.value)
}
if base.kind == .index {
base_addr := b.build_index_addr(base_id, base)
return b.get_field_ptr(base_addr, node.value)
}
base_val := b.build_expr(base_id)
base_typ := b.value_type(base_val)
if base_typ > 0 && base_typ < b.m.type_store.types.len
&& b.m.type_store.types[base_typ].kind == .struct_t {
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(base_typ))
b.emit2(.store, b.void_type, base_val, alloca)
return b.get_field_ptr(alloca, node.value)
}
return b.m.get_or_add_const(b.m.type_store.get_ptr(b.i8_type), '0')
}
fn (mut b Builder) build_arrow_selector_base_ptr(base_id flat.NodeId, base flat.Node) ValueID {
if base.kind == .ident {
if addr := b.vars[base.value] {
return b.emit1(.load, b.deref_type(addr), addr)
}
}
if base.kind == .selector && b.selector_has_addressable_root(&base) {
base_addr := b.build_selector_addr(base)
return b.emit1(.load, b.deref_type(base_addr), base_addr)
}
return b.build_expr(base_id)
}
fn (mut b Builder) build_call(id flat.NodeId, node flat.Node) ValueID {
fn_node_id := b.a.child(&node, 0)
fn_node := b.a.nodes[int(fn_node_id)]
fn_name := fn_node.value
mut is_method := false
mut is_c_call := false
mut base_id := flat.NodeId(0)
mut actual_name := fn_name
if fn_node.kind == .selector {
base_id = b.a.child(&fn_node, 0)
base := b.a.nodes[int(base_id)]
if base.kind == .ident && base.value == 'C' {
actual_name = fn_node.value
is_c_call = true
} else {
mut found_name := fn_node.value
mut resolved_handled := false
if builder_name := b.builder_method_name_for_base(base, fn_node.value) {
found_name = builder_name
is_method = true
resolved_handled = true
}
if !resolved_handled {
if resolved := b.resolved_call_name(id) {
found_name = resolved
if b.resolved_selector_has_receiver(resolved, node) {
is_method = true
}
resolved_handled = true
}
}
if !resolved_handled {
if typed_name := b.typed_receiver_method_name(base_id, fn_node.value) {
found_name = typed_name
is_method = true
resolved_handled = true
}
}
if !resolved_handled && base.kind == .ident {
mut candidates := []string{}
full_name := '${base.value}.${fn_node.value}'
candidates << full_name
candidates << full_name.replace('.', '__')
if b.cur_module.len > 0 && b.cur_module != 'main' && b.cur_module != 'builtin' {
qualified_name := '${b.cur_module}.${base.value}.${fn_node.value}'
candidates << qualified_name
candidates << qualified_name.replace('.', '__')
}
for candidate in candidates {
if candidate in b.fn_ids {
found_name = candidate
break
}
}
}
if !resolved_handled && base.kind == .selector {
qualified_base := b.selector_qualified_name(base)
if qualified_base.len > 0 {
full_name := '${qualified_base}.${fn_node.value}'
c_name := full_name.replace('.', '__')
if full_name in b.fn_ids {
found_name = full_name
} else if c_name in b.fn_ids {
found_name = c_name
}
}
}
if found_name == fn_node.value {
base_is_array := b.call_base_is_array(base_id)
for fname, _ in b.fn_ids {
if fname.ends_with('.${fn_node.value}') || fname.ends_with('__${fn_node.value}') {
if fname.starts_with('array.') && !base_is_array {
continue
}
found_name = fname
is_method = true
break
}
}
}
actual_name = found_name
}
} else {
if resolved := b.resolved_call_name(id) {
actual_name = resolved
}
}
if fn_node.kind == .selector && !is_c_call && actual_name !in b.fn_ids {
if typed_name := b.typed_receiver_method_name(base_id, fn_node.value) {
actual_name = typed_name
is_method = true
}
}
if actual_name.starts_with('C.') {
actual_name = actual_name[2..]
is_c_call = true
}
if fn_node.kind == .selector && fn_node.value in ['set', 'clear', 'has'] {
if result := b.build_flag_enum_method_call(base_id, fn_node.value, node) {
return result
}
}
if fn_node.kind == .selector && fn_node.value == 'delete_last' {
return b.build_array_len_mutator_call(base_id, fn_node.value)
}
if fn_node.kind == .selector && b.call_base_is_array(base_id) {
if fn_node.value == 'pop' {
return b.build_array_pop_call(id, base_id)
}
if fn_node.value in ['delete_last', 'clear'] {
return b.build_array_len_mutator_call(base_id, fn_node.value)
}
if fn_node.value in ['first', 'last'] {
return b.build_array_first_last_call(id, base_id, fn_node.value)
}
if fn_node.value == 'index' && node.children_count > 1 {
return b.build_array_index_call(base_id, b.a.child(&node, 1))
}
if fn_node.value == 'join' && node.children_count > 1 {
return b.build_array_join_call(base_id, b.a.child(&node, 1))
}
if fn_node.value == 'repeat' && node.children_count > 1 {
return b.build_array_repeat_call(base_id, b.a.child(&node, 1), flat.NodeId(-1))
}
if fn_node.value == 'repeat_to_depth' && node.children_count > 2 {
return b.build_array_repeat_call(base_id, b.a.child(&node, 1), b.a.child(&node, 2))
}
}
if fn_node.kind == .selector && fn_node.value == 'delete' && node.children_count > 1
&& b.expr_type_name_for_map(base_id).trim_left('&').starts_with('map[') {
return b.build_map_delete_call(base_id, b.a.child(&node, 1))
}
if fn_node.kind == .selector && fn_node.value == 'clone'
&& b.expr_type_name_for_map(base_id).trim_left('&').starts_with('map[') {
return b.build_map_clone_call(base_id)
}
if actual_name == 'FILE' {
if node.children_count > 1 {
return b.build_expr(b.a.child(&node, 1))
}
return b.m.get_or_add_const(b.m.type_store.get_ptr(b.i8_type), '0')
}
if actual_name == 'IError' {
if node.children_count > 1 {
return b.build_expr(b.a.child(&node, 1))
}
return b.m.get_or_add_const(b.m.type_store.get_ptr(b.i8_type), '0')
}
if actual_name in ['ast.Expr.name', 'Expr.name', 'ast.SelectorExpr.name', 'SelectorExpr.name',
'ast.[]Expr.name_list', '[]Expr.name_list', 'name_list', 'types__Type__str'] {
return b.m.add_value(.string_literal, b.str_type, '', 0)
}
if actual_name == 'join_path' || actual_name == 'os.join_path' {
return b.build_join_path_call(node)
}
if fn_node.kind == .selector && node.children_count == 2 {
base := b.a.child_node(fn_node, 0)
if base.kind == .ident && base.value == 'C' && actual_name !in b.fn_ids {
return b.build_expr(b.a.child(&node, 1))
}
}
if fn_node.kind == .selector && fn_node.value in ['has', 'all'] && node.children_count == 2 {
base_type_name := b.checked_expr_type_name(base_id).trim_left('&')
if b.is_flag_enum_type_name(base_type_name) {
base_val := b.build_expr(base_id)
arg_id := b.a.child(&node, 1)
arg_node := b.a.nodes[int(arg_id)]
arg := if arg_node.kind == .enum_val && base_type_name.len > 0 {
b.build_enum_val_with_type(arg_node, base_type_name) or { b.build_expr(arg_id) }
} else if arg_node.kind == .infix && base_type_name.len > 0 {
b.build_enum_expr_with_type(arg_node, base_type_name) or { b.build_expr(arg_id) }
} else {
b.build_expr(arg_id)
}
masked := b.emit2(.and_, b.value_type(base_val), base_val, arg)
if fn_node.value == 'has' {
zero := b.m.get_or_add_const(b.value_type(masked), '0')
return b.emit2(.ne, b.i1_type, masked, zero)
}
return b.emit2(.eq, b.i1_type, masked, arg)
}
}
if node.children_count == 2 && actual_name !in b.fn_ids {
mut cast_name := actual_name
if fn_node.kind == .selector {
base := b.a.child_node(fn_node, 0)
if base.kind == .ident {
full_name := '${base.value}.${fn_node.value}'
if _ := b.canonical_sum_type_name(full_name) {
cast_name = full_name
}
}
}
if value := b.build_sum_type_cast(cast_name, b.a.child(&node, 1)) {
return value
}
if actual_name in ['TypeID', 'ValueID', 'BlockID', 'NodeId'] {
return b.build_expr(b.a.child(&node, 1))
}
}
mut resolved_name := actual_name
if resolved_name !in b.fn_ids && !resolved_name.contains('.') {
qualified_name := ssa_fn_name_in_module(b.cur_module, resolved_name)
if qualified_name in b.fn_ids {
resolved_name = qualified_name
} else if resolved_name == 'new' && node.children_count == 1 && 'bench.new' in b.fn_ids {
resolved_name = 'bench.new'
} else if fn_node.kind == .ident {
if imported_name := b.unqualified_call_candidate(resolved_name, node.children_count - 1) {
resolved_name = imported_name
}
}
}
if resolved_name !in b.fn_ids && resolved_name.contains('.') {
c_name := resolved_name.replace('.', '__')
if c_name in b.fn_ids {
resolved_name = c_name
} else if is_method {
moduleless_name := drop_first_fn_qualifier(resolved_name)
if moduleless_name != resolved_name && moduleless_name in b.fn_ids {
resolved_name = moduleless_name
}
}
if resolved_name !in b.fn_ids && is_method {
unqualified_method := resolved_name.all_after('.')
if unqualified_method in b.fn_ids {
resolved_name = unqualified_method
}
}
}
if resolved_name !in b.fn_ids && resolved_name.contains('__') {
dotted_name := resolved_name.replace('__', '.')
if dotted_name in b.fn_ids {
resolved_name = dotted_name
} else if is_method {
unqualified_method := dotted_name.all_after('.')
if unqualified_method in b.fn_ids {
resolved_name = unqualified_method
}
}
}
if resolved_name !in b.fn_ids && resolved_name.contains('.') {
method_name := resolved_name.all_after_last('.')
if method_name in ['vstring', 'vstring_with_len', 'vstring_literal',
'vstring_literal_with_len'] {
u8_name := 'u8.${method_name}'
if u8_name in b.fn_ids {
resolved_name = u8_name
}
}
}
mut fn_idx := 0
if is_c_call {
if idx := b.c_fn_ids[resolved_name] {
fn_idx = idx
} else if idx := b.fn_ids[resolved_name] {
fn_idx = idx
} else {
panic('ssa: unknown function `${actual_name}`')
}
} else if idx := b.fn_ids[resolved_name] {
fn_idx = idx
} else {
if fn_node.kind == .ident {
if _ := b.vars[fn_node.value] {
return b.build_indirect_call(id, node, fn_node_id)
}
} else if fn_node.kind == .selector && b.is_function_value_expr(fn_node_id) {
return b.build_indirect_call(id, node, fn_node_id)
}
panic('ssa: unknown function `${actual_name}`')
}
fn_ref := b.m.add_value(.func_ref, b.void_type, resolved_name, fn_idx)
ret_type := b.m.funcs[fn_idx].typ
mut param_types := []TypeID{}
if is_c_call {
if ft_id := b.c_fn_types[resolved_name] {
ft := b.m.type_store.types[ft_id]
param_types = ft.params.clone()
}
} else if ft_id := b.fn_types[resolved_name] {
ft := b.m.type_store.types[ft_id]
param_types = ft.params.clone()
}
if resolved_name == 'map__set' {
return b.build_map_set_call(node)
}
mut args := []ValueID{}
args << fn_ref
if resolved_name == 'error_posix' && node.children_count == 1 && param_types.len > 0 {
args << b.default_system_error_value(param_types[0])
return b.m.add_instr(.call, b.cur_block, ret_type, args)
}
if is_method {
if param_types.len > 0 {
pt := b.m.type_store.types[param_types[0]]
if pt.kind == .ptr_t {
base_node := b.a.nodes[int(base_id)]
if base_node.kind == .ident {
if addr := b.vars[base_node.value] {
if b.should_pass_ident_addr_for_ptr_param(addr, param_types[0]) {
args << addr
} else {
args << b.build_expr(base_id)
}
} else {
args << b.build_expr(base_id)
}
} else if base_node.kind == .selector {
addr := b.build_selector_addr(base_node)
if b.should_pass_ident_addr_for_ptr_param(addr, param_types[0]) {
args << addr
} else {
args << b.build_expr(base_id)
}
} else if base_node.kind == .index {
addr := b.build_index_addr(base_id, base_node)
if b.should_pass_ident_addr_for_ptr_param(addr, param_types[0]) {
args << addr
} else {
args << b.build_expr(base_id)
}
} else {
args << b.build_expr(base_id)
}
} else {
args << b.coerce_value_for_param(b.build_expr(base_id), param_types[0])
}
} else {
args << b.build_expr(base_id)
}
}
for i in 1 .. node.children_count {
arg_id := b.a.child(&node, i)
param_idx := if is_method { i } else { i - 1 }
if resolved_name == 'fixed_array_contains_string' && param_idx == 0 {
if arg := b.build_const_string_array_arg(arg_id) {
args << arg
continue
}
}
if param_idx < param_types.len {
param_type := param_types[param_idx]
if b.is_option_type(param_type) {
arg_node := b.a.nodes[int(arg_id)]
if arg_node.kind == .none_expr {
args << b.default_value_for_type(param_type)
continue
}
mut arg := b.build_expr(arg_id)
if b.value_type(arg) != param_type {
arg = b.build_option_value(param_type, true, arg)
}
args << arg
continue
}
pt := b.m.type_store.types[param_type]
if pt.kind == .ptr_t {
arg_node := b.a.nodes[int(arg_id)]
if arg_node.kind == .ident {
if addr := b.vars[arg_node.value] {
if b.should_pass_ident_addr_for_ptr_param(addr, param_type) {
args << addr
continue
}
}
} else if arg_node.kind == .selector {
addr := b.build_selector_addr(arg_node)
if b.should_pass_ident_addr_for_ptr_param(addr, param_type) {
args << addr
continue
}
} else if arg_node.kind == .index {
addr := b.build_index_addr(arg_id, arg_node)
if b.should_pass_ident_addr_for_ptr_param(addr, param_type) {
args << addr
continue
}
}
}
}
args << b.build_expr(arg_id)
}
return b.m.add_instr(.call, b.cur_block, ret_type, args)
}
fn (b &Builder) unqualified_call_candidate(name string, arg_count int) ?string {
mut fallback := ''
for candidate, _ in b.fn_ids {
if !candidate.ends_with('.${name}') && !candidate.ends_with('__${name}') {
continue
}
if !b.fn_param_count_matches(candidate, arg_count) {
continue
}
if is_single_qualified_name(candidate) {
return candidate
}
if fallback.len == 0 {
fallback = candidate
}
}
if fallback.len > 0 {
return fallback
}
return none
}
fn (b &Builder) fn_param_count_matches(name string, arg_count int) bool {
if ft_id := b.fn_types[name] {
if ft_id > 0 && ft_id < b.m.type_store.types.len {
return b.m.type_store.types[ft_id].params.len == arg_count
}
}
return false
}
fn is_single_qualified_name(name string) bool {
mut dots := 0
for ch in name {
if ch == `.` {
dots++
} else if ch == `_` {
break
}
}
return dots == 1
}
fn (mut b Builder) coerce_value_for_param(value ValueID, param_type TypeID) ValueID {
value_type := b.value_type(value)
if value_type > 0 && value_type < b.m.type_store.types.len {
typ := b.m.type_store.types[value_type]
if typ.kind == .ptr_t && typ.elem_type == param_type {
return b.emit1(.load, param_type, value)
}
}
return value
}
fn (mut b Builder) build_flag_enum_method_call(base_id flat.NodeId, method string, node flat.Node) ?ValueID {
if !b.is_flag_enum_expr(base_id) {
return none
}
base_addr := b.build_lvalue_addr(base_id)
flag_type := b.deref_type(base_addr)
mut flag := if node.children_count > 1 {
b.build_expr(b.a.child(&node, 1))
} else {
b.m.get_or_add_const(flag_type, '0')
}
flag = b.coerce_int_value(flag, flag_type)
current := b.emit1(.load, flag_type, base_addr)
if method == 'has' {
masked := b.emit2(.and_, flag_type, current, flag)
zero := b.m.get_or_add_const(flag_type, '0')
return b.emit2(.ne, b.i1_type, masked, zero)
}
result := if method == 'set' {
b.emit2(.or_, flag_type, current, flag)
} else {
all_bits := b.m.get_or_add_const(flag_type, '-1')
inverted := b.emit2(.xor, flag_type, flag, all_bits)
b.emit2(.and_, flag_type, current, inverted)
}
b.emit2(.store, b.void_type, result, base_addr)
return b.m.get_or_add_const(b.i64_type, '0')
}
fn (b &Builder) is_flag_enum_expr(id flat.NodeId) bool {
name := b.checked_expr_type_name(id)
if b.is_flag_enum_type_name(name) {
return true
}
if int(id) < 0 {
return false
}
node := b.a.nodes[int(id)]
if node.typ.len > 0 && b.is_flag_enum_type_name(node.typ) {
return true
}
return false
}
fn (mut b Builder) coerce_int_value(value ValueID, to_type TypeID) ValueID {
from_type := b.value_type(value)
if from_type == to_type || !b.is_int_type(from_type) || !b.is_int_type(to_type) {
return value
}
from_width := b.scalar_type_width(from_type)
to_width := b.scalar_type_width(to_type)
if to_width > 0 && from_width > 0 && to_width < from_width {
return b.emit1(.trunc, to_type, value)
}
if to_width > from_width {
if b.is_unsigned_type(from_type) {
return b.emit1(.zext, to_type, value)
}
return b.emit1(.sext, to_type, value)
}
return b.emit1(.bitcast, to_type, value)
}
fn (mut b Builder) build_array_len_mutator_call(base_id flat.NodeId, method string) ValueID {
arr_addr := b.build_lvalue_addr(base_id)
len_ptr := b.get_field_ptr(arr_addr, 'len')
zero := b.m.get_or_add_const(b.i64_type, '0')
if method == 'delete_last' {
len32 := b.emit1(.load, b.i32_type, len_ptr)
len64 := b.emit1(.zext, b.i64_type, len32)
one := b.m.get_or_add_const(b.i64_type, '1')
new_len := b.emit2(.sub, b.i64_type, len64, one)
new_len32 := b.emit1(.trunc, b.i32_type, new_len)
b.emit2(.store, b.void_type, new_len32, len_ptr)
} else {
zero32 := b.m.get_or_add_const(b.i32_type, '0')
b.emit2(.store, b.void_type, zero32, len_ptr)
}
return zero
}
fn (mut b Builder) build_array_pop_call(id flat.NodeId, base_id flat.NodeId) ValueID {
arr_addr := b.build_lvalue_addr(base_id)
base := b.emit1(.load, b.array_type, arr_addr)
elem_type := b.array_method_elem_type(id, base_id)
len_ptr := b.get_field_ptr(arr_addr, 'len')
len32 := b.emit1(.load, b.i32_type, len_ptr)
len64 := b.emit1(.zext, b.i64_type, len32)
one := b.m.get_or_add_const(b.i64_type, '1')
index := b.emit2(.sub, b.i64_type, len64, one)
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
fn_ref := b.m.add_value(.func_ref, ptr_i8, 'array_get', b.fn_ids['array_get'])
elem_ptr := b.emit3(.call, ptr_i8, fn_ref, base, index)
typed_ptr := b.emit1(.bitcast, b.m.type_store.get_ptr(elem_type), elem_ptr)
value := b.emit1(.load, elem_type, typed_ptr)
new_len32 := b.emit1(.trunc, b.i32_type, index)
b.emit2(.store, b.void_type, new_len32, len_ptr)
return value
}
fn (mut b Builder) build_array_first_last_call(id flat.NodeId, base_id flat.NodeId, method string) ValueID {
base := b.build_expr(base_id)
elem_type := b.array_method_elem_type(id, base_id)
index := if method == 'last' {
len := b.load_struct_field_from_value(base, b.array_type, 'len')
one := b.m.get_or_add_const(b.i64_type, '1')
b.emit2(.sub, b.i64_type, len, one)
} else {
b.m.get_or_add_const(b.i64_type, '0')
}
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
fn_ref := b.m.add_value(.func_ref, ptr_i8, 'array_get', b.fn_ids['array_get'])
elem_ptr := b.emit3(.call, ptr_i8, fn_ref, base, index)
typed_ptr := b.emit1(.bitcast, b.m.type_store.get_ptr(elem_type), elem_ptr)
return b.emit1(.load, elem_type, typed_ptr)
}
fn (mut b Builder) build_array_index_call(base_id flat.NodeId, needle_id flat.NodeId) ValueID {
base := b.build_expr(base_id)
elem_type := b.array_receiver_elem_type(base_id)
fn_name := if elem_type == b.str_type { 'array_index_string' } else { 'array_index_int' }
needle_type := if elem_type == b.str_type { b.str_type } else { b.i64_type }
mut needle := b.build_expr(needle_id)
needle = b.coerce_int_value(needle, needle_type)
fn_ref := b.m.add_value(.func_ref, b.i64_type, fn_name, b.fn_ids[fn_name])
return b.emit3(.call, b.i64_type, fn_ref, base, needle)
}
fn (mut b Builder) build_array_join_call(base_id flat.NodeId, sep_id flat.NodeId) ValueID {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
ptr_i64 := b.m.type_store.get_ptr(b.i64_type)
ptr_array := b.m.type_store.get_ptr(b.array_type)
base := b.build_expr(base_id)
sep := b.build_expr(sep_id)
arr_alloca := b.emit0(.alloca, ptr_array)
builder_alloca := b.emit0(.alloca, ptr_array)
i_alloca := b.emit0(.alloca, ptr_i64)
b.emit2(.store, b.void_type, base, arr_alloca)
zero := b.m.get_or_add_const(b.i64_type, '0')
one := b.m.get_or_add_const(b.i64_type, '1')
b.emit2(.store, b.void_type, zero, i_alloca)
new_ref := b.m.add_value(.func_ref, b.array_type, 'strings.new_builder',
b.fn_ids['strings.new_builder'])
initial_cap := b.m.get_or_add_const(b.i64_type, '16')
builder := b.emit2(.call, b.array_type, new_ref, initial_cap)
b.emit2(.store, b.void_type, builder, builder_alloca)
data_ptr := b.get_field_ptr(arr_alloca, 'data')
len_ptr := b.get_field_ptr(arr_alloca, 'len')
data := b.emit1(.load, ptr_i8, data_ptr)
len32 := b.emit1(.load, b.i32_type, len_ptr)
len := b.emit1(.zext, b.i64_type, len32)
loop := b.m.add_block(b.cur_func, 'array_join_loop')
body := b.m.add_block(b.cur_func, 'array_join_body')
write_sep := b.m.add_block(b.cur_func, 'array_join_write_sep')
write_elem := b.m.add_block(b.cur_func, 'array_join_write_elem')
done := b.m.add_block(b.cur_func, 'array_join_done')
b.emit1(.jmp, b.void_type, ValueID(loop))
b.cur_block = loop
i_val := b.emit1(.load, b.i64_type, i_alloca)
more := b.emit2(.lt, b.i1_type, i_val, len)
b.emit3(.br, b.void_type, more, ValueID(body), ValueID(done))
b.cur_block = body
needs_sep := b.emit2(.gt, b.i1_type, i_val, zero)
b.emit3(.br, b.void_type, needs_sep, ValueID(write_sep), ValueID(write_elem))
b.cur_block = write_sep
write_ref := b.m.add_value(.func_ref, b.void_type, 'strings.Builder.write_string',
b.fn_ids['strings.Builder.write_string'])
b.emit3(.call, b.void_type, write_ref, builder_alloca, sep)
b.emit1(.jmp, b.void_type, ValueID(write_elem))
b.cur_block = write_elem
elem_size := b.m.get_or_add_const(b.i64_type, '${b.m.type_size(b.str_type)}')
offset := b.emit2(.mul, b.i64_type, i_val, elem_size)
elem_ptr_raw := b.emit2(.add, ptr_i8, data, offset)
elem_ptr := b.emit1(.bitcast, b.m.type_store.get_ptr(b.str_type), elem_ptr_raw)
elem := b.emit1(.load, b.str_type, elem_ptr)
write_ref2 := b.m.add_value(.func_ref, b.void_type, 'strings.Builder.write_string',
b.fn_ids['strings.Builder.write_string'])
b.emit3(.call, b.void_type, write_ref2, builder_alloca, elem)
next_i := b.emit2(.add, b.i64_type, i_val, one)
b.emit2(.store, b.void_type, next_i, i_alloca)
b.emit1(.jmp, b.void_type, ValueID(loop))
b.cur_block = done
str_ref := b.m.add_value(.func_ref, b.str_type, 'strings.Builder.str',
b.fn_ids['strings.Builder.str'])
return b.emit2(.call, b.str_type, str_ref, builder_alloca)
}
fn (mut b Builder) build_array_repeat_call(base_id flat.NodeId, count_id flat.NodeId, depth_id flat.NodeId) ValueID {
base := b.build_expr(base_id)
mut count := b.build_expr(count_id)
count = b.coerce_int_value(count, b.i64_type)
depth := if int(depth_id) >= 0 {
mut depth_value := b.build_expr(depth_id)
depth_value = b.coerce_int_value(depth_value, b.i64_type)
depth_value
} else {
b.m.get_or_add_const(b.i64_type, '0')
}
fn_ref := b.m.add_value(.func_ref, b.array_type, 'array.repeat_to_depth',
b.fn_ids['array.repeat_to_depth'])
return b.emit4(.call, b.array_type, fn_ref, base, count, depth)
}
fn (mut b Builder) array_receiver_elem_type(base_id flat.NodeId) TypeID {
base_name := b.receiver_type_name(base_id)
if base_name.starts_with('[]') {
return b.resolve_type(base_name[2..])
}
return b.i64_type
}
fn (mut b Builder) array_method_elem_type(id flat.NodeId, base_id flat.NodeId) TypeID {
ret_name := b.checked_expr_type_name(id)
if ret_name.len > 0 && ret_name != 'unknown' && !ret_name.starts_with('[]') {
return b.resolve_type(ret_name)
}
base_name := b.receiver_type_name(base_id)
if base_name.starts_with('[]') {
return b.resolve_type(base_name[2..])
}
return b.i64_type
}
fn (b &Builder) call_base_is_array(base_id flat.NodeId) bool {
if int(base_id) < 0 {
return false
}
base_name := b.receiver_type_name(base_id)
if base_name.starts_with('[]') {
return true
}
base_node := b.a.nodes[int(base_id)]
if base_node.kind == .ident {
if addr := b.vars[base_node.value] {
if b.storage_points_to_array(addr) {
return true
}
}
}
return false
}
fn (b &Builder) storage_points_to_array(addr ValueID) bool {
typ := b.deref_type(addr)
if typ == b.array_type {
return true
}
if typ > 0 && typ < b.m.type_store.types.len {
t := b.m.type_store.types[typ]
return t.kind == .ptr_t && t.elem_type == b.array_type
}
return false
}
fn (b &Builder) receiver_type_name(base_id flat.NodeId) string {
if int(base_id) < 0 {
return ''
}
base_node := b.a.nodes[int(base_id)]
if base_node.kind == .ident {
if var_name := b.var_type_names[base_node.value] {
return var_name
}
} else if base_node.kind == .selector && base_node.children_count > 0 {
base_type := b.receiver_type_name(b.a.child(&base_node, 0)).trim_left('&')
field_type := b.field_type_name(base_type, base_node.value)
if field_type.len > 0 {
return field_type
}
}
return b.checked_expr_type_name(base_id)
}
fn (b &Builder) typed_receiver_method_name(base_id flat.NodeId, method string) ?string {
receiver_name := b.receiver_type_name(base_id).trim_left('&')
if receiver_name.len == 0 {
return none
}
if b.cur_module.len > 0 && b.cur_module != 'main' && b.cur_module != 'builtin'
&& !receiver_name.contains('.') {
module_name := '${b.cur_module}.${receiver_name}.${method}'
if module_name in b.fn_ids {
return module_name
}
module_c_name := module_name.replace('.', '__')
if module_c_name in b.fn_ids {
return module_c_name
}
}
mut candidates := []string{}
candidates << '${receiver_name}.${method}'
candidates << '${receiver_name.replace('.', '__')}__${method}'
short_name := receiver_name.all_after_last('.')
if short_name != receiver_name {
candidates << '${short_name}.${method}'
candidates << '${short_name}__${method}'
}
for candidate in candidates {
if candidate in b.fn_ids {
return candidate
}
}
return none
}
fn (mut b Builder) build_indirect_call(id flat.NodeId, node flat.Node, callee_id flat.NodeId) ValueID {
callee := b.build_expr(callee_id)
mut args := []ValueID{}
args << callee
for i in 1 .. node.children_count {
args << b.build_expr(b.a.child(&node, i))
}
return b.m.add_instr(.call_indirect, b.cur_block, b.call_expr_result_type(id, node), args)
}
fn (b &Builder) is_function_value_expr(id flat.NodeId) bool {
typ := b.checked_expr_type_name(id).trim_space()
return typ.starts_with('fn(') || typ.starts_with('fn (')
}
fn (mut b Builder) call_expr_result_type(id flat.NodeId, node flat.Node) TypeID {
checked := b.checked_expr_type_name(id)
if checked.len > 0 && checked != 'unknown' {
return b.resolve_type(checked)
}
if node.typ.len > 0 && node.typ != 'unknown' {
return b.resolve_type(node.typ)
}
return b.i64_type
}
fn (mut b Builder) build_const_string_array_arg(id flat.NodeId) ?ValueID {
if int(id) < 0 {
return none
}
node := b.a.nodes[int(id)]
if node.kind != .ident {
return none
}
expr_id := b.lookup_const_expr(node.value) or { return none }
expr := b.a.nodes[int(expr_id)]
if expr.kind != .array_literal || expr.children_count == 0 {
return none
}
ptr_string := b.m.type_store.get_ptr(b.str_type)
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
count := b.m.get_or_add_const(b.i64_type, '${expr.children_count}')
alloca := b.emit1(.alloca, ptr_string, count)
stride := 16
for i in 0 .. expr.children_count {
child_id := b.a.child(&expr, i)
child := b.a.nodes[int(child_id)]
if child.kind != .string_literal {
return none
}
value := b.build_expr(child_id)
off := b.m.get_or_add_const(b.i64_type, '${i * stride}')
slot := b.emit2(.get_element_ptr, ptr_string, alloca, off)
b.emit2(.store, b.void_type, value, slot)
}
return b.emit1(.bitcast, ptr_i8, alloca)
}
fn (mut b Builder) default_system_error_value(typ_id TypeID) ValueID {
if typ_id <= 0 || typ_id >= b.m.type_store.types.len {
return b.m.get_or_add_const(b.i64_type, '0')
}
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(typ_id))
typ := b.m.type_store.types[typ_id]
for fi, fname in typ.field_names {
offset := b.m.struct_field_offset(typ_id, fi)
off_const := b.m.get_or_add_const(b.i64_type, '${offset}')
field_type := if fi < typ.fields.len { typ.fields[fi] } else { b.i64_type }
field_ptr := b.emit2(.get_element_ptr, b.m.type_store.get_ptr(field_type), alloca,
off_const)
default_value := if fname == 'msg' {
b.m.add_value(.string_literal, b.str_type, '', 0)
} else if fname == 'code' {
b.m.get_or_add_const(field_type, '-1')
} else {
b.default_value_for_type(field_type)
}
b.emit2(.store, b.void_type, default_value, field_ptr)
}
return b.emit1(.load, typ_id, alloca)
}
fn (mut b Builder) build_const_i32_array_arg(id flat.NodeId) ?ValueID {
if int(id) < 0 {
return none
}
node := b.a.nodes[int(id)]
if node.kind != .ident {
return none
}
expr_id := b.lookup_const_expr(node.value) or { return none }
expr := b.a.nodes[int(expr_id)]
if expr.kind != .array_literal || expr.children_count == 0 {
return none
}
mut values := []string{}
for i in 0 .. expr.children_count {
child_id := b.a.child(&expr, i)
value := b.const_int_literal_value(child_id) or { return none }
values << value
}
ptr_i32 := b.m.type_store.get_ptr(b.i32_type)
count := b.m.get_or_add_const(b.i64_type, '${values.len}')
alloca := b.emit1(.alloca, ptr_i32, count)
for i, value in values {
const_val := b.m.get_or_add_const(b.i32_type, value)
off := b.m.get_or_add_const(b.i64_type, '${i * b.m.type_size(b.i32_type)}')
slot := b.emit2(.get_element_ptr, ptr_i32, alloca, off)
b.emit2(.store, b.void_type, const_val, slot)
}
return alloca
}
fn (b &Builder) const_int_literal_value(id flat.NodeId) ?string {
if int(id) < 0 {
return none
}
node := b.a.nodes[int(id)]
match node.kind {
.int_literal {
return node.value
}
.ident {
if expr_id := b.lookup_const_expr(node.value) {
return b.const_int_literal_value(expr_id)
}
}
.paren, .cast_expr {
if node.children_count > 0 {
return b.const_int_literal_value(b.a.child(&node, 0))
}
}
.prefix {
if node.children_count > 0 && node.op == .minus {
if value := b.const_int_literal_value(b.a.child(&node, 0)) {
return '-' + value
}
}
}
.infix {
if node.children_count >= 2 {
left := b.const_int_literal_value(b.a.child(&node, 0)) or { return none }
right := b.const_int_literal_value(b.a.child(&node, 1)) or { return none }
l := left.i64()
r := right.i64()
return match node.op {
.plus {
'${l + r}'
}
.minus {
'${l - r}'
}
.mul {
'${l * r}'
}
.div {
if r != 0 {
'${l / r}'
} else {
return none
}
}
.mod {
if r != 0 {
'${l % r}'
} else {
return none
}
}
.left_shift {
if r >= 0 && r < 64 {
'${i64(u64(l) << u64(r))}'
} else {
return none
}
}
.right_shift {
if r >= 0 && r < 64 {
'${l >> u64(r)}'
} else {
return none
}
}
.amp {
'${l & r}'
}
.pipe {
'${l | r}'
}
.xor {
'${l ^ r}'
}
else {
return none
}
}
}
}
.call {
if node.children_count == 2 {
return b.const_int_literal_value(b.a.child(&node, 1))
}
}
else {}
}
return none
}
fn (b &Builder) lookup_const_expr(name string) ?flat.NodeId {
if !name.contains('.') && b.cur_module.len > 0 && b.cur_module != 'main' {
qualified := '${b.cur_module}.${name}'
if expr := b.const_exprs[qualified] {
return expr
}
}
if expr := b.const_exprs[name] {
return expr
}
return none
}
fn (mut b Builder) float_literal_type(id flat.NodeId, node flat.Node) TypeID {
checked := b.checked_expr_type_name(id)
if checked == 'f32' || node.typ == 'f32' {
return b.f32_type
}
return b.f64_type
}
fn (mut b Builder) build_map_set_call(node flat.Node) ValueID {
fn_idx := b.fn_ids['v3_map_set_sized']
fn_ref := b.m.add_value(.func_ref, b.void_type, 'v3_map_set_sized', fn_idx)
mut args := []ValueID{}
args << fn_ref
for i in 1 .. node.children_count {
args << b.build_expr(b.a.child(&node, i))
}
key_size, val_size := b.map_set_call_sizes(node)
args << b.m.get_or_add_const(b.i64_type, '${key_size}')
args << b.m.get_or_add_const(b.i64_type, '${val_size}')
return b.m.add_instr(.call, b.cur_block, b.void_type, args)
}
fn (mut b Builder) build_map_delete_call(base_id flat.NodeId, key_id flat.NodeId) ValueID {
map_type := b.expr_type_name_for_map(base_id).trim_left('&')
key_type_name, _ := map_type_parts(map_type)
key_type := if key_type_name.len > 0 { b.resolve_type(key_type_name) } else { b.str_type }
map_ptr := b.map_expr_ptr(base_id)
key_val := b.build_expr(key_id)
key_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(key_type))
b.emit2(.store, b.void_type, key_val, key_alloca)
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
key_ptr := if b.value_type(key_alloca) == ptr_i8 {
key_alloca
} else {
b.emit1(.bitcast, ptr_i8, key_alloca)
}
fn_ref := b.m.add_value(.func_ref, b.void_type, 'map__delete', b.fn_ids['map__delete'])
return b.emit3(.call, b.void_type, fn_ref, map_ptr, key_ptr)
}
fn (mut b Builder) build_map_clone_call(base_id flat.NodeId) ValueID {
fn_ref := b.m.add_value(.func_ref, b.void_type, 'map__clone', b.fn_ids['map__clone'])
base := b.coerce_value_for_param(b.build_expr(base_id), b.map_type)
return b.emit2(.call, b.map_type, fn_ref, base)
}
fn (mut b Builder) map_set_call_sizes(node flat.Node) (int, int) {
if node.children_count <= 1 {
return 16, 8
}
map_type := b.map_arg_type_name(b.a.child(&node, 1))
key_type, val_type := map_type_parts(map_type)
if key_type.len == 0 || val_type.len == 0 {
return 16, 8
}
key_size := b.m.type_size(b.resolve_type(key_type))
val_size := b.m.type_size(b.resolve_type(val_type))
actual_key_size := if key_size > 0 { key_size } else { 8 }
actual_val_size := if val_size > 0 { val_size } else { 8 }
return actual_key_size, actual_val_size
}
fn (b &Builder) map_arg_type_name(id flat.NodeId) string {
if int(id) < 0 {
return ''
}
node := b.a.nodes[int(id)]
if node.kind == .prefix && node.children_count > 0 && node.op == .amp {
return b.expr_type_name_for_map(b.a.child(&node, 0)).trim_left('&')
}
return b.expr_type_name_for_map(id).trim_left('&')
}
fn (b &Builder) expr_type_name_for_map(id flat.NodeId) string {
if int(id) < 0 {
return ''
}
node := b.a.nodes[int(id)]
if node.kind == .ident {
if var_name := b.var_type_names[node.value] {
if var_name.len > 0 {
return var_name
}
}
}
checked := b.checked_expr_type_name(id)
if checked.len > 0 && checked != 'unknown' {
return checked
}
if node.typ.len > 0 {
return node.typ
}
if node.kind == .prefix && node.children_count > 0 && node.op == .amp {
inner := b.expr_type_name_for_map(b.a.child(&node, 0))
if inner.len > 0 {
return '&' + inner
}
}
if node.kind == .selector && node.children_count > 0 {
base_type := b.expr_type_name_for_map(b.a.child(&node, 0)).trim_left('&')
field_key := base_type + '.' + node.value
if field_type := b.struct_field_types[field_key] {
return field_type
}
short_type := base_type.all_after('.')
short_key := short_type + '.' + node.value
if field_type := b.struct_field_types[short_key] {
return field_type
}
}
return ''
}
fn map_type_parts(map_type string) (string, string) {
clean := map_type.trim_left('&')
if !clean.starts_with('map[') {
return '', ''
}
mut depth := 0
for i in 4 .. clean.len {
c := clean[i]
if c == `[` {
depth++
} else if c == `]` {
if depth == 0 {
return clean[4..i], clean[i + 1..]
}
depth--
}
}
return '', ''
}
fn indexed_elem_type_name(container_type string) string {
clean := container_type.trim_left('&')
if clean.starts_with('[]') {
return clean[2..]
}
if clean.starts_with('[') {
idx := clean.index_u8(`]`)
if idx > 0 && idx + 1 < clean.len {
return clean[idx + 1..]
}
}
if clean.starts_with('map[') {
_, val_type := map_type_parts(clean)
return val_type
}
if clean == 'string' {
return 'u8'
}
return ''
}
fn (mut b Builder) build_join_path_call(node flat.Node) ValueID {
if node.children_count <= 1 {
return b.m.add_value(.string_literal, b.str_type, '', 0)
}
mut result := b.build_expr(b.a.child(&node, 1))
if node.children_count == 2 {
return result
}
mut fn_idx := 0
mut found_fn := false
if idx := b.fn_ids['join_path_single'] {
fn_idx = idx
found_fn = true
}
if !found_fn {
if idx := b.fn_ids['os.join_path_single'] {
fn_idx = idx
found_fn = true
}
}
if !found_fn {
panic('ssa: unknown function `join_path_single`')
}
fn_ref := b.m.add_value(.func_ref, b.void_type, 'join_path_single', fn_idx)
for i in 2 .. node.children_count {
arg := b.build_expr(b.a.child(&node, i))
mut args := []ValueID{}
args << fn_ref
args << result
args << arg
result = b.m.add_instr(.call, b.cur_block, b.str_type, args)
}
return result
}
fn (b &Builder) declared_v_type_name(lhs_id flat.NodeId, rhs_id flat.NodeId) string {
if int(rhs_id) >= 0 {
rhs := b.a.nodes[int(rhs_id)]
if rhs.kind == .array_init && b.is_fixed_array_type_name(rhs.value) {
return rhs.value
}
if rhs.kind == .call && rhs.children_count > 0 {
fn_node := b.a.child_node(&rhs, 0)
if fn_node.kind == .selector && fn_node.children_count > 0
&& fn_node.value == 'new_builder' {
base := b.a.child_node(fn_node, 0)
if base.kind == .ident && base.value == 'strings' {
return 'strings.Builder'
}
}
}
if rhs.typ.len > 0 && rhs.typ != 'unknown' {
return rhs.typ
}
}
if int(lhs_id) >= 0 {
lhs := b.a.nodes[int(lhs_id)]
if lhs.typ.len > 0 {
return lhs.typ
}
}
lhs_type := b.checked_expr_type_name(lhs_id)
if lhs_type.len > 0 && lhs_type != 'unknown' {
return lhs_type
}
rhs_type := b.checked_expr_type_name(rhs_id)
if rhs_type.len > 0 && rhs_type != 'unknown' {
return rhs_type
}
return ''
}
fn (b &Builder) checked_expr_type_name(id flat.NodeId) string {
if int(id) < 0 {
return ''
}
node := b.a.nodes[int(id)]
if node.kind == .selector {
if typ := b.selector_type_name(node) {
return typ
}
}
if node.kind == .index && node.children_count > 0 {
base_type := b.checked_expr_type_name(b.a.child(&node, 0))
elem_type := indexed_elem_type_name(base_type)
if elem_type.len > 0 {
return elem_type
}
}
if b.tc != unsafe { nil } {
if typ := b.tc.expr_type(id) {
if typ is types.MultiReturn {
return b.multi_return_c_type(typ)
}
name := typ.name()
if name.len > 0 && name != 'unknown' {
return name
}
}
}
if node.typ.len > 0 && node.typ != 'unknown' {
return node.typ
}
if node.kind == .ident {
if typ := b.var_type_names[node.value] {
return typ
}
}
return ''
}
fn (b &Builder) selector_type_name(node flat.Node) ?string {
if node.kind != .selector || node.children_count == 0 {
return none
}
base_id := b.a.child(&node, 0)
base_type := b.checked_expr_type_name(base_id)
if base_type.len == 0 {
return none
}
if sum_name := b.canonical_sum_type_name(base_type) {
if variants := b.sum_type_variants[sum_name] {
for variant in variants {
if sum_variant_field_name(variant) == node.value {
return variant
}
}
}
}
field_type := b.field_type_name(base_type, node.value)
if field_type.len > 0 {
return field_type
}
return none
}
fn (b &Builder) builder_method_name_for_base(base flat.Node, method string) ?string {
if base.kind != .ident {
return none
}
if !is_builder_method(method) {
return none
}
mut receiver_type := b.var_type_names[base.value] or { '' }
if receiver_type.starts_with('&') {
receiver_type = receiver_type[1..]
}
if receiver_type != 'strings.Builder' && receiver_type != 'Builder' {
return none
}
name := 'strings.Builder.${method}'
if name in b.fn_ids {
return name
}
return none
}
fn is_builder_method(method string) bool {
return method in ['write_string', 'writeln', 'str', 'free', 'write_u8', 'write_ptr',
'write_runes', 'last_n', 'push_many']
}
fn (b &Builder) resolved_call_name(id flat.NodeId) ?string {
if b.tc == unsafe { nil } {
return none
}
if name := b.tc.resolved_call_name(id) {
if name.len > 0 {
return name
}
}
return none
}
fn drop_first_fn_qualifier(name string) string {
idx := name.index('.') or { return name }
if idx + 1 >= name.len {
return name
}
return name[idx + 1..]
}
fn (b &Builder) resolved_selector_has_receiver(resolved string, node flat.Node) bool {
explicit_arg_count := node.children_count - 1
if has_receiver := b.fn_signature_has_receiver(resolved, explicit_arg_count) {
return has_receiver
}
if resolved.contains('.') {
c_name := resolved.replace('.', '__')
if has_receiver := b.fn_signature_has_receiver(c_name, explicit_arg_count) {
return has_receiver
}
unqualified := resolved.all_after('.')
if has_receiver := b.fn_signature_has_receiver(unqualified, explicit_arg_count) {
return has_receiver
}
}
if resolved.contains('__') {
dotted := resolved.replace('__', '.')
if has_receiver := b.fn_signature_has_receiver(dotted, explicit_arg_count) {
return has_receiver
}
unqualified := dotted.all_after('.')
if has_receiver := b.fn_signature_has_receiver(unqualified, explicit_arg_count) {
return has_receiver
}
}
return true
}
fn (b &Builder) fn_signature_has_receiver(name string, explicit_arg_count int) ?bool {
if ft_id := b.fn_types[name] {
ft := b.m.type_store.types[ft_id]
if ft.params.len == explicit_arg_count {
return false
}
if ft.params.len < explicit_arg_count {
return false
}
if ft.params.len == explicit_arg_count + 1 {
return true
}
}
return none
}
fn (b &Builder) should_pass_ident_addr_for_ptr_param(addr ValueID, param_type TypeID) bool {
if param_type <= 0 || param_type >= b.m.type_store.types.len {
return false
}
param := b.m.type_store.types[param_type]
if param.kind != .ptr_t {
return false
}
arg_type := b.deref_type(addr)
if arg_type <= 0 || arg_type >= b.m.type_store.types.len {
return true
}
arg := b.m.type_store.types[arg_type]
return arg.kind != .ptr_t
}
fn (mut b Builder) build_selector(node flat.Node) ValueID {
base_id := b.a.child(&node, 0)
base := b.a.nodes[int(base_id)]
field_name := node.value
if base.kind == .ident {
if value := b.enum_value_for_type(base.value, field_name) {
return b.m.get_or_add_const(b.i64_type, value.str())
}
if b.cur_module.len > 0 && b.cur_module != 'main' && b.cur_module != 'builtin' {
if value := b.enum_value_for_type(b.cur_module + '.' + base.value, field_name) {
return b.m.get_or_add_const(b.i64_type, value.str())
}
}
}
if base.kind == .selector && base.children_count > 0 {
enum_base := b.selector_qualified_name(base)
if enum_base.len > 0 {
if value := b.enum_value_for_type(enum_base, field_name) {
return b.m.get_or_add_const(b.i64_type, value.str())
}
}
}
if base.kind == .ident && base.value == 'os' {
if field_name == 'args' {
if fn_id := b.fn_ids['arguments'] {
fn_ref := b.m.add_value(.func_ref, b.array_type, 'arguments', fn_id)
return b.emit1(.call, b.array_type, fn_ref)
}
}
if field_name == 'path_separator' {
return b.m.add_value(.string_literal, b.str_type, '/', 0)
}
}
if base.kind == .ident && base.value == 'C' {
match field_name {
'SEEK_SET' {
return b.m.get_or_add_const(b.i64_type, '0')
}
'SEEK_CUR' {
return b.m.get_or_add_const(b.i64_type, '1')
}
'SEEK_END' {
return b.m.get_or_add_const(b.i64_type, '2')
}
else {}
}
}
if base.kind == .ident {
const_name := base.value + '.' + field_name
if expr_id := b.lookup_const_expr(const_name) {
return b.build_expr(expr_id)
}
if addr := b.vars[base.value] {
if b.is_option_like_var(base.value) {
field_ptr := b.get_field_ptr(addr, field_name)
return b.emit1(.load, b.deref_type(field_ptr), field_ptr)
}
if field_name == 'len' && b.deref_type(addr) == b.map_type {
return b.load_map_len(addr)
}
if field := b.load_smartcast_sum_selector(addr, base_id, field_name) {
return field
}
field_ptr := b.get_field_ptr(addr, field_name)
return b.load_selector_field(node, b.deref_type(addr), field_ptr)
}
}
if base.kind == .selector && b.selector_has_addressable_root(&base) {
if field_name == 'len' {
base_addr := b.build_selector_addr(base)
if b.deref_type(base_addr) == b.map_type {
return b.load_map_len(base_addr)
}
}
field_ptr := b.build_selector_addr(node)
return b.emit1(.load, b.deref_type(field_ptr), field_ptr)
}
base_val := b.build_expr(base_id)
base_typ := b.value_type(base_val)
if field_name == 'len' && base_typ == b.map_type {
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(base_typ))
b.emit2(.store, b.void_type, base_val, alloca)
return b.load_map_len(alloca)
}
if base_typ > 0 && base_typ < b.m.type_store.types.len {
base_type := b.m.type_store.types[base_typ]
if base_type.kind == .ptr_t && base_type.elem_type > 0
&& base_type.elem_type < b.m.type_store.types.len
&& b.m.type_store.types[base_type.elem_type].kind == .struct_t {
field_ptr := b.get_field_ptr(base_val, field_name)
return b.load_selector_field(node, base_type.elem_type, field_ptr)
}
}
if base_typ > 0 && base_typ < b.m.type_store.types.len
&& b.m.type_store.types[base_typ].kind == .struct_t {
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(base_typ))
b.emit2(.store, b.void_type, base_val, alloca)
field_ptr := b.get_field_ptr(alloca, field_name)
return b.load_selector_field(node, base_typ, field_ptr)
}
return b.m.get_or_add_const(b.i64_type, '0')
}
fn (mut b Builder) load_smartcast_sum_value(sum_addr ValueID, expr_id flat.NodeId) ?ValueID {
sum_type := b.deref_type(sum_addr)
if !b.is_sum_type_id(sum_type) {
return none
}
checked_name := b.checked_expr_type_name(expr_id)
if checked_name.len == 0 || b.resolve_type(checked_name) == sum_type {
return none
}
sum_name := b.sum_name_for_type_id(sum_type) or { return none }
variant := b.find_sum_variant(sum_name, checked_name) or { return none }
variant_type := b.resolve_type(variant)
if variant_type <= 0 || variant_type >= b.m.type_store.types.len {
return none
}
payload_ptr := b.get_field_ptr(sum_addr, sum_variant_field_name(variant))
payload_type := b.deref_type(payload_ptr)
if payload_type > 0 && payload_type < b.m.type_store.types.len {
payload_layout := b.m.type_store.types[payload_type]
if payload_layout.kind == .ptr_t {
variant_addr := b.emit1(.load, payload_type, payload_ptr)
return b.emit1(.load, variant_type, variant_addr)
}
}
return b.emit1(.load, variant_type, payload_ptr)
}
fn (mut b Builder) load_smartcast_sum_selector(sum_addr ValueID, base_id flat.NodeId, field_name string) ?ValueID {
field_ptr := b.smartcast_sum_selector_addr(sum_addr, base_id, field_name) or { return none }
return b.emit1(.load, b.deref_type(field_ptr), field_ptr)
}
fn (mut b Builder) smartcast_sum_selector_addr(sum_addr ValueID, base_id flat.NodeId, field_name string) ?ValueID {
sum_type := b.deref_type(sum_addr)
if !b.is_sum_type_id(sum_type) {
return none
}
base_type_name := b.checked_expr_type_name(base_id)
if base_type_name.len == 0 {
return none
}
if b.resolve_type(base_type_name) == sum_type {
return none
}
sum_name := b.sum_name_for_type_id(sum_type) or { return none }
variant := b.find_sum_variant(sum_name, base_type_name) or { return none }
variant_type := b.resolve_type(variant)
if variant_type <= 0 || variant_type >= b.m.type_store.types.len {
return none
}
variant_layout := b.m.type_store.types[variant_type]
if variant_layout.kind != .struct_t {
return none
}
mut has_field := false
for fname in variant_layout.field_names {
if fname == field_name {
has_field = true
break
}
}
if !has_field {
return none
}
payload_ptr := b.get_field_ptr(sum_addr, sum_variant_field_name(variant))
payload_type := b.deref_type(payload_ptr)
mut variant_addr := payload_ptr
if payload_type > 0 && payload_type < b.m.type_store.types.len {
payload_layout := b.m.type_store.types[payload_type]
if payload_layout.kind == .ptr_t {
variant_addr = b.emit1(.load, payload_type, payload_ptr)
}
}
return b.get_field_ptr(variant_addr, field_name)
}
fn (b &Builder) sum_name_for_type_id(typ_id TypeID) ?string {
if typ_id <= 0 {
return none
}
for sum_name, _ in b.sum_type_variants {
if canonical := b.sum_type_canonical[sum_name] {
if sum_typ := b.struct_types[canonical] {
if sum_typ == typ_id {
return sum_name
}
}
}
}
return none
}
fn (b &Builder) selector_qualified_name(node flat.Node) string {
mut cur := node
mut parts := []string{}
for _ in 0 .. 128 {
if cur.kind == .ident {
if cur.value.len > 0 {
parts << cur.value
}
break
}
if cur.kind != .selector {
break
}
if cur.value.len > 0 {
parts << cur.value
}
if cur.children_count == 0 {
break
}
next_id := b.a.child(&cur, 0)
if int(next_id) < 0 || int(next_id) >= b.a.nodes.len {
break
}
cur = b.a.nodes[int(next_id)]
}
if parts.len == 0 {
return ''
}
mut out := ''
mut i := parts.len - 1
for i >= 0 {
if out.len > 0 {
out += '.'
}
out += parts[i]
if i == 0 {
break
}
i--
}
return out
}
fn (mut b Builder) load_map_len(map_ptr ValueID) ValueID {
state := b.map_state_ptr(b.cur_block, map_ptr)
len_ptr := b.map_state_field_ptr(b.cur_block, state, 3)
return b.emit1(.load, b.i64_type, len_ptr)
}
fn (mut b Builder) load_selector_field(node flat.Node, struct_typ_id TypeID, field_ptr ValueID) ValueID {
field := b.emit1(.load, b.deref_type(field_ptr), field_ptr)
if node.typ.len == 0 || !b.sum_type_has_field(struct_typ_id, node.value) {
return field
}
field_typ := b.value_type(field)
if field_typ <= 0 || field_typ >= b.m.type_store.types.len {
return field
}
field_type := b.m.type_store.types[field_typ]
if field_type.kind != .ptr_t {
return field
}
expected := b.resolve_type(node.typ)
if expected > 0 && expected == field_type.elem_type {
return b.emit1(.load, expected, field)
}
return field
}
fn (b &Builder) is_option_like_var(name string) bool {
typ := b.var_type_names[name] or { return false }
return typ.len > 0 && (typ[0] == `?` || typ[0] == `!` || typ == 'Optional')
}
fn (b &Builder) selector_has_addressable_root(node &flat.Node) bool {
match node.kind {
.ident {
if _ := b.vars[node.value] {
return true
}
}
.selector {
if node.children_count > 0 {
return b.selector_has_addressable_root(b.a.child_node(node, 0))
}
}
else {}
}
return false
}
fn (mut b Builder) build_index(id flat.NodeId, node flat.Node) ValueID {
base_id := b.a.child(&node, 0)
if node.children_count >= 2 {
if base := b.build_const_i32_array_arg(base_id) {
index := b.build_expr(b.a.child(&node, 1))
elem_size := b.m.get_or_add_const(b.i64_type, '${b.m.type_size(b.i32_type)}')
offset := b.emit2(.mul, b.i64_type, index, elem_size)
elem_ptr := b.emit2(.add, b.m.type_store.get_ptr(b.i32_type), base, offset)
return b.emit1(.load, b.i32_type, elem_ptr)
}
}
base := b.build_expr(base_id)
base_typ := b.value_type(base)
if node.value == 'range' {
mut start := b.m.get_or_add_const(b.i64_type, '0')
if node.children_count > 1 {
start_node := b.a.child_node(&node, 1)
if start_node.kind == .empty {
start = b.m.get_or_add_const(b.i64_type, '0')
} else {
start = b.build_expr(b.a.child(&node, 1))
}
}
mut end := b.m.get_or_add_const(b.i64_type, '0')
if node.children_count > 2 {
end_node := b.a.child_node(&node, 2)
if end_node.kind == .empty {
if base_typ == b.str_type {
len32 := b.load_struct_field_from_value(base, b.str_type, 'len')
end = b.emit1(.zext, b.i64_type, len32)
} else {
end = b.load_struct_field_from_value(base, b.array_type, 'len')
}
} else {
end = b.build_expr(b.a.child(&node, 2))
}
} else {
if base_typ == b.str_type {
len32 := b.load_struct_field_from_value(base, b.str_type, 'len')
end = b.emit1(.zext, b.i64_type, len32)
} else {
end = b.load_struct_field_from_value(base, b.array_type, 'len')
}
}
if base_typ == b.str_type {
for substr_name in ['string.substr', 'string__substr', 'substr'] {
if substr_id := b.fn_ids[substr_name] {
substr_ref := b.m.add_value(.func_ref, b.str_type, substr_name, substr_id)
return b.emit4(.call, b.str_type, substr_ref, base, start, end)
}
}
data := b.load_struct_field_from_value(base, b.str_type, 'str')
slice_data := b.emit2(.add, b.m.type_store.get_ptr(b.i8_type), data, start)
slice_len := b.emit2(.sub, b.i64_type, end, start)
return b.emit_make_string(b.cur_block, slice_data, slice_len)
}
fn_ref := b.m.add_value(.func_ref, b.array_type, 'array_slice', b.fn_ids['array_slice'])
return b.emit4(.call, b.array_type, fn_ref, base, start, end)
}
if node.children_count < 2 {
return b.m.get_or_add_const(b.i64_type, '0')
}
index := b.build_expr(b.a.child(&node, 1))
if base_typ == b.str_type {
data := b.load_struct_field_from_value(base, b.str_type, 'str')
elem_ptr := b.emit2(.add, b.m.type_store.get_ptr(b.i8_type), data, index)
return b.emit1(.load, b.i8_type, elem_ptr)
}
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
map_type_name := b.expr_type_name_for_map(base_id)
if map_type_name.starts_with('map[') {
key_type_name, val_type_name := map_type_parts(map_type_name)
if key_type_name.len > 0 && val_type_name.len > 0 {
key_type := b.resolve_type(key_type_name)
val_type := b.resolve_type(val_type_name)
key_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(key_type))
b.emit2(.store, b.void_type, index, key_alloca)
key_ptr := if b.value_type(key_alloca) == ptr_i8 {
key_alloca
} else {
b.emit1(.bitcast, ptr_i8, key_alloca)
}
default_alloca := b.emit0(.alloca, b.m.type_store.get_ptr(val_type))
b.emit2(.store, b.void_type, b.default_value_for_type(val_type), default_alloca)
default_ptr := if b.value_type(default_alloca) == ptr_i8 {
default_alloca
} else {
b.emit1(.bitcast, ptr_i8, default_alloca)
}
map_ptr := b.map_expr_ptr(base_id)
get_ref := b.m.add_value(.func_ref, ptr_i8, 'map__get', b.fn_ids['map__get'])
value_ptr := b.emit4(.call, ptr_i8, get_ref, map_ptr, key_ptr, default_ptr)
typed_value_ptr := if b.m.type_store.get_ptr(val_type) == ptr_i8 {
value_ptr
} else {
b.emit1(.bitcast, b.m.type_store.get_ptr(val_type), value_ptr)
}
return b.emit1(.load, val_type, typed_value_ptr)
}
}
if base_typ > 0 && base_typ < b.m.type_store.types.len {
base_type := b.m.type_store.types[base_typ]
if base_type.kind == .ptr_t {
if base_type.elem_type == b.array_type {
elem_type := b.index_elem_type(id, node)
elem_ptr := b.build_array_data_index_addr(base, index, elem_type)
return b.emit1(.load, elem_type, elem_ptr)
}
elem_type := if base_type.elem_type > 0 { base_type.elem_type } else { b.i8_type }
elem_size := b.m.type_size(elem_type)
offset := if elem_size > 1 {
elem_size_const := b.m.get_or_add_const(b.i64_type, '${elem_size}')
b.emit2(.mul, b.i64_type, index, elem_size_const)
} else {
index
}
elem_ptr := b.emit2(.add, base_typ, base, offset)
return b.emit1(.load, elem_type, elem_ptr)
}
}
fn_ref := b.m.add_value(.func_ref, ptr_i8, 'array_get', b.fn_ids['array_get'])
elem_ptr := b.emit3(.call, ptr_i8, fn_ref, base, index)
elem_type := b.index_elem_type(id, node)
typed_ptr := b.emit1(.bitcast, b.m.type_store.get_ptr(elem_type), elem_ptr)
return b.emit1(.load, elem_type, typed_ptr)
}
fn (mut b Builder) build_index_addr(id flat.NodeId, node flat.Node) ValueID {
if node.children_count < 2 {
return b.m.get_or_add_const(b.m.type_store.get_ptr(b.i8_type), '0')
}
base_id := b.a.child(&node, 0)
if base := b.build_const_i32_array_arg(base_id) {
index := b.build_expr(b.a.child(&node, 1))
elem_size := b.m.get_or_add_const(b.i64_type, '${b.m.type_size(b.i32_type)}')
offset := b.emit2(.mul, b.i64_type, index, elem_size)
return b.emit2(.add, b.m.type_store.get_ptr(b.i32_type), base, offset)
}
base := b.build_expr(base_id)
index := b.build_expr(b.a.child(&node, 1))
base_typ := b.value_type(base)
if base_typ == b.str_type {
data := b.load_struct_field_from_value(base, b.str_type, 'str')
return b.emit2(.add, b.m.type_store.get_ptr(b.i8_type), data, index)
}
if base_typ > 0 && base_typ < b.m.type_store.types.len {
base_type := b.m.type_store.types[base_typ]
if base_type.kind == .ptr_t {
if base_type.elem_type == b.array_type {
elem_type := b.index_elem_type(id, node)
return b.build_array_data_index_addr(base, index, elem_type)
}
elem_type := if base_type.elem_type > 0 { base_type.elem_type } else { b.i8_type }
elem_size := b.m.type_size(elem_type)
offset := if elem_size > 1 {
elem_size_const := b.m.get_or_add_const(b.i64_type, '${elem_size}')
b.emit2(.mul, b.i64_type, index, elem_size_const)
} else {
index
}
return b.emit2(.add, base_typ, base, offset)
}
}
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
fn_ref := b.m.add_value(.func_ref, ptr_i8, 'array_get', b.fn_ids['array_get'])
elem_ptr := b.emit3(.call, ptr_i8, fn_ref, base, index)
elem_type := b.index_elem_type(id, node)
return b.emit1(.bitcast, b.m.type_store.get_ptr(elem_type), elem_ptr)
}
fn (mut b Builder) build_array_data_index_addr(array_ptr ValueID, index ValueID, elem_type TypeID) ValueID {
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
data_ptr := b.get_field_ptr(array_ptr, 'data')
data := b.emit1(.load, ptr_i8, data_ptr)
elem_size := b.m.type_size(elem_type)
offset := if elem_size > 1 {
elem_size_const := b.m.get_or_add_const(b.i64_type, '${elem_size}')
b.emit2(.mul, b.i64_type, index, elem_size_const)
} else {
index
}
elem_ptr := b.emit2(.add, ptr_i8, data, offset)
ptr_elem := b.m.type_store.get_ptr(elem_type)
if ptr_elem == ptr_i8 {
return elem_ptr
}
return b.emit1(.bitcast, ptr_elem, elem_ptr)
}
fn (mut b Builder) load_struct_field_from_value(value ValueID, typ TypeID, field_name string) ValueID {
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(typ))
b.emit2(.store, b.void_type, value, alloca)
field_ptr := b.get_field_ptr(alloca, field_name)
field := b.emit1(.load, b.deref_type(field_ptr), field_ptr)
if typ == b.array_type && field_name in ['offset', 'len', 'cap', 'flags', 'element_size'] {
return b.emit1(.zext, b.i64_type, field)
}
if typ == b.str_type && field_name == 'len' {
return b.emit1(.zext, b.i64_type, field)
}
return field
}
fn (mut b Builder) index_elem_type(id flat.NodeId, node flat.Node) TypeID {
if node.children_count > 0 {
base_type := b.checked_expr_type_name(b.a.child(&node, 0))
elem_type := indexed_elem_type_name(base_type)
if elem_type.len > 0 {
return b.resolve_type(elem_type)
}
}
if node.typ != '' && !node.typ.starts_with('[]') {
return b.resolve_type(node.typ)
}
checked := b.checked_expr_type_name(id)
if checked.len > 0 && checked != 'unknown' && !checked.starts_with('[]') && checked[0] != `?`
&& checked[0] != `!` {
return b.resolve_type(checked)
}
return b.i64_type
}
fn (mut b Builder) build_struct_init(node flat.Node) ValueID {
typ_id, struct_name := b.struct_literal_type(node.value)
if typ_id > 0 {
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(typ_id))
typ := b.m.type_store.types[typ_id]
mut initialized := map[string]bool{}
for i in 0 .. node.children_count {
field_node := b.a.child_node(&node, i)
field_expr_id := b.a.child(field_node, 0)
for fi, fname in typ.field_names {
if fname == field_node.value {
offset := b.m.struct_field_offset(typ_id, fi)
off_const := b.m.get_or_add_const(b.i64_type, '${offset}')
field_type := if fi < typ.fields.len { typ.fields[fi] } else { b.i64_type }
field_type_name := b.field_type_name(struct_name, fname)
field_val := b.coerce_store_value(b.build_field_value(field_expr_id,
field_type_name), field_type)
field_ptr := b.emit2(.get_element_ptr, b.m.type_store.get_ptr(field_type),
alloca, off_const)
b.emit2(.store, b.void_type, field_val, field_ptr)
initialized[fname] = true
break
}
}
}
zero := b.m.get_or_add_const(b.i64_type, '0')
for fi, fname in typ.field_names {
if fname !in initialized {
offset := b.m.struct_field_offset(typ_id, fi)
off_const := b.m.get_or_add_const(b.i64_type, '${offset}')
field_type := if fi < typ.fields.len { typ.fields[fi] } else { b.i64_type }
field_ptr := b.emit2(.get_element_ptr, b.m.type_store.get_ptr(field_type), alloca,
off_const)
default_value := b.default_field_value(struct_name, fname, zero)
b.emit2(.store, b.void_type, default_value, field_ptr)
}
}
return b.emit1(.load, typ_id, alloca)
}
return b.m.get_or_add_const(b.i64_type, '0')
}
fn (mut b Builder) coerce_store_value(value ValueID, target_type TypeID) ValueID {
mut result := value
value_type := b.value_type(value)
if b.is_pointer_type(target_type) && !b.is_pointer_type(value_type) {
result = b.heap_copy_value(value, value_type)
} else if target_type != value_type && b.is_pointer_type(target_type)
&& b.is_pointer_type(value_type) {
result = b.emit1(.bitcast, target_type, value)
}
return result
}
fn (mut b Builder) build_field_value(expr_id flat.NodeId, field_type_name string) ValueID {
if int(expr_id) >= 0 {
expr := b.a.nodes[int(expr_id)]
if expr.kind == .enum_val && field_type_name.len > 0 {
return b.build_enum_val_with_type(expr, field_type_name) or { b.build_expr(expr_id) }
}
if expr.kind == .infix && field_type_name.len > 0 && b.is_enum_type_name(field_type_name) {
return b.build_enum_expr_with_type(expr, field_type_name) or { b.build_expr(expr_id) }
}
}
return b.build_expr(expr_id)
}
fn (mut b Builder) build_enum_expr_with_type(node flat.Node, type_name string) ?ValueID {
if node.kind == .enum_val {
return b.build_enum_val_with_type(node, type_name)
}
if node.kind != .infix || node.children_count < 2 {
return none
}
lhs := b.a.nodes[int(b.a.child(&node, 0))]
rhs := b.a.nodes[int(b.a.child(&node, 1))]
lval := b.enum_const_value_with_type(lhs, type_name) or { return none }
rval := b.enum_const_value_with_type(rhs, type_name) or { return none }
mut result := 0
match node.op {
.pipe { result = lval | rval }
.amp { result = lval & rval }
.xor { result = lval ^ rval }
else { return none }
}
return b.m.get_or_add_const(b.i64_type, result.str())
}
fn (b &Builder) enum_const_value_with_type(node flat.Node, type_name string) ?int {
if node.kind == .enum_val {
return b.enum_value_for_type(type_name, node.value)
}
if node.kind == .infix && node.children_count >= 2 {
lhs := b.a.nodes[int(b.a.child(&node, 0))]
rhs := b.a.nodes[int(b.a.child(&node, 1))]
lval := b.enum_const_value_with_type(lhs, type_name) or { return none }
rval := b.enum_const_value_with_type(rhs, type_name) or { return none }
match node.op {
.pipe { return lval | rval }
.amp { return lval & rval }
.xor { return lval ^ rval }
else { return none }
}
}
return none
}
fn (mut b Builder) default_field_value(struct_name string, field_name string, zero ValueID) ValueID {
if struct_name.ends_with('SystemError') {
if field_name == 'msg' {
return b.m.add_value(.string_literal, b.str_type, '', 0)
}
if field_name == 'code' {
return b.m.get_or_add_const(b.i32_type, '-1')
}
}
field_type := b.field_type_name(struct_name, field_name)
if field_type.starts_with('[]') {
elem_name := field_type[2..]
elem_type := b.resolve_type(elem_name)
elem_size := b.m.type_size(elem_type)
elem_size_const := b.m.get_or_add_const(b.i64_type, '${elem_size}')
if fn_id := b.fn_ids['array_new'] {
fn_ref := b.m.add_value(.func_ref, b.void_type, 'array_new', fn_id)
return b.emit4(.call, b.array_type, fn_ref, elem_size_const, zero, zero)
}
}
if field_type.starts_with('map[') {
key_type, val_type := map_type_parts(field_type)
key_size := b.m.type_size(b.resolve_type(key_type))
val_size := b.m.type_size(b.resolve_type(val_type))
actual_key_size := if key_size > 0 { key_size } else { 8 }
actual_val_size := if val_size > 0 { val_size } else { 8 }
key_size_const := b.m.get_or_add_const(b.i64_type, '${actual_key_size}')
val_size_const := b.m.get_or_add_const(b.i64_type, '${actual_val_size}')
if fn_id := b.fn_ids['new_map'] {
fn_ref := b.m.add_value(.func_ref, b.void_type, 'new_map', fn_id)
mut args := []ValueID{}
args << fn_ref
args << key_size_const
args << val_size_const
args << zero
args << zero
args << zero
args << zero
return b.m.add_instr(.call, b.cur_block, b.map_type, args)
}
}
if field_type.len > 0 {
return b.default_value_for_type(b.resolve_type(field_type))
}
return zero
}
fn (b &Builder) field_type_name(struct_name string, field_name string) string {
key := struct_name + '.' + field_name
if typ := b.struct_field_types[key] {
return typ
}
short_name := struct_name.all_after('.')
short_key := short_name + '.' + field_name
if typ := b.struct_field_types[short_key] {
return typ
}
return ''
}
fn (b &Builder) struct_literal_type(name string) (TypeID, string) {
short_name := name.all_after('.')
if !name.contains('.') && b.cur_module.len > 0 && b.cur_module != 'main'
&& b.cur_module != 'builtin' {
qualified_name := b.cur_module + '.' + short_name
if typ := b.struct_types[qualified_name] {
return typ, qualified_name
}
}
if typ := b.struct_types[name] {
return typ, name
}
if typ := b.struct_types[short_name] {
return typ, short_name
}
return TypeID(0), name
}
fn (mut b Builder) build_heap_struct_init(node flat.Node) ValueID {
typ_id, struct_name := b.struct_literal_type(node.value)
if typ_id > 0 {
alloca := b.emit0(.alloca, b.m.type_store.get_ptr(typ_id))
typ := b.m.type_store.types[typ_id]
mut initialized := map[string]bool{}
for i in 0 .. node.children_count {
field_node := b.a.child_node(&node, i)
field_expr_id := b.a.child(field_node, 0)
for fi, fname in typ.field_names {
if fname == field_node.value {
offset := b.m.struct_field_offset(typ_id, fi)
off_const := b.m.get_or_add_const(b.i64_type, '${offset}')
field_type := if fi < typ.fields.len { typ.fields[fi] } else { b.i64_type }
field_type_name := b.field_type_name(struct_name, fname)
field_val := b.coerce_store_value(b.build_field_value(field_expr_id,
field_type_name), field_type)
field_ptr := b.emit2(.get_element_ptr, b.m.type_store.get_ptr(field_type),
alloca, off_const)
b.emit2(.store, b.void_type, field_val, field_ptr)
initialized[fname] = true
break
}
}
}
zero := b.m.get_or_add_const(b.i64_type, '0')
for fi, fname in typ.field_names {
if fname !in initialized {
offset := b.m.struct_field_offset(typ_id, fi)
off_const := b.m.get_or_add_const(b.i64_type, '${offset}')
field_type := if fi < typ.fields.len { typ.fields[fi] } else { b.i64_type }
field_ptr := b.emit2(.get_element_ptr, b.m.type_store.get_ptr(field_type), alloca,
off_const)
default_value := b.default_field_value(struct_name, fname, zero)
b.emit2(.store, b.void_type, default_value, field_ptr)
}
}
size := b.m.type_size(typ_id)
size_const := b.m.get_or_add_const(b.i64_type, '${size}')
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
src_cast := b.emit1(.bitcast, ptr_i8, alloca)
memdup_ref := b.m.add_value(.func_ref, ptr_i8, 'memdup', b.fn_ids['memdup'])
result := b.emit3(.call, ptr_i8, memdup_ref, src_cast, size_const)
return b.emit1(.bitcast, b.m.type_store.get_ptr(typ_id), result)
}
return b.m.get_or_add_const(b.i64_type, '0')
}
fn (mut b Builder) build_string_interp(node flat.Node) ValueID {
n := node.children_count
if n == 0 {
return b.m.add_value(.string_literal, b.str_type, '', 0)
}
mut parts := []ValueID{}
for i in 0 .. n {
child_id := b.a.child(&node, i)
child := b.a.nodes[int(child_id)]
if child.kind == .string_literal {
parts << b.m.add_value(.string_literal, b.str_type, child.value, 0)
} else {
vtype := b.infer_v_type(child_id)
if vtype == 'string' {
parts << b.build_expr(child_id)
} else {
val := b.build_expr(child_id)
int_str_ref := b.m.add_value(.func_ref, b.str_type, 'int_str', b.fn_ids['int_str'])
parts << b.emit2(.call, b.str_type, int_str_ref, val)
}
}
}
count_const := b.m.get_or_add_const(b.i64_type, '${parts.len}')
alloca := b.emit1(.alloca, b.m.type_store.get_ptr(b.str_type), count_const)
for i, part in parts {
off_const := b.m.get_or_add_const(b.i64_type, '${i * 16}')
ptr := b.emit2(.get_element_ptr, b.m.type_store.get_ptr(b.str_type), alloca, off_const)
b.emit2(.store, b.void_type, part, ptr)
}
fn_ref := b.m.add_value(.func_ref, b.str_type, 'string_plus_many', b.fn_ids['string_plus_many'])
return b.emit3(.call, b.str_type, fn_ref, count_const, alloca)
}
fn (mut b Builder) get_field_ptr(base_addr ValueID, field_name string) ValueID {
mut struct_typ_id := TypeID(0)
base_val := b.m.values[base_addr]
base_type_id := base_val.typ
if base_type_id > 0 && base_type_id < b.m.type_store.types.len {
base_type := b.m.type_store.types[base_type_id]
if base_type.kind == .ptr_t {
elem_type := b.m.type_store.types[base_type.elem_type]
if elem_type.kind == .ptr_t {
struct_typ_id = elem_type.elem_type
} else if elem_type.kind == .struct_t {
struct_typ_id = base_type.elem_type
}
}
}
if struct_typ_id > 0 {
typ := b.m.type_store.types[struct_typ_id]
for fi in 0 .. 512 {
if fi >= typ.field_names.len || fi >= typ.fields.len {
break
}
fname := typ.field_names[fi]
if fname == field_name {
offset := b.m.struct_field_offset(struct_typ_id, fi)
off_const := b.m.get_or_add_const(b.i64_type, '${offset}')
field_type := typ.fields[fi]
ptr_type := b.m.type_store.get_ptr(field_type)
if b.m.type_store.types[b.m.values[base_addr].typ].kind == .ptr_t {
inner := b.m.type_store.types[b.m.values[base_addr].typ]
if inner.kind == .ptr_t && b.m.type_store.types[inner.elem_type].kind == .ptr_t {
loaded := b.emit1(.load, inner.elem_type, base_addr)
return b.emit2(.get_element_ptr, ptr_type, loaded, off_const)
}
}
return b.emit2(.get_element_ptr, ptr_type, base_addr, off_const)
}
}
}
off_const := b.m.get_or_add_const(b.i64_type, '0')
ptr_type := b.m.type_store.get_ptr(b.i64_type)
return b.emit2(.get_element_ptr, ptr_type, base_addr, off_const)
}
fn (mut b Builder) resolve_type(name string) TypeID {
if name.len > 1 && (name[0] == `?` || name[0] == `!`) {
return b.option_type_id(name[1..])
}
if name.starts_with('&') {
inner := b.resolve_type(name[1..])
return b.m.type_store.get_ptr(inner)
}
clean_name := normalize_primitive_type_name(name)
if clean_name != name {
return b.resolve_type(clean_name)
}
if typ := b.primitive_type_id(name) {
return typ
}
if alias := b.type_alias_target(name) {
if alias != name {
return b.resolve_type(alias)
}
}
// SSA-level alias fallback (used when building without a checker).
if alias_typ := b.type_aliases[name] {
if alias_typ > 0 {
return alias_typ
}
}
if b.is_fixed_array_type_name(name) {
elem_type := b.resolve_type(b.fixed_array_elem_type_name(name))
return b.m.type_store.get_ptr(elem_type)
}
if name.starts_with('[]') || name == 'array' || name == 'Array' {
return b.array_type
}
if name == 'strings.Builder' {
return b.array_type
}
if name.starts_with('map[') || name == 'map' || name == 'Map' {
return b.map_type
}
if b.is_enum_type_name(name) {
return b.i32_type
}
if canonical := b.canonical_sum_type_name(name) {
if typ := b.struct_types[canonical] {
return typ
}
}
short_name := name.all_after('.')
if !name.contains('.') && b.cur_module.len > 0 && b.cur_module != 'main'
&& b.cur_module != 'builtin' {
qualified_name := b.cur_module + '.' + short_name
if qualified_name in b.enum_types {
return b.i32_type
}
if typ := b.struct_types[qualified_name] {
return typ
}
}
if typ := b.struct_types[name] {
return typ
}
if typ := b.struct_types[short_name] {
return typ
}
if name == 'Builder' {
return b.array_type
}
return b.i64_type
}
fn (mut b Builder) primitive_type_id(name string) ?TypeID {
return match name {
'int' {
b.i32_type
}
'i8' {
b.i8_type
}
'i16' {
b.i32_type
}
'i32' {
b.i32_type
}
'i64' {
b.i64_type
}
'u8', 'byte' {
b.u8_type
}
'u16' {
b.u16_type
}
'u32' {
b.u32_type
}
'u64' {
b.u64_type
}
'f32' {
b.f32_type
}
'f64' {
b.f64_type
}
'bool' {
b.i1_type
}
'string' {
b.str_type
}
'void', '' {
b.void_type
}
'voidptr' {
b.m.type_store.get_ptr(b.i8_type)
}
else {
none
}
}
}
fn normalize_primitive_type_name(name string) string {
short_name := name.all_after('.')
return match short_name {
'int', 'i8', 'i16', 'i32', 'i64', 'u8', 'byte', 'u16', 'u32', 'u64', 'f32', 'f64', 'bool',
'string', 'void', 'voidptr', '' {
short_name
}
else {
name
}
}
}
fn (mut b Builder) option_type_id(base_name string) TypeID {
key := '?' + base_name
if typ := b.option_types[key] {
return typ
}
base_typ := b.resolve_type(base_name)
mut fields := []TypeID{}
mut field_names := []string{}
fields << b.i1_type
field_names << 'ok'
if base_typ != b.void_type {
fields << base_typ
field_names << 'value'
}
typ_id := b.m.type_store.register(Type{
kind: .struct_t
fields: fields
field_names: field_names
})
b.option_types[key] = typ_id
b.struct_types[key] = typ_id
b.struct_types['!' + base_name] = typ_id
return typ_id
}
fn (b &Builder) option_value_type(typ_id TypeID) TypeID {
if typ_id <= 0 || typ_id >= b.m.type_store.types.len {
return TypeID(0)
}
typ := b.m.type_store.types[typ_id]
if typ.kind != .struct_t || typ.field_names.len == 0 || typ.field_names[0] != 'ok' {
return TypeID(0)
}
if typ.field_names.len > 1 && typ.field_names[1] == 'value' && typ.fields.len > 1 {
return typ.fields[1]
}
return b.void_type
}
fn (b &Builder) option_payload_type_name(id flat.NodeId) string {
name := b.checked_expr_type_name(id)
if name.len > 1 && (name[0] == `?` || name[0] == `!`) {
return name[1..]
}
return name
}
fn (b &Builder) is_option_type(typ_id TypeID) bool {
return b.option_value_type(typ_id) != TypeID(0)
}
fn (b &Builder) is_enum_type_name(name string) bool {
if name.len == 0 {
return false
}
if name in b.enum_types {
return true
}
short_name := name.all_after('.')
return short_name in b.enum_types
}
fn (mut b Builder) resolve_type_in_module(name string, module_name string) TypeID {
if name.len > 1 && (name[0] == `?` || name[0] == `!`) {
return b.option_type_id(name[1..])
}
if name.starts_with('&') {
inner := b.resolve_type_in_module(name[1..], module_name)
return b.m.type_store.get_ptr(inner)
}
if !name.contains('.') && module_name.len > 0 && module_name != 'main'
&& module_name != 'builtin' {
qualified_name := module_name + '.' + name
if qualified_name in b.enum_types {
return b.i32_type
}
if typ := b.struct_types[qualified_name] {
return typ
}
if alias := b.type_alias_target(qualified_name) {
if alias != qualified_name {
return b.resolve_type_in_module(alias, module_name)
}
}
}
return b.resolve_type(name)
}
fn (mut b Builder) resolve_c_decl_type(name string) TypeID {
if name.starts_with('...') {
return b.m.type_store.get_ptr(b.i8_type)
}
if name.starts_with('C.') {
return b.resolve_type(name[2..])
}
return b.resolve_type(name)
}
fn (b &Builder) type_alias_target(name string) ?string {
if b.tc == unsafe { nil } || name.len == 0 {
return none
}
if target := b.tc.type_aliases[name] {
return target
}
if !name.contains('.') && b.cur_module.len > 0 && b.cur_module != 'main'
&& b.cur_module != 'builtin' {
qualified_name := b.cur_module + '.' + name
if target := b.tc.type_aliases[qualified_name] {
return target
}
}
return none
}
fn (b &Builder) value_type(val_id ValueID) TypeID {
if val_id <= 0 || val_id >= b.m.values.len {
return b.i64_type
}
v := b.m.values[val_id]
if v.typ > 0 {
return v.typ
}
return b.i64_type
}
fn (b &Builder) is_float_type(typ TypeID) bool {
if typ <= 0 || typ >= b.m.type_store.types.len {
return false
}
return b.m.type_store.types[typ].kind == .float_t
}
fn (b &Builder) is_struct_type(typ TypeID) bool {
if typ <= 0 || typ >= b.m.type_store.types.len {
return false
}
return b.m.type_store.types[typ].kind == .struct_t
}
fn (b &Builder) is_int_type(typ TypeID) bool {
if typ <= 0 || typ >= b.m.type_store.types.len {
return false
}
return b.m.type_store.types[typ].kind == .int_t
}
fn (b &Builder) is_unsigned_type(typ TypeID) bool {
if typ <= 0 || typ >= b.m.type_store.types.len {
return false
}
t := b.m.type_store.types[typ]
return t.kind == .int_t && t.is_unsigned
}
fn (b &Builder) is_pointer_type(typ TypeID) bool {
if typ <= 0 || typ >= b.m.type_store.types.len {
return false
}
return b.m.type_store.types[typ].kind == .ptr_t
}
fn (b &Builder) scalar_type_width(typ TypeID) int {
if typ <= 0 || typ >= b.m.type_store.types.len {
return 0
}
return b.m.type_store.types[typ].width
}
fn (b &Builder) deref_type(ptr_val ValueID) TypeID {
if ptr_val <= 0 || ptr_val >= b.m.values.len {
return b.i64_type
}
v := b.m.values[ptr_val]
if v.typ > 0 && v.typ < b.m.type_store.types.len {
t := b.m.type_store.types[v.typ]
if t.kind == .ptr_t {
return t.elem_type
}
}
if v.kind == .instruction {
instr := b.m.instrs[v.index]
if instr.op == .alloca || instr.op == .get_element_ptr || instr.op == .bitcast {
if v.typ > 0 && v.typ < b.m.type_store.types.len {
t := b.m.type_store.types[v.typ]
if t.kind == .ptr_t {
return t.elem_type
}
}
}
}
if v.kind == .global {
if v.typ > 0 && v.typ < b.m.type_store.types.len {
t := b.m.type_store.types[v.typ]
if t.kind == .ptr_t {
return t.elem_type
}
}
}
return b.i64_type
}
fn (b &Builder) infer_v_type(id flat.NodeId) string {
if int(id) < 0 {
return 'int'
}
node := b.a.nodes[int(id)]
match node.kind {
.string_literal, .string_interp {
return 'string'
}
.int_literal {
return 'int'
}
.bool_literal {
return 'bool'
}
.ident {
if addr := b.vars[node.value] {
val := b.m.values[addr]
if val.typ == b.str_type || (val.typ > 0 && val.typ < b.m.type_store.types.len
&& b.m.type_store.types[val.typ].kind == .ptr_t
&& b.m.type_store.types[val.typ].elem_type == b.str_type) {
return 'string'
}
}
return 'int'
}
.call {
fn_node := b.a.child_node(&node, 0)
if fn_node.value == 'int_str' {
return 'string'
}
if fn_idx := b.fn_ids[fn_node.value] {
ret := b.m.funcs[fn_idx].typ
if ret == b.str_type {
return 'string'
}
}
return 'int'
}
.infix {
lt := b.infer_v_type(b.a.child(&node, 0))
if lt == 'string' {
return 'string'
}
return lt
}
else {
return 'int'
}
}
}
fn (mut b Builder) emit0(op OpCode, typ TypeID) ValueID {
return b.m.add_instr(op, b.cur_block, typ, []ValueID{})
}
fn (mut b Builder) emit1(op OpCode, typ TypeID, a ValueID) ValueID {
mut ops := []ValueID{}
ops << a
return b.m.add_instr(op, b.cur_block, typ, ops)
}
fn (mut b Builder) emit2(op OpCode, typ TypeID, a ValueID, c ValueID) ValueID {
mut ops := []ValueID{}
ops << a
ops << c
return b.m.add_instr(op, b.cur_block, typ, ops)
}
fn (mut b Builder) emit3(op OpCode, typ TypeID, a ValueID, c ValueID, d ValueID) ValueID {
mut ops := []ValueID{}
ops << a
ops << c
ops << d
return b.m.add_instr(op, b.cur_block, typ, ops)
}
fn (mut b Builder) emit4(op OpCode, typ TypeID, a ValueID, c ValueID, d ValueID, e ValueID) ValueID {
mut ops := []ValueID{}
ops << a
ops << c
ops << d
ops << e
return b.m.add_instr(op, b.cur_block, typ, ops)
}
fn (b &Builder) compound_to_op(op flat.Op) OpCode {
return match op {
.plus_assign { .add }
.minus_assign { .sub }
.mul_assign { .mul }
.div_assign { .sdiv }
.mod_assign { .srem }
.amp_assign { .and_ }
.pipe_assign { .or_ }
.xor_assign { .xor }
.left_shift_assign { .shl }
.right_shift_assign { .ashr }
else { .add }
}
}