aboutsummaryrefslogtreecommitdiff
path: root/src/llvm_backend_utility.cpp
diff options
context:
space:
mode:
authorUsername-Leon <leonardo.temperanza@gmail.com>2025-10-15 15:30:32 +0200
committerUsername-Leon <leonardo.temperanza@gmail.com>2025-10-15 15:30:32 +0200
commit4dd6bb2e871e9b8583a89e62ce36a98d02d48cc3 (patch)
tree25b7599645caf76ebe9136da51e5114006ab93f4 /src/llvm_backend_utility.cpp
parente10093bd991334789031df9ed587d27823ca90dd (diff)
parent596066aa0453752f24cc2fa5087fafe2c4686536 (diff)
Merge branch 'master' of https://github.com/LeonardoTemperanza/Odin
Diffstat (limited to 'src/llvm_backend_utility.cpp')
-rw-r--r--src/llvm_backend_utility.cpp379
1 files changed, 317 insertions, 62 deletions
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 7fe6b1458..182928247 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -286,7 +286,14 @@ gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
}
}
+ bool is_simd_vector_bitcastable = false;
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
+ if (!is_type_internally_pointer_like(src->SimdVector.elem) && !is_type_internally_pointer_like(dst->SimdVector.elem)) {
+ is_simd_vector_bitcastable = true;
+ }
+ }
+
+ if (is_simd_vector_bitcastable) {
res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
return res;
} else if (is_type_array_like(src) && (is_type_simd_vector(dst) || is_type_integer_128bit(dst))) {
@@ -2095,10 +2102,8 @@ gb_internal LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const
cast(char *)str.text, cast(size_t)str.len,
cast(char *)clobbers.text, cast(size_t)clobbers.len,
has_side_effects, is_align_stack,
- dialect
- #if LLVM_VERSION_MAJOR >= 13
- , /*CanThrow*/false
- #endif
+ dialect,
+ /*CanThrow*/false
);
}
@@ -2264,12 +2269,12 @@ gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) {
}
gb_internal void lb_create_objc_block_helper_procs(
- lbModule *m, LLVMTypeRef block_lit_type, isize capture_field_offset,
+ lbModule *m, LLVMTypeRef block_lit_type, isize capture_field_offset, isize block_id,
Slice<lbValue> capture_values, Slice<isize> objc_object_indices,
lbProcedure *&out_copy_helper, lbProcedure *&out_dispose_helper
) {
- gbString copy_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_copy_helper_%lld", m->objc_next_block_id);
- gbString dispose_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_dispose_helper_%lld", m->objc_next_block_id);
+ gbString copy_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$%s::objc_block_copy_helper_%lld", m->module_name, block_id);
+ gbString dispose_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$%s::objc_block_dispose_helper_%lld", m->module_name, block_id);
// copy: Block_Literal *dst, Block_Literal *src, i32 field_apropos
// dispose: Block_Literal *src, i32 field_apropos
@@ -2373,14 +2378,12 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
/// https://www.newosxbook.com/src.php?tree=xnu&file=/libkern/libkern/Block_private.h
/// https://github.com/llvm/llvm-project/blob/21f1f9558df3830ffa637def364e3c0cb0dbb3c0/compiler-rt/lib/BlocksRuntime/Block_private.h
/// https://github.com/apple-oss-distributions/libclosure/blob/3668b0837f47be3cc1c404fb5e360f4ff178ca13/runtime.cpp
-
ast_node(ce, CallExpr, expr);
GB_ASSERT(ce->args.count > 0);
lbModule *m = p->module;
- m->objc_next_block_id += 1;
-
+ const isize block_id = m->objc_next_block_id++;
const isize capture_arg_count = ce->args.count - 1;
Type *block_result_type = type_of_expr(expr);
@@ -2425,7 +2428,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
// Create proc with the block signature
// (takes a block literal pointer as the first parameter, followed by any expected ones from the user's proc)
- gbString block_invoker_name = gb_string_append_fmt(gb_string_make(permanent_allocator(), ""), "__$objc_block_invoker_%lld", m->objc_next_block_id);
+ gbString block_invoker_name = gb_string_append_fmt(gb_string_make(permanent_allocator(), ""), "__$%s::objc_block_invoker_%lld", m->module_name, block_id);
// Add + 1 because the first parameter received is the block literal pointer itself
auto invoker_args = array_make<Type *>(temporary_allocator(), block_forward_args + 1, block_forward_args + 1);
@@ -2452,14 +2455,16 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
lbProcedure *invoker_proc = lb_create_dummy_procedure(m, make_string((u8*)block_invoker_name,
gb_string_length(block_invoker_name)), invoker_proc_type);
+
LLVMSetLinkage(invoker_proc->value, LLVMPrivateLinkage);
+ lb_add_function_type_attributes(invoker_proc->value, lb_get_function_type(m, invoker_proc_type), ProcCC_CDecl);
// Create the block descriptor and block literal
- gbString block_lit_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Literal_");
- block_lit_type_name = gb_string_append_fmt(block_lit_type_name, "%lld", m->objc_next_block_id);
+ gbString block_lit_type_name = gb_string_make(temporary_allocator(), "");
+ block_lit_type_name = gb_string_append_fmt(block_lit_type_name, "__$%s::ObjC_Block_Literal_%lld", m->module_name, block_id);
- gbString block_desc_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Descriptor_");
- block_desc_type_name = gb_string_append_fmt(block_desc_type_name, "%lld", m->objc_next_block_id);
+ gbString block_desc_type_name = gb_string_make(temporary_allocator(), "");
+ block_desc_type_name = gb_string_append_fmt(block_desc_type_name, "__$%s::ObjC_Block_Descriptor_%lld", m->module_name,block_id);
LLVMTypeRef block_lit_type = {};
LLVMTypeRef block_desc_type = {};
@@ -2503,7 +2508,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
// Generate copy and dispose helper functions for captured params that are Objective-C objects (or a Block)
if (has_objc_fields) {
- lb_create_objc_block_helper_procs(m, block_lit_type, capture_fields_offset,
+ lb_create_objc_block_helper_procs(m, block_lit_type, capture_fields_offset, block_id,
slice(captured_values, 0, captured_values.count),
slice(objc_captures, 0, objc_captures.count),
copy_helper, dispose_helper);
@@ -2521,8 +2526,8 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
}
// Create global block descriptor
- gbString desc_global_name = gb_string_make(temporary_allocator(), "__$objc_block_desc_");
- desc_global_name = gb_string_append_fmt(desc_global_name, "%lld", m->objc_next_block_id);
+ gbString desc_global_name = gb_string_make(temporary_allocator(), "");
+ desc_global_name = gb_string_append_fmt(desc_global_name, "__$%s::objc_block_desc_%lld", m->module_name, block_id);
LLVMValueRef p_descriptor = LLVMAddGlobal(m->mod, block_desc_type, desc_global_name);
LLVMSetInitializer(p_descriptor, block_desc_initializer);
@@ -2531,45 +2536,66 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
/// Invoker body
lb_begin_procedure_body(invoker_proc);
{
- auto call_args = array_make<lbValue>(temporary_allocator(), user_proc.param_count, user_proc.param_count);
+ // Reserve 2 extra arguments for: Indirect return values and context.
+ auto call_args = array_make<LLVMValueRef>(temporary_allocator(), 0, user_proc.param_count + 2);
- for (isize i = 1; i < invoker_proc->raw_input_parameters.count; i++) {
- lbValue arg = {};
- arg.type = invoker_args[i];
- arg.value = invoker_proc->raw_input_parameters[i],
- call_args[i-1] = arg;
- }
+ isize block_literal_arg_index = 0;
- LLVMValueRef block_literal = invoker_proc->raw_input_parameters[0];
+ lbFunctionType* user_proc_ft = lb_get_function_type(m, user_proc_value.type);
- // Push context, if needed
- if (user_proc.calling_convention == ProcCC_Odin) {
- LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context");
- lbValue ctx_val = {};
- ctx_val.type = t_context_ptr;
- ctx_val.value = p_context;
+ lbArgKind return_kind = {};
+
+ GB_ASSERT(user_proc.result_count <= 1);
+ if (user_proc.result_count > 0) {
+ return_kind = user_proc_ft->ret.kind;
+
+ if (return_kind == lbArg_Indirect) {
+ // Forward indirect return value
+ array_add(&call_args, invoker_proc->raw_input_parameters[0]);
+ block_literal_arg_index = 1;
+ }
+ }
- lb_push_context_onto_stack(invoker_proc, lb_addr(ctx_val));
+ // Forward raw arguments
+ for (isize i = block_literal_arg_index+1; i < invoker_proc->raw_input_parameters.count; i++) {
+ array_add(&call_args, invoker_proc->raw_input_parameters[i]);
}
+ LLVMValueRef block_literal = invoker_proc->raw_input_parameters[block_literal_arg_index];
+
// Copy capture parameters from the block literal
+ isize capture_arg_in_user_proc_start_index = user_proc_ft->args.count - capture_arg_count;
+ if (user_proc.calling_convention == ProcCC_Odin) {
+ capture_arg_in_user_proc_start_index -= 1;
+ }
+
for (isize i = 0; i < capture_arg_count; i++) {
LLVMValueRef cap_value = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, unsigned(capture_fields_offset + i), "");
- lbValue cap_arg = {};
- cap_arg.value = cap_value;
- cap_arg.type = alloc_type_pointer(captured_values[i].type);
+ // Don't emit load if indirect. Pass the pointer as-is
+ isize cap_arg_index_in_user_proc = capture_arg_in_user_proc_start_index + i;
+
+ if (user_proc_ft->args[cap_arg_index_in_user_proc].kind != lbArg_Indirect) {
+ cap_value = OdinLLVMBuildLoad(invoker_proc, lb_type(invoker_proc->module, captured_values[i].type), cap_value);
+ }
+
+ array_add(&call_args, cap_value);
+ }
- lbValue arg = lb_emit_load(invoker_proc, cap_arg);
- call_args[block_forward_args+i] = arg;
+ // Push context, if needed
+ if (user_proc.calling_convention == ProcCC_Odin) {
+ LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context");
+ array_add(&call_args, p_context);
}
- lbValue result = lb_emit_call(invoker_proc, user_proc_value, call_args, proc_lit->ProcLit.inlining);
+ LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(m, user_proc_value.type);
+ LLVMValueRef ret_val = LLVMBuildCall2(invoker_proc->builder, fnp, user_proc_value.value, call_args.data, (unsigned)call_args.count, "");
- GB_ASSERT(user_proc.result_count <= 1);
- if (user_proc.result_count > 0) {
- GB_ASSERT(result.value != nullptr);
- LLVMBuildRet(p->builder, result.value);
+ if (user_proc.result_count > 0 && return_kind != lbArg_Indirect) {
+ LLVMBuildRet(invoker_proc->builder, ret_val);
+ }
+ else {
+ LLVMBuildRetVoid(invoker_proc->builder);
}
}
lb_end_procedure_body(invoker_proc);
@@ -2585,10 +2611,10 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
}
gbString block_var_name = gb_string_make(temporary_allocator(), "__$objc_block_literal_");
- block_var_name = gb_string_append_fmt(block_var_name, "%lld", m->objc_next_block_id);
+ block_var_name = gb_string_append_fmt(block_var_name, "%lld", block_id);
- lbValue result = {};
- result.type = block_result_type;
+ lbValue block_result = {};
+ block_result.type = block_result_type;
lbValue isa_val = lb_find_runtime_value(m, is_global ? str_lit("_NSConcreteGlobalBlock") : str_lit("_NSConcreteStackBlock"));
lbValue flags_val = lb_const_int(m, t_i32, (u64)raw_flags);
@@ -2596,7 +2622,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
if (is_global) {
LLVMValueRef p_block_lit = LLVMAddGlobal(m->mod, block_lit_type, block_var_name);
- result.value = p_block_lit;
+ block_result.value = p_block_lit;
LLVMValueRef fields_values[5] = {
isa_val.value, // isa
@@ -2611,7 +2637,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
} else {
LLVMValueRef p_block_lit = llvm_alloca(p, block_lit_type, lb_alignof(block_lit_type), block_var_name);
- result.value = p_block_lit;
+ block_result.value = p_block_lit;
// Initialize it
LLVMValueRef f_isa = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 0, "isa");
@@ -2651,7 +2677,20 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
}
}
- return result;
+ return block_result;
+}
+
+gb_internal lbValue lb_handle_objc_block_invoke(lbProcedure *p, Ast *expr) {
+ return {};
+}
+
+gb_internal lbValue lb_handle_objc_super(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+ GB_ASSERT(ce->args.count == 1);
+
+ // NOTE(harold): This doesn't actually do anything by itself,
+ // it has an effect when used on the left side of a selector call expression.
+ return lb_build_expr(p, ce->args[0]);
}
gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
@@ -2767,6 +2806,120 @@ gb_internal lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
return lb_emit_call(p, the_proc, args);
}
+gb_internal lbValue lb_handle_objc_auto_send(lbProcedure *p, Ast *expr, Slice<lbValue> const arg_values) {
+ ast_node(ce, CallExpr, expr);
+
+ lbModule *m = p->module;
+ CheckerInfo *info = m->info;
+ ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr);
+
+ Type *proc_type = data.proc_type;
+ GB_ASSERT(proc_type != nullptr);
+
+ Entity *objc_method_ent = entity_of_node(ce->proc);
+ GB_ASSERT(objc_method_ent != nullptr);
+ GB_ASSERT(objc_method_ent->kind == Entity_Procedure);
+ GB_ASSERT(objc_method_ent->Procedure.objc_selector_name.len > 0);
+
+ auto &proc = proc_type->Proc;
+ GB_ASSERT(proc.param_count >= 2);
+
+ Type *objc_super_orig_type = nullptr;
+ if (ce->args.count > 0) {
+ objc_super_orig_type = unparen_expr(ce->args[0])->tav.objc_super_target;
+ }
+
+ isize arg_offset = 1;
+ lbValue id = {};
+ if (!objc_method_ent->Procedure.is_objc_class_method) {
+ GB_ASSERT(ce->args.count > 0);
+ id = arg_values[0];
+
+ if (objc_super_orig_type) {
+ GB_ASSERT(objc_super_orig_type->kind == Type_Named);
+
+ auto& tn = objc_super_orig_type->Named.type_name->TypeName;
+ lbAddr p_supercls = lb_handle_objc_find_or_register_class(p, tn.objc_class_name, tn.objc_is_implementation ? objc_super_orig_type : nullptr);
+
+ lbValue supercls = lb_addr_load(p, p_supercls);
+ lbAddr p_objc_super = lb_add_local_generated(p, t_objc_super, false);
+
+ lbValue f_id = lb_emit_struct_ep(p, p_objc_super.addr, 0);
+ lbValue f_superclass = lb_emit_struct_ep(p, p_objc_super.addr, 1);
+
+ id = lb_emit_conv(p, id, t_objc_id);
+ lb_emit_store(p, f_id, id);
+ lb_emit_store(p, f_superclass, supercls);
+
+ id = p_objc_super.addr;
+ }
+ } else {
+ Entity *objc_class = objc_method_ent->Procedure.objc_class;
+ if (ce->proc->kind == Ast_SelectorExpr) {
+ // NOTE (harold): If called via a selector expression (ex: Foo.alloc()), then we should use
+ // the lhs-side to determine the class. This allows for class methods to be called
+ // with the correct class as the target, even when the method is defined in a superclass.
+ ast_node(se, SelectorExpr, ce->proc);
+ GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named);
+
+ objc_class = entity_from_expr(se->expr);
+
+ GB_ASSERT(objc_class);
+ GB_ASSERT(objc_class->kind == Entity_TypeName);
+ GB_ASSERT(objc_class->TypeName.objc_class_name != "");
+ }
+
+ Type *class_impl_type = objc_class->TypeName.objc_is_implementation ? objc_class->type : nullptr;
+
+ id = lb_addr_load(p, lb_handle_objc_find_or_register_class(p, objc_class->TypeName.objc_class_name, class_impl_type));
+ arg_offset = 0;
+ }
+
+ lbValue sel = lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, objc_method_ent->Procedure.objc_selector_name));
+
+ auto args = array_make<lbValue>(permanent_allocator(), 0, arg_values.count + 2 - arg_offset);
+
+ array_add(&args, id);
+ array_add(&args, sel);
+
+ for (isize i = arg_offset; i < ce->args.count; i++) {
+ array_add(&args, arg_values[i]);
+ }
+
+ lbValue the_proc = {};
+
+ if (!objc_super_orig_type) {
+ switch (data.kind) {
+ default:
+ GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
+ break;
+ case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend")); break;
+ case ObjcMsg_fpret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret")); break;
+ case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break;
+ case ObjcMsg_stret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret")); break;
+ }
+ } else {
+ switch (data.kind) {
+ default:
+ GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
+ break;
+ case ObjcMsg_normal:
+ case ObjcMsg_fpret:
+ case ObjcMsg_fp2ret:
+ the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSendSuper2"));
+ break;
+ case ObjcMsg_stret:
+ the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSendSuper2_stret"));
+ break;
+ }
+ }
+
+ the_proc = lb_emit_conv(p, the_proc, data.proc_type);
+
+ return lb_emit_call(p, the_proc, args);
+}
+
+
gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
GB_ASSERT(value.kind == ExactValue_Integer);
i64 v = exact_value_to_i64(value);
@@ -2790,7 +2943,7 @@ gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
-struct lbParaPolyEntry {
+struct lbDiagParaPolyEntry {
Entity *entity;
String canonical_name;
isize count;
@@ -2812,7 +2965,7 @@ gb_internal isize lb_total_code_size(lbProcedure *p) {
}
gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
- PtrMap<Entity * /* Parent */, lbParaPolyEntry> procs = {};
+ PtrMap<Entity * /* Parent */, lbDiagParaPolyEntry> procs = {};
map_init(&procs);
defer (map_destroy(&procs));
@@ -2833,9 +2986,9 @@ gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
continue;
}
- lbParaPolyEntry *entry = map_get(&procs, para_poly_parent);
+ lbDiagParaPolyEntry *entry = map_get(&procs, para_poly_parent);
if (entry == nullptr) {
- lbParaPolyEntry entry = {};
+ lbDiagParaPolyEntry entry = {};
entry.entity = para_poly_parent;
entry.count = 0;
@@ -2863,7 +3016,7 @@ gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
}
- auto entries = array_make<lbParaPolyEntry>(heap_allocator(), 0, procs.count);
+ auto entries = array_make<lbDiagParaPolyEntry>(heap_allocator(), 0, procs.count);
defer (array_free(&entries));
for (auto &entry : procs) {
@@ -2871,8 +3024,8 @@ gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
}
array_sort(entries, [](void const *a, void const *b) -> int {
- lbParaPolyEntry *x = cast(lbParaPolyEntry *)a;
- lbParaPolyEntry *y = cast(lbParaPolyEntry *)b;
+ lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
+ lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
if (x->total_code_size > y->total_code_size) {
return -1;
}
@@ -2897,7 +3050,7 @@ gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1);
- gb_printf("%23td | %19d | %25.2f | %.*s\n", code_size, count, average, LIT(name));
+ gb_printf("%23td | %19td | %25.2f | %.*s\n", code_size, count, average, LIT(name));
if (max_count-- <= 0) {
break;
}
@@ -2906,8 +3059,8 @@ gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
gb_printf("------------------------------------------------------------------------------------------\n");
array_sort(entries, [](void const *a, void const *b) -> int {
- lbParaPolyEntry *x = cast(lbParaPolyEntry *)a;
- lbParaPolyEntry *y = cast(lbParaPolyEntry *)b;
+ lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
+ lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
if (x->count > y->count) {
return -1;
}
@@ -2930,7 +3083,7 @@ gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1);
- gb_printf("%19d | %23td | %25.2f | %.*s\n", count, code_size, average, LIT(name));
+ gb_printf("%19td | %23td | %25.2f | %.*s\n", count, code_size, average, LIT(name));
if (max_count-- <= 0) {
break;
}
@@ -2940,8 +3093,8 @@ gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
array_sort(entries, [](void const *a, void const *b) -> int {
- lbParaPolyEntry *x = cast(lbParaPolyEntry *)a;
- lbParaPolyEntry *y = cast(lbParaPolyEntry *)b;
+ lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
+ lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
if (x->count < y->count) {
return -1;
}
@@ -2972,5 +3125,107 @@ gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
gb_printf("%17td | %.*s\n", code_size, LIT(name));
}
+}
+
+struct lbDiagModuleEntry {
+ lbModule *m;
+ String name;
+ isize global_internal_count;
+ isize global_external_count;
+ isize proc_internal_count;
+ isize proc_external_count;
+ isize total_instruction_count;
+};
+
+gb_internal void lb_do_module_diagnostics(lbGenerator *gen) {
+ Array<lbDiagModuleEntry> modules = {};
+ array_init(&modules, heap_allocator());
+ defer (array_free(&modules));
+
+ for (auto &em : gen->modules) {
+ lbModule *m = em.value;
+
+ {
+ lbDiagModuleEntry entry = {};
+ entry.m = m;
+ entry.name = make_string_c(m->module_name);
+ array_add(&modules, entry);
+ }
+ lbDiagModuleEntry &entry = modules[modules.count-1];
+
+ for (LLVMValueRef p = LLVMGetFirstFunction(m->mod); p != nullptr; p = LLVMGetNextFunction(p)) {
+ LLVMBasicBlockRef block = LLVMGetFirstBasicBlock(p);
+ if (block == nullptr) {
+ entry.proc_external_count += 1;
+ } else {
+ entry.proc_internal_count += 1;
+
+ for (; block != nullptr; block = LLVMGetNextBasicBlock(block)) {
+ for (LLVMValueRef i = LLVMGetFirstInstruction(block); i != nullptr; i = LLVMGetNextInstruction(i)) {
+ entry.total_instruction_count += 1;
+ }
+ }
+ }
+ }
+
+ for (LLVMValueRef g = LLVMGetFirstGlobal(m->mod); g != nullptr; g = LLVMGetNextGlobal(g)) {
+ LLVMLinkage linkage = LLVMGetLinkage(g);
+ if (linkage == LLVMExternalLinkage) {
+ entry.global_external_count += 1;
+ } else {
+ entry.global_internal_count += 1;
+ }
+ }
+ }
+
+ array_sort(modules, [](void const *a, void const *b) -> int {
+ lbDiagModuleEntry *x = cast(lbDiagModuleEntry *)a;
+ lbDiagModuleEntry *y = cast(lbDiagModuleEntry *)b;
+
+ if (x->total_instruction_count > y->total_instruction_count) {
+ return -1;
+ }
+ if (x->total_instruction_count < y->total_instruction_count) {
+ return +1;
+ }
+
+ return string_compare(x->name, y->name);
+ });
+
+ gb_printf("Module Diagnostics\n\n");
+ gb_printf("Total Instructions | Global Internals | Global Externals | Proc Internals | Proc Externals | Files | Instructions/File | Instructions/Proc | Module Name\n");
+ gb_printf("-------------------+------------------+------------------+----------------+----------------+-------+-------------------+-------------------+------------\n");
+ for (auto &entry : modules) {
+ isize file_count = 1;
+ if (entry.m->file != nullptr) {
+ file_count = 1;
+ } else if (entry.m->pkg) {
+ file_count = entry.m->pkg->files.count;
+ }
+
+ f64 instructions_per_file = cast(f64)entry.total_instruction_count / gb_max(1.0, cast(f64)file_count);
+ f64 instructions_per_proc = cast(f64)entry.total_instruction_count / gb_max(1.0, cast(f64)entry.proc_internal_count);
+
+ gb_printf("%18td | %16td | %16td | %14td | %14td | %5td | %17.1f | %17.1f | %s \n",
+ entry.total_instruction_count,
+ entry.global_internal_count,
+ entry.global_external_count,
+ entry.proc_internal_count,
+ entry.proc_external_count,
+ file_count,
+ instructions_per_file,
+ instructions_per_proc,
+ entry.m->module_name);
+ }
+
+
+}
+
+gb_internal void lb_do_build_diagnostics(lbGenerator *gen) {
+ lb_do_para_poly_diagnostics(gen);
+ gb_printf("------------------------------------------------------------------------------------------\n");
+ gb_printf("------------------------------------------------------------------------------------------\n\n");
+ lb_do_module_diagnostics(gen);
gb_printf("------------------------------------------------------------------------------------------\n");
+ gb_printf("------------------------------------------------------------------------------------------\n\n");
}