mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-04 01:34:39 +00:00
Reorganize llvm_backend.cpp into separate files for easier maintenance
This commit is contained in:
14681
src/llvm_backend.cpp
14681
src/llvm_backend.cpp
File diff suppressed because it is too large
Load Diff
@@ -425,6 +425,23 @@ lbValue lb_emit_bit_set_card(lbProcedure *p, lbValue x);
|
||||
|
||||
void lb_mem_zero_addr(lbProcedure *p, LLVMValueRef ptr, Type *type);
|
||||
|
||||
void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e);
|
||||
lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type);
|
||||
lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block);
|
||||
|
||||
LLVMValueRef llvm_const_named_struct(LLVMTypeRef t, LLVMValueRef *values, isize value_count_);
|
||||
void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name);
|
||||
|
||||
lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t);
|
||||
bool lb_is_expr_untyped_const(Ast *expr);
|
||||
|
||||
void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment);
|
||||
|
||||
void lb_emit_init_context(lbProcedure *p, lbAddr addr);
|
||||
|
||||
lbCopyElisionHint lb_set_copy_elision_hint(lbProcedure *p, lbAddr const &addr, Ast *ast);
|
||||
void lb_reset_copy_elision_hint(lbProcedure *p, lbCopyElisionHint prev_hint);
|
||||
lbValue lb_consume_copy_elision_hint(lbProcedure *p);
|
||||
|
||||
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
|
||||
#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
|
||||
|
||||
951
src/llvm_backend_const.cpp
Normal file
951
src/llvm_backend_const.cpp
Normal file
@@ -0,0 +1,951 @@
|
||||
bool lb_is_const(lbValue value) {
|
||||
LLVMValueRef v = value.value;
|
||||
if (is_type_untyped_nil(value.type) || is_type_untyped_undef(value.type)) {
|
||||
// TODO(bill): Is this correct behaviour?
|
||||
return true;
|
||||
}
|
||||
if (LLVMIsConstant(v)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool lb_is_const_or_global(lbValue value) {
|
||||
if (lb_is_const(value)) {
|
||||
return true;
|
||||
}
|
||||
if (LLVMGetValueKind(value.value) == LLVMGlobalVariableValueKind) {
|
||||
LLVMTypeRef t = LLVMGetElementType(LLVMTypeOf(value.value));
|
||||
if (!lb_is_type_kind(t, LLVMPointerTypeKind)) {
|
||||
return false;
|
||||
}
|
||||
LLVMTypeRef elem = LLVMGetElementType(t);
|
||||
return lb_is_type_kind(elem, LLVMFunctionTypeKind);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool lb_is_elem_const(Ast *elem, Type *elem_type) {
|
||||
if (!elem_type_can_be_constant(elem_type)) {
|
||||
return false;
|
||||
}
|
||||
if (elem->kind == Ast_FieldValue) {
|
||||
elem = elem->FieldValue.value;
|
||||
}
|
||||
TypeAndValue tav = type_and_value_of_expr(elem);
|
||||
GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(elem), type_to_string(tav.type));
|
||||
return tav.value.kind != ExactValue_Invalid;
|
||||
}
|
||||
|
||||
|
||||
bool lb_is_const_nil(lbValue value) {
|
||||
LLVMValueRef v = value.value;
|
||||
if (LLVMIsConstant(v)) {
|
||||
if (LLVMIsAConstantAggregateZero(v)) {
|
||||
return true;
|
||||
} else if (LLVMIsAConstantPointerNull(v)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool lb_is_expr_constant_zero(Ast *expr) {
|
||||
GB_ASSERT(expr != nullptr);
|
||||
auto v = exact_value_to_integer(expr->tav.value);
|
||||
if (v.kind == ExactValue_Integer) {
|
||||
return big_int_cmp_zero(&v.value_integer) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String lb_get_const_string(lbModule *m, lbValue value) {
|
||||
GB_ASSERT(lb_is_const(value));
|
||||
GB_ASSERT(LLVMIsConstant(value.value));
|
||||
|
||||
Type *t = base_type(value.type);
|
||||
GB_ASSERT(are_types_identical(t, t_string));
|
||||
|
||||
|
||||
|
||||
unsigned ptr_indices[1] = {0};
|
||||
unsigned len_indices[1] = {1};
|
||||
LLVMValueRef underlying_ptr = LLVMConstExtractValue(value.value, ptr_indices, gb_count_of(ptr_indices));
|
||||
LLVMValueRef underlying_len = LLVMConstExtractValue(value.value, len_indices, gb_count_of(len_indices));
|
||||
|
||||
GB_ASSERT(LLVMGetConstOpcode(underlying_ptr) == LLVMGetElementPtr);
|
||||
underlying_ptr = LLVMGetOperand(underlying_ptr, 0);
|
||||
GB_ASSERT(LLVMIsAGlobalVariable(underlying_ptr));
|
||||
underlying_ptr = LLVMGetInitializer(underlying_ptr);
|
||||
|
||||
size_t length = 0;
|
||||
char const *text = LLVMGetAsString(underlying_ptr, &length);
|
||||
|
||||
isize real_length = cast(isize)LLVMConstIntGetSExtValue(underlying_len);
|
||||
|
||||
return make_string(cast(u8 const *)text, real_length);
|
||||
}
|
||||
|
||||
|
||||
LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
|
||||
LLVMTypeRef src = LLVMTypeOf(val);
|
||||
if (src == dst) {
|
||||
return val;
|
||||
}
|
||||
if (LLVMIsNull(val)) {
|
||||
return LLVMConstNull(dst);
|
||||
}
|
||||
|
||||
GB_ASSERT(LLVMSizeOf(dst) == LLVMSizeOf(src));
|
||||
LLVMTypeKind kind = LLVMGetTypeKind(dst);
|
||||
switch (kind) {
|
||||
case LLVMPointerTypeKind:
|
||||
return LLVMConstPointerCast(val, dst);
|
||||
case LLVMStructTypeKind:
|
||||
return LLVMConstBitCast(val, dst);
|
||||
default:
|
||||
GB_PANIC("Unhandled const cast %s to %s", LLVMPrintTypeToString(src), LLVMPrintTypeToString(dst));
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
|
||||
GB_ASSERT(is_type_pointer(value.type));
|
||||
GB_ASSERT(is_type_pointer(t));
|
||||
GB_ASSERT(lb_is_const(value));
|
||||
|
||||
lbValue res = {};
|
||||
res.value = LLVMConstPointerCast(value.value, lb_type(m, t));
|
||||
res.type = t;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
LLVMValueRef llvm_const_named_struct(LLVMTypeRef t, LLVMValueRef *values, isize value_count_) {
|
||||
unsigned value_count = cast(unsigned)value_count_;
|
||||
unsigned elem_count = LLVMCountStructElementTypes(t);
|
||||
GB_ASSERT(value_count == elem_count);
|
||||
for (unsigned i = 0; i < elem_count; i++) {
|
||||
LLVMTypeRef elem_type = LLVMStructGetTypeAtIndex(t, i);
|
||||
values[i] = llvm_const_cast(values[i], elem_type);
|
||||
}
|
||||
return LLVMConstNamedStruct(t, values, value_count);
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize value_count_) {
|
||||
unsigned value_count = cast(unsigned)value_count_;
|
||||
for (unsigned i = 0; i < value_count; i++) {
|
||||
values[i] = llvm_const_cast(values[i], elem_type);
|
||||
}
|
||||
return LLVMConstArray(elem_type, values, value_count);
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_const_slice(lbModule *m, lbValue data, lbValue len) {
|
||||
GB_ASSERT(is_type_pointer(data.type));
|
||||
GB_ASSERT(are_types_identical(len.type, t_int));
|
||||
LLVMValueRef vals[2] = {
|
||||
data.value,
|
||||
len.value,
|
||||
};
|
||||
return LLVMConstStructInContext(m->ctx, vals, gb_count_of(vals), false);
|
||||
}
|
||||
|
||||
|
||||
lbValue lb_const_nil(lbModule *m, Type *type) {
|
||||
LLVMValueRef v = LLVMConstNull(lb_type(m, type));
|
||||
return lbValue{v, type};
|
||||
}
|
||||
|
||||
lbValue lb_const_undef(lbModule *m, Type *type) {
|
||||
LLVMValueRef v = LLVMGetUndef(lb_type(m, type));
|
||||
return lbValue{v, type};
|
||||
}
|
||||
|
||||
|
||||
|
||||
lbValue lb_const_int(lbModule *m, Type *type, u64 value) {
|
||||
lbValue res = {};
|
||||
res.value = LLVMConstInt(lb_type(m, type), cast(unsigned long long)value, !is_type_unsigned(type));
|
||||
res.type = type;
|
||||
return res;
|
||||
}
|
||||
|
||||
lbValue lb_const_string(lbModule *m, String const &value) {
|
||||
return lb_const_value(m, t_string, exact_value_string(value));
|
||||
}
|
||||
|
||||
|
||||
lbValue lb_const_bool(lbModule *m, Type *type, bool value) {
|
||||
lbValue res = {};
|
||||
res.value = LLVMConstInt(lb_type(m, type), value, false);
|
||||
res.type = type;
|
||||
return res;
|
||||
}
|
||||
|
||||
LLVMValueRef lb_const_f16(lbModule *m, f32 f, Type *type=t_f16) {
|
||||
GB_ASSERT(type_size_of(type) == 2);
|
||||
|
||||
u16 u = f32_to_f16(f);
|
||||
if (is_type_different_to_arch_endianness(type)) {
|
||||
u = gb_endian_swap16(u);
|
||||
}
|
||||
LLVMValueRef i = LLVMConstInt(LLVMInt16TypeInContext(m->ctx), u, false);
|
||||
return LLVMConstBitCast(i, lb_type(m, type));
|
||||
}
|
||||
|
||||
LLVMValueRef lb_const_f32(lbModule *m, f32 f, Type *type=t_f32) {
|
||||
GB_ASSERT(type_size_of(type) == 4);
|
||||
u32 u = bit_cast<u32>(f);
|
||||
if (is_type_different_to_arch_endianness(type)) {
|
||||
u = gb_endian_swap32(u);
|
||||
}
|
||||
LLVMValueRef i = LLVMConstInt(LLVMInt32TypeInContext(m->ctx), u, false);
|
||||
return LLVMConstBitCast(i, lb_type(m, type));
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool lb_is_expr_untyped_const(Ast *expr) {
|
||||
auto const &tv = type_and_value_of_expr(expr);
|
||||
if (is_type_untyped(tv.type)) {
|
||||
return tv.value.kind != ExactValue_Invalid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t) {
|
||||
GB_ASSERT(is_type_typed(t));
|
||||
auto const &tv = type_and_value_of_expr(expr);
|
||||
return lb_const_value(m, t, tv.value);
|
||||
}
|
||||
|
||||
lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos) {
|
||||
lbModule *m = p->module;
|
||||
|
||||
LLVMValueRef fields[4] = {};
|
||||
fields[0]/*file*/ = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)).value;
|
||||
fields[1]/*line*/ = lb_const_int(m, t_i32, pos.line).value;
|
||||
fields[2]/*column*/ = lb_const_int(m, t_i32, pos.column).value;
|
||||
fields[3]/*procedure*/ = lb_find_or_add_entity_string(p->module, procedure).value;
|
||||
|
||||
lbValue res = {};
|
||||
res.value = llvm_const_named_struct(lb_type(m, t_source_code_location), fields, gb_count_of(fields));
|
||||
res.type = t_source_code_location;
|
||||
return res;
|
||||
}
|
||||
|
||||
lbValue lb_emit_source_code_location(lbProcedure *p, Ast *node) {
|
||||
String proc_name = {};
|
||||
if (p->entity) {
|
||||
proc_name = p->entity->token.string;
|
||||
}
|
||||
TokenPos pos = {};
|
||||
if (node) {
|
||||
pos = ast_token(node).pos;
|
||||
}
|
||||
return lb_emit_source_code_location(p, proc_name, pos);
|
||||
}
|
||||
|
||||
LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local) {
|
||||
bool is_local = allow_local && m->curr_procedure != nullptr;
|
||||
bool is_const = true;
|
||||
if (is_local) {
|
||||
for (isize i = 0; i < count; i++) {
|
||||
GB_ASSERT(values[i] != nullptr);
|
||||
if (!LLVMIsConstant(values[i])) {
|
||||
is_const = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_const) {
|
||||
lbProcedure *p = m->curr_procedure;
|
||||
GB_ASSERT(p != nullptr);
|
||||
lbAddr v = lb_add_local_generated(p, type, false);
|
||||
lbValue ptr = lb_addr_get_ptr(p, v);
|
||||
for (isize i = 0; i < count; i++) {
|
||||
lbValue elem = lb_emit_array_epi(p, ptr, i);
|
||||
LLVMBuildStore(p->builder, values[i], elem.value);
|
||||
}
|
||||
return lb_addr_load(p, v).value;
|
||||
}
|
||||
|
||||
return llvm_const_array(lb_type(m, elem_type), values, cast(unsigned int)count);
|
||||
}
|
||||
|
||||
LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, BigInt const *a) {
|
||||
if (big_int_is_zero(a)) {
|
||||
return LLVMConstNull(lb_type(m, original_type));
|
||||
}
|
||||
|
||||
size_t sz = cast(size_t)type_size_of(original_type);
|
||||
u64 rop64[4] = {}; // 2 u64 is the maximum we will ever need, so doubling it will be fine :P
|
||||
u8 *rop = cast(u8 *)rop64;
|
||||
|
||||
size_t max_count = 0;
|
||||
size_t written = 0;
|
||||
size_t size = 1;
|
||||
size_t nails = 0;
|
||||
mp_endian endian = MP_LITTLE_ENDIAN;
|
||||
|
||||
max_count = mp_pack_count(a, nails, size);
|
||||
GB_ASSERT_MSG(sz >= max_count, "max_count: %tu, sz: %tu, written: %tu", max_count, sz, written);
|
||||
GB_ASSERT(gb_size_of(rop64) >= sz);
|
||||
|
||||
mp_err err = mp_pack(rop, sz, &written,
|
||||
MP_LSB_FIRST,
|
||||
size, endian, nails,
|
||||
a);
|
||||
GB_ASSERT(err == MP_OKAY);
|
||||
|
||||
if (!is_type_endian_little(original_type)) {
|
||||
for (size_t i = 0; i < sz/2; i++) {
|
||||
u8 tmp = rop[i];
|
||||
rop[i] = rop[sz-1-i];
|
||||
rop[sz-1-i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef value = LLVMConstIntOfArbitraryPrecision(lb_type(m, original_type), cast(unsigned)(sz+7/8), cast(u64 *)rop);
|
||||
if (big_int_is_neg(a)) {
|
||||
value = LLVMConstNeg(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) {
|
||||
LLVMContextRef ctx = m->ctx;
|
||||
|
||||
type = default_type(type);
|
||||
Type *original_type = type;
|
||||
|
||||
lbValue res = {};
|
||||
res.type = original_type;
|
||||
type = core_type(type);
|
||||
value = convert_exact_value_for_type(value, type);
|
||||
|
||||
if (value.kind == ExactValue_Typeid) {
|
||||
return lb_typeid(m, value.value_typeid);
|
||||
}
|
||||
|
||||
if (value.kind == ExactValue_Invalid) {
|
||||
return lb_const_nil(m, type);
|
||||
}
|
||||
|
||||
if (value.kind == ExactValue_Procedure) {
|
||||
Ast *expr = unparen_expr(value.value_procedure);
|
||||
if (expr->kind == Ast_ProcLit) {
|
||||
return lb_generate_anonymous_proc_lit(m, str_lit("_proclit"), expr);
|
||||
}
|
||||
Entity *e = entity_from_expr(expr);
|
||||
return lb_find_procedure_value_from_entity(m, e);
|
||||
}
|
||||
|
||||
bool is_local = allow_local && m->curr_procedure != nullptr;
|
||||
|
||||
// GB_ASSERT_MSG(is_type_typed(type), "%s", type_to_string(type));
|
||||
|
||||
if (is_type_slice(type)) {
|
||||
if (value.kind == ExactValue_String) {
|
||||
GB_ASSERT(is_type_u8_slice(type));
|
||||
res.value = lb_find_or_add_entity_string_byte_slice(m, value.value_string).value;
|
||||
return res;
|
||||
} else {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
|
||||
isize count = cl->elems.count;
|
||||
if (count == 0) {
|
||||
return lb_const_nil(m, type);
|
||||
}
|
||||
count = gb_max(cast(isize)cl->max_count, count);
|
||||
Type *elem = base_type(type)->Slice.elem;
|
||||
Type *t = alloc_type_array(elem, count);
|
||||
lbValue backing_array = lb_const_value(m, t, value, allow_local);
|
||||
|
||||
LLVMValueRef array_data = nullptr;
|
||||
|
||||
if (is_local) {
|
||||
// NOTE(bill, 2020-06-08): This is a bit of a hack but a "constant" slice needs
|
||||
// its backing data on the stack
|
||||
lbProcedure *p = m->curr_procedure;
|
||||
LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
|
||||
|
||||
LLVMTypeRef llvm_type = lb_type(m, t);
|
||||
array_data = LLVMBuildAlloca(p->builder, llvm_type, "");
|
||||
LLVMSetAlignment(array_data, 16); // TODO(bill): Make this configurable
|
||||
LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
|
||||
LLVMBuildStore(p->builder, backing_array.value, array_data);
|
||||
|
||||
{
|
||||
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
|
||||
LLVMValueRef ptr = LLVMBuildInBoundsGEP(p->builder, array_data, indices, 2, "");
|
||||
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true);
|
||||
lbAddr slice = lb_add_local_generated(p, type, false);
|
||||
lb_fill_slice(p, slice, {ptr, alloc_type_pointer(elem)}, {len, t_int});
|
||||
return lb_addr_load(p, slice);
|
||||
}
|
||||
} else {
|
||||
isize max_len = 7+8+1;
|
||||
char *str = gb_alloc_array(permanent_allocator(), char, max_len);
|
||||
u32 id = cast(u32)gb_atomic32_fetch_add(&m->gen->global_array_index, 1);
|
||||
isize len = gb_snprintf(str, max_len, "csba$%x", id);
|
||||
|
||||
String name = make_string(cast(u8 *)str, len-1);
|
||||
|
||||
Entity *e = alloc_entity_constant(nullptr, make_token_ident(name), t, value);
|
||||
array_data = LLVMAddGlobal(m->mod, lb_type(m, t), str);
|
||||
LLVMSetInitializer(array_data, backing_array.value);
|
||||
|
||||
lbValue g = {};
|
||||
g.value = array_data;
|
||||
g.type = t;
|
||||
|
||||
lb_add_entity(m, e, g);
|
||||
lb_add_member(m, name, g);
|
||||
|
||||
{
|
||||
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
|
||||
LLVMValueRef ptr = LLVMConstInBoundsGEP(array_data, indices, 2);
|
||||
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true);
|
||||
LLVMValueRef values[2] = {ptr, len};
|
||||
|
||||
res.value = llvm_const_named_struct(lb_type(m, original_type), values, 2);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} else if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
|
||||
if (is_type_rune_array(type) && value.kind == ExactValue_String) {
|
||||
i64 count = type->Array.count;
|
||||
Type *elem = type->Array.elem;
|
||||
LLVMTypeRef et = lb_type(m, elem);
|
||||
|
||||
Rune rune;
|
||||
isize offset = 0;
|
||||
isize width = 1;
|
||||
String s = value.value_string;
|
||||
|
||||
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)count);
|
||||
|
||||
for (i64 i = 0; i < count && offset < s.len; i++) {
|
||||
width = utf8_decode(s.text+offset, s.len-offset, &rune);
|
||||
offset += width;
|
||||
|
||||
elems[i] = LLVMConstInt(et, rune, true);
|
||||
|
||||
}
|
||||
GB_ASSERT(offset == s.len);
|
||||
|
||||
res.value = llvm_const_array(et, elems, cast(unsigned)count);
|
||||
return res;
|
||||
}
|
||||
GB_PANIC("This should not have happened!\n");
|
||||
|
||||
LLVMValueRef data = LLVMConstStringInContext(ctx,
|
||||
cast(char const *)value.value_string.text,
|
||||
cast(unsigned)value.value_string.len,
|
||||
false /*DontNullTerminate*/);
|
||||
res.value = data;
|
||||
return res;
|
||||
} else if (is_type_u8_array(type) && value.kind == ExactValue_String) {
|
||||
GB_ASSERT(type->Array.count == value.value_string.len);
|
||||
LLVMValueRef data = LLVMConstStringInContext(ctx,
|
||||
cast(char const *)value.value_string.text,
|
||||
cast(unsigned)value.value_string.len,
|
||||
true /*DontNullTerminate*/);
|
||||
res.value = data;
|
||||
return res;
|
||||
} else if (is_type_array(type) &&
|
||||
value.kind != ExactValue_Invalid &&
|
||||
value.kind != ExactValue_String &&
|
||||
value.kind != ExactValue_Compound) {
|
||||
|
||||
i64 count = type->Array.count;
|
||||
Type *elem = type->Array.elem;
|
||||
|
||||
|
||||
lbValue single_elem = lb_const_value(m, elem, value, allow_local);
|
||||
|
||||
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)count);
|
||||
for (i64 i = 0; i < count; i++) {
|
||||
elems[i] = single_elem.value;
|
||||
}
|
||||
|
||||
res.value = llvm_const_array(lb_type(m, elem), elems, cast(unsigned)count);
|
||||
return res;
|
||||
}
|
||||
|
||||
switch (value.kind) {
|
||||
case ExactValue_Invalid:
|
||||
res.value = LLVMConstNull(lb_type(m, original_type));
|
||||
return res;
|
||||
case ExactValue_Bool:
|
||||
res.value = LLVMConstInt(lb_type(m, original_type), value.value_bool, false);
|
||||
return res;
|
||||
case ExactValue_String:
|
||||
{
|
||||
LLVMValueRef ptr = lb_find_or_add_entity_string_ptr(m, value.value_string);
|
||||
lbValue res = {};
|
||||
res.type = default_type(original_type);
|
||||
if (is_type_cstring(res.type)) {
|
||||
res.value = ptr;
|
||||
} else {
|
||||
if (value.value_string.len == 0) {
|
||||
ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
|
||||
}
|
||||
LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string.len, true);
|
||||
LLVMValueRef values[2] = {ptr, str_len};
|
||||
GB_ASSERT(is_type_string(original_type));
|
||||
|
||||
res.value = llvm_const_named_struct(lb_type(m, original_type), values, 2);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
case ExactValue_Integer:
|
||||
if (is_type_pointer(type)) {
|
||||
LLVMTypeRef t = lb_type(m, original_type);
|
||||
LLVMValueRef i = lb_big_int_to_llvm(m, t_uintptr, &value.value_integer);
|
||||
res.value = LLVMConstIntToPtr(i, t);
|
||||
} else {
|
||||
res.value = lb_big_int_to_llvm(m, original_type, &value.value_integer);
|
||||
}
|
||||
return res;
|
||||
case ExactValue_Float:
|
||||
if (is_type_different_to_arch_endianness(type)) {
|
||||
u64 u = bit_cast<u64>(value.value_float);
|
||||
u = gb_endian_swap64(u);
|
||||
res.value = LLVMConstReal(lb_type(m, original_type), bit_cast<f64>(u));
|
||||
} else {
|
||||
res.value = LLVMConstReal(lb_type(m, original_type), value.value_float);
|
||||
}
|
||||
return res;
|
||||
case ExactValue_Complex:
|
||||
{
|
||||
LLVMValueRef values[2] = {};
|
||||
switch (8*type_size_of(type)) {
|
||||
case 32:
|
||||
values[0] = lb_const_f16(m, cast(f32)value.value_complex->real);
|
||||
values[1] = lb_const_f16(m, cast(f32)value.value_complex->imag);
|
||||
break;
|
||||
case 64:
|
||||
values[0] = lb_const_f32(m, cast(f32)value.value_complex->real);
|
||||
values[1] = lb_const_f32(m, cast(f32)value.value_complex->imag);
|
||||
break;
|
||||
case 128:
|
||||
values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_complex->real);
|
||||
values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_complex->imag);
|
||||
break;
|
||||
}
|
||||
|
||||
res.value = llvm_const_named_struct(lb_type(m, original_type), values, 2);
|
||||
return res;
|
||||
}
|
||||
break;
|
||||
case ExactValue_Quaternion:
|
||||
{
|
||||
LLVMValueRef values[4] = {};
|
||||
switch (8*type_size_of(type)) {
|
||||
case 64:
|
||||
// @QuaternionLayout
|
||||
values[3] = lb_const_f16(m, cast(f32)value.value_quaternion->real);
|
||||
values[0] = lb_const_f16(m, cast(f32)value.value_quaternion->imag);
|
||||
values[1] = lb_const_f16(m, cast(f32)value.value_quaternion->jmag);
|
||||
values[2] = lb_const_f16(m, cast(f32)value.value_quaternion->kmag);
|
||||
break;
|
||||
case 128:
|
||||
// @QuaternionLayout
|
||||
values[3] = lb_const_f32(m, cast(f32)value.value_quaternion->real);
|
||||
values[0] = lb_const_f32(m, cast(f32)value.value_quaternion->imag);
|
||||
values[1] = lb_const_f32(m, cast(f32)value.value_quaternion->jmag);
|
||||
values[2] = lb_const_f32(m, cast(f32)value.value_quaternion->kmag);
|
||||
break;
|
||||
case 256:
|
||||
// @QuaternionLayout
|
||||
values[3] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->real);
|
||||
values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->imag);
|
||||
values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->jmag);
|
||||
values[2] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion->kmag);
|
||||
break;
|
||||
}
|
||||
|
||||
res.value = llvm_const_named_struct(lb_type(m, original_type), values, 4);
|
||||
return res;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExactValue_Pointer:
|
||||
res.value = LLVMConstIntToPtr(LLVMConstInt(lb_type(m, t_uintptr), value.value_pointer, false), lb_type(m, original_type));
|
||||
return res;
|
||||
|
||||
case ExactValue_Compound:
|
||||
if (is_type_slice(type)) {
|
||||
return lb_const_value(m, type, value, allow_local);
|
||||
} else if (is_type_array(type)) {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
Type *elem_type = type->Array.elem;
|
||||
isize elem_count = cl->elems.count;
|
||||
if (elem_count == 0 || !elem_type_can_be_constant(elem_type)) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
if (cl->elems[0]->kind == Ast_FieldValue) {
|
||||
// TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand
|
||||
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count);
|
||||
|
||||
isize value_index = 0;
|
||||
for (i64 i = 0; i < type->Array.count; i++) {
|
||||
bool found = false;
|
||||
|
||||
for (isize j = 0; j < elem_count; j++) {
|
||||
Ast *elem = cl->elems[j];
|
||||
ast_node(fv, FieldValue, elem);
|
||||
if (is_ast_range(fv->field)) {
|
||||
ast_node(ie, BinaryExpr, fv->field);
|
||||
TypeAndValue lo_tav = ie->left->tav;
|
||||
TypeAndValue hi_tav = ie->right->tav;
|
||||
GB_ASSERT(lo_tav.mode == Addressing_Constant);
|
||||
GB_ASSERT(hi_tav.mode == Addressing_Constant);
|
||||
|
||||
TokenKind op = ie->op.kind;
|
||||
i64 lo = exact_value_to_i64(lo_tav.value);
|
||||
i64 hi = exact_value_to_i64(hi_tav.value);
|
||||
if (op != Token_RangeHalf) {
|
||||
hi += 1;
|
||||
}
|
||||
if (lo == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
for (i64 k = lo; k < hi; k++) {
|
||||
values[value_index++] = val;
|
||||
}
|
||||
|
||||
found = true;
|
||||
i += (hi-lo-1);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
TypeAndValue index_tav = fv->field->tav;
|
||||
GB_ASSERT(index_tav.mode == Addressing_Constant);
|
||||
i64 index = exact_value_to_i64(index_tav.value);
|
||||
if (index == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
values[value_index++] = val;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
values[value_index++] = LLVMConstNull(lb_type(m, elem_type));
|
||||
}
|
||||
}
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local);
|
||||
return res;
|
||||
} else {
|
||||
GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count);
|
||||
|
||||
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count);
|
||||
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
}
|
||||
for (isize i = elem_count; i < type->Array.count; i++) {
|
||||
values[i] = LLVMConstNull(lb_type(m, elem_type));
|
||||
}
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local);
|
||||
return res;
|
||||
}
|
||||
} else if (is_type_enumerated_array(type)) {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
Type *elem_type = type->EnumeratedArray.elem;
|
||||
isize elem_count = cl->elems.count;
|
||||
if (elem_count == 0 || !elem_type_can_be_constant(elem_type)) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
if (cl->elems[0]->kind == Ast_FieldValue) {
|
||||
// TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand
|
||||
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->EnumeratedArray.count);
|
||||
|
||||
isize value_index = 0;
|
||||
|
||||
i64 total_lo = exact_value_to_i64(type->EnumeratedArray.min_value);
|
||||
i64 total_hi = exact_value_to_i64(type->EnumeratedArray.max_value);
|
||||
|
||||
for (i64 i = total_lo; i <= total_hi; i++) {
|
||||
bool found = false;
|
||||
|
||||
for (isize j = 0; j < elem_count; j++) {
|
||||
Ast *elem = cl->elems[j];
|
||||
ast_node(fv, FieldValue, elem);
|
||||
if (is_ast_range(fv->field)) {
|
||||
ast_node(ie, BinaryExpr, fv->field);
|
||||
TypeAndValue lo_tav = ie->left->tav;
|
||||
TypeAndValue hi_tav = ie->right->tav;
|
||||
GB_ASSERT(lo_tav.mode == Addressing_Constant);
|
||||
GB_ASSERT(hi_tav.mode == Addressing_Constant);
|
||||
|
||||
TokenKind op = ie->op.kind;
|
||||
i64 lo = exact_value_to_i64(lo_tav.value);
|
||||
i64 hi = exact_value_to_i64(hi_tav.value);
|
||||
if (op != Token_RangeHalf) {
|
||||
hi += 1;
|
||||
}
|
||||
if (lo == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
for (i64 k = lo; k < hi; k++) {
|
||||
values[value_index++] = val;
|
||||
}
|
||||
|
||||
found = true;
|
||||
i += (hi-lo-1);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
TypeAndValue index_tav = fv->field->tav;
|
||||
GB_ASSERT(index_tav.mode == Addressing_Constant);
|
||||
i64 index = exact_value_to_i64(index_tav.value);
|
||||
if (index == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
values[value_index++] = val;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
values[value_index++] = LLVMConstNull(lb_type(m, elem_type));
|
||||
}
|
||||
}
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local);
|
||||
return res;
|
||||
} else {
|
||||
GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count);
|
||||
|
||||
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->EnumeratedArray.count);
|
||||
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
}
|
||||
for (isize i = elem_count; i < type->EnumeratedArray.count; i++) {
|
||||
values[i] = LLVMConstNull(lb_type(m, elem_type));
|
||||
}
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local);
|
||||
return res;
|
||||
}
|
||||
} else if (is_type_simd_vector(type)) {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
|
||||
Type *elem_type = type->SimdVector.elem;
|
||||
isize elem_count = cl->elems.count;
|
||||
if (elem_count == 0) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
GB_ASSERT(elem_type_can_be_constant(elem_type));
|
||||
|
||||
isize total_elem_count = cast(isize)type->SimdVector.count;
|
||||
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, total_elem_count);
|
||||
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
}
|
||||
LLVMTypeRef et = lb_type(m, elem_type);
|
||||
|
||||
for (isize i = elem_count; i < type->SimdVector.count; i++) {
|
||||
values[i] = LLVMConstNull(et);
|
||||
}
|
||||
for (isize i = 0; i < total_elem_count; i++) {
|
||||
values[i] = llvm_const_cast(values[i], et);
|
||||
}
|
||||
|
||||
res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
|
||||
return res;
|
||||
} else if (is_type_struct(type)) {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
|
||||
if (cl->elems.count == 0) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
|
||||
if (is_type_raw_union(type)) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
|
||||
isize offset = 0;
|
||||
if (type->Struct.custom_align > 0) {
|
||||
offset = 1;
|
||||
}
|
||||
|
||||
isize value_count = type->Struct.fields.count + offset;
|
||||
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, value_count);
|
||||
bool *visited = gb_alloc_array(temporary_allocator(), bool, value_count);
|
||||
|
||||
if (cl->elems.count > 0) {
|
||||
if (cl->elems[0]->kind == Ast_FieldValue) {
|
||||
isize elem_count = cl->elems.count;
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
ast_node(fv, FieldValue, cl->elems[i]);
|
||||
String name = fv->field->Ident.token.string;
|
||||
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
|
||||
Selection sel = lookup_field(type, name, false);
|
||||
Entity *f = type->Struct.fields[sel.index[0]];
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
values[offset+f->Variable.field_index] = lb_const_value(m, f->type, tav.value, allow_local).value;
|
||||
visited[offset+f->Variable.field_index] = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for_array(i, cl->elems) {
|
||||
Entity *f = type->Struct.fields[i];
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
ExactValue val = {};
|
||||
if (tav.mode != Addressing_Invalid) {
|
||||
val = tav.value;
|
||||
}
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
values[offset+f->Variable.field_index] = lb_const_value(m, f->type, val, allow_local).value;
|
||||
visited[offset+f->Variable.field_index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (isize i = 0; i < type->Struct.fields.count; i++) {
|
||||
if (!visited[offset+i]) {
|
||||
GB_ASSERT(values[offset+i] == nullptr);
|
||||
values[offset+i] = lb_const_nil(m, get_struct_field_type(type, i)).value;
|
||||
}
|
||||
}
|
||||
|
||||
if (type->Struct.custom_align > 0) {
|
||||
values[0] = LLVMConstNull(lb_alignment_prefix_type_hack(m, type->Struct.custom_align));
|
||||
}
|
||||
|
||||
bool is_constant = true;
|
||||
|
||||
for (isize i = 0; i < value_count; i++) {
|
||||
LLVMValueRef val = values[i];
|
||||
if (!LLVMIsConstant(val)) {
|
||||
GB_ASSERT(is_local);
|
||||
GB_ASSERT(LLVMGetInstructionOpcode(val) == LLVMLoad);
|
||||
is_constant = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_constant) {
|
||||
res.value = llvm_const_named_struct(lb_type(m, original_type), values, cast(unsigned)value_count);
|
||||
return res;
|
||||
} else {
|
||||
// TODO(bill): THIS IS HACK BUT IT WORKS FOR WHAT I NEED
|
||||
LLVMValueRef *old_values = values;
|
||||
LLVMValueRef *new_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, value_count);
|
||||
for (isize i = 0; i < value_count; i++) {
|
||||
LLVMValueRef old_value = old_values[i];
|
||||
if (LLVMIsConstant(old_value)) {
|
||||
new_values[i] = old_value;
|
||||
} else {
|
||||
new_values[i] = LLVMConstNull(LLVMTypeOf(old_value));
|
||||
}
|
||||
}
|
||||
LLVMValueRef constant_value = llvm_const_named_struct(lb_type(m, original_type), new_values, cast(unsigned)value_count);
|
||||
|
||||
|
||||
GB_ASSERT(is_local);
|
||||
lbProcedure *p = m->curr_procedure;
|
||||
lbAddr v = lb_add_local_generated(p, res.type, true);
|
||||
LLVMBuildStore(p->builder, constant_value, v.addr.value);
|
||||
for (isize i = 0; i < value_count; i++) {
|
||||
LLVMValueRef val = old_values[i];
|
||||
if (!LLVMIsConstant(val)) {
|
||||
LLVMValueRef dst = LLVMBuildStructGEP(p->builder, v.addr.value, cast(unsigned)i, "");
|
||||
LLVMBuildStore(p->builder, val, dst);
|
||||
}
|
||||
}
|
||||
return lb_addr_load(p, v);
|
||||
|
||||
}
|
||||
} else if (is_type_bit_set(type)) {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
if (cl->elems.count == 0) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
|
||||
i64 sz = type_size_of(type);
|
||||
if (sz == 0) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
|
||||
u64 bits = 0;
|
||||
for_array(i, cl->elems) {
|
||||
Ast *e = cl->elems[i];
|
||||
GB_ASSERT(e->kind != Ast_FieldValue);
|
||||
|
||||
TypeAndValue tav = e->tav;
|
||||
if (tav.mode != Addressing_Constant) {
|
||||
continue;
|
||||
}
|
||||
GB_ASSERT(tav.value.kind == ExactValue_Integer);
|
||||
i64 v = big_int_to_i64(&tav.value.value_integer);
|
||||
i64 lower = type->BitSet.lower;
|
||||
bits |= 1ull<<cast(u64)(v-lower);
|
||||
}
|
||||
if (is_type_different_to_arch_endianness(type)) {
|
||||
i64 size = type_size_of(type);
|
||||
switch (size) {
|
||||
case 2: bits = cast(u64)gb_endian_swap16(cast(u16)bits); break;
|
||||
case 4: bits = cast(u64)gb_endian_swap32(cast(u32)bits); break;
|
||||
case 8: bits = cast(u64)gb_endian_swap64(cast(u64)bits); break;
|
||||
}
|
||||
}
|
||||
|
||||
res.value = LLVMConstInt(lb_type(m, original_type), bits, false);
|
||||
return res;
|
||||
} else {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
break;
|
||||
case ExactValue_Procedure:
|
||||
{
|
||||
Ast *expr = value.value_procedure;
|
||||
GB_ASSERT(expr != nullptr);
|
||||
if (expr->kind == Ast_ProcLit) {
|
||||
return lb_generate_anonymous_proc_lit(m, str_lit("_proclit"), expr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExactValue_Typeid:
|
||||
return lb_typeid(m, value.value_typeid);
|
||||
}
|
||||
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
|
||||
983
src/llvm_backend_debug.cpp
Normal file
983
src/llvm_backend_debug.cpp
Normal file
@@ -0,0 +1,983 @@
|
||||
LLVMMetadataRef lb_get_llvm_metadata(lbModule *m, void *key) {
|
||||
if (key == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
auto found = map_get(&m->debug_values, hash_pointer(key));
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void lb_set_llvm_metadata(lbModule *m, void *key, LLVMMetadataRef value) {
|
||||
if (key != nullptr) {
|
||||
map_set(&m->debug_values, hash_pointer(key), value);
|
||||
}
|
||||
}
|
||||
|
||||
LLVMMetadataRef lb_get_llvm_file_metadata_from_node(lbModule *m, Ast *node) {
|
||||
if (node == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return lb_get_llvm_metadata(m, node->file);
|
||||
}
|
||||
|
||||
LLVMMetadataRef lb_get_current_debug_scope(lbProcedure *p) {
|
||||
GB_ASSERT_MSG(p->debug_info != nullptr, "missing debug information for %.*s", LIT(p->name));
|
||||
|
||||
for (isize i = p->scope_stack.count-1; i >= 0; i--) {
|
||||
Scope *s = p->scope_stack[i];
|
||||
LLVMMetadataRef md = lb_get_llvm_metadata(p->module, s);
|
||||
if (md) {
|
||||
return md;
|
||||
}
|
||||
}
|
||||
return p->debug_info;
|
||||
}
|
||||
|
||||
LLVMMetadataRef lb_debug_location_from_token_pos(lbProcedure *p, TokenPos pos) {
|
||||
LLVMMetadataRef scope = lb_get_current_debug_scope(p);
|
||||
GB_ASSERT_MSG(scope != nullptr, "%.*s", LIT(p->name));
|
||||
return LLVMDIBuilderCreateDebugLocation(p->module->ctx, cast(unsigned)pos.line, cast(unsigned)pos.column, scope, nullptr);
|
||||
}
|
||||
LLVMMetadataRef lb_debug_location_from_ast(lbProcedure *p, Ast *node) {
|
||||
GB_ASSERT(node != nullptr);
|
||||
return lb_debug_location_from_token_pos(p, ast_token(node).pos);
|
||||
}
|
||||
|
||||
LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type) {
|
||||
Type *original_type = type;
|
||||
|
||||
LLVMContextRef ctx = m->ctx;
|
||||
i64 size = type_size_of(type); // Check size
|
||||
|
||||
GB_ASSERT(type != t_invalid);
|
||||
|
||||
unsigned const word_size = cast(unsigned)build_context.word_size;
|
||||
unsigned const word_bits = cast(unsigned)(8*build_context.word_size);
|
||||
|
||||
GB_ASSERT(type->kind == Type_Proc);
|
||||
LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx);
|
||||
unsigned parameter_count = 1;
|
||||
for (i32 i = 0; i < type->Proc.param_count; i++) {
|
||||
Entity *e = type->Proc.params->Tuple.variables[i];
|
||||
if (e->kind == Entity_Variable) {
|
||||
parameter_count += 1;
|
||||
}
|
||||
}
|
||||
LLVMMetadataRef *parameters = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, parameter_count);
|
||||
|
||||
unsigned param_index = 0;
|
||||
if (type->Proc.result_count == 0) {
|
||||
parameters[param_index++] = nullptr;
|
||||
} else {
|
||||
parameters[param_index++] = lb_debug_type(m, type->Proc.results);
|
||||
}
|
||||
|
||||
LLVMMetadataRef parent_scope = nullptr;
|
||||
LLVMMetadataRef scope = nullptr;
|
||||
LLVMMetadataRef file = nullptr;
|
||||
|
||||
for (i32 i = 0; i < type->Proc.param_count; i++) {
|
||||
Entity *e = type->Proc.params->Tuple.variables[i];
|
||||
if (e->kind != Entity_Variable) {
|
||||
continue;
|
||||
}
|
||||
parameters[param_index] = lb_debug_type(m, e->type);
|
||||
param_index += 1;
|
||||
}
|
||||
|
||||
LLVMDIFlags flags = LLVMDIFlagZero;
|
||||
if (type->Proc.diverging) {
|
||||
flags = LLVMDIFlagNoReturn;
|
||||
}
|
||||
|
||||
return LLVMDIBuilderCreateSubroutineType(m->debug_builder, file, parameters, parameter_count, flags);
|
||||
}
|
||||
|
||||
LLVMMetadataRef lb_debug_struct_field(lbModule *m, String const &name, Type *type, u64 offset_in_bits) {
|
||||
unsigned field_line = 1;
|
||||
LLVMDIFlags field_flags = LLVMDIFlagZero;
|
||||
|
||||
AstPackage *pkg = m->info->runtime_package;
|
||||
GB_ASSERT(pkg->files.count != 0);
|
||||
LLVMMetadataRef file = lb_get_llvm_metadata(m, pkg->files[0]);
|
||||
LLVMMetadataRef scope = file;
|
||||
|
||||
return LLVMDIBuilderCreateMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line,
|
||||
8*cast(u64)type_size_of(type), 8*cast(u32)type_align_of(type), offset_in_bits,
|
||||
field_flags, lb_debug_type(m, type)
|
||||
);
|
||||
}
|
||||
LLVMMetadataRef lb_debug_basic_struct(lbModule *m, String const &name, u64 size_in_bits, u32 align_in_bits, LLVMMetadataRef *elements, unsigned element_count) {
|
||||
AstPackage *pkg = m->info->runtime_package;
|
||||
GB_ASSERT(pkg->files.count != 0);
|
||||
LLVMMetadataRef file = lb_get_llvm_metadata(m, pkg->files[0]);
|
||||
LLVMMetadataRef scope = file;
|
||||
|
||||
return LLVMDIBuilderCreateStructType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, 1, size_in_bits, align_in_bits, LLVMDIFlagZero, nullptr, elements, element_count, 0, nullptr, "", 0);
|
||||
}
|
||||
|
||||
|
||||
LLVMMetadataRef lb_debug_type_basic_type(lbModule *m, String const &name, u64 size_in_bits, LLVMDWARFTypeEncoding encoding, LLVMDIFlags flags = LLVMDIFlagZero) {
|
||||
LLVMMetadataRef basic_type = LLVMDIBuilderCreateBasicType(m->debug_builder, cast(char const *)name.text, name.len, size_in_bits, encoding, flags);
|
||||
#if 1
|
||||
LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(m->debug_builder, basic_type, cast(char const *)name.text, name.len, nullptr, 0, nullptr, cast(u32)size_in_bits);
|
||||
return final_decl;
|
||||
#else
|
||||
return basic_type;
|
||||
#endif
|
||||
}
|
||||
|
||||
LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
|
||||
Type *original_type = type;
|
||||
|
||||
LLVMContextRef ctx = m->ctx;
|
||||
i64 size = type_size_of(type); // Check size
|
||||
|
||||
GB_ASSERT(type != t_invalid);
|
||||
|
||||
unsigned const word_size = cast(unsigned)build_context.word_size;
|
||||
unsigned const word_bits = cast(unsigned)(8*build_context.word_size);
|
||||
|
||||
switch (type->kind) {
|
||||
case Type_Basic:
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_llvm_bool: return lb_debug_type_basic_type(m, str_lit("llvm bool"), 1, LLVMDWARFTypeEncoding_Boolean);
|
||||
case Basic_bool: return lb_debug_type_basic_type(m, str_lit("bool"), 8, LLVMDWARFTypeEncoding_Boolean);
|
||||
case Basic_b8: return lb_debug_type_basic_type(m, str_lit("b8"), 8, LLVMDWARFTypeEncoding_Boolean);
|
||||
case Basic_b16: return lb_debug_type_basic_type(m, str_lit("b16"), 16, LLVMDWARFTypeEncoding_Boolean);
|
||||
case Basic_b32: return lb_debug_type_basic_type(m, str_lit("b32"), 32, LLVMDWARFTypeEncoding_Boolean);
|
||||
case Basic_b64: return lb_debug_type_basic_type(m, str_lit("b64"), 64, LLVMDWARFTypeEncoding_Boolean);
|
||||
|
||||
case Basic_i8: return lb_debug_type_basic_type(m, str_lit("i8"), 8, LLVMDWARFTypeEncoding_Signed);
|
||||
case Basic_u8: return lb_debug_type_basic_type(m, str_lit("u8"), 8, LLVMDWARFTypeEncoding_Unsigned);
|
||||
case Basic_i16: return lb_debug_type_basic_type(m, str_lit("i16"), 16, LLVMDWARFTypeEncoding_Signed);
|
||||
case Basic_u16: return lb_debug_type_basic_type(m, str_lit("u16"), 16, LLVMDWARFTypeEncoding_Unsigned);
|
||||
case Basic_i32: return lb_debug_type_basic_type(m, str_lit("i32"), 32, LLVMDWARFTypeEncoding_Signed);
|
||||
case Basic_u32: return lb_debug_type_basic_type(m, str_lit("u32"), 32, LLVMDWARFTypeEncoding_Unsigned);
|
||||
case Basic_i64: return lb_debug_type_basic_type(m, str_lit("i64"), 64, LLVMDWARFTypeEncoding_Signed);
|
||||
case Basic_u64: return lb_debug_type_basic_type(m, str_lit("u64"), 64, LLVMDWARFTypeEncoding_Unsigned);
|
||||
case Basic_i128: return lb_debug_type_basic_type(m, str_lit("i128"), 128, LLVMDWARFTypeEncoding_Signed);
|
||||
case Basic_u128: return lb_debug_type_basic_type(m, str_lit("u128"), 128, LLVMDWARFTypeEncoding_Unsigned);
|
||||
|
||||
case Basic_rune: return lb_debug_type_basic_type(m, str_lit("rune"), 32, LLVMDWARFTypeEncoding_Utf);
|
||||
|
||||
|
||||
case Basic_f16: return lb_debug_type_basic_type(m, str_lit("f16"), 16, LLVMDWARFTypeEncoding_Float);
|
||||
case Basic_f32: return lb_debug_type_basic_type(m, str_lit("f32"), 32, LLVMDWARFTypeEncoding_Float);
|
||||
case Basic_f64: return lb_debug_type_basic_type(m, str_lit("f64"), 64, LLVMDWARFTypeEncoding_Float);
|
||||
|
||||
case Basic_int: return lb_debug_type_basic_type(m, str_lit("int"), word_bits, LLVMDWARFTypeEncoding_Signed);
|
||||
case Basic_uint: return lb_debug_type_basic_type(m, str_lit("uint"), word_bits, LLVMDWARFTypeEncoding_Unsigned);
|
||||
case Basic_uintptr: return lb_debug_type_basic_type(m, str_lit("uintptr"), word_bits, LLVMDWARFTypeEncoding_Unsigned);
|
||||
|
||||
case Basic_typeid:
|
||||
return lb_debug_type_basic_type(m, str_lit("typeid"), word_bits, LLVMDWARFTypeEncoding_Unsigned);
|
||||
|
||||
// Endian Specific Types
|
||||
case Basic_i16le: return lb_debug_type_basic_type(m, str_lit("i16le"), 16, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagLittleEndian);
|
||||
case Basic_u16le: return lb_debug_type_basic_type(m, str_lit("u16le"), 16, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagLittleEndian);
|
||||
case Basic_i32le: return lb_debug_type_basic_type(m, str_lit("i32le"), 32, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagLittleEndian);
|
||||
case Basic_u32le: return lb_debug_type_basic_type(m, str_lit("u32le"), 32, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagLittleEndian);
|
||||
case Basic_i64le: return lb_debug_type_basic_type(m, str_lit("i64le"), 64, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagLittleEndian);
|
||||
case Basic_u64le: return lb_debug_type_basic_type(m, str_lit("u64le"), 64, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagLittleEndian);
|
||||
case Basic_i128le: return lb_debug_type_basic_type(m, str_lit("i128le"), 128, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagLittleEndian);
|
||||
case Basic_u128le: return lb_debug_type_basic_type(m, str_lit("u128le"), 128, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagLittleEndian);
|
||||
|
||||
case Basic_f16le: return lb_debug_type_basic_type(m, str_lit("f16le"), 16, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
|
||||
case Basic_f32le: return lb_debug_type_basic_type(m, str_lit("f32le"), 32, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
|
||||
case Basic_f64le: return lb_debug_type_basic_type(m, str_lit("f64le"), 64, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
|
||||
|
||||
case Basic_i16be: return lb_debug_type_basic_type(m, str_lit("i16be"), 16, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagBigEndian);
|
||||
case Basic_u16be: return lb_debug_type_basic_type(m, str_lit("u16be"), 16, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagBigEndian);
|
||||
case Basic_i32be: return lb_debug_type_basic_type(m, str_lit("i32be"), 32, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagBigEndian);
|
||||
case Basic_u32be: return lb_debug_type_basic_type(m, str_lit("u32be"), 32, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagBigEndian);
|
||||
case Basic_i64be: return lb_debug_type_basic_type(m, str_lit("i64be"), 64, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagBigEndian);
|
||||
case Basic_u64be: return lb_debug_type_basic_type(m, str_lit("u64be"), 64, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagBigEndian);
|
||||
case Basic_i128be: return lb_debug_type_basic_type(m, str_lit("i128be"), 128, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagBigEndian);
|
||||
case Basic_u128be: return lb_debug_type_basic_type(m, str_lit("u128be"), 128, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagBigEndian);
|
||||
|
||||
case Basic_f16be: return lb_debug_type_basic_type(m, str_lit("f16be"), 16, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
|
||||
case Basic_f32be: return lb_debug_type_basic_type(m, str_lit("f32be"), 32, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
|
||||
case Basic_f64be: return lb_debug_type_basic_type(m, str_lit("f64be"), 64, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
|
||||
|
||||
case Basic_complex32:
|
||||
{
|
||||
LLVMMetadataRef elements[2] = {};
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f16, 0);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 4);
|
||||
return lb_debug_basic_struct(m, str_lit("complex32"), 64, 32, elements, gb_count_of(elements));
|
||||
}
|
||||
case Basic_complex64:
|
||||
{
|
||||
LLVMMetadataRef elements[2] = {};
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f32, 0);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 4);
|
||||
return lb_debug_basic_struct(m, str_lit("complex64"), 64, 32, elements, gb_count_of(elements));
|
||||
}
|
||||
case Basic_complex128:
|
||||
{
|
||||
LLVMMetadataRef elements[2] = {};
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f64, 0);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 8);
|
||||
return lb_debug_basic_struct(m, str_lit("complex128"), 128, 64, elements, gb_count_of(elements));
|
||||
}
|
||||
|
||||
case Basic_quaternion64:
|
||||
{
|
||||
LLVMMetadataRef elements[4] = {};
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 0);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f16, 4);
|
||||
elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f16, 8);
|
||||
elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f16, 12);
|
||||
return lb_debug_basic_struct(m, str_lit("quaternion64"), 128, 32, elements, gb_count_of(elements));
|
||||
}
|
||||
case Basic_quaternion128:
|
||||
{
|
||||
LLVMMetadataRef elements[4] = {};
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 0);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f32, 4);
|
||||
elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f32, 8);
|
||||
elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f32, 12);
|
||||
return lb_debug_basic_struct(m, str_lit("quaternion128"), 128, 32, elements, gb_count_of(elements));
|
||||
}
|
||||
case Basic_quaternion256:
|
||||
{
|
||||
LLVMMetadataRef elements[4] = {};
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 0);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f64, 8);
|
||||
elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f64, 16);
|
||||
elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f64, 24);
|
||||
return lb_debug_basic_struct(m, str_lit("quaternion256"), 256, 32, elements, gb_count_of(elements));
|
||||
}
|
||||
|
||||
|
||||
|
||||
case Basic_rawptr:
|
||||
{
|
||||
LLVMMetadataRef void_type = lb_debug_type_basic_type(m, str_lit("void"), 8, LLVMDWARFTypeEncoding_Unsigned);
|
||||
return LLVMDIBuilderCreatePointerType(m->debug_builder, void_type, word_bits, word_bits, LLVMDWARFTypeEncoding_Address, "rawptr", 6);
|
||||
}
|
||||
case Basic_string:
|
||||
{
|
||||
LLVMMetadataRef elements[2] = {};
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data"), t_u8_ptr, 0);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, word_bits);
|
||||
return lb_debug_basic_struct(m, str_lit("string"), 2*word_bits, word_bits, elements, gb_count_of(elements));
|
||||
}
|
||||
case Basic_cstring:
|
||||
{
|
||||
LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("char"), 8, LLVMDWARFTypeEncoding_Unsigned);
|
||||
return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, word_bits, word_bits, 0, "cstring", 7);
|
||||
}
|
||||
case Basic_any:
|
||||
{
|
||||
LLVMMetadataRef elements[2] = {};
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data"), t_rawptr, 0);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("id"), t_typeid, word_bits);
|
||||
return lb_debug_basic_struct(m, str_lit("any"), 2*word_bits, word_bits, elements, gb_count_of(elements));
|
||||
}
|
||||
|
||||
// Untyped types
|
||||
case Basic_UntypedBool: GB_PANIC("Basic_UntypedBool"); break;
|
||||
case Basic_UntypedInteger: GB_PANIC("Basic_UntypedInteger"); break;
|
||||
case Basic_UntypedFloat: GB_PANIC("Basic_UntypedFloat"); break;
|
||||
case Basic_UntypedComplex: GB_PANIC("Basic_UntypedComplex"); break;
|
||||
case Basic_UntypedQuaternion: GB_PANIC("Basic_UntypedQuaternion"); break;
|
||||
case Basic_UntypedString: GB_PANIC("Basic_UntypedString"); break;
|
||||
case Basic_UntypedRune: GB_PANIC("Basic_UntypedRune"); break;
|
||||
case Basic_UntypedNil: GB_PANIC("Basic_UntypedNil"); break;
|
||||
case Basic_UntypedUndef: GB_PANIC("Basic_UntypedUndef"); break;
|
||||
|
||||
default: GB_PANIC("Basic Unhandled"); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_Named:
|
||||
GB_PANIC("Type_Named should be handled in lb_debug_type separately");
|
||||
|
||||
case Type_Pointer:
|
||||
return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->Pointer.elem), word_bits, word_bits, 0, nullptr, 0);
|
||||
|
||||
case Type_Array: {
|
||||
LLVMMetadataRef subscripts[1] = {};
|
||||
subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
|
||||
0ll,
|
||||
type->Array.count
|
||||
);
|
||||
|
||||
return LLVMDIBuilderCreateArrayType(m->debug_builder,
|
||||
8*cast(uint64_t)type_size_of(type),
|
||||
8*cast(unsigned)type_align_of(type),
|
||||
lb_debug_type(m, type->Array.elem),
|
||||
subscripts, gb_count_of(subscripts));
|
||||
}
|
||||
|
||||
case Type_EnumeratedArray: {
|
||||
LLVMMetadataRef subscripts[1] = {};
|
||||
subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
|
||||
0ll,
|
||||
type->EnumeratedArray.count
|
||||
);
|
||||
|
||||
LLVMMetadataRef array_type = LLVMDIBuilderCreateArrayType(m->debug_builder,
|
||||
8*cast(uint64_t)type_size_of(type),
|
||||
8*cast(unsigned)type_align_of(type),
|
||||
lb_debug_type(m, type->EnumeratedArray.elem),
|
||||
subscripts, gb_count_of(subscripts));
|
||||
gbString name = type_to_string(type, temporary_allocator());
|
||||
return LLVMDIBuilderCreateTypedef(m->debug_builder, array_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
|
||||
}
|
||||
|
||||
|
||||
case Type_Struct:
|
||||
case Type_Union:
|
||||
case Type_Slice:
|
||||
case Type_DynamicArray:
|
||||
case Type_Map:
|
||||
case Type_BitSet:
|
||||
{
|
||||
unsigned tag = DW_TAG_structure_type;
|
||||
if (is_type_raw_union(type) || is_type_union(type)) {
|
||||
tag = DW_TAG_union_type;
|
||||
}
|
||||
u64 size_in_bits = cast(u64)(8*type_size_of(type));
|
||||
u32 align_in_bits = cast(u32)(8*type_size_of(type));
|
||||
LLVMDIFlags flags = LLVMDIFlagZero;
|
||||
|
||||
LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
|
||||
m->debug_builder, tag, "", 0, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0
|
||||
);
|
||||
lbIncompleteDebugType idt = {};
|
||||
idt.type = type;
|
||||
idt.metadata = temp_forward_decl;
|
||||
|
||||
array_add(&m->debug_incomplete_types, idt);
|
||||
lb_set_llvm_metadata(m, type, temp_forward_decl);
|
||||
return temp_forward_decl;
|
||||
}
|
||||
|
||||
case Type_Enum:
|
||||
{
|
||||
LLVMMetadataRef scope = nullptr;
|
||||
LLVMMetadataRef file = nullptr;
|
||||
unsigned line = 0;
|
||||
unsigned element_count = cast(unsigned)type->Enum.fields.count;
|
||||
LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
|
||||
Type *bt = base_enum_type(type);
|
||||
LLVMBool is_unsigned = is_type_unsigned(bt);
|
||||
for (unsigned i = 0; i < element_count; i++) {
|
||||
Entity *f = type->Enum.fields[i];
|
||||
GB_ASSERT(f->kind == Entity_Constant);
|
||||
String name = f->token.string;
|
||||
i64 value = exact_value_to_i64(f->Constant.value);
|
||||
elements[i] = LLVMDIBuilderCreateEnumerator(m->debug_builder, cast(char const *)name.text, cast(size_t)name.len, value, is_unsigned);
|
||||
}
|
||||
LLVMMetadataRef class_type = lb_debug_type(m, bt);
|
||||
return LLVMDIBuilderCreateEnumerationType(m->debug_builder, scope, "", 0, file, line, 8*type_size_of(type), 8*cast(unsigned)type_align_of(type), elements, element_count, class_type);
|
||||
}
|
||||
|
||||
case Type_Tuple:
|
||||
if (type->Tuple.variables.count == 1) {
|
||||
return lb_debug_type(m, type->Tuple.variables[0]->type);
|
||||
} else {
|
||||
type_set_offsets(type);
|
||||
LLVMMetadataRef parent_scope = nullptr;
|
||||
LLVMMetadataRef scope = nullptr;
|
||||
LLVMMetadataRef file = nullptr;
|
||||
unsigned line = 0;
|
||||
u64 size_in_bits = 8*cast(u64)type_size_of(type);
|
||||
u32 align_in_bits = 8*cast(u32)type_align_of(type);
|
||||
LLVMDIFlags flags = LLVMDIFlagZero;
|
||||
|
||||
unsigned element_count = cast(unsigned)type->Tuple.variables.count;
|
||||
LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
|
||||
|
||||
for (unsigned i = 0; i < element_count; i++) {
|
||||
Entity *f = type->Tuple.variables[i];
|
||||
GB_ASSERT(f->kind == Entity_Variable);
|
||||
String name = f->token.string;
|
||||
unsigned field_line = 0;
|
||||
LLVMDIFlags field_flags = LLVMDIFlagZero;
|
||||
u64 offset_in_bits = 8*cast(u64)type->Tuple.offsets[i];
|
||||
elements[i] = LLVMDIBuilderCreateMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line,
|
||||
8*cast(u64)type_size_of(f->type), 8*cast(u32)type_align_of(f->type), offset_in_bits,
|
||||
field_flags, lb_debug_type(m, f->type)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return LLVMDIBuilderCreateStructType(m->debug_builder, parent_scope, "", 0, file, line,
|
||||
size_in_bits, align_in_bits, flags,
|
||||
nullptr, elements, element_count, 0, nullptr,
|
||||
"", 0
|
||||
);
|
||||
}
|
||||
|
||||
case Type_Proc:
|
||||
{
|
||||
LLVMMetadataRef proc_underlying_type = lb_debug_type_internal_proc(m, type);
|
||||
LLVMMetadataRef pointer_type = LLVMDIBuilderCreatePointerType(m->debug_builder, proc_underlying_type, word_bits, word_bits, 0, nullptr, 0);
|
||||
gbString name = type_to_string(type, temporary_allocator());
|
||||
return LLVMDIBuilderCreateTypedef(m->debug_builder, pointer_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_SimdVector:
|
||||
return LLVMDIBuilderCreateVectorType(m->debug_builder, cast(unsigned)type->SimdVector.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->SimdVector.elem), nullptr, 0);
|
||||
|
||||
case Type_RelativePointer: {
|
||||
LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativePointer.base_integer);
|
||||
gbString name = type_to_string(type, temporary_allocator());
|
||||
return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
|
||||
}
|
||||
|
||||
case Type_RelativeSlice:
|
||||
{
|
||||
unsigned element_count = 0;
|
||||
LLVMMetadataRef elements[2] = {};
|
||||
Type *base_integer = type->RelativeSlice.base_integer;
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data_offset"), base_integer, 0);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("len"), base_integer, 8*type_size_of(base_integer));
|
||||
gbString name = type_to_string(type, temporary_allocator());
|
||||
return LLVMDIBuilderCreateStructType(m->debug_builder, nullptr, name, gb_string_length(name), nullptr, 0, 2*word_bits, word_bits, LLVMDIFlagZero, nullptr, elements, element_count, 0, nullptr, "", 0);
|
||||
}
|
||||
}
|
||||
|
||||
GB_PANIC("Invalid type %s", type_to_string(type));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LLVMMetadataRef lb_get_base_scope_metadata(lbModule *m, Scope *scope) {
|
||||
LLVMMetadataRef found = nullptr;
|
||||
for (;;) {
|
||||
if (scope == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (scope->flags & ScopeFlag_Proc) {
|
||||
found = lb_get_llvm_metadata(m, scope->procedure_entity);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
if (scope->flags & ScopeFlag_File) {
|
||||
found = lb_get_llvm_metadata(m, scope->file);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
scope = scope->parent;
|
||||
}
|
||||
}
|
||||
|
||||
LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
|
||||
GB_ASSERT(type != nullptr);
|
||||
LLVMMetadataRef found = lb_get_llvm_metadata(m, type);
|
||||
if (found != nullptr) {
|
||||
return found;
|
||||
}
|
||||
|
||||
if (type->kind == Type_Named) {
|
||||
LLVMMetadataRef file = nullptr;
|
||||
unsigned line = 0;
|
||||
LLVMMetadataRef scope = nullptr;
|
||||
|
||||
|
||||
if (type->Named.type_name != nullptr) {
|
||||
Entity *e = type->Named.type_name;
|
||||
scope = lb_get_base_scope_metadata(m, e->scope);
|
||||
if (scope != nullptr) {
|
||||
file = LLVMDIScopeGetFile(scope);
|
||||
}
|
||||
line = cast(unsigned)e->token.pos.line;
|
||||
}
|
||||
// TODO(bill): location data for Type_Named
|
||||
|
||||
u64 size_in_bits = 8*type_size_of(type);
|
||||
u32 align_in_bits = 8*cast(u32)type_align_of(type);
|
||||
String name = type->Named.name;
|
||||
char const *name_text = cast(char const *)name.text;
|
||||
size_t name_len = cast(size_t)name.len;
|
||||
unsigned tag = DW_TAG_structure_type;
|
||||
if (is_type_raw_union(type) || is_type_union(type)) {
|
||||
tag = DW_TAG_union_type;
|
||||
}
|
||||
LLVMDIFlags flags = LLVMDIFlagZero;
|
||||
|
||||
Type *bt = base_type(type->Named.base);
|
||||
|
||||
lbIncompleteDebugType idt = {};
|
||||
idt.type = type;
|
||||
|
||||
switch (bt->kind) {
|
||||
case Type_Enum:
|
||||
{
|
||||
unsigned line = 0;
|
||||
unsigned element_count = cast(unsigned)bt->Enum.fields.count;
|
||||
LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
|
||||
Type *ct = base_enum_type(type);
|
||||
LLVMBool is_unsigned = is_type_unsigned(ct);
|
||||
for (unsigned i = 0; i < element_count; i++) {
|
||||
Entity *f = bt->Enum.fields[i];
|
||||
GB_ASSERT(f->kind == Entity_Constant);
|
||||
String name = f->token.string;
|
||||
i64 value = exact_value_to_i64(f->Constant.value);
|
||||
elements[i] = LLVMDIBuilderCreateEnumerator(m->debug_builder, cast(char const *)name.text, cast(size_t)name.len, value, is_unsigned);
|
||||
}
|
||||
LLVMMetadataRef class_type = lb_debug_type(m, ct);
|
||||
return LLVMDIBuilderCreateEnumerationType(m->debug_builder, scope, name_text, name_len, file, line, 8*type_size_of(type), 8*cast(unsigned)type_align_of(type), elements, element_count, class_type);
|
||||
}
|
||||
|
||||
|
||||
case Type_Basic:
|
||||
case Type_Pointer:
|
||||
case Type_Array:
|
||||
case Type_EnumeratedArray:
|
||||
case Type_Tuple:
|
||||
case Type_Proc:
|
||||
case Type_SimdVector:
|
||||
case Type_RelativePointer:
|
||||
case Type_RelativeSlice:
|
||||
{
|
||||
LLVMMetadataRef debug_bt = lb_debug_type(m, bt);
|
||||
LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(m->debug_builder, debug_bt, name_text, name_len, file, line, scope, align_in_bits);
|
||||
lb_set_llvm_metadata(m, type, final_decl);
|
||||
return final_decl;
|
||||
}
|
||||
|
||||
case Type_Slice:
|
||||
case Type_DynamicArray:
|
||||
case Type_Map:
|
||||
case Type_Struct:
|
||||
case Type_Union:
|
||||
case Type_BitSet:
|
||||
LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
|
||||
m->debug_builder, tag, name_text, name_len, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0
|
||||
);
|
||||
idt.metadata = temp_forward_decl;
|
||||
|
||||
array_add(&m->debug_incomplete_types, idt);
|
||||
lb_set_llvm_metadata(m, type, temp_forward_decl);
|
||||
return temp_forward_decl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LLVMMetadataRef dt = lb_debug_type_internal(m, type);
|
||||
lb_set_llvm_metadata(m, type, dt);
|
||||
return dt;
|
||||
}
|
||||
|
||||
void lb_debug_complete_types(lbModule *m) {
|
||||
unsigned const word_size = cast(unsigned)build_context.word_size;
|
||||
unsigned const word_bits = cast(unsigned)(8*build_context.word_size);
|
||||
|
||||
for_array(debug_incomplete_type_index, m->debug_incomplete_types) {
|
||||
auto const &idt = m->debug_incomplete_types[debug_incomplete_type_index];
|
||||
GB_ASSERT(idt.type != nullptr);
|
||||
GB_ASSERT(idt.metadata != nullptr);
|
||||
|
||||
Type *t = idt.type;
|
||||
Type *bt = base_type(t);
|
||||
|
||||
LLVMMetadataRef parent_scope = nullptr;
|
||||
LLVMMetadataRef file = nullptr;
|
||||
unsigned line_number = 0;
|
||||
u64 size_in_bits = 8*type_size_of(t);
|
||||
u32 align_in_bits = cast(u32)(8*type_align_of(t));
|
||||
LLVMDIFlags flags = LLVMDIFlagZero;
|
||||
|
||||
LLVMMetadataRef derived_from = nullptr;
|
||||
|
||||
LLVMMetadataRef *elements = nullptr;
|
||||
unsigned element_count = 0;
|
||||
|
||||
|
||||
unsigned runtime_lang = 0; // Objective-C runtime version
|
||||
char const *unique_id = "";
|
||||
LLVMMetadataRef vtable_holder = nullptr;
|
||||
size_t unique_id_len = 0;
|
||||
|
||||
|
||||
LLVMMetadataRef record_scope = nullptr;
|
||||
|
||||
switch (bt->kind) {
|
||||
case Type_Slice:
|
||||
case Type_DynamicArray:
|
||||
case Type_Map:
|
||||
case Type_Struct:
|
||||
case Type_Union:
|
||||
case Type_BitSet: {
|
||||
bool is_union = is_type_raw_union(bt) || is_type_union(bt);
|
||||
|
||||
String name = str_lit("<anonymous-struct>");
|
||||
if (t->kind == Type_Named) {
|
||||
name = t->Named.name;
|
||||
if (t->Named.type_name && t->Named.type_name->pkg && t->Named.type_name->pkg->name.len != 0) {
|
||||
name = concatenate3_strings(temporary_allocator(), t->Named.type_name->pkg->name, str_lit("."), t->Named.name);
|
||||
}
|
||||
|
||||
LLVMMetadataRef file = nullptr;
|
||||
unsigned line = 0;
|
||||
LLVMMetadataRef file_scope = nullptr;
|
||||
|
||||
if (t->Named.type_name != nullptr) {
|
||||
Entity *e = t->Named.type_name;
|
||||
file_scope = lb_get_llvm_metadata(m, e->scope);
|
||||
if (file_scope != nullptr) {
|
||||
file = LLVMDIScopeGetFile(file_scope);
|
||||
}
|
||||
line = cast(unsigned)e->token.pos.line;
|
||||
}
|
||||
// TODO(bill): location data for Type_Named
|
||||
|
||||
} else {
|
||||
name = make_string_c(type_to_string(t, temporary_allocator()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
switch (bt->kind) {
|
||||
case Type_Slice:
|
||||
element_count = 2;
|
||||
elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->Slice.elem), 0*word_bits);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*word_bits);
|
||||
break;
|
||||
case Type_DynamicArray:
|
||||
element_count = 4;
|
||||
elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->DynamicArray.elem), 0*word_bits);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*word_bits);
|
||||
elements[2] = lb_debug_struct_field(m, str_lit("cap"), t_int, 2*word_bits);
|
||||
elements[3] = lb_debug_struct_field(m, str_lit("allocator"), t_allocator, 3*word_bits);
|
||||
break;
|
||||
|
||||
case Type_Map:
|
||||
bt = bt->Map.internal_type;
|
||||
/*fallthrough*/
|
||||
case Type_Struct:
|
||||
if (file == nullptr) {
|
||||
if (bt->Struct.node) {
|
||||
file = lb_get_llvm_metadata(m, bt->Struct.node->file);
|
||||
line_number = cast(unsigned)ast_token(bt->Struct.node).pos.line;
|
||||
}
|
||||
}
|
||||
|
||||
type_set_offsets(bt);
|
||||
{
|
||||
isize element_offset = 0;
|
||||
record_scope = lb_get_llvm_metadata(m, bt->Struct.scope);
|
||||
switch (bt->Struct.soa_kind) {
|
||||
case StructSoa_Slice: element_offset = 1; break;
|
||||
case StructSoa_Dynamic: element_offset = 3; break;
|
||||
}
|
||||
element_count = cast(unsigned)(bt->Struct.fields.count + element_offset);
|
||||
elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
|
||||
switch (bt->Struct.soa_kind) {
|
||||
case StructSoa_Slice:
|
||||
elements[0] = LLVMDIBuilderCreateMemberType(
|
||||
m->debug_builder, record_scope,
|
||||
".len", 4,
|
||||
file, 0,
|
||||
8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
|
||||
8*type_size_of(bt)-word_bits,
|
||||
LLVMDIFlagZero, lb_debug_type(m, t_int)
|
||||
);
|
||||
break;
|
||||
case StructSoa_Dynamic:
|
||||
elements[0] = LLVMDIBuilderCreateMemberType(
|
||||
m->debug_builder, record_scope,
|
||||
".len", 4,
|
||||
file, 0,
|
||||
8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
|
||||
8*type_size_of(bt)-word_bits + 0*word_bits,
|
||||
LLVMDIFlagZero, lb_debug_type(m, t_int)
|
||||
);
|
||||
elements[1] = LLVMDIBuilderCreateMemberType(
|
||||
m->debug_builder, record_scope,
|
||||
".cap", 4,
|
||||
file, 0,
|
||||
8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
|
||||
8*type_size_of(bt)-word_bits + 1*word_bits,
|
||||
LLVMDIFlagZero, lb_debug_type(m, t_int)
|
||||
);
|
||||
elements[2] = LLVMDIBuilderCreateMemberType(
|
||||
m->debug_builder, record_scope,
|
||||
".allocator", 12,
|
||||
file, 0,
|
||||
8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
|
||||
8*type_size_of(bt)-word_bits + 2*word_bits,
|
||||
LLVMDIFlagZero, lb_debug_type(m, t_allocator)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
for_array(j, bt->Struct.fields) {
|
||||
Entity *f = bt->Struct.fields[j];
|
||||
String fname = f->token.string;
|
||||
|
||||
unsigned field_line = 0;
|
||||
LLVMDIFlags field_flags = LLVMDIFlagZero;
|
||||
u64 offset_in_bits = 8*cast(u64)bt->Struct.offsets[j];
|
||||
|
||||
elements[element_offset+j] = LLVMDIBuilderCreateMemberType(
|
||||
m->debug_builder, record_scope,
|
||||
cast(char const *)fname.text, cast(size_t)fname.len,
|
||||
file, field_line,
|
||||
8*cast(u64)type_size_of(f->type), 8*cast(u32)type_align_of(f->type),
|
||||
offset_in_bits,
|
||||
field_flags, lb_debug_type(m, f->type)
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Type_Union:
|
||||
{
|
||||
if (file == nullptr) {
|
||||
GB_ASSERT(bt->Union.node != nullptr);
|
||||
file = lb_get_llvm_metadata(m, bt->Union.node->file);
|
||||
line_number = cast(unsigned)ast_token(bt->Union.node).pos.line;
|
||||
}
|
||||
|
||||
isize index_offset = 1;
|
||||
if (is_type_union_maybe_pointer(bt)) {
|
||||
index_offset = 0;
|
||||
}
|
||||
record_scope = lb_get_llvm_metadata(m, bt->Union.scope);
|
||||
element_count = cast(unsigned)bt->Union.variants.count;
|
||||
if (index_offset > 0) {
|
||||
element_count += 1;
|
||||
}
|
||||
|
||||
elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
|
||||
if (index_offset > 0) {
|
||||
Type *tag_type = union_tag_type(bt);
|
||||
unsigned field_line = 0;
|
||||
u64 offset_in_bits = 8*cast(u64)bt->Union.variant_block_size;
|
||||
LLVMDIFlags field_flags = LLVMDIFlagZero;
|
||||
|
||||
elements[0] = LLVMDIBuilderCreateMemberType(
|
||||
m->debug_builder, record_scope,
|
||||
"tag", 3,
|
||||
file, field_line,
|
||||
8*cast(u64)type_size_of(tag_type), 8*cast(u32)type_align_of(tag_type),
|
||||
offset_in_bits,
|
||||
field_flags, lb_debug_type(m, tag_type)
|
||||
);
|
||||
}
|
||||
|
||||
for_array(j, bt->Union.variants) {
|
||||
Type *variant = bt->Union.variants[j];
|
||||
|
||||
unsigned field_index = cast(unsigned)(index_offset+j);
|
||||
|
||||
char name[16] = {};
|
||||
gb_snprintf(name, gb_size_of(name), "v%u", field_index);
|
||||
isize name_len = gb_strlen(name);
|
||||
|
||||
unsigned field_line = 0;
|
||||
LLVMDIFlags field_flags = LLVMDIFlagZero;
|
||||
u64 offset_in_bits = 0;
|
||||
|
||||
elements[field_index] = LLVMDIBuilderCreateMemberType(
|
||||
m->debug_builder, record_scope,
|
||||
name, name_len,
|
||||
file, field_line,
|
||||
8*cast(u64)type_size_of(variant), 8*cast(u32)type_align_of(variant),
|
||||
offset_in_bits,
|
||||
field_flags, lb_debug_type(m, variant)
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_BitSet:
|
||||
{
|
||||
if (file == nullptr) {
|
||||
GB_ASSERT(bt->BitSet.node != nullptr);
|
||||
file = lb_get_llvm_metadata(m, bt->BitSet.node->file);
|
||||
line_number = cast(unsigned)ast_token(bt->BitSet.node).pos.line;
|
||||
}
|
||||
|
||||
LLVMMetadataRef bit_set_field_type = lb_debug_type(m, t_bool);
|
||||
LLVMMetadataRef scope = file;
|
||||
|
||||
Type *elem = base_type(bt->BitSet.elem);
|
||||
if (elem->kind == Type_Enum) {
|
||||
element_count = cast(unsigned)elem->Enum.fields.count;
|
||||
elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
|
||||
for_array(i, elem->Enum.fields) {
|
||||
Entity *f = elem->Enum.fields[i];
|
||||
GB_ASSERT(f->kind == Entity_Constant);
|
||||
i64 val = exact_value_to_i64(f->Constant.value);
|
||||
String name = f->token.string;
|
||||
u64 offset_in_bits = cast(u64)(val - bt->BitSet.lower);
|
||||
elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
|
||||
m->debug_builder,
|
||||
scope,
|
||||
cast(char const *)name.text, name.len,
|
||||
file, line_number,
|
||||
1,
|
||||
offset_in_bits,
|
||||
0,
|
||||
LLVMDIFlagZero,
|
||||
bit_set_field_type
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
||||
char name[32] = {};
|
||||
|
||||
GB_ASSERT(is_type_integer(elem));
|
||||
i64 count = bt->BitSet.upper - bt->BitSet.lower + 1;
|
||||
GB_ASSERT(0 <= count);
|
||||
|
||||
element_count = cast(unsigned)count;
|
||||
elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
|
||||
for (unsigned i = 0; i < element_count; i++) {
|
||||
u64 offset_in_bits = i;
|
||||
i64 val = bt->BitSet.lower + cast(i64)i;
|
||||
gb_snprintf(name, gb_count_of(name), "%lld", cast(long long)val);
|
||||
elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
|
||||
m->debug_builder,
|
||||
scope,
|
||||
name, gb_strlen(name),
|
||||
file, line_number,
|
||||
1,
|
||||
offset_in_bits,
|
||||
0,
|
||||
LLVMDIFlagZero,
|
||||
bit_set_field_type
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LLVMMetadataRef final_metadata = nullptr;
|
||||
if (is_union) {
|
||||
final_metadata = LLVMDIBuilderCreateUnionType(
|
||||
m->debug_builder,
|
||||
parent_scope,
|
||||
cast(char const *)name.text, cast(size_t)name.len,
|
||||
file, line_number,
|
||||
size_in_bits, align_in_bits,
|
||||
flags,
|
||||
elements, element_count,
|
||||
runtime_lang,
|
||||
unique_id, unique_id_len
|
||||
);
|
||||
} else {
|
||||
final_metadata = LLVMDIBuilderCreateStructType(
|
||||
m->debug_builder,
|
||||
parent_scope,
|
||||
cast(char const *)name.text, cast(size_t)name.len,
|
||||
file, line_number,
|
||||
size_in_bits, align_in_bits,
|
||||
flags,
|
||||
derived_from,
|
||||
elements, element_count,
|
||||
runtime_lang,
|
||||
vtable_holder,
|
||||
unique_id, unique_id_len
|
||||
);
|
||||
}
|
||||
|
||||
LLVMMetadataReplaceAllUsesWith(idt.metadata, final_metadata);
|
||||
lb_set_llvm_metadata(m, idt.type, final_metadata);
|
||||
} break;
|
||||
default:
|
||||
GB_PANIC("invalid incomplete debug type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
array_clear(&m->debug_incomplete_types);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token) {
|
||||
if (p->debug_info == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (type == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (type == t_invalid) {
|
||||
return;
|
||||
}
|
||||
if (p->body == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
lbModule *m = p->module;
|
||||
String const &name = token.string;
|
||||
if (name == "" || name == "_") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lb_get_llvm_metadata(m, ptr) != nullptr) {
|
||||
// Already been set
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
AstFile *file = p->body->file;
|
||||
|
||||
LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p);
|
||||
LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
|
||||
GB_ASSERT(llvm_scope != nullptr);
|
||||
if (llvm_file == nullptr) {
|
||||
llvm_file = LLVMDIScopeGetFile(llvm_scope);
|
||||
}
|
||||
|
||||
if (llvm_file == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned alignment_in_bits = cast(unsigned)(8*type_align_of(type));
|
||||
|
||||
LLVMDIFlags flags = LLVMDIFlagZero;
|
||||
LLVMBool always_preserve = build_context.optimization_level == 0;
|
||||
|
||||
LLVMMetadataRef debug_type = lb_debug_type(m, type);
|
||||
|
||||
LLVMMetadataRef var_info = LLVMDIBuilderCreateAutoVariable(
|
||||
m->debug_builder, llvm_scope,
|
||||
cast(char const *)name.text, cast(size_t)name.len,
|
||||
llvm_file, token.pos.line,
|
||||
debug_type,
|
||||
always_preserve, flags, alignment_in_bits
|
||||
);
|
||||
|
||||
LLVMValueRef storage = ptr;
|
||||
LLVMValueRef instr = ptr;
|
||||
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
|
||||
LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
|
||||
lb_set_llvm_metadata(m, ptr, llvm_expr);
|
||||
LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr);
|
||||
}
|
||||
|
||||
void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
|
||||
if (!p->debug_info || !p->body) {
|
||||
return;
|
||||
}
|
||||
LLVMMetadataRef loc = LLVMGetCurrentDebugLocation2(p->builder);
|
||||
if (!loc) {
|
||||
return;
|
||||
}
|
||||
TokenPos pos = {};
|
||||
|
||||
pos.file_id = p->body->file ? p->body->file->id : 0;
|
||||
pos.line = LLVMDILocationGetLine(loc);
|
||||
pos.column = LLVMDILocationGetColumn(loc);
|
||||
|
||||
Token token = {};
|
||||
token.kind = Token_context;
|
||||
token.string = str_lit("context");
|
||||
token.pos = pos;
|
||||
|
||||
lb_add_debug_local_variable(p, ctx.addr.value, t_context, token);
|
||||
}
|
||||
3630
src/llvm_backend_expr.cpp
Normal file
3630
src/llvm_backend_expr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2490
src/llvm_backend_general.cpp
Normal file
2490
src/llvm_backend_general.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2221
src/llvm_backend_proc.cpp
Normal file
2221
src/llvm_backend_proc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2233
src/llvm_backend_stmt.cpp
Normal file
2233
src/llvm_backend_stmt.cpp
Normal file
File diff suppressed because it is too large
Load Diff
855
src/llvm_backend_type.cpp
Normal file
855
src/llvm_backend_type.cpp
Normal file
@@ -0,0 +1,855 @@
|
||||
isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
|
||||
isize index = type_info_index(info, type, false);
|
||||
if (index >= 0) {
|
||||
auto *set = &info->minimum_dependency_type_info_set;
|
||||
for_array(i, set->entries) {
|
||||
if (set->entries[i].ptr == index) {
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err_on_not_found) {
|
||||
GB_PANIC("NOT FOUND lb_type_info_index %s @ index %td", type_to_string(type), index);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
lbValue lb_typeid(lbModule *m, Type *type) {
|
||||
type = default_type(type);
|
||||
|
||||
u64 id = cast(u64)lb_type_info_index(m->info, type);
|
||||
GB_ASSERT(id >= 0);
|
||||
|
||||
u64 kind = Typeid_Invalid;
|
||||
u64 named = is_type_named(type) && type->kind != Type_Basic;
|
||||
u64 special = 0;
|
||||
u64 reserved = 0;
|
||||
|
||||
Type *bt = base_type(type);
|
||||
TypeKind tk = bt->kind;
|
||||
switch (tk) {
|
||||
case Type_Basic: {
|
||||
u32 flags = bt->Basic.flags;
|
||||
if (flags & BasicFlag_Boolean) kind = Typeid_Boolean;
|
||||
if (flags & BasicFlag_Integer) kind = Typeid_Integer;
|
||||
if (flags & BasicFlag_Unsigned) kind = Typeid_Integer;
|
||||
if (flags & BasicFlag_Float) kind = Typeid_Float;
|
||||
if (flags & BasicFlag_Complex) kind = Typeid_Complex;
|
||||
if (flags & BasicFlag_Pointer) kind = Typeid_Pointer;
|
||||
if (flags & BasicFlag_String) kind = Typeid_String;
|
||||
if (flags & BasicFlag_Rune) kind = Typeid_Rune;
|
||||
} break;
|
||||
case Type_Pointer: kind = Typeid_Pointer; break;
|
||||
case Type_Array: kind = Typeid_Array; break;
|
||||
case Type_EnumeratedArray: kind = Typeid_Enumerated_Array; break;
|
||||
case Type_Slice: kind = Typeid_Slice; break;
|
||||
case Type_DynamicArray: kind = Typeid_Dynamic_Array; break;
|
||||
case Type_Map: kind = Typeid_Map; break;
|
||||
case Type_Struct: kind = Typeid_Struct; break;
|
||||
case Type_Enum: kind = Typeid_Enum; break;
|
||||
case Type_Union: kind = Typeid_Union; break;
|
||||
case Type_Tuple: kind = Typeid_Tuple; break;
|
||||
case Type_Proc: kind = Typeid_Procedure; break;
|
||||
case Type_BitSet: kind = Typeid_Bit_Set; break;
|
||||
case Type_SimdVector: kind = Typeid_Simd_Vector; break;
|
||||
case Type_RelativePointer: kind = Typeid_Relative_Pointer; break;
|
||||
case Type_RelativeSlice: kind = Typeid_Relative_Slice; break;
|
||||
}
|
||||
|
||||
if (is_type_cstring(type)) {
|
||||
special = 1;
|
||||
} else if (is_type_integer(type) && !is_type_unsigned(type)) {
|
||||
special = 1;
|
||||
}
|
||||
|
||||
u64 data = 0;
|
||||
if (build_context.word_size == 4) {
|
||||
GB_ASSERT(id <= (1u<<24u));
|
||||
data |= (id &~ (1u<<24)) << 0u; // index
|
||||
data |= (kind &~ (1u<<5)) << 24u; // kind
|
||||
data |= (named &~ (1u<<1)) << 29u; // kind
|
||||
data |= (special &~ (1u<<1)) << 30u; // kind
|
||||
data |= (reserved &~ (1u<<1)) << 31u; // kind
|
||||
} else {
|
||||
GB_ASSERT(build_context.word_size == 8);
|
||||
GB_ASSERT(id <= (1ull<<56u));
|
||||
data |= (id &~ (1ull<<56)) << 0ul; // index
|
||||
data |= (kind &~ (1ull<<5)) << 56ull; // kind
|
||||
data |= (named &~ (1ull<<1)) << 61ull; // kind
|
||||
data |= (special &~ (1ull<<1)) << 62ull; // kind
|
||||
data |= (reserved &~ (1ull<<1)) << 63ull; // kind
|
||||
}
|
||||
|
||||
lbValue res = {};
|
||||
res.value = LLVMConstInt(lb_type(m, t_typeid), data, false);
|
||||
res.type = t_typeid;
|
||||
return res;
|
||||
}
|
||||
|
||||
lbValue lb_type_info(lbModule *m, Type *type) {
|
||||
type = default_type(type);
|
||||
|
||||
isize index = lb_type_info_index(m->info, type);
|
||||
GB_ASSERT(index >= 0);
|
||||
|
||||
LLVMTypeRef it = lb_type(m, t_int);
|
||||
LLVMValueRef indices[2] = {
|
||||
LLVMConstInt(it, 0, false),
|
||||
LLVMConstInt(it, index, true),
|
||||
};
|
||||
|
||||
lbValue value = {};
|
||||
value.value = LLVMConstGEP(lb_global_type_info_data_ptr(m).value, indices, gb_count_of(indices));
|
||||
value.type = t_type_info_ptr;
|
||||
return value;
|
||||
}
|
||||
|
||||
lbValue lb_get_type_info_ptr(lbModule *m, Type *type) {
|
||||
i32 index = cast(i32)lb_type_info_index(m->info, type);
|
||||
GB_ASSERT(index >= 0);
|
||||
// gb_printf_err("%d %s\n", index, type_to_string(type));
|
||||
|
||||
LLVMValueRef indices[2] = {
|
||||
LLVMConstInt(lb_type(m, t_int), 0, false),
|
||||
LLVMConstInt(lb_type(m, t_int), index, false),
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = t_type_info_ptr;
|
||||
res.value = LLVMConstGEP(lb_global_type_info_data_ptr(m).value, indices, cast(unsigned)gb_count_of(indices));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
lbValue lb_type_info_member_types_offset(lbProcedure *p, isize count) {
|
||||
GB_ASSERT(p->module == &p->module->gen->default_module);
|
||||
lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_types.addr, lb_global_type_info_member_types_index);
|
||||
lb_global_type_info_member_types_index += cast(i32)count;
|
||||
return offset;
|
||||
}
|
||||
lbValue lb_type_info_member_names_offset(lbProcedure *p, isize count) {
|
||||
GB_ASSERT(p->module == &p->module->gen->default_module);
|
||||
lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_names.addr, lb_global_type_info_member_names_index);
|
||||
lb_global_type_info_member_names_index += cast(i32)count;
|
||||
return offset;
|
||||
}
|
||||
lbValue lb_type_info_member_offsets_offset(lbProcedure *p, isize count) {
|
||||
GB_ASSERT(p->module == &p->module->gen->default_module);
|
||||
lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_offsets.addr, lb_global_type_info_member_offsets_index);
|
||||
lb_global_type_info_member_offsets_index += cast(i32)count;
|
||||
return offset;
|
||||
}
|
||||
lbValue lb_type_info_member_usings_offset(lbProcedure *p, isize count) {
|
||||
GB_ASSERT(p->module == &p->module->gen->default_module);
|
||||
lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_usings.addr, lb_global_type_info_member_usings_index);
|
||||
lb_global_type_info_member_usings_index += cast(i32)count;
|
||||
return offset;
|
||||
}
|
||||
lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) {
|
||||
GB_ASSERT(p->module == &p->module->gen->default_module);
|
||||
lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_tags.addr, lb_global_type_info_member_tags_index);
|
||||
lb_global_type_info_member_tags_index += cast(i32)count;
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
|
||||
lbModule *m = p->module;
|
||||
LLVMContextRef ctx = m->ctx;
|
||||
CheckerInfo *info = m->info;
|
||||
|
||||
{
|
||||
// NOTE(bill): Set the type_table slice with the global backing array
|
||||
lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table"));
|
||||
Type *type = base_type(lb_global_type_info_data_entity->type);
|
||||
GB_ASSERT(is_type_array(type));
|
||||
|
||||
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
|
||||
LLVMValueRef values[2] = {
|
||||
LLVMConstInBoundsGEP(lb_global_type_info_data_ptr(m).value, indices, gb_count_of(indices)),
|
||||
LLVMConstInt(lb_type(m, t_int), type->Array.count, true),
|
||||
};
|
||||
LLVMValueRef slice = llvm_const_named_struct(llvm_addr_type(global_type_table), values, gb_count_of(values));
|
||||
|
||||
LLVMSetInitializer(global_type_table.value, slice);
|
||||
}
|
||||
|
||||
|
||||
// Useful types
|
||||
Type *t_i64_slice_ptr = alloc_type_pointer(alloc_type_slice(t_i64));
|
||||
Type *t_string_slice_ptr = alloc_type_pointer(alloc_type_slice(t_string));
|
||||
Entity *type_info_flags_entity = find_core_entity(info->checker, str_lit("Type_Info_Flags"));
|
||||
Type *t_type_info_flags = type_info_flags_entity->type;
|
||||
|
||||
|
||||
i32 type_info_member_types_index = 0;
|
||||
i32 type_info_member_names_index = 0;
|
||||
i32 type_info_member_offsets_index = 0;
|
||||
|
||||
for_array(type_info_type_index, info->type_info_types) {
|
||||
Type *t = info->type_info_types[type_info_type_index];
|
||||
if (t == nullptr || t == t_invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
isize entry_index = lb_type_info_index(info, t, false);
|
||||
if (entry_index <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lbValue tag = {};
|
||||
lbValue ti_ptr = lb_emit_array_epi(p, lb_global_type_info_data_ptr(m), cast(i32)entry_index);
|
||||
lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 4);
|
||||
|
||||
lbValue type_info_flags = lb_const_int(p->module, t_type_info_flags, type_info_flags_of_type(t));
|
||||
|
||||
lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 0), lb_const_int(m, t_int, type_size_of(t)));
|
||||
lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 1), lb_const_int(m, t_int, type_align_of(t)));
|
||||
lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 2), type_info_flags);
|
||||
lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 3), lb_typeid(m, t));
|
||||
|
||||
|
||||
switch (t->kind) {
|
||||
case Type_Named: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_named_ptr);
|
||||
|
||||
LLVMValueRef pkg_name = nullptr;
|
||||
if (t->Named.type_name->pkg) {
|
||||
pkg_name = lb_const_string(m, t->Named.type_name->pkg->name).value;
|
||||
} else {
|
||||
pkg_name = LLVMConstNull(lb_type(m, t_string));
|
||||
}
|
||||
|
||||
String proc_name = {};
|
||||
if (t->Named.type_name->parent_proc_decl) {
|
||||
DeclInfo *decl = t->Named.type_name->parent_proc_decl;
|
||||
if (decl->entity && decl->entity->kind == Entity_Procedure) {
|
||||
proc_name = decl->entity->token.string;
|
||||
}
|
||||
}
|
||||
TokenPos pos = t->Named.type_name->token.pos;
|
||||
|
||||
lbValue loc = lb_emit_source_code_location(p, proc_name, pos);
|
||||
|
||||
LLVMValueRef vals[4] = {
|
||||
lb_const_string(p->module, t->Named.type_name->token.string).value,
|
||||
lb_get_type_info_ptr(m, t->Named.base).value,
|
||||
pkg_name,
|
||||
loc.value
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
break;
|
||||
}
|
||||
|
||||
case Type_Basic:
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_bool:
|
||||
case Basic_b8:
|
||||
case Basic_b16:
|
||||
case Basic_b32:
|
||||
case Basic_b64:
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_boolean_ptr);
|
||||
break;
|
||||
|
||||
case Basic_i8:
|
||||
case Basic_u8:
|
||||
case Basic_i16:
|
||||
case Basic_u16:
|
||||
case Basic_i32:
|
||||
case Basic_u32:
|
||||
case Basic_i64:
|
||||
case Basic_u64:
|
||||
case Basic_i128:
|
||||
case Basic_u128:
|
||||
|
||||
case Basic_i16le:
|
||||
case Basic_u16le:
|
||||
case Basic_i32le:
|
||||
case Basic_u32le:
|
||||
case Basic_i64le:
|
||||
case Basic_u64le:
|
||||
case Basic_i128le:
|
||||
case Basic_u128le:
|
||||
case Basic_i16be:
|
||||
case Basic_u16be:
|
||||
case Basic_i32be:
|
||||
case Basic_u32be:
|
||||
case Basic_i64be:
|
||||
case Basic_u64be:
|
||||
case Basic_i128be:
|
||||
case Basic_u128be:
|
||||
|
||||
case Basic_int:
|
||||
case Basic_uint:
|
||||
case Basic_uintptr: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_integer_ptr);
|
||||
|
||||
lbValue is_signed = lb_const_bool(m, t_bool, (t->Basic.flags & BasicFlag_Unsigned) == 0);
|
||||
// NOTE(bill): This is matches the runtime layout
|
||||
u8 endianness_value = 0;
|
||||
if (t->Basic.flags & BasicFlag_EndianLittle) {
|
||||
endianness_value = 1;
|
||||
} else if (t->Basic.flags & BasicFlag_EndianBig) {
|
||||
endianness_value = 2;
|
||||
}
|
||||
lbValue endianness = lb_const_int(m, t_u8, endianness_value);
|
||||
|
||||
LLVMValueRef vals[2] = {
|
||||
is_signed.value,
|
||||
endianness.value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
break;
|
||||
}
|
||||
|
||||
case Basic_rune:
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_rune_ptr);
|
||||
break;
|
||||
|
||||
case Basic_f16:
|
||||
case Basic_f32:
|
||||
case Basic_f64:
|
||||
case Basic_f16le:
|
||||
case Basic_f32le:
|
||||
case Basic_f64le:
|
||||
case Basic_f16be:
|
||||
case Basic_f32be:
|
||||
case Basic_f64be:
|
||||
{
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_float_ptr);
|
||||
|
||||
// NOTE(bill): This is matches the runtime layout
|
||||
u8 endianness_value = 0;
|
||||
if (t->Basic.flags & BasicFlag_EndianLittle) {
|
||||
endianness_value = 1;
|
||||
} else if (t->Basic.flags & BasicFlag_EndianBig) {
|
||||
endianness_value = 2;
|
||||
}
|
||||
lbValue endianness = lb_const_int(m, t_u8, endianness_value);
|
||||
|
||||
LLVMValueRef vals[1] = {
|
||||
endianness.value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
}
|
||||
break;
|
||||
|
||||
case Basic_complex32:
|
||||
case Basic_complex64:
|
||||
case Basic_complex128:
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_complex_ptr);
|
||||
break;
|
||||
|
||||
case Basic_quaternion64:
|
||||
case Basic_quaternion128:
|
||||
case Basic_quaternion256:
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_quaternion_ptr);
|
||||
break;
|
||||
|
||||
case Basic_rawptr:
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
|
||||
break;
|
||||
|
||||
case Basic_string:
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr);
|
||||
break;
|
||||
|
||||
case Basic_cstring:
|
||||
{
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr);
|
||||
LLVMValueRef vals[1] = {
|
||||
lb_const_bool(m, t_bool, true).value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
}
|
||||
break;
|
||||
|
||||
case Basic_any:
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_any_ptr);
|
||||
break;
|
||||
|
||||
case Basic_typeid:
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_typeid_ptr);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_Pointer: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
|
||||
lbValue gep = lb_get_type_info_ptr(m, t->Pointer.elem);
|
||||
|
||||
LLVMValueRef vals[1] = {
|
||||
gep.value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
break;
|
||||
}
|
||||
case Type_Array: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_array_ptr);
|
||||
i64 ez = type_size_of(t->Array.elem);
|
||||
|
||||
LLVMValueRef vals[3] = {
|
||||
lb_get_type_info_ptr(m, t->Array.elem).value,
|
||||
lb_const_int(m, t_int, ez).value,
|
||||
lb_const_int(m, t_int, t->Array.count).value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
break;
|
||||
}
|
||||
case Type_EnumeratedArray: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
|
||||
|
||||
LLVMValueRef vals[6] = {
|
||||
lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value,
|
||||
lb_get_type_info_ptr(m, t->EnumeratedArray.index).value,
|
||||
lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
|
||||
lb_const_int(m, t_int, t->EnumeratedArray.count).value,
|
||||
|
||||
// Unions
|
||||
LLVMConstNull(lb_type(m, t_type_info_enum_value)),
|
||||
LLVMConstNull(lb_type(m, t_type_info_enum_value)),
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
|
||||
// NOTE(bill): Union assignment
|
||||
lbValue min_value = lb_emit_struct_ep(p, tag, 4);
|
||||
lbValue max_value = lb_emit_struct_ep(p, tag, 5);
|
||||
|
||||
lbValue min_v = lb_const_value(m, t_i64, t->EnumeratedArray.min_value);
|
||||
lbValue max_v = lb_const_value(m, t_i64, t->EnumeratedArray.max_value);
|
||||
|
||||
lb_emit_store(p, min_value, min_v);
|
||||
lb_emit_store(p, max_value, max_v);
|
||||
break;
|
||||
}
|
||||
case Type_DynamicArray: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_dynamic_array_ptr);
|
||||
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_get_type_info_ptr(m, t->DynamicArray.elem).value,
|
||||
lb_const_int(m, t_int, type_size_of(t->DynamicArray.elem)).value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
break;
|
||||
}
|
||||
case Type_Slice: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_slice_ptr);
|
||||
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_get_type_info_ptr(m, t->Slice.elem).value,
|
||||
lb_const_int(m, t_int, type_size_of(t->Slice.elem)).value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
break;
|
||||
}
|
||||
case Type_Proc: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_procedure_ptr);
|
||||
|
||||
LLVMValueRef params = LLVMConstNull(lb_type(m, t_type_info_ptr));
|
||||
LLVMValueRef results = LLVMConstNull(lb_type(m, t_type_info_ptr));
|
||||
if (t->Proc.params != nullptr) {
|
||||
params = lb_get_type_info_ptr(m, t->Proc.params).value;
|
||||
}
|
||||
if (t->Proc.results != nullptr) {
|
||||
results = lb_get_type_info_ptr(m, t->Proc.results).value;
|
||||
}
|
||||
|
||||
LLVMValueRef vals[4] = {
|
||||
params,
|
||||
results,
|
||||
lb_const_bool(m, t_bool, t->Proc.variadic).value,
|
||||
lb_const_int(m, t_u8, t->Proc.calling_convention).value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
break;
|
||||
}
|
||||
case Type_Tuple: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_tuple_ptr);
|
||||
|
||||
|
||||
lbValue memory_types = lb_type_info_member_types_offset(p, t->Tuple.variables.count);
|
||||
lbValue memory_names = lb_type_info_member_names_offset(p, t->Tuple.variables.count);
|
||||
|
||||
|
||||
for_array(i, t->Tuple.variables) {
|
||||
// NOTE(bill): offset is not used for tuples
|
||||
Entity *f = t->Tuple.variables[i];
|
||||
|
||||
lbValue index = lb_const_int(m, t_int, i);
|
||||
lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
|
||||
|
||||
// TODO(bill): Make this constant if possible, 'lb_const_store' does not work
|
||||
lb_emit_store(p, type_info, lb_type_info(m, f->type));
|
||||
if (f->token.string.len > 0) {
|
||||
lbValue name = lb_emit_ptr_offset(p, memory_names, index);
|
||||
lb_emit_store(p, name, lb_const_string(m, f->token.string));
|
||||
}
|
||||
}
|
||||
|
||||
lbValue count = lb_const_int(m, t_int, t->Tuple.variables.count);
|
||||
|
||||
LLVMValueRef types_slice = llvm_const_slice(m, memory_types, count);
|
||||
LLVMValueRef names_slice = llvm_const_slice(m, memory_names, count);
|
||||
|
||||
LLVMValueRef vals[2] = {
|
||||
types_slice,
|
||||
names_slice,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Type_Enum:
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enum_ptr);
|
||||
|
||||
{
|
||||
GB_ASSERT(t->Enum.base_type != nullptr);
|
||||
// GB_ASSERT_MSG(type_size_of(t_type_info_enum_value) == 16, "%lld == 16", cast(long long)type_size_of(t_type_info_enum_value));
|
||||
|
||||
|
||||
LLVMValueRef vals[3] = {};
|
||||
vals[0] = lb_type_info(m, t->Enum.base_type).value;
|
||||
if (t->Enum.fields.count > 0) {
|
||||
auto fields = t->Enum.fields;
|
||||
lbValue name_array = lb_generate_global_array(m, t_string, fields.count,
|
||||
str_lit("$enum_names"), cast(i64)entry_index);
|
||||
lbValue value_array = lb_generate_global_array(m, t_type_info_enum_value, fields.count,
|
||||
str_lit("$enum_values"), cast(i64)entry_index);
|
||||
|
||||
|
||||
LLVMValueRef *name_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count);
|
||||
LLVMValueRef *value_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count);
|
||||
|
||||
GB_ASSERT(is_type_integer(t->Enum.base_type));
|
||||
|
||||
LLVMTypeRef align_type = lb_alignment_prefix_type_hack(m, type_align_of(t));
|
||||
LLVMTypeRef array_type = LLVMArrayType(lb_type(m, t_u8), 8);
|
||||
|
||||
for_array(i, fields) {
|
||||
name_values[i] = lb_const_string(m, fields[i]->token.string).value;
|
||||
value_values[i] = lb_const_value(m, t_i64, fields[i]->Constant.value).value;
|
||||
}
|
||||
|
||||
LLVMValueRef name_init = llvm_const_array(lb_type(m, t_string), name_values, cast(unsigned)fields.count);
|
||||
LLVMValueRef value_init = llvm_const_array(lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count);
|
||||
LLVMSetInitializer(name_array.value, name_init);
|
||||
LLVMSetInitializer(value_array.value, value_init);
|
||||
|
||||
lbValue v_count = lb_const_int(m, t_int, fields.count);
|
||||
|
||||
vals[1] = llvm_const_slice(m, lb_array_elem(p, name_array), v_count);
|
||||
vals[2] = llvm_const_slice(m, lb_array_elem(p, value_array), v_count);
|
||||
} else {
|
||||
vals[1] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[1]->type));
|
||||
vals[2] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[2]->type));
|
||||
}
|
||||
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_Union: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr);
|
||||
|
||||
{
|
||||
LLVMValueRef vals[7] = {};
|
||||
|
||||
isize variant_count = gb_max(0, t->Union.variants.count);
|
||||
lbValue memory_types = lb_type_info_member_types_offset(p, variant_count);
|
||||
|
||||
// NOTE(bill): Zeroth is nil so ignore it
|
||||
for (isize variant_index = 0; variant_index < variant_count; variant_index++) {
|
||||
Type *vt = t->Union.variants[variant_index];
|
||||
lbValue tip = lb_get_type_info_ptr(m, vt);
|
||||
|
||||
lbValue index = lb_const_int(m, t_int, variant_index);
|
||||
lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
|
||||
lb_emit_store(p, type_info, lb_type_info(m, vt));
|
||||
}
|
||||
|
||||
lbValue count = lb_const_int(m, t_int, variant_count);
|
||||
vals[0] = llvm_const_slice(m, memory_types, count);
|
||||
|
||||
i64 tag_size = union_tag_size(t);
|
||||
i64 tag_offset = align_formula(t->Union.variant_block_size, tag_size);
|
||||
|
||||
if (tag_size > 0) {
|
||||
vals[1] = lb_const_int(m, t_uintptr, tag_offset).value;
|
||||
vals[2] = lb_type_info(m, union_tag_type(t)).value;
|
||||
} else {
|
||||
vals[1] = lb_const_int(m, t_uintptr, 0).value;
|
||||
vals[2] = LLVMConstNull(lb_type(m, t_type_info_ptr));
|
||||
}
|
||||
|
||||
if (is_type_comparable(t) && !is_type_simple_compare(t)) {
|
||||
vals[3] = lb_get_equal_proc_for_type(m, t).value;
|
||||
}
|
||||
|
||||
vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
|
||||
vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value;
|
||||
vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value;
|
||||
|
||||
for (isize i = 0; i < gb_count_of(vals); i++) {
|
||||
if (vals[i] == nullptr) {
|
||||
vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i)));
|
||||
}
|
||||
}
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Type_Struct: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_struct_ptr);
|
||||
|
||||
LLVMValueRef vals[12] = {};
|
||||
|
||||
|
||||
{
|
||||
lbValue is_packed = lb_const_bool(m, t_bool, t->Struct.is_packed);
|
||||
lbValue is_raw_union = lb_const_bool(m, t_bool, t->Struct.is_raw_union);
|
||||
lbValue is_custom_align = lb_const_bool(m, t_bool, t->Struct.custom_align != 0);
|
||||
vals[5] = is_packed.value;
|
||||
vals[6] = is_raw_union.value;
|
||||
vals[7] = is_custom_align.value;
|
||||
if (is_type_comparable(t) && !is_type_simple_compare(t)) {
|
||||
vals[8] = lb_get_equal_proc_for_type(m, t).value;
|
||||
}
|
||||
|
||||
|
||||
if (t->Struct.soa_kind != StructSoa_None) {
|
||||
lbValue kind = lb_emit_struct_ep(p, tag, 9);
|
||||
Type *kind_type = type_deref(kind.type);
|
||||
|
||||
lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind));
|
||||
lbValue soa_type = lb_type_info(m, t->Struct.soa_elem);
|
||||
lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count);
|
||||
|
||||
vals[9] = soa_kind.value;
|
||||
vals[10] = soa_type.value;
|
||||
vals[11] = soa_len.value;
|
||||
}
|
||||
}
|
||||
|
||||
isize count = t->Struct.fields.count;
|
||||
if (count > 0) {
|
||||
lbValue memory_types = lb_type_info_member_types_offset (p, count);
|
||||
lbValue memory_names = lb_type_info_member_names_offset (p, count);
|
||||
lbValue memory_offsets = lb_type_info_member_offsets_offset(p, count);
|
||||
lbValue memory_usings = lb_type_info_member_usings_offset (p, count);
|
||||
lbValue memory_tags = lb_type_info_member_tags_offset (p, count);
|
||||
|
||||
type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet
|
||||
for (isize source_index = 0; source_index < count; source_index++) {
|
||||
// TODO(bill): Order fields in source order not layout order
|
||||
Entity *f = t->Struct.fields[source_index];
|
||||
lbValue tip = lb_get_type_info_ptr(m, f->type);
|
||||
i64 foffset = 0;
|
||||
if (!t->Struct.is_raw_union) {
|
||||
foffset = t->Struct.offsets[f->Variable.field_index];
|
||||
}
|
||||
GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field);
|
||||
|
||||
lbValue index = lb_const_int(m, t_int, source_index);
|
||||
lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
|
||||
lbValue offset = lb_emit_ptr_offset(p, memory_offsets, index);
|
||||
lbValue is_using = lb_emit_ptr_offset(p, memory_usings, index);
|
||||
|
||||
lb_emit_store(p, type_info, lb_type_info(m, f->type));
|
||||
if (f->token.string.len > 0) {
|
||||
lbValue name = lb_emit_ptr_offset(p, memory_names, index);
|
||||
lb_emit_store(p, name, lb_const_string(m, f->token.string));
|
||||
}
|
||||
lb_emit_store(p, offset, lb_const_int(m, t_uintptr, foffset));
|
||||
lb_emit_store(p, is_using, lb_const_bool(m, t_bool, (f->flags&EntityFlag_Using) != 0));
|
||||
|
||||
if (t->Struct.tags.count > 0) {
|
||||
String tag_string = t->Struct.tags[source_index];
|
||||
if (tag_string.len > 0) {
|
||||
lbValue tag_ptr = lb_emit_ptr_offset(p, memory_tags, index);
|
||||
lb_emit_store(p, tag_ptr, lb_const_string(m, tag_string));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lbValue cv = lb_const_int(m, t_int, count);
|
||||
vals[0] = llvm_const_slice(m, memory_types, cv);
|
||||
vals[1] = llvm_const_slice(m, memory_names, cv);
|
||||
vals[2] = llvm_const_slice(m, memory_offsets, cv);
|
||||
vals[3] = llvm_const_slice(m, memory_usings, cv);
|
||||
vals[4] = llvm_const_slice(m, memory_tags, cv);
|
||||
}
|
||||
for (isize i = 0; i < gb_count_of(vals); i++) {
|
||||
if (vals[i] == nullptr) {
|
||||
vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Type_Map: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr);
|
||||
init_map_internal_types(t);
|
||||
|
||||
LLVMValueRef vals[5] = {
|
||||
lb_get_type_info_ptr(m, t->Map.key).value,
|
||||
lb_get_type_info_ptr(m, t->Map.value).value,
|
||||
lb_get_type_info_ptr(m, t->Map.generated_struct_type).value,
|
||||
lb_get_equal_proc_for_type(m, t->Map.key).value,
|
||||
lb_get_hasher_proc_for_type(m, t->Map.key).value
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
break;
|
||||
}
|
||||
|
||||
case Type_BitSet:
|
||||
{
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_bit_set_ptr);
|
||||
|
||||
GB_ASSERT(is_type_typed(t->BitSet.elem));
|
||||
|
||||
|
||||
LLVMValueRef vals[4] = {
|
||||
lb_get_type_info_ptr(m, t->BitSet.elem).value,
|
||||
LLVMConstNull(lb_type(m, t_type_info_ptr)),
|
||||
lb_const_int(m, t_i64, t->BitSet.lower).value,
|
||||
lb_const_int(m, t_i64, t->BitSet.upper).value,
|
||||
};
|
||||
if (t->BitSet.underlying != nullptr) {
|
||||
vals[1] =lb_get_type_info_ptr(m, t->BitSet.underlying).value;
|
||||
}
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_SimdVector:
|
||||
{
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_simd_vector_ptr);
|
||||
|
||||
LLVMValueRef vals[3] = {};
|
||||
|
||||
vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value;
|
||||
vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
|
||||
vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_RelativePointer:
|
||||
{
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_pointer_ptr);
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_get_type_info_ptr(m, t->RelativePointer.pointer_type).value,
|
||||
lb_get_type_info_ptr(m, t->RelativePointer.base_integer).value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
}
|
||||
break;
|
||||
case Type_RelativeSlice:
|
||||
{
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_slice_ptr);
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_get_type_info_ptr(m, t->RelativeSlice.slice_type).value,
|
||||
lb_get_type_info_ptr(m, t->RelativeSlice.base_integer).value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
res.value = llvm_const_named_struct(lb_type(m, res.type), vals, gb_count_of(vals));
|
||||
lb_emit_store(p, tag, res);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (tag.value != nullptr) {
|
||||
Type *tag_type = type_deref(tag.type);
|
||||
GB_ASSERT(is_type_named(tag_type));
|
||||
// lb_emit_store_union_variant(p, variant_ptr, lb_emit_load(p, tag), tag_type);
|
||||
lb_emit_store_union_variant_tag(p, variant_ptr, tag_type);
|
||||
} else {
|
||||
if (t != t_llvm_bool) {
|
||||
GB_PANIC("Unhandled Type_Info variant: %s", type_to_string(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1257
src/llvm_backend_utility.cpp
Normal file
1257
src/llvm_backend_utility.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1988,6 +1988,38 @@ bool is_type_simple_compare(Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
|
||||
Type *prev_src = src;
|
||||
// Type *prev_dst = dst;
|
||||
src = base_type(type_deref(src));
|
||||
// dst = base_type(type_deref(dst));
|
||||
bool src_is_ptr = src != prev_src;
|
||||
// bool dst_is_ptr = dst != prev_dst;
|
||||
|
||||
GB_ASSERT(is_type_struct(src) || is_type_union(src));
|
||||
for_array(i, src->Struct.fields) {
|
||||
Entity *f = src->Struct.fields[i];
|
||||
if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (is_type_struct(f->type)) {
|
||||
String name = lookup_subtype_polymorphic_field(dst, f->type);
|
||||
if (name.len > 0) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return str_lit("");
|
||||
}
|
||||
|
||||
|
||||
|
||||
Type *strip_type_aliasing(Type *x) {
|
||||
if (x == nullptr) {
|
||||
|
||||
Reference in New Issue
Block a user