mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-29 09:24:33 +00:00
3097 lines
92 KiB
C++
3097 lines
92 KiB
C++
#define IR_FILE_BUFFER_BUF_LEN (4096)
|
|
|
|
struct irFileBuffer {
|
|
gbVirtualMemory vm;
|
|
isize offset;
|
|
gbFile * output;
|
|
char buf[IR_FILE_BUFFER_BUF_LEN];
|
|
};
|
|
|
|
void ir_file_buffer_init(irFileBuffer *f, gbFile *output) {
|
|
isize size = 8*gb_virtual_memory_page_size(nullptr);
|
|
f->vm = gb_vm_alloc(nullptr, size);
|
|
f->offset = 0;
|
|
f->output = output;
|
|
}
|
|
|
|
void ir_file_buffer_destroy(irFileBuffer *f) {
|
|
if (f->offset > 0) {
|
|
// NOTE(bill): finish writing buffered data
|
|
gb_file_write(f->output, f->vm.data, f->offset);
|
|
}
|
|
|
|
gb_vm_free(f->vm);
|
|
}
|
|
|
|
void ir_file_buffer_write(irFileBuffer *f, void const *data, isize len) {
|
|
if (len > f->vm.size) {
|
|
//NOTE(thebirk): Flush the vm data before we print this directly
|
|
// otherwise we get out of order printing which is no good
|
|
gb_file_write(f->output, f->vm.data, f->offset);
|
|
f->offset = 0;
|
|
|
|
gb_file_write(f->output, data, len);
|
|
return;
|
|
}
|
|
|
|
if ((f->vm.size - f->offset) < len) {
|
|
gb_file_write(f->output, f->vm.data, f->offset);
|
|
f->offset = 0;
|
|
}
|
|
u8 *cursor = cast(u8 *)f->vm.data + f->offset;
|
|
gb_memmove(cursor, data, len);
|
|
f->offset += len;
|
|
}
|
|
|
|
|
|
void ir_fprintf(irFileBuffer *f, char const *fmt, ...) {
|
|
va_list va;
|
|
va_start(va, fmt);
|
|
isize len = gb_snprintf_va(f->buf, IR_FILE_BUFFER_BUF_LEN-1, fmt, va);
|
|
ir_file_buffer_write(f, f->buf, len-1);
|
|
va_end(va);
|
|
}
|
|
void ir_write_string(irFileBuffer *f, String s) {
|
|
ir_file_buffer_write(f, s.text, s.len);
|
|
}
|
|
|
|
#if 1
|
|
#define ir_write_str_lit(f, s) ir_write_string((f), str_lit(s))
|
|
#else
|
|
void ir_write_str_lit(irFileBuffer *f, char const *s) {
|
|
isize len = gb_strlen(s);
|
|
ir_file_buffer_write(f, s, len);
|
|
}
|
|
#endif
|
|
void ir_write_byte(irFileBuffer *f, u8 c) {
|
|
ir_file_buffer_write(f, &c, 1);
|
|
}
|
|
void ir_write_escaped_byte(irFileBuffer *f, u8 c) {
|
|
static char const hex_table[] = "0123456789ABCDEF";
|
|
char buf[3];
|
|
buf[0] = '\\';
|
|
buf[1] = hex_table[c >> 4];
|
|
buf[2] = hex_table[c & 0x0f];
|
|
ir_file_buffer_write(f, buf, 3);
|
|
}
|
|
|
|
void ir_write_i64(irFileBuffer *f, i64 i) {
|
|
String str = i64_to_string(i, f->buf, IR_FILE_BUFFER_BUF_LEN-1);
|
|
ir_write_string(f, str);
|
|
}
|
|
void ir_write_u64(irFileBuffer *f, u64 i) {
|
|
String str = u64_to_string(i, f->buf, IR_FILE_BUFFER_BUF_LEN-1);
|
|
ir_write_string(f, str);
|
|
}
|
|
void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_endian) {
|
|
if (x.len == 2) {
|
|
u64 words[2] = {};
|
|
BigInt y = x;
|
|
if (swap_endian) {
|
|
gb_memmove(words, y.d.words, 16);
|
|
u8 *bytes = cast(u8 *)words;
|
|
for (isize i = 0; i < 8; i++) {
|
|
bytes[i] = bytes[16-i]; // byte swap
|
|
}
|
|
y.d.words = words;
|
|
}
|
|
|
|
String s = big_int_to_string(temporary_allocator(), &y, 10);
|
|
ir_write_string(f, s);
|
|
} else {
|
|
i64 i = 0;
|
|
if (x.neg) {
|
|
i = big_int_to_i64(&x);
|
|
} else {
|
|
i = cast(i64)big_int_to_u64(&x);
|
|
}
|
|
if (swap_endian) {
|
|
i64 size = type_size_of(type);
|
|
switch (size) {
|
|
case 2: i = cast(i64)cast(i16)gb_endian_swap16(cast(u16)cast(i16)i); break;
|
|
case 4: i = cast(i64)cast(i32)gb_endian_swap32(cast(u32)cast(i32)i); break;
|
|
case 8: i = cast(i64)gb_endian_swap64(cast(u64)i); break;
|
|
}
|
|
}
|
|
ir_write_i64(f, i);
|
|
}
|
|
}
|
|
|
|
void ir_file_write(irFileBuffer *f, void *data, isize len) {
|
|
ir_file_buffer_write(f, data, len);
|
|
}
|
|
|
|
|
|
bool ir_valid_char(u8 c) {
|
|
if (c >= 0x80) {
|
|
return false;
|
|
}
|
|
|
|
if (gb_char_is_alphanumeric(c)) {
|
|
return true;
|
|
}
|
|
|
|
switch (c) {
|
|
case '$':
|
|
case '-':
|
|
case '.':
|
|
case '_':
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ir_print_escape_string(irFileBuffer *f, String name, bool print_quotes, bool prefix_with_dot) {
|
|
isize extra = 0;
|
|
for (isize i = 0; i < name.len; i++) {
|
|
u8 c = name[i];
|
|
if (!ir_valid_char(c)) {
|
|
extra += 2;
|
|
}
|
|
}
|
|
|
|
if (extra == 0) {
|
|
ir_write_string(f, name);
|
|
return;
|
|
}
|
|
|
|
if (print_quotes) {
|
|
ir_write_byte(f, '"');
|
|
}
|
|
|
|
if (prefix_with_dot) {
|
|
ir_write_byte(f, '.');
|
|
}
|
|
|
|
for (isize i = 0; i < name.len; i++) {
|
|
u8 c = name[i];
|
|
if (ir_valid_char(c)) {
|
|
ir_write_byte(f, c);
|
|
} else {
|
|
ir_write_escaped_byte(f, c);
|
|
}
|
|
}
|
|
|
|
if (print_quotes) {
|
|
ir_write_byte(f, '"');
|
|
}
|
|
}
|
|
|
|
|
|
void ir_print_escape_path(irFileBuffer *f, String path) {
|
|
isize extra = 0;
|
|
for (isize i = 0; i < path.len; i++) {
|
|
u8 c = path[i];
|
|
if (!ir_valid_char(c)) {
|
|
extra += 2;
|
|
}
|
|
}
|
|
|
|
if (extra == 0) {
|
|
ir_write_string(f, path);
|
|
return;
|
|
}
|
|
|
|
for (isize i = 0; i < path.len; i++) {
|
|
u8 c = path[i];
|
|
if (ir_valid_char(c) || c == ':') {
|
|
ir_write_byte(f, c);
|
|
} else if (c == '\\') {
|
|
ir_write_byte(f, '/');
|
|
} else {
|
|
ir_write_escaped_byte(f, c);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ir_print_encoded_local(irFileBuffer *f, String name) {
|
|
ir_write_byte(f, '%');
|
|
ir_print_escape_string(f, name, true, false);
|
|
}
|
|
|
|
void ir_print_encoded_global(irFileBuffer *f, String name, bool remove_prefix) {
|
|
ir_write_byte(f, '@');
|
|
ir_print_escape_string(f, name, true, !remove_prefix);
|
|
}
|
|
|
|
|
|
bool ir_print_debug_location(irFileBuffer *f, irModule *m, irValue *v) {
|
|
if (!m->generate_debug_info) {
|
|
return false;
|
|
}
|
|
|
|
GB_ASSERT_NOT_NULL(v);
|
|
GB_ASSERT(v->kind == irValue_Instr);
|
|
|
|
if (v->loc != nullptr) {
|
|
if (v->loc->kind == irDebugInfo_Location) {
|
|
ir_fprintf(f, ", !dbg !%d", v->loc->id);
|
|
return true;
|
|
}
|
|
} else {
|
|
irProcedure *proc = v->Instr.block->proc;
|
|
if (proc->entity != nullptr) {
|
|
// GB_ASSERT(proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct = false);
|
|
void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hint);
|
|
|
|
|
|
void ir_print_alignment_prefix_hack(irFileBuffer *f, i64 alignment) {
|
|
// NOTE(bill): This is written like this as it may need to
|
|
// changed for specific alignments
|
|
switch (alignment) {
|
|
case 1:
|
|
ir_write_string(f, str_lit("[0 x i8]"));
|
|
break;
|
|
case 2:
|
|
ir_write_string(f, str_lit("[0 x i16]"));
|
|
break;
|
|
case 4:
|
|
ir_write_string(f, str_lit("[0 x i32]"));
|
|
break;
|
|
case 8:
|
|
ir_write_string(f, str_lit("[0 x i64]"));
|
|
break;
|
|
case 16:
|
|
ir_write_string(f, str_lit("[0 x <4 x i32>]"));
|
|
break;
|
|
default:
|
|
GB_PANIC("Invalid alignment");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
|
|
set_procedure_abi_types(t);
|
|
|
|
GB_ASSERT(is_type_proc(t));
|
|
t = base_type(t);
|
|
isize result_count = t->Proc.result_count;
|
|
if (result_count == 0 || t->Proc.return_by_pointer) {
|
|
ir_write_string(f, str_lit("void"));
|
|
} else {
|
|
Type *rt = t->Proc.abi_compat_result_type;
|
|
if (!is_type_tuple(rt)) {
|
|
ir_print_type(f, m, rt);
|
|
} else if (rt->Tuple.variables.count == 1) {
|
|
ir_print_type(f, m, rt->Tuple.variables[0]->type);
|
|
} else {
|
|
ir_write_byte(f, '{');
|
|
for_array(i, rt->Tuple.variables) {
|
|
Entity *e = rt->Tuple.variables[i];
|
|
if (i > 0) {
|
|
ir_write_string(f, str_lit(", "));
|
|
}
|
|
ir_print_type(f, m, e->type);
|
|
}
|
|
ir_write_byte(f, '}');
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) {
|
|
set_procedure_abi_types(t);
|
|
|
|
i64 word_bits = 8*build_context.word_size;
|
|
t = base_type(t);
|
|
GB_ASSERT(is_type_proc(t));
|
|
|
|
isize param_count = t->Proc.param_count;
|
|
isize result_count = t->Proc.result_count;
|
|
|
|
ir_print_proc_results(f, m, t);
|
|
ir_write_string(f, str_lit(" ("));
|
|
if (t->Proc.return_by_pointer) {
|
|
ir_print_type(f, m, reduce_tuple_to_single_type(t->Proc.results));
|
|
// ir_fprintf(f, "* sret noalias ");
|
|
// ir_write_string(f, str_lit("* noalias "));
|
|
ir_write_string(f, str_lit("*"));
|
|
if (build_context.ODIN_OS == "darwin") {
|
|
ir_fprintf(f, " byval");
|
|
}
|
|
if (param_count > 0 || t->Proc.calling_convention == ProcCC_Odin) {
|
|
ir_write_string(f, str_lit(", "));
|
|
}
|
|
}
|
|
isize param_index = 0;
|
|
for (isize i = 0; i < param_count; i++) {
|
|
Entity *e = t->Proc.params->Tuple.variables[i];
|
|
if (e->kind != Entity_Variable) continue;
|
|
if (param_index > 0) ir_write_string(f, str_lit(", "));
|
|
|
|
if (i+1 == param_count && t->Proc.c_vararg) {
|
|
ir_write_string(f, str_lit("..."));
|
|
} else {
|
|
Type *et = t->Proc.abi_compat_params[i];
|
|
if (is_type_tuple(et)) {
|
|
for_array(j, et->Tuple.variables) {
|
|
if (j > 0) ir_write_str_lit(f, ", ");
|
|
|
|
ir_print_type(f, m, et->Tuple.variables[j]->type);
|
|
if (e->flags&EntityFlag_NoAlias) {
|
|
ir_write_str_lit(f, " noalias");
|
|
}
|
|
if (e->flags&EntityFlag_ByVal) {
|
|
ir_write_str_lit(f, " byval");
|
|
}
|
|
ir_write_byte(f, ' ');
|
|
param_index++;
|
|
}
|
|
continue;
|
|
} else {
|
|
ir_print_type(f, m, et);
|
|
}
|
|
}
|
|
|
|
param_index++;
|
|
}
|
|
if (t->Proc.calling_convention == ProcCC_Odin) {
|
|
if (param_index > 0) ir_write_string(f, str_lit(", "));
|
|
ir_print_type(f, m, t_context_ptr);
|
|
}
|
|
ir_write_byte(f, ')');
|
|
}
|
|
|
|
void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
|
|
i64 word_bits = 8*build_context.word_size;
|
|
GB_ASSERT_NOT_NULL(t);
|
|
t = default_type(t);
|
|
GB_ASSERT(is_type_typed(t));
|
|
|
|
switch (t->kind) {
|
|
case Type_Basic:
|
|
switch (t->Basic.kind) {
|
|
case Basic_llvm_bool: ir_write_str_lit(f, "i1"); return;
|
|
case Basic_bool: ir_write_str_lit(f, "i8"); return;
|
|
case Basic_b8: ir_write_str_lit(f, "i8"); return;
|
|
case Basic_b16: ir_write_str_lit(f, "i16"); return;
|
|
case Basic_b32: ir_write_str_lit(f, "i32"); return;
|
|
case Basic_b64: ir_write_str_lit(f, "i64"); return;
|
|
|
|
case Basic_i8: ir_write_str_lit(f, "i8"); return;
|
|
case Basic_u8: ir_write_str_lit(f, "i8"); return;
|
|
case Basic_i16: ir_write_str_lit(f, "i16"); return;
|
|
case Basic_u16: ir_write_str_lit(f, "i16"); return;
|
|
case Basic_i32: ir_write_str_lit(f, "i32"); return;
|
|
case Basic_u32: ir_write_str_lit(f, "i32"); return;
|
|
case Basic_i64: ir_write_str_lit(f, "i64"); return;
|
|
case Basic_u64: ir_write_str_lit(f, "i64"); return;
|
|
case Basic_i128: ir_write_str_lit(f, "i128"); return;
|
|
case Basic_u128: ir_write_str_lit(f, "i128"); return;
|
|
|
|
case Basic_i16le: ir_write_str_lit(f, "i16"); return;
|
|
case Basic_u16le: ir_write_str_lit(f, "i16"); return;
|
|
case Basic_i32le: ir_write_str_lit(f, "i32"); return;
|
|
case Basic_u32le: ir_write_str_lit(f, "i32"); return;
|
|
case Basic_i64le: ir_write_str_lit(f, "i64"); return;
|
|
case Basic_u64le: ir_write_str_lit(f, "i64"); return;
|
|
case Basic_i128le: ir_write_str_lit(f, "i128"); return;
|
|
case Basic_u128le: ir_write_str_lit(f, "i128"); return;
|
|
|
|
case Basic_i16be: ir_write_str_lit(f, "i16"); return;
|
|
case Basic_u16be: ir_write_str_lit(f, "i16"); return;
|
|
case Basic_i32be: ir_write_str_lit(f, "i32"); return;
|
|
case Basic_u32be: ir_write_str_lit(f, "i32"); return;
|
|
case Basic_i64be: ir_write_str_lit(f, "i64"); return;
|
|
case Basic_u64be: ir_write_str_lit(f, "i64"); return;
|
|
case Basic_i128be: ir_write_str_lit(f, "i128"); return;
|
|
case Basic_u128be: ir_write_str_lit(f, "i128"); return;
|
|
|
|
case Basic_rune: ir_write_str_lit(f, "i32"); return;
|
|
|
|
case Basic_int:
|
|
case Basic_uint:
|
|
case Basic_uintptr:
|
|
switch (word_bits) {
|
|
case 32: ir_write_str_lit(f, "i32"); break;
|
|
case 64: ir_write_str_lit(f, "i64"); break;
|
|
default: GB_PANIC("Unhandled word size: %td bits", word_bits); break;
|
|
}
|
|
return;
|
|
|
|
case Basic_f16: ir_write_str_lit(f, "half"); return;
|
|
case Basic_f32: ir_write_str_lit(f, "float"); return;
|
|
case Basic_f64: ir_write_str_lit(f, "double"); return;
|
|
|
|
|
|
case Basic_f16le: ir_write_str_lit(f, "half"); return;
|
|
case Basic_f32le: ir_write_str_lit(f, "float"); return;
|
|
case Basic_f64le: ir_write_str_lit(f, "double"); return;
|
|
|
|
case Basic_f16be: ir_write_str_lit(f, "half"); return;
|
|
case Basic_f32be: ir_write_str_lit(f, "float"); return;
|
|
case Basic_f64be: ir_write_str_lit(f, "double"); return;
|
|
|
|
case Basic_complex32: ir_write_str_lit(f, "%%..complex32"); return;
|
|
case Basic_complex64: ir_write_str_lit(f, "%..complex64"); return;
|
|
case Basic_complex128: ir_write_str_lit(f, "%..complex128"); return;
|
|
|
|
case Basic_quaternion64: ir_write_str_lit(f, "%..quaternion64"); return;
|
|
case Basic_quaternion128: ir_write_str_lit(f, "%..quaternion128"); return;
|
|
case Basic_quaternion256: ir_write_str_lit(f, "%..quaternion256"); return;
|
|
|
|
case Basic_any: ir_write_str_lit(f, "%..any"); return;
|
|
case Basic_rawptr: ir_write_str_lit(f, "%..rawptr"); return;
|
|
case Basic_string: ir_write_str_lit(f, "%..string"); return;
|
|
case Basic_cstring: ir_write_str_lit(f, "i8*"); return;
|
|
|
|
case Basic_typeid: ir_write_str_lit(f, "%..typeid"); return;
|
|
}
|
|
break;
|
|
|
|
case Type_Pointer:
|
|
// TODO(bill, 2019-12-22): Why did I make add this edge case? Is this some weird LLVM issue?
|
|
// if (!is_type_named(t->Pointer.elem) && is_type_empty_struct(t->Pointer.elem)) {
|
|
// ir_print_type(f, m, t_rawptr);
|
|
// } else {
|
|
ir_print_type(f, m, t->Pointer.elem);
|
|
ir_write_byte(f, '*');
|
|
// }
|
|
return;
|
|
case Type_Array:
|
|
ir_write_byte(f, '[');
|
|
ir_write_i64(f, t->Array.count);
|
|
ir_write_str_lit(f, " x ");
|
|
ir_print_type(f, m, t->Array.elem);
|
|
ir_write_byte(f, ']');
|
|
return;
|
|
case Type_EnumeratedArray:
|
|
ir_write_byte(f, '[');
|
|
ir_write_i64(f, t->EnumeratedArray.count);
|
|
ir_write_str_lit(f, " x ");
|
|
ir_print_type(f, m, t->EnumeratedArray.elem);
|
|
ir_write_byte(f, ']');
|
|
return;
|
|
|
|
case Type_Slice:
|
|
ir_write_byte(f, '{');
|
|
ir_print_type(f, m, t->Slice.elem); ir_write_str_lit(f, "*, ");
|
|
ir_print_type(f, m, t_int);
|
|
ir_write_byte(f, '}');
|
|
return;
|
|
case Type_DynamicArray:
|
|
ir_write_byte(f, '{');
|
|
ir_print_type(f, m, t->DynamicArray.elem); ir_write_str_lit(f, "*, ");
|
|
ir_print_type(f, m, t_int); ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, t_int); ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, t_allocator);
|
|
ir_write_byte(f, '}');
|
|
return;
|
|
|
|
case Type_Enum:
|
|
ir_print_type(f, m, base_enum_type(t));
|
|
return;
|
|
|
|
case Type_Union:
|
|
if (t->Union.variants.count == 0) {
|
|
ir_print_encoded_local(f, str_lit("..opaque"));
|
|
} else {
|
|
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
|
|
// LLVM takes the first element's alignment as the entire alignment (like C)
|
|
i64 align = type_align_of(t);
|
|
|
|
if (is_type_union_maybe_pointer_original_alignment(t)) {
|
|
ir_write_byte(f, '{');
|
|
ir_print_type(f, m, t->Union.variants[0]);
|
|
ir_write_byte(f, '}');
|
|
return;
|
|
}
|
|
|
|
i64 block_size = t->Union.variant_block_size;
|
|
|
|
ir_write_byte(f, '{');
|
|
ir_print_alignment_prefix_hack(f, align);
|
|
if (is_type_union_maybe_pointer(t)) {
|
|
ir_fprintf(f, ", ");
|
|
ir_print_type(f, m, t->Union.variants[0]);
|
|
} else {
|
|
ir_fprintf(f, ", [%lld x i8], ", block_size);
|
|
ir_print_type(f, m, union_tag_type(t));
|
|
}
|
|
ir_write_byte(f, '}');
|
|
}
|
|
return;
|
|
|
|
case Type_Struct: {
|
|
if (t->Struct.is_raw_union) {
|
|
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
|
|
// LLVM takes the first element's alignment as the entire alignment (like C)
|
|
i64 size_of_union = type_size_of(t);
|
|
i64 align_of_union = type_align_of(t);
|
|
ir_write_byte(f, '{');
|
|
ir_print_alignment_prefix_hack(f, align_of_union);
|
|
ir_fprintf(f, ", [%lld x i8]}", size_of_union);
|
|
return;
|
|
} else {
|
|
if (t->Struct.is_packed) {
|
|
ir_write_byte(f, '<');
|
|
}
|
|
ir_write_byte(f, '{');
|
|
if (t->Struct.custom_align > 0) {
|
|
ir_print_alignment_prefix_hack(f, t->Struct.custom_align);
|
|
if (t->Struct.fields.count > 0) {
|
|
ir_write_string(f, str_lit(", "));
|
|
}
|
|
}
|
|
for_array(i, t->Struct.fields) {
|
|
if (i > 0) {
|
|
ir_write_string(f, str_lit(", "));
|
|
}
|
|
ir_print_type(f, m, t->Struct.fields[i]->type, true);
|
|
}
|
|
ir_write_byte(f, '}');
|
|
if (t->Struct.is_packed) {
|
|
ir_write_byte(f, '>');
|
|
}
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Type_Named: {
|
|
Type *bt = base_type(t);
|
|
switch (bt->kind) {
|
|
case Type_Struct:
|
|
case Type_Union: {
|
|
Entity *e = t->Named.type_name;
|
|
GB_ASSERT(e != nullptr);
|
|
GB_ASSERT(e->kind == Entity_TypeName);
|
|
|
|
String name = e->TypeName.ir_mangled_name;
|
|
if (name.len > 0) {
|
|
ir_print_encoded_local(f, name);
|
|
} else {
|
|
// TODO(bill): Is this correct behaviour?!
|
|
// GB_ASSERT_MSG(name.len > 0, "%.*s %p", LIT(t->Named.name), e);
|
|
// gb_printf_err("%.*s %p\n", LIT(t->Named.name), t->Named.type_name);
|
|
ir_print_type(f, m, bt);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
ir_print_type(f, m, bt);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
case Type_Tuple:
|
|
if (t->Tuple.variables.count == 1) {
|
|
ir_print_type(f, m, t->Tuple.variables[0]->type);
|
|
} else {
|
|
ir_write_byte(f, '{');
|
|
isize index = 0;
|
|
for_array(i, t->Tuple.variables) {
|
|
if (i > 0) ir_write_string(f, str_lit(", "));
|
|
Entity *e = t->Tuple.variables[i];
|
|
if (e->kind == Entity_Variable) {
|
|
ir_print_type(f, m, e->type);
|
|
index++;
|
|
}
|
|
}
|
|
ir_write_byte(f, '}');
|
|
}
|
|
return;
|
|
|
|
case Type_Proc:
|
|
ir_print_proc_type_without_pointer(f, m, t);
|
|
ir_write_byte(f, '*');
|
|
return;
|
|
|
|
case Type_Map:
|
|
init_map_internal_types(t);
|
|
GB_ASSERT(t->Map.internal_type != nullptr);
|
|
ir_print_type(f, m, t->Map.internal_type);
|
|
break;
|
|
|
|
case Type_BitSet: {
|
|
i64 size = type_size_of(t);
|
|
if (size == 0) {
|
|
ir_write_str_lit(f, "{}");
|
|
return;
|
|
}
|
|
ir_print_type(f, m, bit_set_to_int(t));
|
|
return;
|
|
}
|
|
|
|
case Type_SimdVector:
|
|
if (t->SimdVector.is_x86_mmx) {
|
|
ir_write_str_lit(f, "x86_mmx");
|
|
} else {
|
|
ir_fprintf(f, "<%lld x ", t->SimdVector.count);;
|
|
ir_print_type(f, m, t->SimdVector.elem);
|
|
ir_write_byte(f, '>');
|
|
}
|
|
return;
|
|
|
|
case Type_RelativePointer:
|
|
ir_print_type(f, m, t->RelativePointer.base_integer);
|
|
return;
|
|
|
|
case Type_RelativeSlice:
|
|
ir_write_byte(f, '{');
|
|
ir_print_type(f, m, t->RelativePointer.base_integer);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, t->RelativePointer.base_integer);
|
|
ir_write_byte(f, '}');
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ir_print_debug_encoding(irFileBuffer *f, irDebugInfoKind kind, irDebugEncoding encoding) {
|
|
switch (kind) {
|
|
case irDebugInfo_BasicType:
|
|
switch (encoding) {
|
|
case irDebugBasicEncoding_address: ir_write_str_lit(f, "DW_ATE_address"); return;
|
|
case irDebugBasicEncoding_boolean: ir_write_str_lit(f, "DW_ATE_boolean"); return;
|
|
case irDebugBasicEncoding_float: ir_write_str_lit(f, "DW_ATE_float"); return;
|
|
case irDebugBasicEncoding_signed: ir_write_str_lit(f, "DW_ATE_signed"); return;
|
|
case irDebugBasicEncoding_signed_char: ir_write_str_lit(f, "DW_ATE_signed_char"); return;
|
|
case irDebugBasicEncoding_unsigned: ir_write_str_lit(f, "DW_ATE_unsigned"); return;
|
|
case irDebugBasicEncoding_unsigned_char: ir_write_str_lit(f, "DW_ATE_unsigned_char"); return;
|
|
}
|
|
case irDebugInfo_DerivedType:
|
|
switch (encoding) {
|
|
case irDebugBasicEncoding_member: ir_write_str_lit(f, "DW_TAG_member"); return;
|
|
case irDebugBasicEncoding_pointer_type: ir_write_str_lit(f, "DW_TAG_pointer_type"); return;
|
|
case irDebugBasicEncoding_typedef: ir_write_str_lit(f, "DW_TAG_typedef"); return;
|
|
}
|
|
case irDebugInfo_CompositeType:
|
|
switch (encoding) {
|
|
case irDebugBasicEncoding_array_type: ir_write_str_lit(f, "DW_TAG_array_type"); return;
|
|
case irDebugBasicEncoding_enumeration_type: ir_write_str_lit(f, "DW_TAG_enumeration_type"); return;
|
|
case irDebugBasicEncoding_structure_type: ir_write_str_lit(f, "DW_TAG_structure_type"); return;
|
|
case irDebugBasicEncoding_union_type: ir_write_str_lit(f, "DW_TAG_union_type"); return;
|
|
}
|
|
}
|
|
|
|
GB_PANIC("Unreachable");
|
|
}
|
|
|
|
void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *type);
|
|
|
|
void ir_print_compound_element(irFileBuffer *f, irModule *m, ExactValue v, Type *elem_type) {
|
|
ir_print_type(f, m, elem_type);
|
|
ir_write_byte(f, ' ');
|
|
|
|
if (v.kind == ExactValue_Invalid || !elem_type_can_be_constant(elem_type)) {
|
|
ir_fprintf(f, "zeroinitializer");
|
|
} else {
|
|
ir_print_exact_value(f, m, v, elem_type);
|
|
}
|
|
}
|
|
|
|
void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *type) {
|
|
Type *original_type = type;
|
|
type = core_type(type);
|
|
value = convert_exact_value_for_type(value, type);
|
|
|
|
// NOTE(bill): Is this correct? Does this handle all cases regarding arrays?
|
|
if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
|
|
i64 count = type->Array.count;
|
|
Type *elem = type->Array.elem;
|
|
|
|
if (is_type_rune_array(type)) {
|
|
Rune rune;
|
|
isize offset = 0;
|
|
isize width = 1;
|
|
String s = value.value_string;
|
|
ir_write_byte(f, '[');
|
|
for (i64 i = 0; i < count && offset < s.len; i++) {
|
|
width = gb_utf8_decode(s.text+offset, s.len-offset, &rune);
|
|
if (i > 0) ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, elem);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_exact_value(f, m, exact_value_i64(rune), elem);
|
|
offset += width;
|
|
}
|
|
GB_ASSERT(offset == s.len);
|
|
|
|
ir_write_byte(f, ']');
|
|
return;
|
|
}
|
|
|
|
|
|
ir_write_byte(f, '[');
|
|
|
|
for (i64 i = 0; i < count; i++) {
|
|
if (i > 0) ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, elem);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_exact_value(f, m, value, elem);
|
|
}
|
|
|
|
ir_write_byte(f, ']');
|
|
return;
|
|
} 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;
|
|
ir_write_byte(f, '[');
|
|
|
|
for (i64 i = 0; i < count; i++) {
|
|
if (i > 0) ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, elem);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_exact_value(f, m, value, elem);
|
|
}
|
|
|
|
ir_write_byte(f, ']');
|
|
return;
|
|
}
|
|
|
|
switch (value.kind) {
|
|
case ExactValue_Typeid:
|
|
GB_ASSERT(is_type_typeid(type));
|
|
ir_write_u64(f, ir_typeid_as_integer(m, value.value_typeid));
|
|
break;
|
|
|
|
case ExactValue_Bool:
|
|
if (value.value_bool) {
|
|
ir_write_string(f, are_types_identical(type, t_llvm_bool) ? str_lit("true") : str_lit("1"));
|
|
} else {
|
|
ir_write_string(f, are_types_identical(type, t_llvm_bool) ? str_lit("false") : str_lit("0"));
|
|
}
|
|
break;
|
|
case ExactValue_String: {
|
|
String str = value.value_string;
|
|
Type *t = core_type(type);
|
|
if (str.len == 0 && !is_type_cstring(t)) {
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
break;
|
|
}
|
|
if (is_type_u8_slice(type)) {
|
|
irValue *str_array = ir_add_global_string_array(m, str);
|
|
ir_write_str_lit(f, "{i8* getelementptr inbounds (");
|
|
ir_print_type(f, m, str_array->Global.entity->type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, str_array->Global.entity->type);
|
|
ir_write_str_lit(f, "* ");
|
|
ir_print_encoded_global(f, str_array->Global.entity->token.string, false);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, t_i32);
|
|
ir_write_str_lit(f, " 0, i32 0), ");
|
|
ir_print_type(f, m, t_int);
|
|
ir_fprintf(f, " %lld}", cast(i64)str.len);
|
|
} else if (!is_type_string(type)) {
|
|
GB_ASSERT(is_type_array(type));
|
|
ir_write_str_lit(f, "c\"");
|
|
ir_print_escape_string(f, str, false, false);
|
|
if (type->Array.count == str.len) {
|
|
ir_write_str_lit(f, "\"");
|
|
} else {
|
|
ir_write_str_lit(f, "\\00\"");
|
|
}
|
|
} else if (is_type_cstring(t)) {
|
|
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
|
|
// of the .ll file
|
|
irValue *str_array = ir_add_global_string_array(m, str);
|
|
ir_write_str_lit(f, "getelementptr inbounds (");
|
|
ir_print_type(f, m, str_array->Global.entity->type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, str_array->Global.entity->type);
|
|
ir_write_str_lit(f, "* ");
|
|
ir_print_encoded_global(f, str_array->Global.entity->token.string, false);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, t_i32);
|
|
ir_write_str_lit(f, " 0, i32 0)");
|
|
} else {
|
|
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
|
|
// of the .ll file
|
|
irValue *str_array = ir_add_global_string_array(m, str);
|
|
ir_write_str_lit(f, "{i8* getelementptr inbounds (");
|
|
ir_print_type(f, m, str_array->Global.entity->type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, str_array->Global.entity->type);
|
|
ir_write_str_lit(f, "* ");
|
|
ir_print_encoded_global(f, str_array->Global.entity->token.string, false);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, t_i32);
|
|
ir_write_str_lit(f, " 0, i32 0), ");
|
|
ir_print_type(f, m, t_int);
|
|
ir_fprintf(f, " %lld}", cast(i64)str.len);
|
|
}
|
|
break;
|
|
}
|
|
case ExactValue_Integer: {
|
|
if (is_type_pointer(type)) {
|
|
if (big_int_is_zero(&value.value_integer)) {
|
|
ir_write_str_lit(f, "null");
|
|
} else {
|
|
ir_write_str_lit(f, "inttoptr (");
|
|
ir_print_type(f, m, t_int);
|
|
ir_write_byte(f, ' ');
|
|
ir_write_big_int(f, value.value_integer, type, is_type_different_to_arch_endianness(type));
|
|
ir_write_str_lit(f, " to ");
|
|
ir_print_type(f, m, t_rawptr);
|
|
ir_write_str_lit(f, ")");
|
|
}
|
|
} else {
|
|
ir_write_big_int(f, value.value_integer, type, is_type_different_to_arch_endianness(type));
|
|
}
|
|
break;
|
|
}
|
|
case ExactValue_Float: {
|
|
GB_ASSERT_MSG(is_type_float(type), "%s", type_to_string(type));
|
|
type = core_type(type);
|
|
u64 u_64 = bit_cast<u64>(value.value_float);
|
|
u32 u_32 = bit_cast<u32>(cast(f32)value.value_float);
|
|
|
|
|
|
#if 0
|
|
switch (type->Basic.kind) {
|
|
case Basic_f32:
|
|
// IMPORTANT NOTE(bill): LLVM requires all floating point constants to be
|
|
// a 64 bit number if bits_of(float type) <= 64.
|
|
// https://groups.google.com/forum/#!topic/llvm-dev/IlqV3TbSk6M
|
|
// 64 bit mantissa: 52 bits ==> 52-52 == 0
|
|
// 32 bit mantissa: 23 bits ==> 52-23 == 29
|
|
// 16 bit mantissa: 10 bits ==> 52=10 == 42
|
|
// 29 == 52-23
|
|
u >>= 29;
|
|
u <<= 29;
|
|
break;
|
|
// case Basic_f16:
|
|
// u >>= 42;
|
|
// u <<= 42;
|
|
// break;
|
|
}
|
|
|
|
switch (type->Basic.kind) {
|
|
case 0: break;
|
|
default:
|
|
ir_fprintf(f, "0x%016llx", u);
|
|
break;
|
|
}
|
|
#else
|
|
switch (type->Basic.kind) {
|
|
case Basic_f16:
|
|
ir_fprintf(f, "fptrunc (float bitcast (i32 %u to float) to half)", u_32);
|
|
break;
|
|
case Basic_f16le:
|
|
if (build_context.endian_kind != TargetEndian_Little) {
|
|
u_32 = gb_endian_swap32(u_32);
|
|
}
|
|
ir_fprintf(f, "fptrunc (float bitcast (i32 %u to float) to half)", u_32);
|
|
break;
|
|
case Basic_f16be:
|
|
if (build_context.endian_kind != TargetEndian_Big) {
|
|
u_32 = gb_endian_swap32(u_32);
|
|
}
|
|
ir_fprintf(f, "fptrunc (float bitcast (i32 %u to float) to half)", u_32);
|
|
break;
|
|
|
|
|
|
case Basic_f32:
|
|
ir_fprintf(f, "bitcast (i32 %u to float)", u_32);
|
|
break;
|
|
case Basic_f32le:
|
|
if (build_context.endian_kind != TargetEndian_Little) {
|
|
u_32 = gb_endian_swap32(u_32);
|
|
}
|
|
ir_fprintf(f, "bitcast (i32 %u to float)", u_32);
|
|
break;
|
|
case Basic_f32be:
|
|
if (build_context.endian_kind != TargetEndian_Big) {
|
|
u_32 = gb_endian_swap32(u_32);
|
|
}
|
|
ir_fprintf(f, "bitcast (i32 %u to float)", u_32);
|
|
break;
|
|
|
|
case Basic_f64:
|
|
ir_fprintf(f, "0x%016llx", u_64);
|
|
break;
|
|
case Basic_f64le:
|
|
if (build_context.endian_kind != TargetEndian_Little) {
|
|
u_64 = gb_endian_swap64(u_64);
|
|
}
|
|
ir_fprintf(f, "0x%016llx", u_64);
|
|
break;
|
|
case Basic_f64be:
|
|
if (build_context.endian_kind != TargetEndian_Big) {
|
|
u_64 = gb_endian_swap64(u_64);
|
|
}
|
|
ir_fprintf(f, "0x%016llx", u_64);
|
|
break;
|
|
|
|
default:
|
|
ir_fprintf(f, "0x%016llx", u_64);
|
|
break;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
case ExactValue_Complex: {
|
|
// xy/ri format
|
|
type = core_type(type);
|
|
GB_ASSERT_MSG(is_type_complex(type), "%s", type_to_string(type));
|
|
Type *ft = base_complex_elem_type(type);
|
|
ir_write_byte(f, ' ');
|
|
ir_write_byte(f, '{');
|
|
ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
|
ir_print_exact_value(f, m, exact_value_float(value.value_complex->real), ft);
|
|
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
|
ir_print_exact_value(f, m, exact_value_float(value.value_complex->imag), ft);
|
|
ir_write_byte(f, '}');
|
|
break;
|
|
}
|
|
|
|
case ExactValue_Quaternion: {
|
|
// xyzw/ijkr format
|
|
type = core_type(type);
|
|
GB_ASSERT_MSG(is_type_quaternion(type), "%s", type_to_string(type));
|
|
Type *ft = base_complex_elem_type(type);
|
|
ir_write_byte(f, ' ');
|
|
ir_write_byte(f, '{');
|
|
ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
|
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->imag), ft);
|
|
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
|
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->jmag), ft);
|
|
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
|
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->kmag), ft);
|
|
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
|
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->real), ft);
|
|
ir_write_byte(f, '}');
|
|
break;
|
|
}
|
|
|
|
case ExactValue_Pointer:
|
|
if (value.value_pointer == 0) {
|
|
if (is_type_typeid(type)) {
|
|
ir_write_str_lit(f, "0");
|
|
} else {
|
|
ir_write_str_lit(f, "null");
|
|
}
|
|
} else {
|
|
ir_write_str_lit(f, "inttoptr (");
|
|
ir_print_type(f, m, t_int);
|
|
ir_fprintf(f, " %llu to ", cast(u64)cast(uintptr)value.value_pointer);
|
|
ir_print_type(f, m, t_rawptr);
|
|
ir_write_byte(f, ')');
|
|
}
|
|
break;
|
|
case ExactValue_Compound: {
|
|
type = base_type(type);
|
|
if (is_type_slice(type)) {
|
|
irValue *s = ir_add_module_constant(m, type, value);
|
|
ir_print_value(f, m, s, type);
|
|
} 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) {
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
break;
|
|
}
|
|
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
|
|
ir_write_byte(f, '[');
|
|
for (i64 i = 0; i < type->Array.count; i++) {
|
|
if (i > 0) ir_write_str_lit(f, ", ");
|
|
|
|
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_Ellipsis) {
|
|
hi += 1;
|
|
}
|
|
if (lo == i) {
|
|
TypeAndValue tav = fv->value->tav;
|
|
if (tav.mode != Addressing_Constant) {
|
|
break;
|
|
}
|
|
for (i64 k = lo; k < hi; k++) {
|
|
if (k > lo) ir_write_str_lit(f, ", ");
|
|
|
|
ir_print_compound_element(f, m, tav.value, elem_type);
|
|
}
|
|
|
|
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;
|
|
if (tav.mode != Addressing_Constant) {
|
|
break;
|
|
}
|
|
ir_print_compound_element(f, m, tav.value, elem_type);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
ir_print_type(f, m, elem_type);
|
|
ir_write_byte(f, ' ');
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
}
|
|
}
|
|
ir_write_byte(f, ']');
|
|
} else {
|
|
GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count);
|
|
|
|
ir_write_byte(f, '[');
|
|
|
|
for (isize i = 0; i < elem_count; i++) {
|
|
if (i > 0) ir_write_str_lit(f, ", ");
|
|
TypeAndValue tav = cl->elems[i]->tav;
|
|
GB_ASSERT(tav.mode != Addressing_Invalid);
|
|
ir_print_compound_element(f, m, tav.value, elem_type);
|
|
}
|
|
for (isize i = elem_count; i < type->Array.count; i++) {
|
|
if (i >= elem_count) ir_write_str_lit(f, ", ");
|
|
ir_print_compound_element(f, m, empty_exact_value, elem_type);
|
|
}
|
|
|
|
ir_write_byte(f, ']');
|
|
}
|
|
} 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) {
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
break;
|
|
}
|
|
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
|
|
ir_write_byte(f, '[');
|
|
|
|
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++) {
|
|
if (i > total_lo) ir_write_str_lit(f, ", ");
|
|
|
|
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_Ellipsis) {
|
|
hi += 1;
|
|
}
|
|
if (lo == i) {
|
|
TypeAndValue tav = fv->value->tav;
|
|
if (tav.mode != Addressing_Constant) {
|
|
break;
|
|
}
|
|
for (i64 k = lo; k < hi; k++) {
|
|
if (k > lo) ir_write_str_lit(f, ", ");
|
|
|
|
ir_print_compound_element(f, m, tav.value, elem_type);
|
|
}
|
|
|
|
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;
|
|
if (tav.mode != Addressing_Constant) {
|
|
break;
|
|
}
|
|
ir_print_compound_element(f, m, tav.value, elem_type);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
ir_print_type(f, m, elem_type);
|
|
ir_write_byte(f, ' ');
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
}
|
|
}
|
|
ir_write_byte(f, ']');
|
|
} else {
|
|
GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count);
|
|
|
|
ir_write_byte(f, '[');
|
|
|
|
for (isize i = 0; i < elem_count; i++) {
|
|
if (i > 0) ir_write_str_lit(f, ", ");
|
|
TypeAndValue tav = cl->elems[i]->tav;
|
|
GB_ASSERT(tav.mode != Addressing_Invalid);
|
|
ir_print_compound_element(f, m, tav.value, elem_type);
|
|
}
|
|
for (isize i = elem_count; i < type->EnumeratedArray.count; i++) {
|
|
if (i >= elem_count) ir_write_str_lit(f, ", ");
|
|
ir_print_compound_element(f, m, empty_exact_value, elem_type);
|
|
}
|
|
|
|
ir_write_byte(f, ']');
|
|
}
|
|
} 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) {
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
break;
|
|
}
|
|
GB_ASSERT_MSG(elem_count == type->SimdVector.count, "%td != %td", elem_count, type->SimdVector.count);
|
|
|
|
ir_write_byte(f, '<');
|
|
|
|
for (isize i = 0; i < elem_count; i++) {
|
|
if (i > 0) ir_write_str_lit(f, ", ");
|
|
TypeAndValue tav = cl->elems[i]->tav;
|
|
GB_ASSERT(tav.mode != Addressing_Invalid);
|
|
ir_print_compound_element(f, m, tav.value, elem_type);
|
|
}
|
|
for (isize i = elem_count; i < type->SimdVector.count; i++) {
|
|
if (i >= elem_count) ir_write_str_lit(f, ", ");
|
|
ir_print_compound_element(f, m, empty_exact_value, elem_type);
|
|
}
|
|
|
|
ir_write_byte(f, '>');
|
|
} else if (is_type_struct(type)) {
|
|
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
|
|
defer (gb_temp_arena_memory_end(tmp));
|
|
|
|
ast_node(cl, CompoundLit, value.value_compound);
|
|
|
|
if (cl->elems.count == 0) {
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
break;
|
|
}
|
|
|
|
String tstr = make_string_c(type_to_string(original_type));
|
|
|
|
isize value_count = type->Struct.fields.count;
|
|
ExactValue *values = gb_alloc_array(m->tmp_allocator, ExactValue, value_count);
|
|
bool *visited = gb_alloc_array(m->tmp_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]];
|
|
|
|
values[f->Variable.field_src_index] = tav.value;
|
|
visited[f->Variable.field_src_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;
|
|
}
|
|
values[f->Variable.field_src_index] = val;
|
|
visited[f->Variable.field_src_index] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type->Struct.is_packed) ir_write_byte(f, '<');
|
|
ir_write_byte(f, '{');
|
|
if (type->Struct.custom_align > 0) {
|
|
ir_print_alignment_prefix_hack(f, cast(i64)type->Struct.custom_align);
|
|
ir_write_str_lit(f, " zeroinitializer");
|
|
if (value_count > 0) {
|
|
ir_write_string(f, str_lit(", "));
|
|
}
|
|
}
|
|
|
|
|
|
for (isize i = 0; i < value_count; i++) {
|
|
if (i > 0) ir_write_string(f, str_lit(", "));
|
|
Entity *e = type->Struct.fields[i];
|
|
GB_ASSERT(e->kind == Entity_Variable);
|
|
ir_print_compound_element(f, m, values[e->Variable.field_src_index], e->type);
|
|
}
|
|
|
|
|
|
ir_write_byte(f, '}');
|
|
if (type->Struct.is_packed) ir_write_byte(f, '>');
|
|
} else if (is_type_bit_set(type)) {
|
|
ast_node(cl, CompoundLit, value.value_compound);
|
|
if (cl->elems.count == 0) {
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
break;
|
|
}
|
|
|
|
i64 sz = type_size_of(type);
|
|
if (sz == 0) {
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
break;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
ir_write_u64(f, bits);
|
|
} else {
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ExactValue_Procedure: {
|
|
irValue **found = nullptr;
|
|
Ast *expr = unparen_expr(value.value_procedure);
|
|
GB_ASSERT(expr != nullptr);
|
|
|
|
if (expr->kind == Ast_ProcLit) {
|
|
found = map_get(&m->anonymous_proc_lits, hash_pointer(expr));
|
|
} else {
|
|
Entity *e = strip_entity_wrapping(expr);
|
|
GB_ASSERT(e != nullptr);
|
|
found = map_get(&m->values, hash_entity(e));
|
|
}
|
|
GB_ASSERT_MSG(found != nullptr, "%s", expr_to_string(expr));
|
|
irValue *val = *found;
|
|
ir_print_value(f, m, val, type);
|
|
break;
|
|
}
|
|
default:
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ir_print_block_name(irFileBuffer *f, irBlock *b) {
|
|
if (b != nullptr) {
|
|
ir_print_escape_string(f, b->label, false, false);
|
|
ir_fprintf(f, "-%td", b->index);
|
|
} else {
|
|
ir_write_str_lit(f, "<INVALID-BLOCK>");
|
|
}
|
|
}
|
|
|
|
bool ir_print_is_proc_global(irModule *m, irProcedure *proc) {
|
|
if (proc->entity != nullptr &&
|
|
proc->entity->kind == Entity_Procedure) {
|
|
if (m->entry_point_entity == proc->entity) {
|
|
// gb_printf("%.*s\n", LIT(proc->entity->token.string));
|
|
// TODO(bill): This may not be needed during windows
|
|
return true;
|
|
}
|
|
if (proc->entity->Procedure.link_name.len > 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return proc->is_foreign || proc->is_export;
|
|
}
|
|
|
|
void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hint) {
|
|
if (value == nullptr) {
|
|
ir_write_str_lit(f, "!!!nullptr_VALUE");
|
|
return;
|
|
}
|
|
switch (value->kind) {
|
|
default: GB_PANIC("Unknown irValue kind"); break;
|
|
|
|
case irValue_Constant:
|
|
ir_print_exact_value(f, m, value->Constant.value, type_hint);
|
|
break;
|
|
|
|
case irValue_ConstantSlice: {
|
|
irValueConstantSlice *cs = &value->ConstantSlice;
|
|
if (cs->backing_array == nullptr || cs->count == 0) {
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
} else {
|
|
Type *at = base_type(type_deref(ir_type(cs->backing_array)));
|
|
Type *et = at->Array.elem;
|
|
ir_write_byte(f, '{');
|
|
ir_print_type(f, m, et);
|
|
ir_write_str_lit(f, "* getelementptr inbounds (");
|
|
ir_print_type(f, m, at);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, at);
|
|
ir_write_str_lit(f, "* ");
|
|
ir_print_value(f, m, cs->backing_array, at);
|
|
ir_write_str_lit(f, ", i32 0, i32 0), ");
|
|
ir_print_type(f, m, t_int);
|
|
ir_fprintf(f, " %lld}", cs->count);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case irValue_Nil:
|
|
ir_write_str_lit(f, "zeroinitializer");
|
|
break;
|
|
|
|
case irValue_Undef:
|
|
ir_write_str_lit(f, "undef");
|
|
break;
|
|
|
|
case irValue_TypeName:
|
|
ir_print_encoded_local(f, value->TypeName.name);
|
|
break;
|
|
case irValue_Global: {
|
|
Entity *e = value->Global.entity;
|
|
Scope *scope = e->scope;
|
|
bool in_global_scope = false;
|
|
if (scope != nullptr) {
|
|
in_global_scope = (scope->flags & ScopeFlag_Global) != 0;
|
|
}
|
|
ir_print_encoded_global(f, ir_get_global_name(m, value), in_global_scope);
|
|
break;
|
|
}
|
|
case irValue_Param:
|
|
if (value->Param.index >= 0) {
|
|
ir_fprintf(f, "%%_.%d", value->Param.index);
|
|
} else {
|
|
ir_print_encoded_local(f, value->Param.entity->token.string);
|
|
}
|
|
break;
|
|
case irValue_SourceCodeLocation: {
|
|
irValue *file = value->SourceCodeLocation.file;
|
|
irValue *line = value->SourceCodeLocation.line;
|
|
irValue *column = value->SourceCodeLocation.column;
|
|
irValue *procedure = value->SourceCodeLocation.procedure;
|
|
|
|
ir_write_byte(f, '{');
|
|
ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, file, t_string);
|
|
ir_write_string(f, str_lit(", "));
|
|
ir_print_type(f, m, t_i32); ir_write_byte(f, ' '); ir_print_value(f, m, line, t_i32);
|
|
ir_write_string(f, str_lit(", "));
|
|
ir_print_type(f, m, t_i32); ir_write_byte(f, ' '); ir_print_value(f, m, column, t_i32);
|
|
ir_write_string(f, str_lit(", "));
|
|
ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, procedure, t_string);
|
|
ir_write_byte(f, '}');
|
|
break;
|
|
}
|
|
case irValue_Proc:
|
|
ir_print_encoded_global(f, value->Proc.name, ir_print_is_proc_global(m, &value->Proc));
|
|
break;
|
|
case irValue_Instr:
|
|
ir_fprintf(f, "%%%d", value->index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConvention cc) {
|
|
switch (cc) {
|
|
case ProcCC_Odin: ir_write_str_lit(f, ""); break;
|
|
case ProcCC_Contextless: ir_write_str_lit(f, ""); break;
|
|
// case ProcCC_CDecl: ir_write_str_lit(f, "ccc "); break;
|
|
case ProcCC_CDecl: ir_write_str_lit(f, ""); break;
|
|
case ProcCC_StdCall: ir_write_str_lit(f, "cc 64 "); break;
|
|
case ProcCC_FastCall: ir_write_str_lit(f, "cc 65 "); break;
|
|
case ProcCC_None: ir_write_str_lit(f, ""); break;
|
|
default: GB_PANIC("unknown calling convention: %d", cc);
|
|
}
|
|
}
|
|
|
|
void ir_print_context_parameter_prefix(irFileBuffer *f, irModule *m) {
|
|
ir_print_type(f, m, t_context_ptr);
|
|
// ir_write_str_lit(f, " noalias nonnull nocapture inreg ");
|
|
ir_write_str_lit(f, " noalias nonnull nocapture ");
|
|
}
|
|
|
|
void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
|
GB_ASSERT(value->kind == irValue_Instr);
|
|
irInstr *instr = &value->Instr;
|
|
|
|
ir_write_byte(f, '\t');
|
|
defer (ir_write_byte(f, '\n'));
|
|
|
|
switch (instr->kind) {
|
|
default: {
|
|
GB_PANIC("<unknown instr> %d\n", instr->kind);
|
|
ir_fprintf(f, "; <unknown instr> %d\n", instr->kind);
|
|
break;
|
|
}
|
|
|
|
case irInstr_StartupRuntime: {
|
|
ir_write_str_lit(f, "call void ");
|
|
ir_print_encoded_global(f, str_lit(IR_STARTUP_RUNTIME_PROC_NAME), false);
|
|
ir_write_str_lit(f, "()");
|
|
break;
|
|
}
|
|
|
|
case irInstr_Comment:
|
|
ir_write_str_lit(f, "; ");
|
|
ir_write_string(f, instr->Comment.text);
|
|
break;
|
|
|
|
case irInstr_Local: {
|
|
Type *type = instr->Local.entity->type;
|
|
i64 align = instr->Local.alignment;
|
|
if (align <= 0) {
|
|
align = type_align_of(type);
|
|
}
|
|
ir_fprintf(f, "%%%d = alloca ", value->index);
|
|
ir_print_type(f, m, type);
|
|
ir_fprintf(f, ", align %lld", align);
|
|
break;
|
|
}
|
|
|
|
case irInstr_ZeroInit: {
|
|
Type *type = type_deref(ir_type(instr->ZeroInit.address));
|
|
ir_write_str_lit(f, "; ZeroInit\n\t");
|
|
ir_write_str_lit(f, "store ");
|
|
ir_print_type(f, m, type);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_exact_value(f, m, empty_exact_value, type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, type);
|
|
ir_fprintf(f, "* %%%d, align 1", instr->ZeroInit.address->index);
|
|
// ir_fprintf(f, "* %%%d", instr->ZeroInit.address->index);
|
|
break;
|
|
}
|
|
|
|
case irInstr_Store: {
|
|
Type *type = type_deref(ir_type(instr->Store.address));
|
|
ir_write_str_lit(f, "store ");
|
|
if (instr->Store.is_volatile) {
|
|
ir_write_str_lit(f, "volatile ");
|
|
}
|
|
ir_print_type(f, m, type);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->Store.value, type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, type);
|
|
ir_write_str_lit(f, "* ");
|
|
ir_print_value(f, m, instr->Store.address, type);
|
|
ir_print_debug_location(f, m, value);
|
|
break;
|
|
}
|
|
|
|
case irInstr_Load: {
|
|
Type *type = instr->Load.type;
|
|
ir_fprintf(f, "%%%d = load ", value->index);
|
|
if (instr->Load.is_volatile) {
|
|
ir_write_str_lit(f, "volatile ");
|
|
}
|
|
ir_print_type(f, m, type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, type);
|
|
ir_write_str_lit(f, "* ");
|
|
ir_print_value(f, m, instr->Load.address, type);
|
|
if (instr->Load.custom_align > 0) {
|
|
ir_fprintf(f, ", align %lld", instr->Load.custom_align);
|
|
} else {
|
|
ir_fprintf(f, ", align %lld", type_align_of(type));
|
|
}
|
|
ir_print_debug_location(f, m, value);
|
|
break;
|
|
}
|
|
|
|
case irInstr_InlineCode:
|
|
{
|
|
switch (instr->InlineCode.id) {
|
|
case BuiltinProc_alloca:
|
|
ir_fprintf(f, "%%%d = ", value->index);
|
|
ir_write_str_lit(f, "alloca i8, ");
|
|
ir_print_type(f, m, ir_type(instr->InlineCode.operands[0]));
|
|
ir_write_str_lit(f, " ");
|
|
ir_print_value(f, m, instr->InlineCode.operands[0], ir_type(instr->InlineCode.operands[0]));
|
|
ir_write_str_lit(f, ", align ");
|
|
ir_print_value(f, m, instr->InlineCode.operands[1], t_i32);
|
|
break;
|
|
|
|
case BuiltinProc_cpu_relax:
|
|
if (build_context.metrics.arch == TargetArch_amd64) {
|
|
ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
|
|
} else {
|
|
// ir_write_str_lit(f, "call void asm sideeffect \"yield\", \"\"()");
|
|
}
|
|
break;
|
|
default: GB_PANIC("Unknown inline code %d", instr->InlineCode.id); break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case irInstr_AtomicFence:
|
|
ir_write_str_lit(f, "fence ");
|
|
switch (instr->AtomicFence.id) {
|
|
case BuiltinProc_atomic_fence: ir_write_str_lit(f, "seq_cst"); break;
|
|
case BuiltinProc_atomic_fence_acq: ir_write_str_lit(f, "acquire"); break;
|
|
case BuiltinProc_atomic_fence_rel: ir_write_str_lit(f, "release"); break;
|
|
case BuiltinProc_atomic_fence_acqrel: ir_write_str_lit(f, "acq_rel"); break;
|
|
default: GB_PANIC("Unknown atomic fence"); break;
|
|
}
|
|
break;
|
|
|
|
case irInstr_AtomicStore: {
|
|
Type *type = type_deref(ir_type(instr->AtomicStore.address));
|
|
ir_write_str_lit(f, "store atomic ");
|
|
ir_print_type(f, m, type);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->AtomicStore.value, type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, type);
|
|
ir_write_str_lit(f, "* ");
|
|
ir_print_value(f, m, instr->AtomicStore.address, type);
|
|
|
|
switch (instr->AtomicStore.id) {
|
|
case BuiltinProc_atomic_store: ir_write_str_lit(f, " seq_cst "); break;
|
|
case BuiltinProc_atomic_store_rel: ir_write_str_lit(f, " release"); break;
|
|
case BuiltinProc_atomic_store_relaxed: ir_write_str_lit(f, " monotonic"); break;
|
|
case BuiltinProc_atomic_store_unordered: ir_write_str_lit(f, " unordered"); break;
|
|
default: GB_PANIC("Unknown atomic store"); break;
|
|
}
|
|
|
|
ir_fprintf(f, ", align %lld", type_align_of(type));
|
|
|
|
ir_print_debug_location(f, m, value);
|
|
break;
|
|
}
|
|
|
|
case irInstr_AtomicLoad: {
|
|
Type *type = instr->AtomicLoad.type;
|
|
ir_fprintf(f, "%%%d = load atomic ", value->index);
|
|
ir_print_type(f, m, type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, type);
|
|
ir_write_str_lit(f, "* ");
|
|
ir_print_value(f, m, instr->AtomicLoad.address, type);
|
|
|
|
switch (instr->AtomicLoad.id) {
|
|
case BuiltinProc_atomic_load: ir_fprintf(f, " seq_cst"); break;
|
|
case BuiltinProc_atomic_load_acq: ir_fprintf(f, " acquire"); break;
|
|
case BuiltinProc_atomic_load_relaxed: ir_fprintf(f, " monotonic"); break;
|
|
case BuiltinProc_atomic_load_unordered: ir_fprintf(f, " unordered"); break;
|
|
default: GB_PANIC("Unknown atomic load"); break;
|
|
}
|
|
|
|
ir_fprintf(f, ", align %lld", type_align_of(type));
|
|
ir_print_debug_location(f, m, value);
|
|
break;
|
|
}
|
|
|
|
case irInstr_AtomicRmw: {
|
|
String operation = {};
|
|
String ordering = {};
|
|
switch (instr->AtomicRmw.id) {
|
|
case BuiltinProc_atomic_add:
|
|
case BuiltinProc_atomic_add_acq:
|
|
case BuiltinProc_atomic_add_rel:
|
|
case BuiltinProc_atomic_add_acqrel:
|
|
case BuiltinProc_atomic_add_relaxed:
|
|
operation = str_lit("add");
|
|
break;
|
|
case BuiltinProc_atomic_sub:
|
|
case BuiltinProc_atomic_sub_acq:
|
|
case BuiltinProc_atomic_sub_rel:
|
|
case BuiltinProc_atomic_sub_acqrel:
|
|
case BuiltinProc_atomic_sub_relaxed:
|
|
operation = str_lit("sub");
|
|
break;
|
|
case BuiltinProc_atomic_and:
|
|
case BuiltinProc_atomic_and_acq:
|
|
case BuiltinProc_atomic_and_rel:
|
|
case BuiltinProc_atomic_and_acqrel:
|
|
case BuiltinProc_atomic_and_relaxed:
|
|
operation = str_lit("and");
|
|
break;
|
|
case BuiltinProc_atomic_nand:
|
|
case BuiltinProc_atomic_nand_acq:
|
|
case BuiltinProc_atomic_nand_rel:
|
|
case BuiltinProc_atomic_nand_acqrel:
|
|
case BuiltinProc_atomic_nand_relaxed:
|
|
operation = str_lit("nand");
|
|
break;
|
|
case BuiltinProc_atomic_or:
|
|
case BuiltinProc_atomic_or_acq:
|
|
case BuiltinProc_atomic_or_rel:
|
|
case BuiltinProc_atomic_or_acqrel:
|
|
case BuiltinProc_atomic_or_relaxed:
|
|
operation = str_lit("or");
|
|
break;
|
|
case BuiltinProc_atomic_xor:
|
|
case BuiltinProc_atomic_xor_acq:
|
|
case BuiltinProc_atomic_xor_rel:
|
|
case BuiltinProc_atomic_xor_acqrel:
|
|
case BuiltinProc_atomic_xor_relaxed:
|
|
operation = str_lit("xor");
|
|
break;
|
|
case BuiltinProc_atomic_xchg:
|
|
case BuiltinProc_atomic_xchg_acq:
|
|
case BuiltinProc_atomic_xchg_rel:
|
|
case BuiltinProc_atomic_xchg_acqrel:
|
|
case BuiltinProc_atomic_xchg_relaxed:
|
|
operation = str_lit("xchg");
|
|
break;
|
|
}
|
|
|
|
switch (instr->AtomicRmw.id) {
|
|
case BuiltinProc_atomic_add:
|
|
case BuiltinProc_atomic_sub:
|
|
case BuiltinProc_atomic_and:
|
|
case BuiltinProc_atomic_nand:
|
|
case BuiltinProc_atomic_or:
|
|
case BuiltinProc_atomic_xor:
|
|
case BuiltinProc_atomic_xchg:
|
|
ordering = str_lit("seq_cst");
|
|
break;
|
|
case BuiltinProc_atomic_add_acq:
|
|
case BuiltinProc_atomic_sub_acq:
|
|
case BuiltinProc_atomic_and_acq:
|
|
case BuiltinProc_atomic_nand_acq:
|
|
case BuiltinProc_atomic_or_acq:
|
|
case BuiltinProc_atomic_xor_acq:
|
|
case BuiltinProc_atomic_xchg_acq:
|
|
ordering = str_lit("acquire");
|
|
break;
|
|
case BuiltinProc_atomic_add_rel:
|
|
case BuiltinProc_atomic_sub_rel:
|
|
case BuiltinProc_atomic_and_rel:
|
|
case BuiltinProc_atomic_nand_rel:
|
|
case BuiltinProc_atomic_or_rel:
|
|
case BuiltinProc_atomic_xor_rel:
|
|
case BuiltinProc_atomic_xchg_rel:
|
|
ordering = str_lit("release");
|
|
break;
|
|
case BuiltinProc_atomic_add_acqrel:
|
|
case BuiltinProc_atomic_sub_acqrel:
|
|
case BuiltinProc_atomic_and_acqrel:
|
|
case BuiltinProc_atomic_nand_acqrel:
|
|
case BuiltinProc_atomic_or_acqrel:
|
|
case BuiltinProc_atomic_xor_acqrel:
|
|
case BuiltinProc_atomic_xchg_acqrel:
|
|
ordering = str_lit("acq_rel");
|
|
break;
|
|
case BuiltinProc_atomic_add_relaxed:
|
|
case BuiltinProc_atomic_sub_relaxed:
|
|
case BuiltinProc_atomic_and_relaxed:
|
|
case BuiltinProc_atomic_nand_relaxed:
|
|
case BuiltinProc_atomic_or_relaxed:
|
|
case BuiltinProc_atomic_xor_relaxed:
|
|
case BuiltinProc_atomic_xchg_relaxed:
|
|
ordering = str_lit("monotonic");
|
|
break;
|
|
}
|
|
|
|
Type *type = type_deref(ir_type(instr->AtomicRmw.address));
|
|
ir_write_str_lit(f, "atomicrmw ");
|
|
ir_write_string(f, operation);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_type(f, m, type);
|
|
ir_write_str_lit(f, "* ");
|
|
ir_print_value(f, m, instr->AtomicRmw.address, type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, type);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->AtomicRmw.value, type);
|
|
|
|
ir_write_byte(f, ' ');
|
|
ir_write_string(f, ordering);
|
|
|
|
ir_print_debug_location(f, m, value);
|
|
break;
|
|
}
|
|
|
|
case irInstr_AtomicCxchg: {
|
|
Type *type = type_deref(ir_type(instr->AtomicCxchg.address));
|
|
bool weak = false;
|
|
String success = {};
|
|
String failure = {};
|
|
|
|
switch (instr->AtomicCxchg.id) {
|
|
case BuiltinProc_atomic_cxchgweak:
|
|
case BuiltinProc_atomic_cxchgweak_acq:
|
|
case BuiltinProc_atomic_cxchgweak_rel:
|
|
case BuiltinProc_atomic_cxchgweak_acqrel:
|
|
case BuiltinProc_atomic_cxchgweak_relaxed:
|
|
case BuiltinProc_atomic_cxchgweak_failrelaxed:
|
|
case BuiltinProc_atomic_cxchgweak_failacq:
|
|
case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
|
|
case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed:
|
|
weak = true;
|
|
break;
|
|
}
|
|
|
|
switch (instr->AtomicCxchg.id) {
|
|
case BuiltinProc_atomic_cxchg:
|
|
case BuiltinProc_atomic_cxchgweak:
|
|
success = str_lit("seq_cst");
|
|
failure = str_lit("seq_cst");
|
|
break;
|
|
case BuiltinProc_atomic_cxchg_acq:
|
|
case BuiltinProc_atomic_cxchgweak_acq:
|
|
success = str_lit("acquire");
|
|
failure = str_lit("seq_cst");
|
|
break;
|
|
case BuiltinProc_atomic_cxchg_rel:
|
|
case BuiltinProc_atomic_cxchgweak_rel:
|
|
success = str_lit("release");
|
|
failure = str_lit("seq_cst");
|
|
break;
|
|
case BuiltinProc_atomic_cxchg_acqrel:
|
|
case BuiltinProc_atomic_cxchgweak_acqrel:
|
|
success = str_lit("acq_rel");
|
|
failure = str_lit("seq_cst");
|
|
break;
|
|
case BuiltinProc_atomic_cxchg_relaxed:
|
|
case BuiltinProc_atomic_cxchgweak_relaxed:
|
|
success = str_lit("monotonic");
|
|
failure = str_lit("monotonic");
|
|
break;
|
|
case BuiltinProc_atomic_cxchg_failrelaxed:
|
|
case BuiltinProc_atomic_cxchgweak_failrelaxed:
|
|
success = str_lit("seq_cst");
|
|
failure = str_lit("monotonic");
|
|
break;
|
|
case BuiltinProc_atomic_cxchg_failacq:
|
|
case BuiltinProc_atomic_cxchgweak_failacq:
|
|
success = str_lit("seq_cst");
|
|
failure = str_lit("acquire");
|
|
break;
|
|
case BuiltinProc_atomic_cxchg_acq_failrelaxed:
|
|
case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
|
|
success = str_lit("acquire");
|
|
failure = str_lit("monotonic");
|
|
break;
|
|
case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:
|
|
case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed:
|
|
success = str_lit("acq_rel");
|
|
failure = str_lit("monotonic");
|
|
break;
|
|
}
|
|
|
|
ir_fprintf(f, "%%%d = cmpxchg ", value->index);
|
|
if (weak) {
|
|
ir_write_str_lit(f, "weak ");
|
|
}
|
|
ir_print_type(f, m, type);
|
|
ir_write_str_lit(f, "* ");
|
|
ir_print_value(f, m, instr->AtomicCxchg.address, type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, type); ir_write_str_lit(f, " ");
|
|
ir_print_value(f, m, instr->AtomicCxchg.old_value, type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, type); ir_write_str_lit(f, " ");
|
|
ir_print_value(f, m, instr->AtomicCxchg.new_value, type);
|
|
ir_write_str_lit(f, " ");
|
|
ir_write_string(f, success);
|
|
ir_write_str_lit(f, " ");
|
|
ir_write_string(f, failure);
|
|
break;
|
|
}
|
|
|
|
case irInstr_ArrayElementPtr: {
|
|
Type *et = ir_type(instr->ArrayElementPtr.address);
|
|
ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
|
|
|
|
ir_print_type(f, m, type_deref(et));
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, et);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->ArrayElementPtr.address, et);
|
|
ir_write_str_lit(f, ", i32 0, ");
|
|
|
|
irValue *index =instr->ArrayElementPtr.elem_index;
|
|
Type *t = ir_type(index);
|
|
ir_print_type(f, m, t);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, index, t);
|
|
break;
|
|
}
|
|
|
|
case irInstr_StructElementPtr: {
|
|
Type *et = ir_type(instr->StructElementPtr.address);
|
|
ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
|
|
i32 index = instr->StructElementPtr.elem_index;
|
|
Type *st = base_type(type_deref(et));
|
|
if (is_type_struct(st)) {
|
|
if (st->Struct.custom_align > 0) {
|
|
index += 1;
|
|
}
|
|
} else if (is_type_union(st)) {
|
|
index += 1;
|
|
}
|
|
|
|
ir_print_type(f, m, type_deref(et));
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, et);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->StructElementPtr.address, et);
|
|
ir_write_str_lit(f, ", i32 0, ");
|
|
ir_print_type(f, m, t_i32);
|
|
ir_fprintf(f, " %d", index);
|
|
break;
|
|
}
|
|
|
|
case irInstr_PtrOffset: {
|
|
Type *pt = ir_type(instr->PtrOffset.address);
|
|
ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
|
|
ir_print_type(f, m, type_deref(pt));
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, pt);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->PtrOffset.address, pt);
|
|
|
|
irValue *offset = instr->PtrOffset.offset;
|
|
Type *t = ir_type(offset);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, t);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, offset, t);
|
|
break;
|
|
}
|
|
|
|
case irInstr_Phi: {
|
|
ir_fprintf(f, "%%%d = phi ", value->index);
|
|
ir_print_type(f, m, instr->Phi.type);
|
|
// ir_fprintf(f, " ", value->index);
|
|
ir_write_byte(f, ' ');
|
|
|
|
for (isize i = 0; i < instr->Phi.edges.count; i++) {
|
|
if (i > 0) {
|
|
ir_write_string(f, str_lit(", "));
|
|
}
|
|
|
|
irValue *edge = instr->Phi.edges[i];
|
|
irBlock *block = nullptr;
|
|
if (instr->block != nullptr &&
|
|
i < instr->block->preds.count) {
|
|
block = instr->block->preds[i];
|
|
}
|
|
|
|
ir_write_str_lit(f, "[ ");
|
|
ir_print_value(f, m, edge, instr->Phi.type);
|
|
ir_write_str_lit(f, ", %");
|
|
ir_print_block_name(f, block);
|
|
ir_write_str_lit(f, " ]");
|
|
}
|
|
break;
|
|
}
|
|
|
|
case irInstr_StructExtractValue: {
|
|
Type *et = ir_type(instr->StructExtractValue.address);
|
|
ir_fprintf(f, "%%%d = extractvalue ", value->index);
|
|
i32 index = instr->StructExtractValue.index;
|
|
Type *st = base_type(et);
|
|
if (is_type_struct(st)) {
|
|
if (st->Struct.custom_align > 0) {
|
|
index += 1;
|
|
}
|
|
} else if (is_type_union(st)) {
|
|
index += 1;
|
|
}
|
|
|
|
|
|
ir_print_type(f, m, et);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->StructExtractValue.address, et);
|
|
ir_fprintf(f, ", %d", index);
|
|
break;
|
|
}
|
|
|
|
case irInstr_UnionTagPtr: {
|
|
Type *et = ir_type(instr->UnionTagPtr.address);
|
|
|
|
Type *ut = type_deref(et);
|
|
if (is_type_union_maybe_pointer(ut)) {
|
|
GB_PANIC("union #maybe UnionTagPtr");
|
|
}
|
|
|
|
ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
|
|
Type *t = base_type(type_deref(et));
|
|
GB_ASSERT(is_type_union(t));
|
|
|
|
ir_print_type(f, m, type_deref(et));
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, et);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->UnionTagPtr.address, et);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, t_int);
|
|
ir_write_str_lit(f, " 0, ");
|
|
ir_print_type(f, m, t_i32);
|
|
ir_fprintf(f, " 2 ; UnionTagPtr");
|
|
break;
|
|
}
|
|
|
|
case irInstr_UnionTagValue: {
|
|
Type *et = ir_type(instr->UnionTagValue.address);
|
|
Type *t = base_type(et);
|
|
|
|
if (is_type_union_maybe_pointer(t)) {
|
|
GB_PANIC("union #maybe UnionTagValue");
|
|
}
|
|
|
|
ir_fprintf(f, "%%%d = extractvalue ", value->index);
|
|
GB_ASSERT(is_type_union(t));
|
|
|
|
|
|
ir_print_type(f, m, et);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->UnionTagValue.address, et);
|
|
ir_fprintf(f, ", 2 ; UnionTagValue");
|
|
break;
|
|
}
|
|
|
|
case irInstr_Jump: {
|
|
ir_write_str_lit(f, "br label %");
|
|
ir_print_block_name(f, instr->Jump.block);
|
|
ir_print_debug_location(f, m, value);
|
|
break;
|
|
}
|
|
|
|
case irInstr_If: {
|
|
ir_write_str_lit(f, "br i1");
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->If.cond, t_bool);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_write_str_lit(f, "label %"); ir_print_block_name(f, instr->If.true_block);
|
|
ir_write_str_lit(f, ", label %"); ir_print_block_name(f, instr->If.false_block);
|
|
ir_print_debug_location(f, m, value);
|
|
break;
|
|
}
|
|
|
|
case irInstr_Return: {
|
|
irInstrReturn *ret = &instr->Return;
|
|
ir_write_str_lit(f, "ret ");
|
|
if (ret->value == nullptr) {
|
|
ir_write_str_lit(f, "void");
|
|
} else {
|
|
Type *t = ir_type(ret->value);
|
|
ir_print_type(f, m, t);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, ret->value, t);
|
|
}
|
|
ir_print_debug_location(f, m, value);
|
|
break;
|
|
}
|
|
|
|
case irInstr_Conv: {
|
|
irInstrConv *c = &instr->Conv;
|
|
if (c->kind == irConv_byteswap) {
|
|
int sz = cast(int)(8*type_size_of(c->from));
|
|
ir_fprintf(f, "%%%d = call i%d @llvm.bswap.i%d(", value->index, sz, sz);
|
|
ir_print_type(f, m, c->from);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, c->value, c->from);
|
|
ir_write_byte(f, ')');
|
|
ir_print_debug_location(f, m, value);
|
|
} else {
|
|
ir_fprintf(f, "%%%d = ", value->index);
|
|
ir_write_string(f, ir_conv_strings[c->kind]);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_type(f, m, c->from);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, c->value, c->from);
|
|
ir_write_str_lit(f, " to ");
|
|
ir_print_type(f, m, c->to);
|
|
ir_print_debug_location(f, m, value);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case irInstr_Unreachable: {
|
|
ir_write_str_lit(f, "unreachable");
|
|
break;
|
|
}
|
|
|
|
case irInstr_UnaryOp: {
|
|
irInstrUnaryOp *uo = &value->Instr.UnaryOp;
|
|
Type *type = base_type(ir_type(uo->expr));
|
|
Type *elem_type = type;
|
|
|
|
ir_fprintf(f, "%%%d = ", value->index);
|
|
switch (uo->op) {
|
|
case Token_Sub:
|
|
if (is_type_float(elem_type)) {
|
|
ir_write_str_lit(f, "fsub");
|
|
} else {
|
|
ir_write_str_lit(f, "sub");
|
|
}
|
|
break;
|
|
case Token_Xor:
|
|
case Token_Not:
|
|
GB_ASSERT(is_type_integer(type) || is_type_boolean(type) || is_type_bit_set(type));
|
|
ir_write_str_lit(f, "xor");
|
|
break;
|
|
default:
|
|
GB_PANIC("Unknown unary operator");
|
|
break;
|
|
}
|
|
|
|
ir_write_byte(f, ' ');
|
|
ir_print_type(f, m, type);
|
|
ir_write_byte(f, ' ');
|
|
switch (uo->op) {
|
|
case Token_Sub:
|
|
if (is_type_float(elem_type)) {
|
|
ir_print_exact_value(f, m, exact_value_float(0), elem_type);
|
|
} else {
|
|
ir_write_byte(f, '0');
|
|
}
|
|
break;
|
|
case Token_Xor:
|
|
case Token_Not:
|
|
GB_ASSERT(is_type_integer(type) || is_type_boolean(type) || is_type_bit_set(type));
|
|
ir_write_str_lit(f, "-1");
|
|
break;
|
|
}
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_value(f, m, uo->expr, type);
|
|
ir_print_debug_location(f, m, value);
|
|
break;
|
|
}
|
|
|
|
case irInstr_BinaryOp: {
|
|
irInstrBinaryOp *bo = &value->Instr.BinaryOp;
|
|
Type *type = base_type(ir_type(bo->left));
|
|
Type *elem_type = base_array_type(type);
|
|
|
|
ir_fprintf(f, "%%%d = ", value->index);
|
|
|
|
if (gb_is_between(bo->op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) {
|
|
if (is_type_string(elem_type)) {
|
|
GB_PANIC("Unhandled string type");
|
|
} else if (is_type_float(elem_type)) {
|
|
ir_write_str_lit(f, "fcmp ");
|
|
switch (bo->op) {
|
|
case Token_CmpEq: ir_write_str_lit(f, "oeq"); break;
|
|
case Token_NotEq: ir_write_str_lit(f, "one"); break;
|
|
case Token_Lt: ir_write_str_lit(f, "olt"); break;
|
|
case Token_Gt: ir_write_str_lit(f, "ogt"); break;
|
|
case Token_LtEq: ir_write_str_lit(f, "ole"); break;
|
|
case Token_GtEq: ir_write_str_lit(f, "oge"); break;
|
|
}
|
|
} else if (is_type_complex(elem_type)) {
|
|
GB_PANIC("Unhandled complex type");
|
|
return;
|
|
} else {
|
|
ir_write_str_lit(f, "icmp ");
|
|
if (bo->op != Token_CmpEq &&
|
|
bo->op != Token_NotEq) {
|
|
if (is_type_unsigned(elem_type)) {
|
|
ir_write_str_lit(f, "u");
|
|
} else {
|
|
ir_write_str_lit(f, "s");
|
|
}
|
|
}
|
|
switch (bo->op) {
|
|
case Token_CmpEq: ir_write_str_lit(f, "eq"); break;
|
|
case Token_NotEq: ir_write_str_lit(f, "ne"); break;
|
|
case Token_Lt: ir_write_str_lit(f, "lt"); break;
|
|
case Token_Gt: ir_write_str_lit(f, "gt"); break;
|
|
case Token_LtEq: ir_write_str_lit(f, "le"); break;
|
|
case Token_GtEq: ir_write_str_lit(f, "ge"); break;
|
|
default: GB_PANIC("invalid comparison");break;
|
|
}
|
|
}
|
|
} else {
|
|
if (is_type_float(elem_type)) {
|
|
ir_write_str_lit(f, "f");
|
|
}
|
|
|
|
switch (bo->op) {
|
|
case Token_Add: ir_write_str_lit(f, "add"); break;
|
|
case Token_Sub: ir_write_str_lit(f, "sub"); break;
|
|
case Token_And: ir_write_str_lit(f, "and"); break;
|
|
case Token_Or: ir_write_str_lit(f, "or"); break;
|
|
case Token_Xor: ir_write_str_lit(f, "xor"); break;
|
|
case Token_Shl: ir_write_str_lit(f, "shl"); break;
|
|
case Token_Mul: ir_write_str_lit(f, "mul"); break;
|
|
case Token_Not: ir_write_str_lit(f, "xor"); break;
|
|
case Token_Shr:
|
|
if (is_type_unsigned(elem_type)) {
|
|
ir_write_str_lit(f, "lshr");
|
|
} else {
|
|
ir_write_str_lit(f, "ashr");
|
|
}
|
|
break;
|
|
|
|
case Token_AndNot: GB_PANIC("Token_AndNot Should never be called");
|
|
|
|
default: {
|
|
if (!is_type_float(elem_type)) {
|
|
if (is_type_unsigned(elem_type)) {
|
|
ir_write_str_lit(f, "u");
|
|
} else {
|
|
ir_write_str_lit(f, "s");
|
|
}
|
|
}
|
|
|
|
switch (bo->op) {
|
|
case Token_Quo: ir_write_str_lit(f, "div"); break;
|
|
case Token_Mod: ir_write_str_lit(f, "rem"); break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ir_write_byte(f, ' ');
|
|
ir_print_type(f, m, type);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, bo->left, type);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_value(f, m, bo->right, type);
|
|
|
|
ir_print_debug_location(f, m, value);
|
|
break;
|
|
}
|
|
|
|
case irInstr_Call: {
|
|
irInstrCall *call = &instr->Call;
|
|
Type *proc_type = base_type(ir_type(call->value));
|
|
GB_ASSERT(is_type_proc(proc_type));
|
|
set_procedure_abi_types(proc_type);
|
|
|
|
bool is_c_vararg = proc_type->Proc.c_vararg;
|
|
Type *result_type = call->type;
|
|
if (result_type) {
|
|
ir_fprintf(f, "%%%d = ", value->index);
|
|
}
|
|
ir_write_str_lit(f, "call ");
|
|
ir_print_calling_convention(f, m, proc_type->Proc.calling_convention);
|
|
if (is_c_vararg) {
|
|
ir_print_proc_type_without_pointer(f, m, proc_type);
|
|
} else if (result_type && !proc_type->Proc.return_by_pointer) {
|
|
ir_print_proc_results(f, m, proc_type);
|
|
} else {
|
|
ir_write_str_lit(f, "void");
|
|
}
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, call->value, call->type);
|
|
|
|
bool return_by_pointer = proc_type->Proc.return_by_pointer;
|
|
|
|
isize param_index = 0;
|
|
|
|
ir_write_byte(f, '(');
|
|
if (return_by_pointer) {
|
|
GB_ASSERT(call->return_ptr != nullptr);
|
|
ir_print_type(f, m, proc_type->Proc.results);
|
|
ir_write_str_lit(f, "* ");
|
|
ir_print_value(f, m, call->return_ptr, ir_type(call->return_ptr));
|
|
param_index += 1;
|
|
}
|
|
|
|
|
|
if (call->args.count > 0) {
|
|
TypeTuple *params = &proc_type->Proc.params->Tuple;
|
|
if (proc_type->Proc.c_vararg) {
|
|
isize i = 0;
|
|
isize arg_index = 0;
|
|
for (; i < params->variables.count-1; i++) {
|
|
Entity *e = params->variables[i];
|
|
GB_ASSERT(e != nullptr);
|
|
if (e->kind != Entity_Variable) {
|
|
arg_index++;
|
|
continue;
|
|
}
|
|
|
|
if (param_index > 0) ir_write_str_lit(f, ", ");
|
|
|
|
Type *t = proc_type->Proc.abi_compat_params[i];
|
|
if (is_type_tuple(t)) {
|
|
for_array(j, t->Tuple.variables) {
|
|
if (j > 0) ir_write_str_lit(f, ", ");
|
|
|
|
irValue *arg = call->args[arg_index++];
|
|
|
|
ir_print_type(f, m, t->Tuple.variables[j]->type);
|
|
if (e->flags&EntityFlag_NoAlias) {
|
|
ir_write_str_lit(f, " noalias");
|
|
}
|
|
if (e->flags&EntityFlag_ByVal) {
|
|
ir_write_str_lit(f, " byval");
|
|
}
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, arg, t);
|
|
param_index++;
|
|
}
|
|
} else {
|
|
ir_print_type(f, m, t);
|
|
if (e->flags&EntityFlag_NoAlias) {
|
|
ir_write_str_lit(f, " noalias");
|
|
}
|
|
if (e->flags&EntityFlag_ImplicitReference) {
|
|
ir_write_str_lit(f, " nonnull dereferenceable");
|
|
}
|
|
if (e->flags&EntityFlag_ByVal) {
|
|
ir_write_str_lit(f, " byval");
|
|
}
|
|
ir_write_byte(f, ' ');
|
|
irValue *arg = call->args[arg_index++];
|
|
ir_print_value(f, m, arg, t);
|
|
param_index++;
|
|
}
|
|
}
|
|
while (arg_index < call->args.count) {
|
|
if (param_index > 0) ir_write_str_lit(f, ", ");
|
|
|
|
irValue *arg = call->args[arg_index];
|
|
Type *t = ir_type(arg);
|
|
ir_print_type(f, m, t);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, arg, t);
|
|
arg_index++;
|
|
}
|
|
} else {
|
|
// GB_ASSERT(call->args.count == params->variables.count);
|
|
isize arg_index = 0;
|
|
for_array(i, params->variables) {
|
|
Entity *e = params->variables[i];
|
|
GB_ASSERT(e != nullptr);
|
|
if (e->kind != Entity_Variable) {
|
|
arg_index++;
|
|
continue;
|
|
}
|
|
|
|
if (param_index > 0) ir_write_str_lit(f, ", ");
|
|
|
|
Type *t = proc_type->Proc.abi_compat_params[i];
|
|
if (is_type_tuple(t)) {
|
|
for_array(j, t->Tuple.variables) {
|
|
if (j > 0) ir_write_str_lit(f, ", ");
|
|
|
|
irValue *arg = call->args[arg_index++];
|
|
|
|
ir_print_type(f, m, t->Tuple.variables[j]->type);
|
|
if (e->flags&EntityFlag_NoAlias) {
|
|
ir_write_str_lit(f, " noalias");
|
|
}
|
|
if (e->flags&EntityFlag_ByVal) {
|
|
ir_write_str_lit(f, " byval");
|
|
}
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, arg, t);
|
|
param_index++;
|
|
}
|
|
} else {
|
|
irValue *arg = call->args[arg_index++];
|
|
ir_print_type(f, m, t);
|
|
if (e->flags&EntityFlag_NoAlias) {
|
|
ir_write_str_lit(f, " noalias");
|
|
}
|
|
if (e->flags&EntityFlag_ByVal) {
|
|
ir_write_str_lit(f, " byval");
|
|
}
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, arg, t);
|
|
param_index++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (proc_type->Proc.calling_convention == ProcCC_Odin) {
|
|
if (param_index > 0) ir_write_str_lit(f, ", ");
|
|
|
|
ir_print_context_parameter_prefix(f, m);
|
|
ir_print_value(f, m, call->context_ptr, t_context_ptr);
|
|
}
|
|
ir_write_str_lit(f, ")");
|
|
|
|
if (proc_type->Proc.diverging) {
|
|
ir_write_str_lit(f, " noreturn");
|
|
}
|
|
switch (call->inlining) {
|
|
case ProcInlining_inline: ir_write_str_lit(f, " alwaysinline"); break;
|
|
case ProcInlining_no_inline: ir_write_str_lit(f, " noinline"); break;
|
|
}
|
|
ir_print_debug_location(f, m, value);
|
|
|
|
break;
|
|
}
|
|
|
|
case irInstr_Select: {
|
|
ir_fprintf(f, "%%%d = select i1 ", value->index);
|
|
ir_print_value(f, m, instr->Select.cond, t_bool);
|
|
ir_write_string(f, str_lit(", "));
|
|
ir_print_type(f, m, ir_type(instr->Select.true_value));
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->Select.true_value, ir_type(instr->Select.true_value));
|
|
ir_write_string(f, str_lit(", "));
|
|
ir_print_type(f, m, ir_type(instr->Select.false_value));
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, instr->Select.false_value, ir_type(instr->Select.false_value));
|
|
ir_print_debug_location(f, m, value);
|
|
break;
|
|
}
|
|
|
|
case irInstr_DebugDeclare: {
|
|
if (!m->generate_debug_info) {
|
|
break;
|
|
}
|
|
|
|
irInstrDebugDeclare *dd = &instr->DebugDeclare;
|
|
Type *vt = ir_type(dd->value);
|
|
Entity *e = dd->entity;
|
|
String name = e->token.string;
|
|
TokenPos pos = e->token.pos;
|
|
|
|
irDebugInfo **lookup_di = map_get(&m->debug_info, hash_entity(e));
|
|
if (lookup_di != nullptr) {
|
|
GB_ASSERT_NOT_NULL(*lookup_di);
|
|
irDebugInfo* local_var_di = *lookup_di;
|
|
|
|
ir_write_str_lit(f, "call void @llvm.dbg.declare(");
|
|
ir_write_str_lit(f, "metadata ");
|
|
ir_print_type(f, m, vt);
|
|
ir_write_byte(f, ' ');
|
|
ir_print_value(f, m, dd->value, vt);
|
|
ir_fprintf(f, ", metadata !%d", local_var_di->id);
|
|
ir_write_str_lit(f, ", metadata !DIExpression())");
|
|
ir_print_debug_location(f, m, value);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
|
|
set_procedure_abi_types(proc->type);
|
|
|
|
if (proc->body == nullptr) {
|
|
ir_write_str_lit(f, "declare ");
|
|
// if (proc->tags & ProcTag_dll_import) {
|
|
// ir_write_string(f, "dllimport ");
|
|
// }
|
|
} else {
|
|
ir_write_byte(f, '\n');
|
|
ir_write_str_lit(f, "define ");
|
|
if (proc->is_export) {
|
|
ir_write_str_lit(f, "dllexport ");
|
|
}
|
|
// if (!proc->is_export && !proc->is_foreign && !proc->is_entry_point) {
|
|
// ir_write_string(f, "internal ");
|
|
// }
|
|
}
|
|
|
|
TypeProc *proc_type = &proc->type->Proc;
|
|
|
|
ir_print_calling_convention(f, m, proc_type->calling_convention);
|
|
|
|
isize param_count = proc_type->param_count;
|
|
isize result_count = proc_type->result_count;
|
|
ir_print_proc_results(f, m, proc->type);
|
|
ir_write_byte(f, ' ');
|
|
|
|
// #ifndef GB_SYSTEM_WINDOWS
|
|
#if 0
|
|
if(uses_args)
|
|
ir_write_string(f, "@.nix_argpatch_main");
|
|
else
|
|
#endif
|
|
ir_print_encoded_global(f, proc->name, ir_print_is_proc_global(m, proc));
|
|
|
|
|
|
isize param_index = 0;
|
|
|
|
ir_write_byte(f, '(');
|
|
if (proc_type->return_by_pointer) {
|
|
ir_print_type(f, m, reduce_tuple_to_single_type(proc_type->results));
|
|
ir_write_str_lit(f, "* sret noalias ");
|
|
if (build_context.ODIN_OS == "darwin") {
|
|
ir_fprintf(f, "byval ");
|
|
}
|
|
ir_write_str_lit(f, "%agg.result");
|
|
param_index += 1;
|
|
}
|
|
|
|
if (param_count > 0) {
|
|
TypeTuple *params = &proc_type->params->Tuple;
|
|
isize parameter_index = 0;
|
|
for (isize i = 0; i < param_count; i++, parameter_index++) {
|
|
Entity *e = params->variables[i];
|
|
Type *original_type = e->type;
|
|
Type *abi_type = proc_type->abi_compat_params[i];
|
|
if (e->kind != Entity_Variable) continue;
|
|
if (param_index > 0) ir_write_string(f, str_lit(", "));
|
|
|
|
if (i+1 == params->variables.count && proc_type->c_vararg) {
|
|
ir_write_str_lit(f, " ...");
|
|
} else {
|
|
if (is_type_tuple(abi_type)) {
|
|
for_array(j, abi_type->Tuple.variables) {
|
|
if (j > 0) ir_write_string(f, str_lit(", "));
|
|
|
|
Type *tft = abi_type->Tuple.variables[j]->type;
|
|
ir_print_type(f, m, tft);
|
|
if (e->flags&EntityFlag_NoAlias) {
|
|
ir_write_str_lit(f, " noalias");
|
|
}
|
|
if (e->flags&EntityFlag_ByVal) {
|
|
ir_write_str_lit(f, " byval");
|
|
}
|
|
|
|
|
|
if (proc->body != nullptr) {
|
|
ir_fprintf(f, " %%_.%td", parameter_index+j);
|
|
}
|
|
}
|
|
parameter_index += abi_type->Tuple.variables.count-1;
|
|
param_index += abi_type->Tuple.variables.count-1;
|
|
} else {
|
|
ir_print_type(f, m, abi_type);
|
|
if (e->flags&EntityFlag_NoAlias) {
|
|
ir_write_str_lit(f, " noalias");
|
|
}
|
|
if (e->flags&EntityFlag_ByVal) {
|
|
ir_write_str_lit(f, " byval");
|
|
}
|
|
if (proc->body != nullptr) {
|
|
ir_fprintf(f, " %%_.%td", parameter_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
param_index++;
|
|
}
|
|
}
|
|
if (proc_type->calling_convention == ProcCC_Odin) {
|
|
if (param_index > 0) ir_write_str_lit(f, ", ");
|
|
|
|
ir_print_context_parameter_prefix(f, m);
|
|
ir_write_str_lit(f, "%__.context_ptr");
|
|
}
|
|
|
|
ir_write_str_lit(f, ") ");
|
|
|
|
switch (proc->inlining) {
|
|
default:
|
|
ir_fprintf(f, "#0 ");
|
|
break;
|
|
case ProcInlining_inline:
|
|
ir_write_str_lit(f, "alwaysinline ");
|
|
ir_fprintf(f, "#1 ");
|
|
break;
|
|
case ProcInlining_no_inline:
|
|
ir_write_str_lit(f, "noinline ");
|
|
ir_fprintf(f, "#2 ");
|
|
break;
|
|
}
|
|
|
|
if (proc_type->diverging) {
|
|
ir_write_str_lit(f, "noreturn ");
|
|
}
|
|
|
|
if (m->generate_debug_info && proc->entity != nullptr && proc->body != nullptr) {
|
|
irDebugInfo **di_ = map_get(&proc->module->debug_info, hash_pointer(proc->entity));
|
|
if (di_ != nullptr) {
|
|
irDebugInfo *di = *di_;
|
|
GB_ASSERT(di->kind == irDebugInfo_Proc);
|
|
ir_fprintf(f, "!dbg !%d ", di->id); // TODO(lachsinc): !dbg
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (proc->body != nullptr) {
|
|
// ir_fprintf(f, "nounwind uwtable {\n");
|
|
|
|
ir_write_str_lit(f, "{\n");
|
|
for_array(i, proc->blocks) {
|
|
irBlock *block = proc->blocks[i];
|
|
|
|
if (i > 0) ir_write_byte(f, '\n');
|
|
ir_print_block_name(f, block);
|
|
ir_write_str_lit(f, ":\n");
|
|
|
|
for_array(j, block->instrs) {
|
|
irValue *value = block->instrs[j];
|
|
ir_print_instr(f, m, value);
|
|
}
|
|
}
|
|
ir_write_str_lit(f, "}\n");
|
|
} else {
|
|
ir_write_byte(f, '\n');
|
|
}
|
|
|
|
for_array(i, proc->children) {
|
|
ir_print_proc(f, m, proc->children[i]);
|
|
}
|
|
}
|
|
|
|
|
|
void ir_print_type_name(irFileBuffer *f, irModule *m, irValue *v) {
|
|
GB_ASSERT(v->kind == irValue_TypeName);
|
|
Type *t = base_type(v->TypeName.type);
|
|
|
|
ir_print_encoded_local(f, v->TypeName.name);
|
|
ir_write_string(f, str_lit(" = type "));
|
|
|
|
|
|
switch (t->kind) {
|
|
case Type_Union:
|
|
if (t->Union.variants.count == 0) {
|
|
ir_write_string(f, str_lit("{}"));
|
|
} else {
|
|
ir_print_type(f, m, t);
|
|
}
|
|
break;
|
|
case Type_Struct:
|
|
if (t->Struct.fields.count == 0) {
|
|
if (t->Struct.is_packed) {
|
|
ir_write_byte(f, '<');
|
|
}
|
|
ir_write_byte(f, '{');
|
|
if (t->Struct.custom_align > 0) {
|
|
ir_print_alignment_prefix_hack(f, t->Struct.custom_align);
|
|
}
|
|
ir_write_byte(f, '}');
|
|
if (t->Struct.is_packed) {
|
|
ir_write_byte(f, '>');
|
|
}
|
|
} else {
|
|
ir_print_type(f, m, t);
|
|
}
|
|
break;
|
|
default:
|
|
ir_print_type(f, m, t);
|
|
break;
|
|
}
|
|
|
|
ir_write_byte(f, '\n');
|
|
}
|
|
|
|
bool ir_print_global_type_allowed(Type *t) {
|
|
if (t == nullptr) {
|
|
return true;
|
|
}
|
|
t = core_type(t);
|
|
switch (t->kind) {
|
|
case Type_DynamicArray:
|
|
case Type_Map:
|
|
case Type_Union:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void print_llvm_ir(irGen *ir) {
|
|
irModule *m = &ir->module;
|
|
|
|
irFileBuffer buf = {}, *f = &buf;
|
|
ir_file_buffer_init(f, &ir->output_file);
|
|
defer (ir_file_buffer_destroy(f));
|
|
|
|
i32 word_bits = cast(i32)(8*build_context.word_size);
|
|
if (build_context.ODIN_OS == "darwin") {
|
|
GB_ASSERT(word_bits == 64);
|
|
ir_write_str_lit(f, "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n");
|
|
ir_write_str_lit(f, "target triple = \"x86_64-apple-macosx10.8\"\n");
|
|
ir_write_str_lit(f, "\n");
|
|
} else if (build_context.ODIN_OS == "windows") {
|
|
ir_fprintf(f, "target triple = \"x86%s-pc-windows-msvc\"\n\n", word_bits == 64 ? "_64" : "");
|
|
if (word_bits == 64 && build_context.metrics.arch == TargetArch_amd64) {
|
|
ir_fprintf(f, "target datalayout = \"e-m:w-i64:64-f80:128-n8:16:32:64-S128\"\n\n");
|
|
}
|
|
}
|
|
|
|
ir_print_encoded_local(f, str_lit("..opaque"));
|
|
ir_write_str_lit(f, " = type {};\n");
|
|
ir_print_encoded_local(f, str_lit("..string"));
|
|
ir_write_str_lit(f, " = type {i8*, ");
|
|
ir_print_type(f, m, t_int);
|
|
ir_write_str_lit(f, "} ; Basic_string\n");
|
|
ir_print_encoded_local(f, str_lit("..rawptr"));
|
|
ir_write_str_lit(f, " = type i8* ; Basic_rawptr\n");
|
|
|
|
ir_print_encoded_local(f, str_lit("..complex32"));
|
|
ir_write_str_lit(f, " = type {half, half} ; Basic_complex32\n");
|
|
ir_print_encoded_local(f, str_lit("..complex64"));
|
|
ir_write_str_lit(f, " = type {float, float} ; Basic_complex64\n");
|
|
ir_print_encoded_local(f, str_lit("..complex128"));
|
|
ir_write_str_lit(f, " = type {double, double} ; Basic_complex128\n");
|
|
|
|
ir_print_encoded_local(f, str_lit("..quaternion64"));
|
|
ir_write_str_lit(f, " = type {half, half, half, half} ; Basic_quaternion64\n");
|
|
ir_print_encoded_local(f, str_lit("..quaternion128"));
|
|
ir_write_str_lit(f, " = type {float, float, float, float} ; Basic_quaternion128\n");
|
|
ir_print_encoded_local(f, str_lit("..quaternion256"));
|
|
ir_write_str_lit(f, " = type {double, double, double, double} ; Basic_quaternion256\n");
|
|
|
|
ir_print_encoded_local(f, str_lit("..typeid"));
|
|
ir_write_str_lit(f, " = type ");
|
|
ir_print_type(f, m, t_uintptr);
|
|
ir_write_str_lit(f, " ; Basic_typeid\n");
|
|
|
|
ir_print_encoded_local(f, str_lit("..any"));
|
|
ir_write_str_lit(f, " = type {");
|
|
ir_print_type(f, m, t_rawptr);
|
|
ir_write_str_lit(f, ", ");
|
|
ir_print_type(f, m, t_typeid);
|
|
ir_write_str_lit(f, "} ; Basic_any\n");
|
|
|
|
ir_write_str_lit(f, "declare void @llvm.dbg.declare(metadata, metadata, metadata) #3 \n");
|
|
|
|
if (string_map_get(&m->members, str_lit("llvm.bswap.i16")) == nullptr) {
|
|
ir_write_str_lit(f, "declare i16 @llvm.bswap.i16(i16) \n");
|
|
}
|
|
if (string_map_get(&m->members, str_lit("llvm.bswap.i32")) == nullptr) {
|
|
ir_write_str_lit(f, "declare i32 @llvm.bswap.i32(i32) \n");
|
|
}
|
|
if (string_map_get(&m->members, str_lit("llvm.bswap.i64")) == nullptr) {
|
|
ir_write_str_lit(f, "declare i64 @llvm.bswap.i64(i64) \n");
|
|
}
|
|
if (string_map_get(&m->members, str_lit("llvm.bswap.i128")) == nullptr) {
|
|
ir_write_str_lit(f, "declare i128 @llvm.bswap.i128(i128) \n");
|
|
}
|
|
ir_write_byte(f, '\n');
|
|
|
|
|
|
for_array(member_index, m->members.entries) {
|
|
auto *entry = &m->members.entries[member_index];
|
|
irValue *v = entry->value;
|
|
if (v->kind != irValue_TypeName) {
|
|
continue;
|
|
}
|
|
ir_print_type_name(f, m, v);
|
|
}
|
|
|
|
ir_write_byte(f, '\n');
|
|
|
|
bool dll_main_found = false;
|
|
|
|
// NOTE(bill): Print foreign prototypes first
|
|
for_array(member_index, m->members.entries) {
|
|
auto *entry = &m->members.entries[member_index];
|
|
irValue *v = entry->value;
|
|
if (v->kind != irValue_Proc) {
|
|
continue;
|
|
}
|
|
|
|
if (v->Proc.body == nullptr) {
|
|
ir_print_proc(f, m, &v->Proc);
|
|
}
|
|
}
|
|
|
|
if (ir->print_chkstk) {
|
|
// TODO(bill): Clean up this code
|
|
ir_write_str_lit(f, "\n\n");
|
|
ir_write_str_lit(f, "define void @__chkstk() #0 {\n");
|
|
ir_write_str_lit(f, "\tcall void asm sideeffect \"push %rcx \\09\\0Apush %rax \\09\\0Acmp $$0x1000,%rax \\09\\0Alea 24(%rsp),%rcx \\09\\0Ajb 1f \\09\\0A2: \\09\\0Asub $$0x1000,%rcx \\09\\0Aorl $$0,(%rcx) \\09\\0Asub $$0x1000,%rax \\09\\0Acmp $$0x1000,%rax \\09\\0Aja 2b \\09\\0A1: \\09\\0Asub %rax,%rcx \\09\\0Aorl $$0,(%rcx) \\09\\0Apop %rax \\09\\0Apop %rcx \\09\\0Aret \\09\\0A\", \"~{dirflag},~{fpsr},~{flags}\"()\n");
|
|
ir_write_str_lit(f, "\tret void\n");
|
|
ir_write_str_lit(f, "}\n\n");
|
|
}
|
|
|
|
// NOTE(bill): Print procedures with bodies next
|
|
for_array(member_index, m->members.entries) {
|
|
auto *entry = &m->members.entries[member_index];
|
|
irValue *v = entry->value;
|
|
if (v->kind != irValue_Proc) {
|
|
continue;
|
|
}
|
|
|
|
if (v->Proc.body != nullptr) {
|
|
ir_print_proc(f, m, &v->Proc);
|
|
}
|
|
}
|
|
|
|
for_array(member_index, m->members.entries) {
|
|
auto *entry = &m->members.entries[member_index];
|
|
irValue *v = entry->value;
|
|
if (v->kind != irValue_Global) {
|
|
continue;
|
|
}
|
|
irValueGlobal *g = &v->Global;
|
|
Scope *scope = g->entity->scope;
|
|
bool in_global_scope = false;
|
|
if (scope != nullptr) {
|
|
// TODO(bill): Fix this rule. What should it be?
|
|
in_global_scope = (scope->flags & ScopeFlag_Global) != 0;
|
|
}
|
|
|
|
ir_print_encoded_global(f, ir_get_global_name(m, v), in_global_scope);
|
|
ir_write_string(f, str_lit(" = "));
|
|
if (g->is_foreign) {
|
|
ir_write_string(f, str_lit("external "));
|
|
}
|
|
if (g->is_export) {
|
|
ir_write_string(f, str_lit("dllexport "));
|
|
}
|
|
|
|
if (g->is_private) {
|
|
ir_write_string(f, str_lit("private "));
|
|
} else if (g->is_internal) {
|
|
ir_write_string(f, str_lit("internal "));
|
|
}
|
|
if (g->thread_local_model.len > 0) {
|
|
String model = g->thread_local_model;
|
|
if (model == "default") {
|
|
ir_write_string(f, str_lit("thread_local "));
|
|
} else {
|
|
ir_fprintf(f, "thread_local(%.*s) ", LIT(model));
|
|
|
|
}
|
|
}
|
|
if (g->is_constant) {
|
|
if (g->is_unnamed_addr) {
|
|
ir_write_string(f, str_lit("unnamed_addr "));
|
|
}
|
|
ir_write_string(f, str_lit("constant "));
|
|
} else {
|
|
ir_write_string(f, str_lit("global "));
|
|
}
|
|
|
|
|
|
ir_print_type(f, m, g->entity->type);
|
|
ir_write_byte(f, ' ');
|
|
if (!g->is_foreign) {
|
|
if (g->value != nullptr && ir_print_global_type_allowed(g->entity->type)) {
|
|
ir_print_value(f, m, g->value, g->entity->type);
|
|
} else {
|
|
ir_write_string(f, str_lit("zeroinitializer"));
|
|
}
|
|
if (m->generate_debug_info) {
|
|
irDebugInfo **di_lookup = map_get(&m->debug_info, hash_entity(g->entity));
|
|
if (di_lookup != nullptr) {
|
|
irDebugInfo *di = *di_lookup;
|
|
GB_ASSERT(di);
|
|
GB_ASSERT(di->kind == irDebugInfo_GlobalVariableExpression);
|
|
ir_fprintf(f, ", !dbg !%d", di->id);
|
|
}
|
|
}
|
|
}
|
|
ir_write_byte(f, '\n');
|
|
}
|
|
|
|
// TODO(lachsinc): Attribute map inside ir module?
|
|
ir_fprintf(f, "attributes #0 = {nounwind uwtable}\n");
|
|
ir_fprintf(f, "attributes #1 = {nounwind alwaysinline uwtable}\n");
|
|
ir_fprintf(f, "attributes #2 = {nounwind noinline optnone uwtable}\n");
|
|
ir_fprintf(f, "attributes #3 = {nounwind readnone}\n");
|
|
|
|
if (m->generate_debug_info) {
|
|
ir_write_byte(f, '\n');
|
|
|
|
i32 diec = cast(i32)m->debug_info.entries.count;
|
|
|
|
i32 di_version = diec+1;
|
|
i32 di_debug_info = diec+2;
|
|
i32 di_code_view = diec+3;
|
|
i32 di_wchar_size = diec+4;
|
|
|
|
|
|
|
|
ir_fprintf(f, "!llvm.dbg.cu = !{!%d}\n", m->debug_compile_unit->id);
|
|
ir_fprintf(f, "!llvm.ident = !{!%d}\n", di_version);
|
|
ir_fprintf(f, "!llvm.module.flags = !{!%d, !%d, !%d}\n", di_debug_info, di_code_view, di_wchar_size);
|
|
|
|
ir_fprintf(f, "!0 = !{}\n");
|
|
|
|
for_array(di_index, m->debug_info.entries) {
|
|
irDebugInfo *di = m->debug_info.entries[di_index].value;
|
|
GB_ASSERT_MSG(di != nullptr, "Invalid irDebugInfo");
|
|
ir_fprintf(f, "!%d = ", di->id);
|
|
switch (di->kind) {
|
|
case irDebugInfo_CompileUnit: {
|
|
irDebugInfo **found = map_get(&m->debug_info, hash_pointer(di->CompileUnit.file));
|
|
GB_ASSERT_MSG(found != nullptr, "Missing debug info for: %.*s\n", LIT(di->CompileUnit.file->fullpath));
|
|
irDebugInfo *file = *found;
|
|
ir_fprintf(f,
|
|
"distinct !DICompileUnit("
|
|
"language: DW_LANG_C_plus_plus" // Is this good enough?
|
|
", file: !%d"
|
|
", producer: \"Odin %.*s\""
|
|
", runtimeVersion: 0"
|
|
", isOptimized: false"
|
|
", emissionKind: FullDebug"
|
|
", retainedTypes: !0" // TODO(lachsinc)
|
|
", enums: !%d"
|
|
", globals: !%d"
|
|
")",
|
|
file->id,
|
|
LIT(build_context.ODIN_VERSION),
|
|
m->debug_compile_unit->CompileUnit.enums->id,
|
|
m->debug_compile_unit->CompileUnit.globals->id);
|
|
break;
|
|
}
|
|
case irDebugInfo_File:
|
|
ir_fprintf(f, "!DIFile(filename: \""); ir_print_escape_path(f, di->File.filename);
|
|
ir_fprintf(f, "\", directory: \""); ir_print_escape_path(f, di->File.directory);
|
|
ir_fprintf(f, "\"");
|
|
ir_fprintf(f, ")");
|
|
break;
|
|
case irDebugInfo_Proc:
|
|
// TODO(lachsinc): We need to store scope info inside di, not just file info, for procs.
|
|
// Should all subprograms have distinct ??
|
|
ir_fprintf(f, "distinct !DISubprogram("
|
|
"name: \"%.*s\""
|
|
", linkageName: \"%.*s\""
|
|
", scope: !%d"
|
|
", file: !%d"
|
|
", line: %d"
|
|
", scopeLine: %d"
|
|
", isDefinition: true"
|
|
", isLocal: false" // TODO(lachsinc): Is this fine?
|
|
", flags: DIFlagPrototyped"
|
|
", isOptimized: false"
|
|
", unit: !%d"
|
|
", type: !%d",
|
|
LIT(di->Proc.entity->token.string),
|
|
LIT(di->Proc.name),
|
|
di->Proc.file->id, // TODO(lachsinc): HACK For now lets pretend all procs scope's == file.
|
|
di->Proc.file->id,
|
|
di->Proc.pos.line,
|
|
di->Proc.pos.line, // NOTE(lachsinc): Assume scopeLine always same as line.
|
|
m->debug_compile_unit->id,
|
|
di->Proc.type->id);
|
|
ir_write_byte(f, ')'); // !DISubprogram(
|
|
break;
|
|
case irDebugInfo_ProcType:
|
|
ir_fprintf(f, "!DISubroutineType(types: !%d)",
|
|
di->ProcType.types->id);
|
|
break;
|
|
case irDebugInfo_Location:
|
|
GB_ASSERT_NOT_NULL(di->Location.scope);
|
|
ir_fprintf(f, "!DILocation("
|
|
"line: %d"
|
|
", column: %d"
|
|
", scope: !%d)",
|
|
di->Location.pos.line,
|
|
di->Location.pos.column,
|
|
di->Location.scope->id);
|
|
break;
|
|
case irDebugInfo_LexicalBlock:
|
|
GB_ASSERT_NOT_NULL(di->LexicalBlock.file);
|
|
GB_ASSERT_NOT_NULL(di->LexicalBlock.scope);
|
|
ir_fprintf(f, "distinct !DILexicalBlock("
|
|
"line: %d"
|
|
", column: %d"
|
|
", file: !%d"
|
|
", scope: !%d)",
|
|
di->LexicalBlock.pos.line,
|
|
di->LexicalBlock.pos.column,
|
|
di->LexicalBlock.file->id,
|
|
di->LexicalBlock.scope->id);
|
|
break;
|
|
case irDebugInfo_GlobalVariableExpression: {
|
|
ir_fprintf(f, "!DIGlobalVariableExpression("
|
|
"var: !%d"
|
|
", expr: !DIExpression(",
|
|
di->GlobalVariableExpression.var->id);
|
|
if (di->GlobalVariableExpression.var->GlobalVariable.variable->Global.is_constant) {
|
|
ir_write_str_lit(f, "DW_OP_constu, ");
|
|
ir_print_value(f, m, di->GlobalVariable.variable, ir_type(di->GlobalVariable.variable));
|
|
ir_write_str_lit(f, ", DW_OP_stack_value");
|
|
} else {
|
|
// NOTE(lachsinc): non-const globals expect empty "!DIExpression()"
|
|
}
|
|
ir_write_byte(f, ')'); // !DIExpression(
|
|
ir_write_byte(f, ')'); // !DIGlobalVariableExpression(
|
|
break;
|
|
}
|
|
case irDebugInfo_GlobalVariable: {
|
|
ir_fprintf(f, "distinct !DIGlobalVariable("
|
|
"name: \"%.*s\""
|
|
", scope: !%d"
|
|
", file: !%d"
|
|
", line: %d"
|
|
", type: !%d"
|
|
", isLocal: true" // TODO(lachsinc): Check locality ??
|
|
", isDefinition: true)", // TODO(lachsinc): ??
|
|
LIT(di->GlobalVariable.name),
|
|
di->GlobalVariable.scope->id,
|
|
di->GlobalVariable.file->id,
|
|
di->GlobalVariable.pos.line,
|
|
di->GlobalVariable.type->id);
|
|
break;
|
|
}
|
|
case irDebugInfo_LocalVariable: {
|
|
ir_fprintf(f, "!DILocalVariable("
|
|
"scope: !%d"
|
|
", file: !%d"
|
|
", line: %d"
|
|
", type: !%d",
|
|
di->LocalVariable.scope->id,
|
|
di->LocalVariable.file->id,
|
|
di->LocalVariable.pos.line,
|
|
di->LocalVariable.type->id);
|
|
if (di->DerivedType.name.len > 0) {
|
|
ir_fprintf(f, ", name: \"%.*s\"", LIT(di->LocalVariable.name));
|
|
}
|
|
if (di->LocalVariable.arg > 0) {
|
|
ir_fprintf(f, ", arg: %d", di->LocalVariable.arg);
|
|
}
|
|
ir_write_byte(f, ')');
|
|
break;
|
|
}
|
|
case irDebugInfo_BasicType:
|
|
ir_fprintf(f, "!DIBasicType("
|
|
"name: \"%.*s\""
|
|
", size: %d"
|
|
", encoding: ",
|
|
LIT(di->BasicType.name),
|
|
di->BasicType.size);
|
|
ir_print_debug_encoding(f, irDebugInfo_BasicType, di->BasicType.encoding);
|
|
ir_write_byte(f, ')');
|
|
break;
|
|
case irDebugInfo_DerivedType: {
|
|
if (di->DerivedType.tag == irDebugBasicEncoding_member) {
|
|
// NOTE(lachsinc): We crash llvm super hard if we don't specify a name :)
|
|
Type *t = di->DerivedType.type;
|
|
GB_ASSERT_MSG(di->DerivedType.name.len > 0, "%s", type_to_string(di->DerivedType.type));
|
|
}
|
|
ir_write_str_lit(f, "!DIDerivedType(tag: ");
|
|
ir_print_debug_encoding(f, irDebugInfo_DerivedType, di->DerivedType.tag);
|
|
if (di->DerivedType.name.len > 0) {
|
|
ir_fprintf(f, ", name: \"%.*s\"", LIT(di->DerivedType.name));
|
|
}
|
|
if (di->DerivedType.base_type != nullptr) {
|
|
ir_fprintf(f, ", baseType: !%d", di->DerivedType.base_type->id);
|
|
} else {
|
|
ir_write_str_lit(f, ", baseType: null"); // Valid/required for rawptr
|
|
}
|
|
if (di->DerivedType.size > 0) ir_fprintf(f, ", size: %d", di->DerivedType.size);
|
|
if (di->DerivedType.align > 0) ir_fprintf(f, ", align: %d", di->DerivedType.align);
|
|
if (di->DerivedType.offset > 0) ir_fprintf(f, ", offset: %d", di->DerivedType.offset);
|
|
if (di->DerivedType.flags > 0) {
|
|
// TODO(lachsinc): Handle in a more generic manner.
|
|
if (di->DerivedType.flags & irDebugInfoFlag_Bitfield) ir_write_str_lit(f, ", flags: DIFlagBitField, extraData: i64 0");
|
|
}
|
|
ir_write_byte(f, ')');
|
|
break;
|
|
}
|
|
case irDebugInfo_CompositeType: {
|
|
if (di->CompositeType.tag == irDebugBasicEncoding_array_type) {
|
|
GB_ASSERT_NOT_NULL(di->CompositeType.base_type);
|
|
GB_ASSERT(di->CompositeType.array_count >= 0);
|
|
GB_ASSERT(di->CompositeType.name.len == 0);
|
|
GB_ASSERT(di->CompositeType.size >= 0);
|
|
}
|
|
|
|
if (di->CompositeType.tag == irDebugBasicEncoding_union_type) {
|
|
GB_ASSERT_NOT_NULL(di->CompositeType.file); // Union _requires_ file to be valid.
|
|
}
|
|
|
|
ir_write_str_lit(f, "!DICompositeType(tag: ");
|
|
ir_print_debug_encoding(f, irDebugInfo_CompositeType, di->CompositeType.tag);
|
|
if (di->CompositeType.name.len > 0) {
|
|
ir_fprintf(f, ", name: \"%.*s\"", LIT(di->CompositeType.name));
|
|
}
|
|
if (di->CompositeType.scope != nullptr) {
|
|
ir_fprintf(f, ", scope: !%d", di->CompositeType.scope->id);
|
|
}
|
|
if (di->CompositeType.file != nullptr) {
|
|
ir_fprintf(f, ", file: !%d"
|
|
", line: %d",
|
|
di->CompositeType.file->id,
|
|
di->CompositeType.pos.line);
|
|
}
|
|
if (di->CompositeType.size > 0) ir_fprintf(f, ", size: %d", di->CompositeType.size);
|
|
if (di->CompositeType.align > 0) ir_fprintf(f, ", align: %d", di->CompositeType.align);
|
|
if (di->CompositeType.base_type != nullptr) {
|
|
GB_ASSERT(di->CompositeType.tag != irDebugBasicEncoding_structure_type);
|
|
GB_ASSERT(di->CompositeType.tag != irDebugBasicEncoding_union_type);
|
|
ir_fprintf(f, ", baseType: !%d", di->CompositeType.base_type->id);
|
|
}
|
|
if (di->CompositeType.tag == irDebugBasicEncoding_array_type) {
|
|
ir_fprintf(f, ", elements: !{!DISubrange(count: %d)}", di->CompositeType.array_count);
|
|
} else {
|
|
if (di->CompositeType.elements != nullptr) {
|
|
ir_fprintf(f, ", elements: !%d", di->CompositeType.elements->id);
|
|
}
|
|
}
|
|
ir_write_byte(f, ')');
|
|
break;
|
|
}
|
|
case irDebugInfo_Enumerator: {
|
|
ir_fprintf(f, "!DIEnumerator("
|
|
"name: \"%.*s\""
|
|
", value: %lld)",
|
|
LIT(di->Enumerator.name),
|
|
di->Enumerator.value);
|
|
break;
|
|
}
|
|
case irDebugInfo_DebugInfoArray:
|
|
ir_fprintf(f, "!{");
|
|
for_array(element_index, di->DebugInfoArray.elements) {
|
|
irDebugInfo *elem = di->DebugInfoArray.elements[element_index];
|
|
if (element_index > 0) ir_write_str_lit(f, ", ");
|
|
if (elem != nullptr) {
|
|
ir_fprintf(f, "!%d", elem->id);
|
|
} else {
|
|
ir_fprintf(f, "null"); // NOTE(lachsinc): Proc's can contain "nullptr" entries to represent void return values.
|
|
}
|
|
}
|
|
ir_write_byte(f, '}');
|
|
break;
|
|
|
|
default:
|
|
GB_PANIC("Unhandled irDebugInfo kind %d", di->kind);
|
|
break;
|
|
}
|
|
|
|
ir_write_byte(f, '\n');
|
|
}
|
|
|
|
|
|
ir_fprintf(f, "!%d = !{!\"Odin version %.*s \"}\n", di_version, LIT(build_context.ODIN_VERSION));
|
|
ir_fprintf(f, "!%d = !{i32 2, !\"Debug Info Version\", i32 3}\n", di_debug_info);
|
|
ir_fprintf(f, "!%d = !{i32 2, !\"CodeView\", i32 1}\n", di_code_view);
|
|
ir_fprintf(f, "!%d = !{i32 1, !\"wchar_size\", i32 2}\n", di_wchar_size);
|
|
}
|
|
}
|