10158 lines
352 KiB
V
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 }
|
|
}
|
|
}
|