Files
Odin/src/ssa/emit.cpp
T
2016-10-30 13:20:45 +00:00

1151 lines
35 KiB
C++

ssaValue *ssa_type_info (ssaProcedure *proc, Type *type);
ssaValue *ssa_emit_conv (ssaProcedure *proc, ssaValue *value, Type *t, b32 is_argument = false);
ssaValue *ssa_build_expr (ssaProcedure *proc, AstNode *expr);
void ssa_build_stmt (ssaProcedure *proc, AstNode *node);
void ssa_build_cond (ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block);
void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d);
ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) {
GB_ASSERT(instr->kind == ssaValue_Instr);
ssaBlock *b = proc->curr_block;
instr->Instr.parent = b;
if (b != NULL) {
ssaInstr *i = ssa_get_last_instr(b);
if (!ssa_is_instr_terminating(i)) {
array_add(&b->instrs, instr);
}
}
return instr;
}
ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value) {
return ssa_emit(p, ssa_make_instr_store(p, address, value));
}
ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address) {
return ssa_emit(p, ssa_make_instr_load(p, address));
}
ssaValue *ssa_emit_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) {
return ssa_emit(p, ssa_make_instr_select(p, cond, t, f));
}
ssaValue *ssa_emit_zero_init(ssaProcedure *p, ssaValue *address) {
return ssa_emit(p, ssa_make_instr_zero_init(p, address));
}
ssaValue *ssa_emit_comment(ssaProcedure *p, String text) {
return ssa_emit(p, ssa_make_instr_comment(p, text));
}
ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count) {
Type *pt = base_type(ssa_type(value));
GB_ASSERT(pt->kind == Type_Proc);
Type *results = pt->Proc.results;
return ssa_emit(p, ssa_make_instr_call(p, value, args, arg_count, results));
}
ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name_, ssaValue **args, isize arg_count) {
String name = make_string(name_);
ssaValue **found = map_get(&proc->module->members, hash_string(name));
GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name));
ssaValue *gp = *found;
return ssa_emit_call(proc, gp, args, arg_count);
}
void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) {
isize count = proc->defer_stmts.count;
isize i = count;
while (i --> 0) {
ssaDefer d = proc->defer_stmts[i];
if (kind == ssaDeferExit_Default) {
if (proc->scope_index == d.scope_index &&
d.scope_index > 1) {
ssa_build_defer_stmt(proc, d);
array_pop(&proc->defer_stmts);
continue;
} else {
break;
}
} else if (kind == ssaDeferExit_Return) {
ssa_build_defer_stmt(proc, d);
} else if (kind == ssaDeferExit_Branch) {
GB_ASSERT(block != NULL);
isize lower_limit = block->scope_index+1;
if (lower_limit < d.scope_index) {
ssa_build_defer_stmt(proc, d);
}
}
}
}
void ssa_open_scope(ssaProcedure *proc) {
proc->scope_index++;
}
void ssa_close_scope(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) {
ssa_emit_defer_stmts(proc, kind, block);
GB_ASSERT(proc->scope_index > 0);
proc->scope_index--;
}
void ssa_emit_unreachable(ssaProcedure *proc) {
ssa_emit(proc, ssa_make_instr_unreachable(proc));
}
void ssa_emit_return(ssaProcedure *proc, ssaValue *v) {
ssa_emit_defer_stmts(proc, ssaDeferExit_Return, NULL);
ssa_emit(proc, ssa_make_instr_return(proc, v));
}
void ssa_emit_jump(ssaProcedure *proc, ssaBlock *target_block) {
ssaBlock *b = proc->curr_block;
if (b == NULL) {
return;
}
ssa_emit(proc, ssa_make_instr_jump(proc, target_block));
ssa_add_edge(b, target_block);
proc->curr_block = NULL;
}
void ssa_emit_if(ssaProcedure *proc, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) {
ssaBlock *b = proc->curr_block;
if (b == NULL) {
return;
}
ssa_emit(proc, ssa_make_instr_if(proc, cond, true_block, false_block));
ssa_add_edge(b, true_block);
ssa_add_edge(b, false_block);
proc->curr_block = NULL;
}
void ssa_emit_startup_runtime(ssaProcedure *proc) {
GB_ASSERT(proc->parent == NULL && proc->name == "main");
ssa_emit(proc, ssa_alloc_instr(proc, ssaInstr_StartupRuntime));
}
ssaValue *ssa_addr_store(ssaProcedure *proc, ssaAddr addr, ssaValue *value) {
if (addr.addr == NULL) {
return NULL;
}
if (addr.kind == ssaAddr_Vector) {
ssaValue *v = ssa_emit_load(proc, addr.addr);
Type *elem_type = base_type(ssa_type(v))->Vector.elem;
ssaValue *elem = ssa_emit_conv(proc, value, elem_type);
ssaValue *out = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, elem, addr.Vector.index));
return ssa_emit_store(proc, addr.addr, out);
} else {
ssaValue *v = ssa_emit_conv(proc, value, ssa_addr_type(addr));
return ssa_emit_store(proc, addr.addr, v);
}
}
ssaValue *ssa_addr_load(ssaProcedure *proc, ssaAddr addr) {
if (addr.addr == NULL) {
GB_PANIC("Illegal addr load");
return NULL;
}
if (addr.kind == ssaAddr_Vector) {
ssaValue *v = ssa_emit_load(proc, addr.addr);
return ssa_emit(proc, ssa_make_instr_extract_element(proc, v, addr.Vector.index));
}
Type *t = base_type(ssa_type(addr.addr));
if (t->kind == Type_Proc) {
// NOTE(bill): Imported procedures don't require a load as they are pointers
return addr.addr;
}
return ssa_emit_load(proc, addr.addr);
}
ssaValue *ssa_emit_ptr_offset(ssaProcedure *proc, ssaValue *ptr, ssaValue *offset) {
offset = ssa_emit_conv(proc, offset, t_int);
return ssa_emit(proc, ssa_make_instr_ptr_offset(proc, ptr, offset));
}
ssaValue *ssa_emit_arith(ssaProcedure *proc, TokenKind op, ssaValue *left, ssaValue *right, Type *type) {
Type *t_left = ssa_type(left);
Type *t_right = ssa_type(right);
if (op == Token_Add) {
if (is_type_pointer(t_left)) {
ssaValue *ptr = ssa_emit_conv(proc, left, type);
ssaValue *offset = right;
return ssa_emit_ptr_offset(proc, ptr, offset);
} else if (is_type_pointer(ssa_type(right))) {
ssaValue *ptr = ssa_emit_conv(proc, right, type);
ssaValue *offset = left;
return ssa_emit_ptr_offset(proc, ptr, offset);
}
} else if (op == Token_Sub) {
if (is_type_pointer(t_left) && is_type_integer(t_right)) {
// ptr - int
ssaValue *ptr = ssa_emit_conv(proc, left, type);
ssaValue *offset = right;
return ssa_emit_ptr_offset(proc, ptr, offset);
} else if (is_type_pointer(t_left) && is_type_pointer(t_right)) {
GB_ASSERT(is_type_integer(type));
Type *ptr_type = t_left;
ssaModule *m = proc->module;
ssaValue *x = ssa_emit_conv(proc, left, type);
ssaValue *y = ssa_emit_conv(proc, right, type);
ssaValue *diff = ssa_emit_arith(proc, op, x, y, type);
ssaValue *elem_size = ssa_make_const_int(m->allocator, type_size_of(m->sizes, m->allocator, ptr_type));
return ssa_emit_arith(proc, Token_Quo, diff, elem_size, type);
}
}
switch (op) {
case Token_AndNot: {
// NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1)
// NOTE(bill): "not" `x` == `x` "xor" `-1`
ssaValue *neg = ssa_add_module_constant(proc->module, type, make_exact_value_integer(-1));
op = Token_Xor;
right = ssa_emit_arith(proc, op, right, neg, type);
GB_ASSERT(right->Instr.kind == ssaInstr_BinaryOp);
right->Instr.BinaryOp.type = type;
op = Token_And;
} /* fallthrough */
case Token_Add:
case Token_Sub:
case Token_Mul:
case Token_Quo:
case Token_Mod:
case Token_And:
case Token_Or:
case Token_Xor:
left = ssa_emit_conv(proc, left, type);
right = ssa_emit_conv(proc, right, type);
break;
}
return ssa_emit(proc, ssa_make_instr_binary_op(proc, op, left, right, type));
}
ssaValue *ssa_emit_comp(ssaProcedure *proc, TokenKind op_kind, ssaValue *left, ssaValue *right) {
Type *a = base_type(ssa_type(left));
Type *b = base_type(ssa_type(right));
if (are_types_identical(a, b)) {
// NOTE(bill): No need for a conversion
} else if (left->kind == ssaValue_Constant || left->kind == ssaValue_Nil) {
left = ssa_emit_conv(proc, left, ssa_type(right));
} else if (right->kind == ssaValue_Constant || right->kind == ssaValue_Nil) {
right = ssa_emit_conv(proc, right, ssa_type(left));
}
Type *result = t_bool;
if (is_type_vector(a)) {
result = make_type_vector(proc->module->allocator, t_bool, a->Vector.count);
}
return ssa_emit(proc, ssa_make_instr_binary_op(proc, op_kind, left, right, result));
}
ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, ssaValue *index) {
Type *st = base_type(type_deref(ssa_type(s)));
GB_ASSERT(is_type_array(st));
// NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32
index = ssa_emit_conv(proc, index, t_i32);
return ssa_emit(proc, ssa_make_instr_array_element_ptr(proc, s, index));
}
ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, i32 index) {
return ssa_emit_array_ep(proc, s, ssa_make_const_i32(proc->module->allocator, index));
}
ssaValue *ssa_emit_struct_ep(ssaProcedure *proc, ssaValue *s, i32 index) {
gbAllocator a = proc->module->allocator;
Type *t = base_type(type_deref(ssa_type(s)));
Type *result_type = NULL;
ssaValue *gep = NULL;
if (is_type_struct(t)) {
GB_ASSERT(t->Record.field_count > 0);
GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
result_type = make_type_pointer(a, t->Record.fields[index]->type);
} else if (is_type_tuple(t)) {
GB_ASSERT(t->Tuple.variable_count > 0);
GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
result_type = make_type_pointer(a, t->Tuple.variables[index]->type);
} else if (is_type_slice(t)) {
switch (index) {
case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break;
case 1: result_type = make_type_pointer(a, t_int); break;
case 2: result_type = make_type_pointer(a, t_int); break;
}
} else if (is_type_string(t)) {
switch (index) {
case 0: result_type = make_type_pointer(a, t_u8_ptr); break;
case 1: result_type = make_type_pointer(a, t_int); break;
}
} else if (is_type_any(t)) {
switch (index) {
case 0: result_type = make_type_pointer(a, t_type_info_ptr); break;
case 1: result_type = make_type_pointer(a, t_rawptr); break;
}
} else if (is_type_maybe(t)) {
switch (index) {
case 0: result_type = make_type_pointer(a, t->Maybe.elem); break;
case 1: result_type = make_type_pointer(a, t_bool); break;
}
} else if (is_type_union(t)) {
switch (index) {
case 1: result_type = make_type_pointer(a, t_int); break;
case 0:
default:
GB_PANIC("TODO(bill): struct_gep 0 for unions");
break;
}
} else {
GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(ssa_type(s)), index);
}
GB_ASSERT(result_type != NULL);
gep = ssa_make_instr_struct_element_ptr(proc, s, index, result_type);
return ssa_emit(proc, gep);
}
ssaValue *ssa_emit_array_ev(ssaProcedure *proc, ssaValue *s, i32 index) {
Type *st = base_type(ssa_type(s));
GB_ASSERT(is_type_array(st));
return ssa_emit(proc, ssa_make_instr_array_extract_value(proc, s, index));
}
ssaValue *ssa_emit_struct_ev(ssaProcedure *proc, ssaValue *s, i32 index) {
// NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32
gbAllocator a = proc->module->allocator;
Type *t = base_type(ssa_type(s));
Type *result_type = NULL;
if (is_type_struct(t)) {
GB_ASSERT(t->Record.field_count > 0);
GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
result_type = t->Record.fields[index]->type;
} else if (is_type_tuple(t)) {
GB_ASSERT(t->Tuple.variable_count > 0);
GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
result_type = t->Tuple.variables[index]->type;
} else if (is_type_slice(t)) {
switch (index) {
case 0: result_type = make_type_pointer(a, t->Slice.elem); break;
case 1: result_type = t_int; break;
case 2: result_type = t_int; break;
}
} else if (is_type_string(t)) {
switch (index) {
case 0: result_type = t_u8_ptr; break;
case 1: result_type = t_int; break;
}
} else if (is_type_any(t)) {
switch (index) {
case 0: result_type = t_type_info_ptr; break;
case 1: result_type = t_rawptr; break;
}
} else if (is_type_maybe(t)) {
switch (index) {
case 0: result_type = t->Maybe.elem; break;
case 1: result_type = t_bool; break;
}
} else if (is_type_union(t)) {
switch (index) {
case 1: result_type = t_int; break;
case 0:
default:
GB_PANIC("TODO(bill): struct_gep 0 for unions");
break;
}
} else {
GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(ssa_type(s)), index);
}
GB_ASSERT(result_type != NULL);
return ssa_emit(proc, ssa_make_instr_struct_extract_value(proc, s, index, result_type));
}
ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) {
GB_ASSERT(sel.index.count > 0);
for_array(i, sel.index) {
i32 index = cast(i32)sel.index[i];
if (is_type_pointer(type)) {
type = type_deref(type);
e = ssa_emit_load(proc, e);
e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies?
}
type = base_type(type);
if (is_type_raw_union(type)) {
type = type->Record.fields[index]->type;
e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type));
} else if (type->kind == Type_Record) {
type = type->Record.fields[index]->type;
e = ssa_emit_struct_ep(proc, e, index);
} else if (type->kind == Type_Basic) {
switch (type->Basic.kind) {
case Basic_any: {
if (index == 0) {
type = t_type_info_ptr;
} else if (index == 1) {
type = t_rawptr;
}
e = ssa_emit_struct_ep(proc, e, index);
} break;
case Basic_string:
e = ssa_emit_struct_ep(proc, e, index);
break;
default:
GB_PANIC("un-gep-able type");
break;
}
} else if (type->kind == Type_Slice) {
e = ssa_emit_struct_ep(proc, e, index);
} else {
GB_PANIC("un-gep-able type");
}
}
return e;
}
ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) {
GB_ASSERT(sel.index.count > 0);
for_array(i, sel.index) {
isize index = sel.index[i];
if (is_type_pointer(type)) {
type = type_deref(type);
e = ssa_emit_load(proc, e);
e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies?
}
type = base_type(type);
if (is_type_raw_union(type)) {
type = type->Record.fields[index]->type;
e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type));
} else {
e = ssa_emit_struct_ev(proc, e, index);
}
}
return e;
}
ssaValue *ssa_array_elem(ssaProcedure *proc, ssaValue *array) {
return ssa_emit_array_ep(proc, array, v_zero32);
}
ssaValue *ssa_array_len(ssaProcedure *proc, ssaValue *array) {
Type *t = ssa_type(array);
GB_ASSERT(t->kind == Type_Array);
return ssa_make_const_int(proc->module->allocator, t->Array.count);
}
ssaValue *ssa_array_cap(ssaProcedure *proc, ssaValue *array) {
return ssa_array_len(proc, array);
}
ssaValue *ssa_slice_elem(ssaProcedure *proc, ssaValue *slice) {
Type *t = ssa_type(slice);
GB_ASSERT(t->kind == Type_Slice);
return ssa_emit_struct_ev(proc, slice, 0);
}
ssaValue *ssa_slice_len(ssaProcedure *proc, ssaValue *slice) {
Type *t = ssa_type(slice);
GB_ASSERT(t->kind == Type_Slice);
return ssa_emit_struct_ev(proc, slice, 1);
}
ssaValue *ssa_slice_cap(ssaProcedure *proc, ssaValue *slice) {
Type *t = ssa_type(slice);
GB_ASSERT(t->kind == Type_Slice);
return ssa_emit_struct_ev(proc, slice, 2);
}
ssaValue *ssa_string_elem(ssaProcedure *proc, ssaValue *string) {
Type *t = ssa_type(string);
GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
return ssa_emit_struct_ev(proc, string, 0);
}
ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *string) {
Type *t = ssa_type(string);
GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
return ssa_emit_struct_ev(proc, string, 1);
}
ssaValue *ssa_add_local_slice(ssaProcedure *proc, Type *slice_type, ssaValue *base, ssaValue *low, ssaValue *high, ssaValue *max) {
// TODO(bill): array bounds checking for slice creation
// TODO(bill): check that low < high <= max
gbAllocator a = proc->module->allocator;
Type *bt = base_type(ssa_type(base));
if (low == NULL) {
low = v_zero;
}
if (high == NULL) {
switch (bt->kind) {
case Type_Array: high = ssa_array_len(proc, base); break;
case Type_Slice: high = ssa_slice_len(proc, base); break;
case Type_Pointer: high = v_one; break;
}
}
if (max == NULL) {
switch (bt->kind) {
case Type_Array: max = ssa_array_cap(proc, base); break;
case Type_Slice: max = ssa_slice_cap(proc, base); break;
case Type_Pointer: max = high; break;
}
}
GB_ASSERT(max != NULL);
ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int);
ssaValue *elem = NULL;
switch (bt->kind) {
case Type_Array: elem = ssa_array_elem(proc, base); break;
case Type_Slice: elem = ssa_slice_elem(proc, base); break;
case Type_Pointer: elem = ssa_emit_load(proc, base); break;
}
elem = ssa_emit_ptr_offset(proc, elem, low);
ssaValue *slice = ssa_add_local_generated(proc, slice_type);
ssaValue *gep = NULL;
gep = ssa_emit_struct_ep(proc, slice, 0);
ssa_emit_store(proc, gep, elem);
gep = ssa_emit_struct_ep(proc, slice, 1);
ssa_emit_store(proc, gep, len);
gep = ssa_emit_struct_ep(proc, slice, 2);
ssa_emit_store(proc, gep, cap);
return slice;
}
ssaValue *ssa_emit_string(ssaProcedure *proc, ssaValue *elem, ssaValue *len) {
ssaValue *str = ssa_add_local_generated(proc, t_string);
ssaValue *str_elem = ssa_emit_struct_ep(proc, str, 0);
ssaValue *str_len = ssa_emit_struct_ep(proc, str, 1);
ssa_emit_store(proc, str_elem, elem);
ssa_emit_store(proc, str_len, len);
return ssa_emit_load(proc, str);
}
String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
Type *prev_src = src;
// Type *prev_dst = dst;
src = base_type(type_deref(src));
// dst = base_type(type_deref(dst));
b32 src_is_ptr = src != prev_src;
// b32 dst_is_ptr = dst != prev_dst;
GB_ASSERT(is_type_struct(src));
for (isize i = 0; i < src->Record.field_count; i++) {
Entity *f = src->Record.fields[i];
if (f->kind == Entity_Variable && f->flags & EntityFlag_Anonymous) {
if (are_types_identical(dst, f->type)) {
return f->token.string;
}
if (src_is_ptr && is_type_pointer(dst)) {
if (are_types_identical(type_deref(dst), f->type)) {
return f->token.string;
}
}
String name = lookup_polymorphic_field(info, dst, f->type);
if (name.len > 0) {
return name;
}
}
}
return make_string("");
}
ssaValue *ssa_emit_bitcast(ssaProcedure *proc, ssaValue *data, Type *type) {
return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, ssa_type(data), type));
}
ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_argument) {
Type *src_type = ssa_type(value);
if (are_types_identical(t, src_type)) {
return value;
}
Type *src = get_enum_base_type(base_type(src_type));
Type *dst = get_enum_base_type(base_type(t));
if (value->kind == ssaValue_Constant) {
if (is_type_any(dst)) {
ssaValue *default_value = ssa_add_local_generated(proc, default_type(src_type));
ssa_emit_store(proc, default_value, value);
return ssa_emit_conv(proc, ssa_emit_load(proc, default_value), t_any, is_argument);
} else if (dst->kind == Type_Basic) {
ExactValue ev = value->Constant.value;
if (is_type_float(dst)) {
ev = exact_value_to_float(ev);
} else if (is_type_string(dst)) {
// Handled elsewhere
GB_ASSERT(ev.kind == ExactValue_String);
} else if (is_type_integer(dst)) {
ev = exact_value_to_integer(ev);
} else if (is_type_pointer(dst)) {
// IMPORTANT NOTE(bill): LLVM doesn't support pointer constants expect `null`
ssaValue *i = ssa_add_module_constant(proc->module, t_uint, ev);
return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, i, t_uint, dst));
}
return ssa_add_module_constant(proc->module, t, ev);
}
}
if (are_types_identical(src, dst)) {
return value;
}
if (is_type_maybe(dst)) {
ssaValue *maybe = ssa_add_local_generated(proc, dst);
ssaValue *val = ssa_emit_struct_ep(proc, maybe, 0);
ssaValue *set = ssa_emit_struct_ep(proc, maybe, 1);
ssa_emit_store(proc, val, value);
ssa_emit_store(proc, set, v_true);
return ssa_emit_load(proc, maybe);
}
// integer -> integer
if (is_type_integer(src) && is_type_integer(dst)) {
GB_ASSERT(src->kind == Type_Basic &&
dst->kind == Type_Basic);
i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
if (sz == dz) {
// NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment
return value;
}
ssaConvKind kind = ssaConv_trunc;
if (dz >= sz) {
kind = ssaConv_zext;
}
return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
}
// boolean -> integer
if (is_type_boolean(src) && is_type_integer(dst)) {
return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_zext, value, src, dst));
}
// integer -> boolean
if (is_type_integer(src) && is_type_boolean(dst)) {
return ssa_emit_comp(proc, Token_NotEq, value, v_zero);
}
// float -> float
if (is_type_float(src) && is_type_float(dst)) {
i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
ssaConvKind kind = ssaConv_fptrunc;
if (dz >= sz) {
kind = ssaConv_fpext;
}
return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
}
// float <-> integer
if (is_type_float(src) && is_type_integer(dst)) {
ssaConvKind kind = ssaConv_fptosi;
if (is_type_unsigned(dst)) {
kind = ssaConv_fptoui;
}
return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
}
if (is_type_integer(src) && is_type_float(dst)) {
ssaConvKind kind = ssaConv_sitofp;
if (is_type_unsigned(src)) {
kind = ssaConv_uitofp;
}
return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
}
// Pointer <-> int
if (is_type_pointer(src) && is_type_int_or_uint(dst)) {
return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_ptrtoint, value, src, dst));
}
if (is_type_int_or_uint(src) && is_type_pointer(dst)) {
return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, value, src, dst));
}
if (is_type_union(dst)) {
for (isize i = 0; i < dst->Record.field_count; i++) {
Entity *f = dst->Record.fields[i];
if (are_types_identical(f->type, src_type)) {
ssa_emit_comment(proc, make_string("union - child to parent"));
gbAllocator allocator = proc->module->allocator;
ssaValue *parent = ssa_add_local_generated(proc, t);
ssaValue *tag = ssa_make_const_int(allocator, i);
ssa_emit_store(proc, ssa_emit_struct_ep(proc, parent, 1), tag);
ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr);
Type *tag_type = src_type;
Type *tag_type_ptr = make_type_pointer(allocator, tag_type);
ssaValue *underlying = ssa_emit_bitcast(proc, data, tag_type_ptr);
ssa_emit_store(proc, underlying, value);
return ssa_emit_load(proc, parent);
}
}
}
// NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's
// subtype polymorphism casting
if (true || is_argument) {
Type *sb = base_type(type_deref(src));
b32 src_is_ptr = src != sb;
if (is_type_struct(sb)) {
String field_name = lookup_polymorphic_field(proc->module->info, t, src);
// gb_printf("field_name: %.*s\n", LIT(field_name));
if (field_name.len > 0) {
// NOTE(bill): It can be casted
Selection sel = lookup_field(proc->module->allocator, sb, field_name, false);
if (sel.entity != NULL) {
ssa_emit_comment(proc, make_string("cast - polymorphism"));
if (src_is_ptr) {
value = ssa_emit_load(proc, value);
}
return ssa_emit_deep_field_ev(proc, sb, value, sel);
}
}
}
}
// Pointer <-> Pointer
if (is_type_pointer(src) && is_type_pointer(dst)) {
return ssa_emit_bitcast(proc, value, dst);
}
// proc <-> proc
if (is_type_proc(src) && is_type_proc(dst)) {
return ssa_emit_bitcast(proc, value, dst);
}
// pointer -> proc
if (is_type_pointer(src) && is_type_proc(dst)) {
return ssa_emit_bitcast(proc, value, dst);
}
// proc -> pointer
if (is_type_proc(src) && is_type_pointer(dst)) {
return ssa_emit_bitcast(proc, value, dst);
}
// []byte/[]u8 <-> string
if (is_type_u8_slice(src) && is_type_string(dst)) {
ssaValue *elem = ssa_slice_elem(proc, value);
ssaValue *len = ssa_slice_len(proc, value);
return ssa_emit_string(proc, elem, len);
}
if (is_type_string(src) && is_type_u8_slice(dst)) {
ssaValue *elem = ssa_string_elem(proc, value);
ssaValue *elem_ptr = ssa_add_local_generated(proc, ssa_type(elem));
ssa_emit_store(proc, elem_ptr, elem);
ssaValue *len = ssa_string_len(proc, value);
ssaValue *slice = ssa_add_local_slice(proc, dst, elem_ptr, v_zero, len, len);
return ssa_emit_load(proc, slice);
}
if (is_type_vector(dst)) {
Type *dst_elem = dst->Vector.elem;
value = ssa_emit_conv(proc, value, dst_elem);
ssaValue *v = ssa_add_local_generated(proc, t);
v = ssa_emit_load(proc, v);
v = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, value, v_zero32));
// NOTE(bill): Broadcast lowest value to all values
isize index_count = dst->Vector.count;
i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count);
for (isize i = 0; i < index_count; i++) {
indices[i] = 0;
}
v = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, v, indices, index_count));
return v;
}
if (is_type_any(dst)) {
ssaValue *result = ssa_add_local_generated(proc, t_any);
if (is_type_untyped_nil(src)) {
return ssa_emit_load(proc, result);
}
ssaValue *data = NULL;
if (value->kind == ssaValue_Instr &&
value->Instr.kind == ssaInstr_Load) {
// NOTE(bill): Addressable value
data = value->Instr.Load.address;
} else {
// NOTE(bill): Non-addressable value
data = ssa_add_local_generated(proc, src_type);
ssa_emit_store(proc, data, value);
}
GB_ASSERT(is_type_pointer(ssa_type(data)));
GB_ASSERT(is_type_typed(src_type));
data = ssa_emit_conv(proc, data, t_rawptr);
ssaValue *ti = ssa_type_info(proc, src_type);
ssaValue *gep0 = ssa_emit_struct_ep(proc, result, 0);
ssaValue *gep1 = ssa_emit_struct_ep(proc, result, 1);
ssa_emit_store(proc, gep0, ti);
ssa_emit_store(proc, gep1, data);
return ssa_emit_load(proc, result);
}
if (is_type_untyped_nil(src) && type_has_nil(dst)) {
return ssa_make_value_nil(proc->module->allocator, t);
}
gb_printf_err("ssa_emit_conv: src -> dst\n");
gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t));
gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst));
GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t));
return NULL;
}
ssaValue *ssa_emit_transmute(ssaProcedure *proc, ssaValue *value, Type *t) {
Type *src_type = ssa_type(value);
if (are_types_identical(t, src_type)) {
return value;
}
Type *src = base_type(src_type);
Type *dst = base_type(t);
if (are_types_identical(t, src_type)) {
return value;
}
i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
if (sz == dz) {
return ssa_emit_bitcast(proc, value, dst);
}
GB_PANIC("Invalid transmute conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t));
return NULL;
}
ssaValue *ssa_emit_down_cast(ssaProcedure *proc, ssaValue *value, Type *t) {
GB_ASSERT(is_type_pointer(ssa_type(value)));
gbAllocator allocator = proc->module->allocator;
String field_name = check_down_cast_name(t, type_deref(ssa_type(value)));
GB_ASSERT(field_name.len > 0);
Selection sel = lookup_field(proc->module->allocator, t, field_name, false);
Type *t_u8_ptr = make_type_pointer(allocator, t_u8);
ssaValue *bytes = ssa_emit_conv(proc, value, t_u8_ptr);
i64 offset_ = type_offset_of_from_selection(proc->module->sizes, allocator, type_deref(t), sel);
ssaValue *offset = ssa_make_const_int(allocator, -offset_);
ssaValue *head = ssa_emit_ptr_offset(proc, bytes, offset);
return ssa_emit_conv(proc, head, t);
}
ssaValue *ssa_emit_union_cast(ssaProcedure *proc, ssaValue *value, Type *tuple) {
GB_ASSERT(tuple->kind == Type_Tuple);
gbAllocator a = proc->module->allocator;
Type *src_type = ssa_type(value);
b32 is_ptr = is_type_pointer(src_type);
ssaValue *v = ssa_add_local_generated(proc, tuple);
if (is_ptr) {
Type *src = base_type(type_deref(src_type));
Type *src_ptr = src_type;
GB_ASSERT(is_type_union(src));
Type *dst_ptr = tuple->Tuple.variables[0]->type;
Type *dst = type_deref(dst_ptr);
ssaValue *tag = ssa_emit_load(proc, ssa_emit_struct_ep(proc, value, 1));
ssaValue *dst_tag = NULL;
for (isize i = 1; i < src->Record.field_count; i++) {
Entity *f = src->Record.fields[i];
if (are_types_identical(f->type, dst)) {
dst_tag = ssa_make_const_int(a, i);
break;
}
}
GB_ASSERT(dst_tag != NULL);
ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok");
ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end");
ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag);
ssa_emit_if(proc, cond, ok_block, end_block);
proc->curr_block = ok_block;
ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0);
ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1);
ssaValue *data = ssa_emit_conv(proc, value, dst_ptr);
ssa_emit_store(proc, gep0, data);
ssa_emit_store(proc, gep1, v_true);
ssa_emit_jump(proc, end_block);
proc->curr_block = end_block;
} else {
Type *src = base_type(src_type);
GB_ASSERT(is_type_union(src));
Type *dst = tuple->Tuple.variables[0]->type;
Type *dst_ptr = make_type_pointer(a, dst);
ssaValue *tag = ssa_emit_struct_ev(proc, value, 1);
ssaValue *dst_tag = NULL;
for (isize i = 1; i < src->Record.field_count; i++) {
Entity *f = src->Record.fields[i];
if (are_types_identical(f->type, dst)) {
dst_tag = ssa_make_const_int(a, i);
break;
}
}
GB_ASSERT(dst_tag != NULL);
// HACK(bill): This is probably not very efficient
ssaValue *union_copy = ssa_add_local_generated(proc, src_type);
ssa_emit_store(proc, union_copy, value);
ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok");
ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end");
ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag);
ssa_emit_if(proc, cond, ok_block, end_block);
proc->curr_block = ok_block;
ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0);
ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1);
ssaValue *data = ssa_emit_load(proc, ssa_emit_conv(proc, union_copy, dst_ptr));
ssa_emit_store(proc, gep0, data);
ssa_emit_store(proc, gep1, v_true);
ssa_emit_jump(proc, end_block);
proc->curr_block = end_block;
}
return ssa_emit_load(proc, v);
}
isize ssa_type_info_index(CheckerInfo *info, Type *type) {
type = default_type(type);
isize entry_index = -1;
HashKey key = hash_pointer(type);
auto *found_entry_index = map_get(&info->type_info_map, key);
if (found_entry_index) {
entry_index = *found_entry_index;
}
if (entry_index < 0) {
// NOTE(bill): Do manual search
// TODO(bill): This is O(n) and can be very slow
for_array(i, info->type_info_map.entries){
auto *e = &info->type_info_map.entries[i];
Type *prev_type = cast(Type *)e->key.ptr;
if (are_types_identical(prev_type, type)) {
entry_index = e->value;
// NOTE(bill): Add it to the search map
map_set(&info->type_info_map, key, entry_index);
break;
}
}
}
if (entry_index < 0) {
compiler_error("Type_Info for `%s` could not be found", type_to_string(type));
}
return entry_index;
}
ssaValue *ssa_type_info(ssaProcedure *proc, Type *type) {
ssaValue **found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_NAME)));
GB_ASSERT(found != NULL);
ssaValue *type_info_data = *found;
CheckerInfo *info = proc->module->info;
type = default_type(type);
i32 entry_index = ssa_type_info_index(info, type);
// gb_printf_err("%d %s\n", entry_index, type_to_string(type));
return ssa_emit_array_ep(proc, type_info_data, ssa_make_const_i32(proc->module->allocator, entry_index));
}
ssaValue *ssa_emit_logical_binary_expr(ssaProcedure *proc, AstNode *expr) {
ast_node(be, BinaryExpr, expr);
#if 0
ssaBlock *true_ = ssa_add_block(proc, NULL, "logical.cmp.true");
ssaBlock *false_ = ssa_add_block(proc, NULL, "logical.cmp.false");
ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done");
ssaValue *result = ssa_add_local_generated(proc, t_bool);
ssa_build_cond(proc, expr, true_, false_);
proc->curr_block = true_;
ssa_emit_store(proc, result, v_true);
ssa_emit_jump(proc, done);
proc->curr_block = false_;
ssa_emit_store(proc, result, v_false);
ssa_emit_jump(proc, done);
proc->curr_block = done;
return ssa_emit_load(proc, result);
#else
ssaBlock *rhs = ssa_add_block(proc, NULL, "logical.cmp.rhs");
ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done");
Type *type = type_of_expr(proc->module->info, expr);
type = default_type(type);
ssaValue *short_circuit = NULL;
if (be->op.kind == Token_CmpAnd) {
ssa_build_cond(proc, be->left, rhs, done);
short_circuit = v_false;
} else if (be->op.kind == Token_CmpOr) {
ssa_build_cond(proc, be->left, done, rhs);
short_circuit = v_true;
}
if (rhs->preds.count == 0) {
proc->curr_block = done;
return short_circuit;
}
if (done->preds.count == 0) {
proc->curr_block = rhs;
return ssa_build_expr(proc, be->right);
}
Array<ssaValue *> edges = {};
array_init(&edges, proc->module->allocator, done->preds.count+1);
for_array(i, done->preds) {
array_add(&edges, short_circuit);
}
proc->curr_block = rhs;
array_add(&edges, ssa_build_expr(proc, be->right));
ssa_emit_jump(proc, done);
proc->curr_block = done;
return ssa_emit(proc, ssa_make_instr_phi(proc, edges, type));
#endif
}
void ssa_emit_bounds_check(ssaProcedure *proc, Token token, ssaValue *index, ssaValue *len) {
if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
return;
}
index = ssa_emit_conv(proc, index, t_int);
len = ssa_emit_conv(proc, len, t_int);
ssa_emit(proc, ssa_make_instr_bounds_check(proc, token.pos, index, len));
// gbAllocator a = proc->module->allocator;
// ssaValue **args = gb_alloc_array(a, ssaValue *, 5);
// args[0] = ssa_emit_global_string(proc, token.pos.file);
// args[1] = ssa_make_const_int(a, token.pos.line);
// args[2] = ssa_make_const_int(a, token.pos.column);
// args[3] = ssa_emit_conv(proc, index, t_int);
// args[4] = ssa_emit_conv(proc, len, t_int);
// ssa_emit_global_call(proc, "__bounds_check_error", args, 5);
}
void ssa_emit_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low, ssaValue *high, ssaValue *max, b32 is_substring) {
if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
return;
}
low = ssa_emit_conv(proc, low, t_int);
high = ssa_emit_conv(proc, high, t_int);
max = ssa_emit_conv(proc, max, t_int);
ssa_emit(proc, ssa_make_instr_slice_bounds_check(proc, token.pos, low, high, max, is_substring));
// gbAllocator a = proc->module->allocator;
// ssaValue **args = gb_alloc_array(a, ssaValue *, 6);
// args[0] = ssa_emit_global_string(proc, token.pos.file);
// args[1] = ssa_make_const_int(a, token.pos.line);
// args[2] = ssa_make_const_int(a, token.pos.column);
// args[3] = ssa_emit_conv(proc, low, t_int);
// args[4] = ssa_emit_conv(proc, high, t_int);
// args[5] = ssa_emit_conv(proc, max, t_int);
// if (!is_substring) {
// ssa_emit_global_call(proc, "__slice_expr_error", args, 6);
// } else {
// ssa_emit_global_call(proc, "__substring_expr_error", args, 5);
// }
}