diff options
| author | Daniel Gavin <danielgavin5@hotmail.com> | 2021-05-20 12:15:14 +0200 |
|---|---|---|
| committer | Daniel Gavin <danielgavin5@hotmail.com> | 2021-05-20 12:15:14 +0200 |
| commit | 44ee0f2cdc773fc3ba3261de076b8a8a7fb64f03 (patch) | |
| tree | c408237d1bd2e6bfcb0c5526d9c0d8cdb97bbf69 /src/llvm_backend.cpp | |
| parent | 50035f257eb33769211ca49a30c51f9a20440a0e (diff) | |
| parent | 92abddddc5ca4be622e93856c7246159b594e9e9 (diff) | |
Merge branch 'master' into prototype-fmt
Diffstat (limited to 'src/llvm_backend.cpp')
| -rw-r--r-- | src/llvm_backend.cpp | 2487 |
1 files changed, 1564 insertions, 923 deletions
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 758f8e5d1..08c9445bd 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1,8 +1,21 @@ +#define MULTITHREAD_OBJECT_GENERATION 1 + +#ifndef USE_SEPARTE_MODULES +#define USE_SEPARTE_MODULES build_context.use_separate_modules +#endif + +#ifndef MULTITHREAD_OBJECT_GENERATION +#define MULTITHREAD_OBJECT_GENERATION 0 +#endif + + #include "llvm_backend.hpp" #include "llvm_abi.cpp" #include "llvm_backend_opt.cpp" -gb_global lbAddr lb_global_type_info_data = {}; +gb_global ThreadPool lb_thread_pool = {}; + +gb_global Entity *lb_global_type_info_data_entity = {}; gb_global lbAddr lb_global_type_info_member_types = {}; gb_global lbAddr lb_global_type_info_member_names = {}; gb_global lbAddr lb_global_type_info_member_offsets = {}; @@ -17,6 +30,12 @@ gb_global isize lb_global_type_info_member_usings_index = 0; gb_global isize lb_global_type_info_member_tags_index = 0; +lbValue lb_global_type_info_data_ptr(lbModule *m) { + lbValue v = lb_find_value_from_entity(m, lb_global_type_info_data_entity); + return v; +} + + struct lbLoopData { lbAddr idx_addr; lbValue idx; @@ -74,6 +93,15 @@ bool lb_is_instr_terminating(LLVMValueRef instr) { +lbModule *lb_pkg_module(lbGenerator *gen, AstPackage *pkg) { + auto *found = map_get(&gen->modules, hash_pointer(pkg)); + if (found) { + return *found; + } + return &gen->default_module; +} + + lbAddr lb_addr(lbValue addr) { lbAddr v = {lbAddr_Default, addr}; if (addr.type != nullptr && is_type_relative_pointer(type_deref(addr.type))) { @@ -184,7 +212,7 @@ void lb_emit_bounds_check(lbProcedure *p, Token token, lbValue index, lbValue le if (build_context.no_bounds_check) { return; } - if ((p->module->state_flags & StateFlag_no_bounds_check) != 0) { + if ((p->state_flags & StateFlag_no_bounds_check) != 0) { return; } @@ -209,7 +237,7 @@ void lb_emit_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValu if (build_context.no_bounds_check) { return; } - if ((p->module->state_flags & StateFlag_no_bounds_check) != 0) { + if ((p->state_flags & StateFlag_no_bounds_check) != 0) { return; } @@ -372,18 +400,7 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { GB_ASSERT(is_type_polymorphic(e->type)); { - lbValue *found = nullptr; - if (p->module != e->code_gen_module) { - gb_mutex_lock(&p->module->mutex); - } - GB_ASSERT(e->code_gen_module != nullptr); - found = map_get(&e->code_gen_module->values, hash_entity(e)); - if (p->module != e->code_gen_module) { - gb_mutex_unlock(&p->module->mutex); - } - GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(e->token.string)); - - lb_emit_call(p, *found, args); + lb_emit_call(p, lb_find_procedure_value_from_entity(p->module, e), args); } return; @@ -501,6 +518,38 @@ void lb_const_store(lbValue ptr, lbValue value) { } +bool lb_is_type_proc_recursive(Type *t) { + for (;;) { + if (t == nullptr) { + return false; + } + switch (t->kind) { + case Type_Named: + t = t->Named.base; + break; + case Type_Pointer: + t = t->Pointer.elem; + break; + case Type_Array: + t = t->Array.elem; + break; + case Type_EnumeratedArray: + t = t->EnumeratedArray.elem; + break; + case Type_Slice: + t = t->Slice.elem; + break; + case Type_DynamicArray: + t = t->DynamicArray.elem; + break; + case Type_Proc: + return true; + default: + return false; + } + } +} + void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { GB_ASSERT(value.value != nullptr); Type *a = type_deref(ptr.type); @@ -513,7 +562,7 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type)); } - if (is_type_proc(a)) { + if (lb_is_type_proc_recursive(a)) { // NOTE(bill, 2020-11-11): Because of certain LLVM rules, a procedure value may be // stored as regular pointer with no procedure information @@ -1110,7 +1159,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { case Basic_uintptr: return LLVMIntTypeInContext(ctx, 8*cast(unsigned)build_context.word_size); - case Basic_rawptr: return LLVMPointerType(LLVMInt8Type(), 0); + case Basic_rawptr: return LLVMPointerType(LLVMInt8TypeInContext(ctx), 0); case Basic_string: { char const *name = "..string"; @@ -1126,7 +1175,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMStructSetBody(type, fields, 2, false); return type; } - case Basic_cstring: return LLVMPointerType(LLVMInt8Type(), 0); + case Basic_cstring: return LLVMPointerType(LLVMInt8TypeInContext(ctx), 0); case Basic_any: { char const *name = "..any"; @@ -1452,21 +1501,35 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } if (param_index < param_count) { params[param_index++] = lb_type(m, t_rawptr); - // params[param_index++] = lb_type(m, t_context_ptr); } GB_ASSERT(param_index == param_count); - lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, type->Proc.calling_convention); + { + for_array(j, ft->args) { + auto arg = ft->args[j]; + GB_ASSERT_MSG(LLVMGetTypeContext(arg.type) == ft->ctx, + "\n\t%s %td/%td" + "\n\tArgTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p", + LLVMPrintTypeToString(arg.type), + j, ft->args.count, + LLVMGetTypeContext(arg.type), ft->ctx, LLVMGetGlobalContext()); + } + GB_ASSERT_MSG(LLVMGetTypeContext(ft->ret.type) == ft->ctx, + "\n\t%s" + "\n\tRetTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p", + LLVMPrintTypeToString(ft->ret.type), + LLVMGetTypeContext(ft->ret.type), ft->ctx, LLVMGetGlobalContext()); + } + map_set(&m->function_type_map, hash_type(type), ft); LLVMTypeRef new_abi_fn_ptr_type = lb_function_type_to_llvm_ptr(ft, type->Proc.c_vararg); LLVMTypeRef new_abi_fn_type = LLVMGetElementType(new_abi_fn_ptr_type); - // LLVMTypeRef new_ret = LLVMGetReturnType(new_abi_fn_type); - // LLVMTypeRef old_ret = LLVMGetReturnType(old_abi_fn_type); - // unsigned new_count = LLVMCountParamTypes(new_abi_fn_type); - // unsigned old_count = LLVMCountParamTypes(old_abi_fn_type); - // GB_ASSERT_MSG(new_count == old_count, "%u %u, %s %s", new_count, old_count, LLVMPrintTypeToString(new_abi_fn_type), LLVMPrintTypeToString(old_abi_fn_type)); + GB_ASSERT_MSG(LLVMGetTypeContext(new_abi_fn_type) == m->ctx, + "\n\tFuncTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p", + LLVMGetTypeContext(new_abi_fn_type), m->ctx, LLVMGetGlobalContext()); + return new_abi_fn_ptr_type; } @@ -1823,13 +1886,32 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { case Type_Pointer: return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->Pointer.elem), word_bits, word_bits, 0, nullptr, 0); - case Type_Array: + case Type_Array: { + LLVMMetadataRef subscripts[1] = {}; + subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, + 0ll, + type->Array.count + ); + return LLVMDIBuilderCreateArrayType(m->debug_builder, - type->Array.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->Array.elem), nullptr, 0); + 8*cast(uint64_t)type_size_of(type), + 8*cast(unsigned)type_align_of(type), + lb_debug_type(m, type->Array.elem), + subscripts, gb_count_of(subscripts)); + } case Type_EnumeratedArray: { + LLVMMetadataRef subscripts[1] = {}; + subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, + 0ll, + type->EnumeratedArray.count + ); + LLVMMetadataRef array_type = LLVMDIBuilderCreateArrayType(m->debug_builder, - type->EnumeratedArray.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->EnumeratedArray.elem), nullptr, 0); + 8*cast(uint64_t)type_size_of(type), + 8*cast(unsigned)type_align_of(type), + lb_debug_type(m, type->EnumeratedArray.elem), + subscripts, gb_count_of(subscripts)); gbString name = type_to_string(type, temporary_allocator()); return LLVMDIBuilderCreateTypedef(m->debug_builder, array_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type))); } @@ -2187,7 +2269,7 @@ void lb_debug_complete_types(lbModule *m) { 8*type_size_of(bt)-word_bits + 1*word_bits, LLVMDIFlagZero, lb_debug_type(m, t_int) ); - elements[3] = LLVMDIBuilderCreateMemberType( + elements[2] = LLVMDIBuilderCreateMemberType( m->debug_builder, record_scope, ".allocator", 12, file, 0, @@ -2463,20 +2545,41 @@ lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) { } } -LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value) { +LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef ctx, char const *name, LLVMTypeRef type) { String s = make_string_c(name); // NOTE(2021-02-25, bill); All this attributes require a type associated with them // and the current LLVM C API does not expose this functionality yet. // It is better to ignore the attributes for the time being if (s == "byval") { - return nullptr; + // return nullptr; } else if (s == "byref") { return nullptr; } else if (s == "preallocated") { return nullptr; } else if (s == "sret") { - return nullptr; + // return nullptr; + } + + unsigned kind = LLVMGetEnumAttributeKindForName(name, s.len); + GB_ASSERT_MSG(kind != 0, "unknown attribute: %s", name); + return LLVMCreateEnumAttribute(ctx, kind, 0); +} + +LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value) { + String s = make_string_c(name); + + // NOTE(2021-02-25, bill); All this attributes require a type associated with them + // and the current LLVM C API does not expose this functionality yet. + // It is better to ignore the attributes for the time being + if (s == "byval") { + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); + } else if (s == "byref") { + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); + } else if (s == "preallocated") { + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); + } else if (s == "sret") { + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); } unsigned kind = LLVMGetEnumAttributeKindForName(name, s.len); @@ -2514,10 +2617,18 @@ void lb_ensure_abi_function_type(lbModule *m, lbProcedure *p) { GB_ASSERT(p->abi_function_type != nullptr); } -lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { +lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) { GB_ASSERT(entity != nullptr); + GB_ASSERT(entity->kind == Entity_Procedure); + + String link_name = {}; - String link_name = lb_get_entity_name(m, entity); + if (ignore_body) { + lbModule *other_module = lb_pkg_module(m->gen, entity->pkg); + link_name = lb_get_entity_name(other_module, entity); + } else { + link_name = lb_get_entity_name(m, entity); + } { StringHashKey key = string_hash_string(link_name); @@ -2588,6 +2699,10 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { lb_add_attribute_to_proc(m, p->value, "noreturn"); } + if (pt->Proc.calling_convention == ProcCC_Naked) { + lb_add_attribute_to_proc(m, p->value, "naked"); + } + switch (p->inlining) { case ProcInlining_inline: lb_add_attribute_to_proc(m, p->value, "alwaysinline"); @@ -2681,6 +2796,11 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { } } + if (ignore_body) { + p->body = nullptr; + LLVMSetLinkage(p->value, LLVMExternalLinkage); + } + if (m->debug_builder) { // Debug Information Type *bt = base_type(p->type); @@ -2851,72 +2971,6 @@ Type *struct_type_from_systemv_distribute_struct_fields(Type *abi_type) { } -lbValue lb_add_param(lbProcedure *p, Entity *e, Ast *expr, Type *abi_type, i32 index) { - lbParamPasskind kind = lbParamPass_Value; - lbValue v = lb_value_param(p, e, abi_type, index, &kind); - array_add(&p->params, v); - - lbValue res = {}; - - switch (kind) { - case lbParamPass_Value: { - lbAddr l = lb_add_local(p, e->type, e, false, index); - lbValue x = v; - if (abi_type == t_llvm_bool) { - x = lb_emit_conv(p, x, t_bool); - } - lb_addr_store(p, l, x); - return x; - } - case lbParamPass_Pointer: - lb_add_entity(p->module, e, v); - return lb_emit_load(p, v); - - case lbParamPass_Integer: { - lbAddr l = lb_add_local(p, e->type, e, false, index); - lbValue iptr = lb_emit_conv(p, l.addr, alloc_type_pointer(abi_type)); - lb_emit_store(p, iptr, v); - return lb_addr_load(p, l); - } - - case lbParamPass_ConstRef: - lb_add_entity(p->module, e, v); - return lb_emit_load(p, v); - - case lbParamPass_BitCast: { - lbAddr l = lb_add_local(p, e->type, e, false, index); - lbValue x = lb_emit_transmute(p, v, e->type); - lb_addr_store(p, l, x); - return x; - } - case lbParamPass_Tuple: { - lbAddr l = lb_add_local(p, e->type, e, true, index); - Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); - lbValue ptr = lb_emit_transmute(p, l.addr, alloc_type_pointer(st)); - if (abi_type->Tuple.variables.count > 0) { - array_pop(&p->params); - } - for_array(i, abi_type->Tuple.variables) { - Type *t = abi_type->Tuple.variables[i]->type; - GB_ASSERT(!is_type_tuple(t)); - - lbParamPasskind elem_kind = lbParamPass_Value; - lbValue elem = lb_value_param(p, nullptr, t, index+cast(i32)i, &elem_kind); - array_add(&p->params, elem); - - lbValue dst = lb_emit_struct_ep(p, ptr, cast(i32)i); - lb_emit_store(p, dst, elem); - } - return lb_addr_load(p, l); - } - - } - - - GB_PANIC("Unreachable"); - return {}; -} - void lb_start_block(lbProcedure *p, lbBlock *b) { GB_ASSERT(b != nullptr); if (!b->appended) { @@ -3113,20 +3167,7 @@ void lb_begin_procedure_body(lbProcedure *p) { } } - if (p->tags != 0) { - u64 in = p->tags; - u64 out = p->module->state_flags; - if (in & ProcTag_bounds_check) { - out |= StateFlag_bounds_check; - out &= ~StateFlag_no_bounds_check; - } else if (in & ProcTag_no_bounds_check) { - out |= StateFlag_no_bounds_check; - out &= ~StateFlag_bounds_check; - } - p->module->state_flags = out; - } - - p->builder = LLVMCreateBuilder(); + p->builder = LLVMCreateBuilderInContext(p->module->ctx); p->decl_block = lb_create_block(p, "decls", true); p->entry_block = lb_create_block(p, "entry", true); @@ -3143,11 +3184,15 @@ void lb_begin_procedure_body(lbProcedure *p) { lbValue return_ptr_value = {}; if (ft->ret.kind == lbArg_Indirect) { // NOTE(bill): this must be parameter 0 + + String name = str_lit("agg.result"); + Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); - Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); + Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false); e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; return_ptr_value.value = LLVMGetParam(p->value, 0); + LLVMSetValueName2(return_ptr_value.value, cast(char const *)name.text, name.len); return_ptr_value.type = ptr_type; p->return_ptr = lb_addr(return_ptr_value); @@ -3273,9 +3318,11 @@ void lb_end_procedure_body(lbProcedure *p) { LLVMBuildBr(p->builder, p->entry_block->block); LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); + LLVMValueRef instr = nullptr; + // Make sure there is a "ret void" at the end of a procedure with no return type if (p->type->Proc.result_count == 0) { - LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block); + instr = LLVMGetLastInstruction(p->curr_block->block); if (!lb_is_instr_terminating(instr)) { lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); LLVMBuildRetVoid(p->builder); @@ -3287,7 +3334,7 @@ void lb_end_procedure_body(lbProcedure *p) { // Make sure every block terminates, and if not, make it unreachable for (block = first_block; block != nullptr; block = LLVMGetNextBasicBlock(block)) { - LLVMValueRef instr = LLVMGetLastInstruction(block); + instr = LLVMGetLastInstruction(block); if (instr == nullptr || !lb_is_instr_terminating(instr)) { LLVMPositionBuilderAtEnd(p->builder, block); LLVMBuildUnreachable(p->builder); @@ -3295,7 +3342,7 @@ void lb_end_procedure_body(lbProcedure *p) { } p->curr_block = nullptr; - p->module->state_flags = 0; + p->state_flags = 0; } void lb_end_procedure(lbProcedure *p) { LLVMDisposeBuilder(p->builder); @@ -3362,6 +3409,20 @@ void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, lbBlock *fals LLVMBuildCondBr(p->builder, cv, true_block->block, false_block->block); } +bool lb_is_expr_untyped_const(Ast *expr) { + auto const &tv = type_and_value_of_expr(expr); + if (is_type_untyped(tv.type)) { + return tv.value.kind != ExactValue_Invalid; + } + return false; +} + +lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t) { + GB_ASSERT(is_type_typed(t)); + auto const &tv = type_and_value_of_expr(expr); + return lb_const_value(m, t, tv.value); +} + lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block) { GB_ASSERT(cond != nullptr); GB_ASSERT(true_block != nullptr); @@ -3393,8 +3454,13 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f case_end; } - lbValue v = lb_build_expr(p, cond); - // v = lb_emit_conv(p, v, t_bool); + lbValue v = {}; + if (lb_is_expr_untyped_const(cond)) { + v = lb_expr_untyped_const_to_typed(p->module, cond, t_llvm_bool); + } else { + v = lb_build_expr(p, cond); + } + v = lb_emit_conv(p, v, t_llvm_bool); lb_emit_if(p, v, true_block, false_block); @@ -3419,6 +3485,21 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p LLVMSetAlignment(ptr, alignment); LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); + + + if (!zero_init) { + // If there is any padding of any kind, just zero init regardless of zero_init parameter + LLVMTypeKind kind = LLVMGetTypeKind(llvm_type); + if (kind == LLVMStructTypeKind) { + i64 sz = type_size_of(type); + if (type_size_of_struct_pretend_is_packed(type) != sz) { + zero_init = true; + } + } else if (kind == LLVMArrayTypeKind) { + zero_init = true; + } + } + if (zero_init) { LLVMTypeKind kind = LLVMGetTypeKind(llvm_type); @@ -3544,16 +3625,14 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) { Ast *ident = vd->names[i]; GB_ASSERT(ident->kind == Ast_Ident); Entity *e = entity_of_node(ident); - if (e == nullptr) { - continue; - } + GB_ASSERT(e != nullptr); if (e->kind != Entity_TypeName) { continue; } bool polymorphic_struct = false; if (e->type != nullptr && e->kind == Entity_TypeName) { - Type *bt = base_type(e->type); + Type *bt = base_type(e->type); if (bt->kind == Type_Struct) { polymorphic_struct = bt->Struct.is_polymorphic; } @@ -3575,12 +3654,16 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) { Ast *ident = vd->names[i]; GB_ASSERT(ident->kind == Ast_Ident); Entity *e = entity_of_node(ident); - if (e == nullptr) { - continue; - } + GB_ASSERT(e != nullptr); if (e->kind != Entity_Procedure) { continue; } + GB_ASSERT (vd->values[i] != nullptr); + + Ast *value = unparen_expr(vd->values[i]); + if (value->kind != Ast_ProcLit) { + continue; // It's an alias + } CheckerInfo *info = p->module->info; DeclInfo *decl = decl_info_of_entity(e); @@ -3948,63 +4031,105 @@ void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type, } -void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, Type *val_type, - lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) { +void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, + AstRangeStmt *rs, Scope *scope) { + bool ADD_EXTRA_WRAPPING_CHECK = true; + lbModule *m = p->module; - // TODO(bill): How should the behaviour work for lower and upper bounds checking for iteration? - // If 'lower' is changed, should 'val' do so or is that not typical behaviour? + lb_open_scope(p, scope); - lbValue lower = lb_build_expr(p, node->left); - lbValue upper = {}; + Type *val0_type = nullptr; + Type *val1_type = nullptr; + if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) { + val0_type = type_of_expr(rs->vals[0]); + } + if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) { + val1_type = type_of_expr(rs->vals[1]); + } - lbValue val = {}; - lbValue idx = {}; - lbBlock *loop = nullptr; - lbBlock *done = nullptr; - lbBlock *body = nullptr; + if (val0_type != nullptr) { + Entity *e = entity_of_node(rs->vals[0]); + lb_add_local(p, e->type, e, true); + } + if (val1_type != nullptr) { + Entity *e = entity_of_node(rs->vals[1]); + lb_add_local(p, e->type, e, true); + } - if (val_type == nullptr) { - val_type = lower.type; + TokenKind op = Token_Lt; + switch (node->op.kind) { + case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeFull: op = Token_LtEq; break; + case Token_RangeHalf: op = Token_Lt; break; + default: GB_PANIC("Invalid interval operator"); break; } - lbAddr value = lb_add_local_generated(p, val_type, false); + + lbValue lower = lb_build_expr(p, node->left); + lbValue upper = {}; // initialized each time in the loop + + lbAddr value = lb_add_local_generated(p, val0_type ? val0_type : lower.type, false); lb_addr_store(p, value, lower); lbAddr index = lb_add_local_generated(p, t_int, false); lb_addr_store(p, index, lb_const_int(m, t_int, 0)); - loop = lb_create_block(p, "for.interval.loop"); + lbBlock *loop = lb_create_block(p, "for.interval.loop"); + lbBlock *body = lb_create_block(p, "for.interval.body"); + lbBlock *done = lb_create_block(p, "for.interval.done"); + lb_emit_jump(p, loop); lb_start_block(p, loop); - body = lb_create_block(p, "for.interval.body"); - done = lb_create_block(p, "for.interval.done"); - - - TokenKind op = Token_Lt; - switch (node->op.kind) { - case Token_Ellipsis: op = Token_LtEq; break; - case Token_RangeHalf: op = Token_Lt; break; - default: GB_PANIC("Invalid interval operator"); break; - } - upper = lb_build_expr(p, node->right); - lbValue curr_value = lb_addr_load(p, value); lbValue cond = lb_emit_comp(p, op, curr_value, upper); lb_emit_if(p, cond, body, done); lb_start_block(p, body); - val = lb_addr_load(p, value); - idx = lb_addr_load(p, index); + lbValue val = lb_addr_load(p, value); + lbValue idx = lb_addr_load(p, index); + if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], val); + if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], idx); - lb_emit_increment(p, value.addr); - lb_emit_increment(p, index.addr); + { + // NOTE: this check block will most likely be optimized out, and is here + // to make this code easier to read + lbBlock *check = nullptr; + lbBlock *post = lb_create_block(p, "for.interval.post"); - if (val_) *val_ = val; - if (idx_) *idx_ = idx; - if (loop_) *loop_ = loop; - if (done_) *done_ = done; + lbBlock *continue_block = post; + + if (ADD_EXTRA_WRAPPING_CHECK && + op == Token_LtEq) { + check = lb_create_block(p, "for.interval.check"); + continue_block = check; + } + + lb_push_target_list(p, rs->label, done, continue_block, nullptr); + + lb_build_stmt(p, rs->body); + + lb_close_scope(p, lbDeferExit_Default, nullptr); + lb_pop_target_list(p); + + if (check != nullptr) { + lb_emit_jump(p, check); + lb_start_block(p, check); + + lbValue check_cond = lb_emit_comp(p, Token_NotEq, curr_value, upper); + lb_emit_if(p, check_cond, post, done); + } else { + lb_emit_jump(p, post); + } + + lb_start_block(p, post); + lb_emit_increment(p, value.addr); + lb_emit_increment(p, index.addr); + lb_emit_jump(p, loop); + } + + lb_start_block(p, done); } void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) { @@ -4159,6 +4284,11 @@ void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs, Scope *sco void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) { Ast *expr = unparen_expr(rs->expr); + if (is_ast_range(expr)) { + lb_build_range_interval(p, &expr->BinaryExpr, rs, scope); + return; + } + Type *expr_type = type_of_expr(expr); if (expr_type != nullptr) { Type *et = base_type(type_deref(expr_type)); @@ -4195,10 +4325,7 @@ void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) { bool is_map = false; TypeAndValue tav = type_and_value_of_expr(expr); - - if (is_ast_range(expr)) { - lb_build_range_interval(p, &expr->BinaryExpr, val0_type, &val, &key, &loop, &done); - } else if (tav.mode == Addressing_Type) { + if (tav.mode == Addressing_Type) { lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done); } else { Type *expr_type = type_of_expr(expr); @@ -4347,7 +4474,7 @@ void lb_build_inline_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *s ExactValue start = start_expr->tav.value; ExactValue end = end_expr->tav.value; - if (op == Token_Ellipsis) { // .. [start, end] + if (op != Token_RangeHalf) { // .. [start, end] (or ..=) ExactValue index = exact_value_i64(0); for (ExactValue val = start; compare_exact_values(Token_LtEq, val, end); @@ -4358,7 +4485,7 @@ void lb_build_inline_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *s lb_build_stmt(p, rs->body); } - } else if (op == Token_RangeHalf) { // ..< [start, end) + } else { // ..< [start, end) ExactValue index = exact_value_i64(0); for (ExactValue val = start; compare_exact_values(Token_Lt, val, end); @@ -4525,6 +4652,7 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) { TokenKind op = Token_Invalid; switch (ie->op.kind) { case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeFull: op = Token_LtEq; break; case Token_RangeHalf: op = Token_Lt; break; default: GB_PANIC("Invalid interval operator"); break; } @@ -4763,6 +4891,9 @@ lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast if (done->preds.count == 0) { lb_start_block(p, rhs); + if (lb_is_expr_untyped_const(right)) { + return lb_expr_untyped_const_to_typed(m, right, type); + } return lb_build_expr(p, right); } @@ -4777,7 +4908,12 @@ lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast } lb_start_block(p, rhs); - lbValue edge = lb_build_expr(p, right); + lbValue edge = {}; + if (lb_is_expr_untyped_const(right)) { + edge = lb_expr_untyped_const_to_typed(m, right, type); + } else { + edge = lb_build_expr(p, right); + } incoming_values[done->preds.count] = edge.value; incoming_blocks[done->preds.count] = p->curr_block->block; @@ -4815,12 +4951,12 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { LLVMSetCurrentDebugLocation2(p->builder, prev_debug_location); }); - u64 prev_state_flags = p->module->state_flags; - defer (p->module->state_flags = prev_state_flags); + u16 prev_state_flags = p->state_flags; + defer (p->state_flags = prev_state_flags); if (node->state_flags != 0) { - u64 in = node->state_flags; - u64 out = p->module->state_flags; + u16 in = node->state_flags; + u16 out = p->state_flags; if (in & StateFlag_bounds_check) { out |= StateFlag_bounds_check; @@ -4830,7 +4966,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { out &= ~StateFlag_bounds_check; } - p->module->state_flags = out; + p->state_flags = out; } switch (node->kind) { @@ -5412,6 +5548,7 @@ lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) { args[0] = x; args[1] = y; switch (sz) { + case 16: return lb_emit_runtime_call(p, "min_f16", args); case 32: return lb_emit_runtime_call(p, "min_f32", args); case 64: return lb_emit_runtime_call(p, "min_f64", args); } @@ -5429,6 +5566,7 @@ lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) { args[0] = x; args[1] = y; switch (sz) { + case 16: return lb_emit_runtime_call(p, "max_f16", args); case 32: return lb_emit_runtime_call(p, "max_f32", args); case 64: return lb_emit_runtime_call(p, "max_f64", args); } @@ -5462,9 +5600,10 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) { isize max_len = 7+8+1; char *name = gb_alloc_array(permanent_allocator(), char, max_len); - isize len = gb_snprintf(name, max_len, "csbs$%x", m->global_array_index); + + u32 id = cast(u32)gb_atomic32_fetch_add(&m->gen->global_array_index, 1); + isize len = gb_snprintf(name, max_len, "csbs$%x", id); len -= 1; - m->global_array_index++; LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); @@ -5504,9 +5643,9 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str) { isize max_len = 7+8+1; name = gb_alloc_array(permanent_allocator(), char, max_len); - isize len = gb_snprintf(name, max_len, "csbs$%x", m->global_array_index); + u32 id = cast(u32)gb_atomic32_fetch_add(&m->gen->global_array_index, 1); + isize len = gb_snprintf(name, max_len, "csbs$%x", id); len -= 1; - m->global_array_index++; } LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); @@ -5628,7 +5767,7 @@ lbValue lb_type_info(lbModule *m, Type *type) { }; lbValue value = {}; - value.value = LLVMConstGEP(lb_global_type_info_data.addr.value, indices, gb_count_of(indices)); + value.value = LLVMConstGEP(lb_global_type_info_data_ptr(m).value, indices, gb_count_of(indices)); value.type = t_type_info_ptr; return value; } @@ -5661,6 +5800,112 @@ LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_ return llvm_const_array(lb_type(m, elem_type), values, cast(unsigned int)count); } +lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { + GB_ASSERT(is_type_proc(e->type)); + e = strip_entity_wrapping(e); + GB_ASSERT(e != nullptr); + auto *found = map_get(&m->values, hash_entity(e)); + if (found) { + return *found; + } + + bool ignore_body = false; + + if (USE_SEPARTE_MODULES) { + lbModule *other_module = lb_pkg_module(m->gen, e->pkg); + ignore_body = other_module != m; + } + + lbProcedure *missing_proc = lb_create_procedure(m, e, ignore_body); + found = map_get(&m->values, hash_entity(e)); + if (found) { + return *found; + } + + GB_PANIC("Error in: %s, missing procedure %.*s\n", token_pos_to_string(e->token.pos), LIT(e->token.string)); + return {}; +} + +lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { + e = strip_entity_wrapping(e); + GB_ASSERT(e != nullptr); + + GB_ASSERT(e->token.string != "_"); + + if (e->kind == Entity_Procedure) { + return lb_find_procedure_value_from_entity(m, e); + } + + auto *found = map_get(&m->values, hash_entity(e)); + if (found) { + return *found; + } + + if (USE_SEPARTE_MODULES) { + lbModule *other_module = lb_pkg_module(m->gen, e->pkg); + + // TODO(bill): correct this logic + bool is_external = other_module != m; + if (!is_external) { + if (e->code_gen_module != nullptr) { + other_module = e->code_gen_module; + } else { + other_module = nullptr; + } + is_external = other_module != m; + } + + if (is_external) { + String name = lb_get_entity_name(other_module, e); + + lbValue g = {}; + g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); + g.type = alloc_type_pointer(e->type); + lb_add_entity(m, e, g); + lb_add_member(m, name, g); + + LLVMSetLinkage(g.value, LLVMExternalLinkage); + + // if (other_module != nullptr) { + // lbValue *other_found = string_map_get(&other_module->members, name); + // if (other_found) { + // lbValue other_g = *other_found; + // } + // } + + // LLVMSetLinkage(other_g.value, LLVMExternalLinkage); + + if (e->Variable.thread_local_model != "") { + LLVMSetThreadLocal(g.value, true); + + String m = e->Variable.thread_local_model; + LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel; + if (m == "default") { + mode = LLVMGeneralDynamicTLSModel; + } else if (m == "localdynamic") { + mode = LLVMLocalDynamicTLSModel; + } else if (m == "initialexec") { + mode = LLVMInitialExecTLSModel; + } else if (m == "localexec") { + mode = LLVMLocalExecTLSModel; + } else { + GB_PANIC("Unhandled thread local mode %.*s", LIT(m)); + } + LLVMSetThreadLocalMode(g.value, mode); + } + + + return g; + } + } + + GB_PANIC("\n\tError in: %s, missing value %.*s\n", token_pos_to_string(e->token.pos), LIT(e->token.string)); + return {}; +} + + + + lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) { LLVMContextRef ctx = m->ctx; @@ -5687,14 +5932,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc return lb_generate_anonymous_proc_lit(m, str_lit("_proclit"), expr); } Entity *e = entity_from_expr(expr); - e = strip_entity_wrapping(e); - GB_ASSERT(e != nullptr); - auto *found = map_get(&m->values, hash_entity(e)); - if (found) { - return *found; - } - - GB_PANIC("Error in: %s, missing procedure %.*s\n", token_pos_to_string(e->token.pos), LIT(e->token.string)); + return lb_find_procedure_value_from_entity(m, e); } bool is_local = allow_local && m->curr_procedure != nullptr; @@ -5743,8 +5981,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } else { isize max_len = 7+8+1; char *str = gb_alloc_array(permanent_allocator(), char, max_len); - isize len = gb_snprintf(str, max_len, "csba$%x", m->global_array_index); - m->global_array_index++; + u32 id = cast(u32)gb_atomic32_fetch_add(&m->gen->global_array_index, 1); + isize len = gb_snprintf(str, max_len, "csba$%x", id); String name = make_string(cast(u8 *)str, len-1); @@ -5797,7 +6035,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = llvm_const_array(et, elems, cast(unsigned)count); return res; } - GB_PANIC("HERE!\n"); + GB_PANIC("This should not have happened!\n"); LLVMValueRef data = LLVMConstStringInContext(ctx, cast(char const *)value.value_string.text, @@ -6799,15 +7037,29 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { case Token_And: case Token_Or: case Token_Xor: - case Token_AndNot: - case Token_Shl: - case Token_Shr: { + case Token_AndNot: { Type *type = default_type(tv.type); lbValue left = lb_build_expr(p, be->left); lbValue right = lb_build_expr(p, be->right); return lb_emit_arith(p, be->op.kind, left, right, type); } + case Token_Shl: + case Token_Shr: { + lbValue left, right; + Type *type = default_type(tv.type); + left = lb_build_expr(p, be->left); + + if (lb_is_expr_untyped_const(be->right)) { + // NOTE(bill): RHS shift operands can still be untyped + // Just bypass the standard lb_build_expr + right = lb_expr_untyped_const_to_typed(p->module, be->right, type); + } else { + right = lb_build_expr(p, be->right); + } + return lb_emit_arith(p, be->op.kind, left, right, type); + } + case Token_CmpEq: case Token_NotEq: case Token_Lt: @@ -7598,10 +7850,13 @@ lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p GB_ASSERT(pt->kind == Type_Proc); GB_ASSERT(pt->Proc.calling_convention == ProcCC_Odin); - Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false); + String name = str_lit("__.context_ptr"); + + Entity *e = alloc_entity_param(nullptr, make_token_ident(name), t_context_ptr, false, false); e->flags |= EntityFlag_NoAlias; LLVMValueRef context_ptr = LLVMGetParam(p->value, LLVMCountParams(p->value)-1); + LLVMSetValueName2(context_ptr, cast(char const *)name.text, name.len); context_ptr = LLVMBuildPointerCast(p->builder, context_ptr, lb_type(p->module, e->type), ""); lbValue param = {context_ptr, e->type}; @@ -8084,6 +8339,8 @@ Array<lbValue> lb_value_to_array(lbProcedure *p, lbValue value) { lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array<lbValue> const &processed_args, Type *abi_rt, lbAddr context_ptr, ProcInlining inlining) { + GB_ASSERT(p->module->ctx == LLVMGetTypeContext(LLVMTypeOf(value.value))); + unsigned arg_count = cast(unsigned)processed_args.count; if (return_ptr.value != nullptr) { arg_count += 1; @@ -8158,31 +8415,18 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, } } -lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array<lbValue> const &args) { - // LLVMMetadataRef curr_loc = LLVMGetCurrentDebugLocation2(p->builder); - // LLVMSetCurrentDebugLocation2(p->builder, nullptr); - // defer (if (curr_loc) { - // LLVMSetCurrentDebugLocation2(p->builder, curr_loc); - // }); - - String name = make_string_c(c_name); - - AstPackage *pkg = p->module->info->runtime_package; +lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name) { + AstPackage *pkg = m->info->runtime_package; Entity *e = scope_lookup_current(pkg->scope, name); + return lb_find_procedure_value_from_entity(m, e); +} - lbValue *found = nullptr; - if (p->module != e->code_gen_module) { - gb_mutex_lock(&p->module->mutex); - } - GB_ASSERT(e->code_gen_module != nullptr); - found = map_get(&e->code_gen_module->values, hash_entity(e)); - if (p->module != e->code_gen_module) { - gb_mutex_unlock(&p->module->mutex); - } - GB_ASSERT_MSG(found != nullptr, "%s", c_name); - return lb_emit_call(p, *found, args); +lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array<lbValue> const &args) { + String name = make_string_c(c_name); + lbValue proc = lb_lookup_runtime_procedure(p->module, name); + return lb_emit_call(p, proc, args); } lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining, bool use_return_ptr_hint) { @@ -8321,9 +8565,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, if (e != nullptr && entity_has_deferred_procedure(e)) { DeferredProcedureKind kind = e->Procedure.deferred_procedure.kind; Entity *deferred_entity = e->Procedure.deferred_procedure.entity; - lbValue *deferred_found = map_get(&p->module->values, hash_entity(deferred_entity)); - GB_ASSERT(deferred_found != nullptr); - lbValue deferred = *deferred_found; + lbValue deferred = lb_find_procedure_value_from_entity(p->module, deferred_entity); auto in_args = args; @@ -8645,7 +8887,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, switch (id) { case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); - String name = bd->name; + String name = bd->name.string; GB_ASSERT(name == "location"); String procedure = p->entity->token.string; TokenPos pos = ast_token(ce->proc).pos; @@ -9134,9 +9376,11 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_overflow_sub: case BuiltinProc_overflow_mul: { - Type *tuple = tv.type; - GB_ASSERT(is_type_tuple(tuple)); - Type *type = tuple->Tuple.variables[0]->type; + Type *main_type = tv.type; + Type *type = main_type; + if (is_type_tuple(main_type)) { + type = main_type->Tuple.variables[0]->type; + } lbValue x = lb_build_expr(p, ce->args[0]); lbValue y = lb_build_expr(p, ce->args[1]); @@ -9166,21 +9410,85 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, args[0] = x.value; args[1] = y.value; - Type *res_type = nullptr; - { + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + + if (is_type_tuple(main_type)) { + Type *res_type = nullptr; gbAllocator a = permanent_allocator(); res_type = alloc_type_tuple(); array_init(&res_type->Tuple.variables, a, 2); res_type->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, type, false, 0); res_type->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1); + + res.type = res_type; + } else { + res.value = LLVMBuildExtractValue(p->builder, res.value, 0, ""); + res.type = type; } + return res; + } + + case BuiltinProc_sqrt: + { + Type *type = tv.type; + + lbValue x = lb_build_expr(p, ce->args[0]); + x = lb_emit_conv(p, x, type); + + char const *name = "llvm.sqrt"; + LLVMTypeRef types[1] = {lb_type(p->module, type)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[1] = {}; + args[0] = x.value; lbValue res = {}; res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); - res.type = res_type; + res.type = type; return res; } + case BuiltinProc_mem_copy: + case BuiltinProc_mem_copy_non_overlapping: + { + + + lbValue dst = lb_build_expr(p, ce->args[0]); + lbValue src = lb_build_expr(p, ce->args[1]); + lbValue len = lb_build_expr(p, ce->args[2]); + dst = lb_emit_conv(p, dst, t_rawptr); + src = lb_emit_conv(p, src, t_rawptr); + len = lb_emit_conv(p, len, t_int); + + char const *name = nullptr; + switch (id) { + case BuiltinProc_mem_copy: name = "llvm.memmove"; break; + case BuiltinProc_mem_copy_non_overlapping: name = "llvm.memcpy"; break; + } + + LLVMTypeRef types[3] = { + lb_type(p->module, t_rawptr), + lb_type(p->module, t_rawptr), + lb_type(p->module, t_int) + }; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]), LLVMPrintTypeToString(types[2])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[4] = {}; + args[0] = dst.value; + args[1] = src.value; + args[2] = len.value; + args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, false); // is_volatile parameter + + LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + + return {}; + } + case BuiltinProc_atomic_fence: LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); @@ -9390,16 +9698,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, single_threaded ); - GB_ASSERT(tv.type->kind == Type_Tuple); - Type *fix_typed = alloc_type_tuple(); - array_init(&fix_typed->Tuple.variables, permanent_allocator(), 2); - fix_typed->Tuple.variables[0] = tv.type->Tuple.variables[0]; - fix_typed->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1); + if (tv.type->kind == Type_Tuple) { + Type *fix_typed = alloc_type_tuple(); + array_init(&fix_typed->Tuple.variables, permanent_allocator(), 2); + fix_typed->Tuple.variables[0] = tv.type->Tuple.variables[0]; + fix_typed->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1); - lbValue res = {}; - res.value = value; - res.type = fix_typed; - return res; + lbValue res = {}; + res.value = value; + res.type = fix_typed; + return res; + } else { + lbValue res = {}; + res.value = LLVMBuildExtractValue(p->builder, value, 0, ""); + res.type = tv.type; + return res; + } } @@ -9490,7 +9804,8 @@ lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterVal switch (param_value.kind) { case ParameterValue_Constant: if (is_type_constant_type(parameter_type)) { - return lb_const_value(p->module, parameter_type, param_value.value); + auto res = lb_const_value(p->module, parameter_type, param_value.value); + return res; } else { ExactValue ev = param_value.value; lbValue arg = {}; @@ -9900,24 +10215,6 @@ void lb_emit_increment(lbProcedure *p, lbValue addr) { } -LLVMValueRef lb_lookup_runtime_procedure(lbModule *m, String const &name) { - AstPackage *pkg = m->info->runtime_package; - Entity *e = scope_lookup_current(pkg->scope, name); - - lbValue *found = nullptr; - if (m != e->code_gen_module) { - gb_mutex_lock(&m->mutex); - } - GB_ASSERT(e->code_gen_module != nullptr); - found = map_get(&e->code_gen_module->values, hash_entity(e)); - if (m != e->code_gen_module) { - gb_mutex_unlock(&m->mutex); - } - GB_ASSERT(found != nullptr); - - return found->value; -} - lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) { GB_ASSERT(type_size_of(value.type) == type_size_of(end_type)); @@ -10242,7 +10539,7 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) { lb_start_block(p, block_diff_ptr); - if (type->kind == Type_Struct) { + if (type->kind == Type_Struct) { type_set_offsets(type); lbBlock *block_false = lb_create_block(p, "bfalse"); @@ -10268,6 +10565,56 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) { lb_start_block(p, block_false); LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false)); + } else if (type->kind == Type_Union) { + if (is_type_union_maybe_pointer(type)) { + Type *v = type->Union.variants[0]; + Type *pv = alloc_type_pointer(v); + + lbValue left = lb_emit_load(p, lb_emit_conv(p, lhs, pv)); + lbValue right = lb_emit_load(p, lb_emit_conv(p, rhs, pv)); + + lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right); + ok = lb_emit_conv(p, ok, t_bool); + LLVMBuildRet(p->builder, ok.value); + } else { + lbBlock *block_false = lb_create_block(p, "bfalse"); + lbBlock *block_switch = lb_create_block(p, "bswitch"); + + lbValue left_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, lhs)); + lbValue right_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, rhs)); + + lbValue tag_eq = lb_emit_comp(p, Token_CmpEq, left_tag, right_tag); + lb_emit_if(p, tag_eq, block_switch, block_false); + + lb_start_block(p, block_switch); + LLVMValueRef v_switch = LLVMBuildSwitch(p->builder, left_tag.value, block_false->block, cast(unsigned)type->Union.variants.count); + + + for_array(i, type->Union.variants) { + lbBlock *case_block = lb_create_block(p, "bcase"); + lb_start_block(p, case_block); + + Type *v = type->Union.variants[i]; + lbValue case_tag = lb_const_union_tag(p->module, type, v); + + Type *vp = alloc_type_pointer(v); + + lbValue left = lb_emit_load(p, lb_emit_conv(p, lhs, vp)); + lbValue right = lb_emit_load(p, lb_emit_conv(p, rhs, vp)); + lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right); + ok = lb_emit_conv(p, ok, t_bool); + + LLVMBuildRet(p->builder, ok.value); + + + LLVMAddCase(v_switch, case_tag.value, case_block->block); + } + + lb_start_block(p, block_false); + + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false)); + } + } else { lbValue left = lb_emit_load(p, lhs); lbValue right = lb_emit_load(p, rhs); @@ -10336,6 +10683,9 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { lbValue data = {x, t_rawptr}; lbValue seed = {y, t_uintptr}; + LLVMAttributeRef nonnull_attr = lb_create_enum_attribute(m->ctx, "nonnull"); + LLVMAddAttributeAtIndex(p->value, 1+0, nonnull_attr); + if (is_type_simple_compare(type)) { lbValue res = lb_simple_compare_hash(p, type, data, seed); LLVMBuildRet(p->builder, res.value); @@ -10358,6 +10708,48 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { seed = lb_emit_call(p, field_hasher, args); } LLVMBuildRet(p->builder, seed.value); + } else if (type->kind == Type_Union) { + auto args = array_make<lbValue>(permanent_allocator(), 2); + + if (is_type_union_maybe_pointer(type)) { + Type *v = type->Union.variants[0]; + lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v); + + args[0] = data; + args[1] = seed; + lbValue res = lb_emit_call(p, variant_hasher, args); + LLVMBuildRet(p->builder, res.value); + } + + lbBlock *end_block = lb_create_block(p, "bend"); + data = lb_emit_conv(p, data, pt); + + lbValue tag_ptr = lb_emit_union_tag_ptr(p, data); + lbValue tag = lb_emit_load(p, tag_ptr); + + LLVMValueRef v_switch = LLVMBuildSwitch(p->builder, tag.value, end_block->block, cast(unsigned)type->Union.variants.count); + + for_array(i, type->Union.variants) { + lbBlock *case_block = lb_create_block(p, "bcase"); + lb_start_block(p, case_block); + + Type *v = type->Union.variants[i]; + Type *vp = alloc_type_pointer(v); + lbValue case_tag = lb_const_union_tag(p->module, type, v); + + lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v); + + args[0] = data; + args[1] = seed; + lbValue res = lb_emit_call(p, variant_hasher, args); + LLVMBuildRet(p->builder, res.value); + + LLVMAddCase(v_switch, case_tag.value, case_block->block); + } + + lb_start_block(p, end_block); + LLVMBuildRet(p->builder, seed.value); + } else if (type->kind == Type_Array) { lbAddr pres = lb_add_local_generated(p, t_uintptr, false); lb_addr_store(p, pres, seed); @@ -10419,6 +10811,30 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { return {p->value, p->type}; } +lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right, Type *type) { + GB_ASSERT((is_type_struct(type) || is_type_union(type)) && is_type_comparable(type)); + lbValue left_ptr = lb_address_from_load_or_generate_local(p, left); + lbValue right_ptr = lb_address_from_load_or_generate_local(p, right); + lbValue res = {}; + if (is_type_simple_compare(type)) { + // TODO(bill): Test to see if this is actually faster!!!! + auto args = array_make<lbValue>(permanent_allocator(), 3); + args[0] = lb_emit_conv(p, left_ptr, t_rawptr); + args[1] = lb_emit_conv(p, right_ptr, t_rawptr); + args[2] = lb_const_int(p->module, t_int, type_size_of(type)); + res = lb_emit_runtime_call(p, "memory_equal", args); + } else { + lbValue value = lb_get_equal_proc_for_type(p->module, type); + auto args = array_make<lbValue>(permanent_allocator(), 2); + args[0] = lb_emit_conv(p, left_ptr, t_rawptr); + args[1] = lb_emit_conv(p, right_ptr, t_rawptr); + res = lb_emit_call(p, value, args); + } + if (op_kind == Token_NotEq) { + res = lb_emit_unary_arith(p, Token_Not, res, res.type); + } + return res; +} lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) { @@ -10447,42 +10863,22 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri Type *lt = left.type; Type *rt = right.type; - // if (is_type_bit_set(lt) && is_type_bit_set(rt)) { - // Type *blt = base_type(lt); - // Type *brt = base_type(rt); - // i64 bits = gb_max(blt->BitSet.bits, brt->BitSet.bits); - // i64 bytes = bits / 8; - // switch (bytes) { - // case 1: - // left = lb_emit_conv(p, left, t_u8); - // right = lb_emit_conv(p, right, t_u8); - // break; - // case 2: - // left = lb_emit_conv(p, left, t_u16); - // right = lb_emit_conv(p, right, t_u16); - // break; - // case 4: - // left = lb_emit_conv(p, left, t_u32); - // right = lb_emit_conv(p, right, t_u32); - // break; - // case 8: - // left = lb_emit_conv(p, left, t_u64); - // right = lb_emit_conv(p, right, t_u64); - // break; - // default: GB_PANIC("Unknown integer size"); break; - // } - // } - lt = left.type; rt = right.type; i64 ls = type_size_of(lt); i64 rs = type_size_of(rt); + + // NOTE(bill): Quick heuristic, larger types are usually the target type if (ls < rs) { left = lb_emit_conv(p, left, rt); } else if (ls > rs) { right = lb_emit_conv(p, right, lt); } else { - right = lb_emit_conv(p, right, lt); + if (is_type_union(rt)) { + left = lb_emit_conv(p, left, rt); + } else { + right = lb_emit_conv(p, right, lt); + } } } @@ -10548,28 +10944,12 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } - if (is_type_struct(a) && is_type_comparable(a)) { - lbValue left_ptr = lb_address_from_load_or_generate_local(p, left); - lbValue right_ptr = lb_address_from_load_or_generate_local(p, right); - lbValue res = {}; - if (is_type_simple_compare(a)) { - // TODO(bill): Test to see if this is actually faster!!!! - auto args = array_make<lbValue>(permanent_allocator(), 3); - args[0] = lb_emit_conv(p, left_ptr, t_rawptr); - args[1] = lb_emit_conv(p, right_ptr, t_rawptr); - args[2] = lb_const_int(p->module, t_int, type_size_of(a)); - res = lb_emit_runtime_call(p, "memory_equal", args); - } else { - lbValue value = lb_get_equal_proc_for_type(p->module, a); - auto args = array_make<lbValue>(permanent_allocator(), 2); - args[0] = lb_emit_conv(p, left_ptr, t_rawptr); - args[1] = lb_emit_conv(p, right_ptr, t_rawptr); - res = lb_emit_call(p, value, args); - } - if (op_kind == Token_NotEq) { - res = lb_emit_unary_arith(p, Token_Not, res, res.type); - } - return res; + if ((is_type_struct(a) || is_type_union(a)) && is_type_comparable(a)) { + return lb_compare_records(p, op_kind, left, right, a); + } + + if ((is_type_struct(b) || is_type_union(b)) && is_type_comparable(b)) { + return lb_compare_records(p, op_kind, left, right, b); } if (is_type_string(a)) { @@ -10783,12 +11163,9 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) { - auto *found = map_get(&m->anonymous_proc_lits, hash_pointer(expr)); - if (found != nullptr) { - lbValue value = {}; - value.value = (*found)->value; - value.type = (*found)->type; - return value; + lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, hash_pointer(expr)); + if (found) { + return lb_find_procedure_value_from_entity(m, (*found)->entity); } ast_node(pl, ProcLit, expr); @@ -10797,7 +11174,7 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A // parent$count isize name_len = prefix_name.len + 1 + 8 + 1; char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); - i32 name_id = cast(i32)m->anonymous_proc_lits.entries.count; + i32 name_id = cast(i32)m->gen->anonymous_proc_lits.entries.count; name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id); String name = make_string((u8 *)name_text, name_len-1); @@ -10811,6 +11188,7 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags); e->file = expr->file; e->decl_info = pl->decl; + e->code_gen_module = m; lbProcedure *p = lb_create_procedure(m, e); lbValue value = {}; @@ -10825,11 +11203,59 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A } map_set(&m->anonymous_proc_lits, hash_pointer(expr), p); + map_set(&m->gen->anonymous_proc_lits, hash_pointer(expr), p); return value; } -lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos, bool do_conversion_check=true) { +lbValue lb_emit_union_cast_only_ok_check(lbProcedure *p, lbValue value, Type *type, TokenPos pos) { + GB_ASSERT(is_type_tuple(type)); + lbModule *m = p->module; + + Type *src_type = value.type; + bool is_ptr = is_type_pointer(src_type); + + + // IMPORTANT NOTE(bill): This assumes that the value is completely ignored + // so when it does an assignment, it complete ignores the value. + // Just make it two booleans and ignore the first one + // + // _, ok := x.(T); + // + Type *ok_type = type->Tuple.variables[1]->type; + Type *gen_tuple_types[2] = {}; + gen_tuple_types[0] = ok_type; + gen_tuple_types[1] = ok_type; + + Type *gen_tuple = alloc_type_tuple_from_field_types(gen_tuple_types, gb_count_of(gen_tuple_types), false, true); + + lbAddr v = lb_add_local_generated(p, gen_tuple, false); + + if (is_ptr) { + value = lb_emit_load(p, value); + } + Type *src = base_type(type_deref(src_type)); + GB_ASSERT_MSG(is_type_union(src), "%s", type_to_string(src_type)); + Type *dst = type->Tuple.variables[0]->type; + + lbValue cond = {}; + + if (is_type_union_maybe_pointer(src)) { + lbValue data = lb_emit_transmute(p, value, dst); + cond = lb_emit_comp_against_nil(p, Token_NotEq, data); + } else { + lbValue tag = lb_emit_union_tag_value(p, value); + lbValue dst_tag = lb_const_union_tag(m, src, dst); + cond = lb_emit_comp(p, Token_CmpEq, tag, dst_tag); + } + + lbValue gep1 = lb_emit_struct_ep(p, v.addr, 1); + lb_emit_store(p, gep1, cond); + + return lb_addr_load(p, v); +} + +lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos) { lbModule *m = p->module; Type *src_type = value.type; @@ -10893,7 +11319,7 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p lb_start_block(p, end_block); if (!is_tuple) { - if (do_conversion_check) { + { // NOTE(bill): Panic on invalid conversion Type *dst_type = tuple->Tuple.variables[0]->type; @@ -10981,15 +11407,66 @@ lbValue lb_emit_any_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos } +lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) { + auto *found = map_get(&m->values, hash_entity(e)); + if (found) { + auto v = *found; + // NOTE(bill): This is because pointers are already pointers in LLVM + if (is_type_proc(v.type)) { + return v; + } + return lb_emit_load(p, v); + } else if (e != nullptr && e->kind == Entity_Variable) { + return lb_addr_load(p, lb_build_addr(p, expr)); + } + + if (e->kind == Entity_Procedure) { + return lb_find_procedure_value_from_entity(m, e); + } + if (USE_SEPARTE_MODULES) { + lbModule *other_module = lb_pkg_module(m->gen, e->pkg); + if (other_module != m) { + + String name = lb_get_entity_name(other_module, e); + + lbValue g = {}; + g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); + g.type = alloc_type_pointer(e->type); + LLVMSetLinkage(g.value, LLVMExternalLinkage); + + lb_add_entity(m, e, g); + lb_add_member(m, name, g); + return lb_emit_load(p, g); + } + } + + String pkg = {}; + if (e->pkg) { + pkg = e->pkg->name; + } + gb_printf_err("Error in: %s\n", token_pos_to_string(ast_token(expr).pos)); + GB_PANIC("nullptr value for expression from identifier: %.*s.%.*s (%p) : %s @ %p", LIT(pkg), LIT(e->token.string), e, type_to_string(e->type), expr); + return {}; +} + +bool lb_is_expr_constant_zero(Ast *expr) { + GB_ASSERT(expr != nullptr); + auto v = exact_value_to_integer(expr->tav.value); + if (v.kind == ExactValue_Integer) { + return big_int_cmp_zero(&v.value_integer) == 0; + } + return false; +} + lbValue lb_build_expr(lbProcedure *p, Ast *expr) { lbModule *m = p->module; - u64 prev_state_flags = p->module->state_flags; - defer (p->module->state_flags = prev_state_flags); + u16 prev_state_flags = p->state_flags; + defer (p->state_flags = prev_state_flags); if (expr->state_flags != 0) { - u64 in = expr->state_flags; - u64 out = p->module->state_flags; + u16 in = expr->state_flags; + u16 out = p->state_flags; if (in & StateFlag_bounds_check) { out |= StateFlag_bounds_check; @@ -10999,7 +11476,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { out &= ~StateFlag_bounds_check; } - p->module->state_flags = out; + p->state_flags = out; } expr = unparen_expr(expr); @@ -11009,6 +11486,12 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "invalid expression '%s' (tv.mode = %d, tv.type = %s) @ %s\n Current Proc: %.*s : %s", expr_to_string(expr), tv.mode, type_to_string(tv.type), token_pos_to_string(expr_pos), LIT(p->name), type_to_string(p->type)); if (tv.value.kind != ExactValue_Invalid) { + // NOTE(bill): The commented out code below is just for debug purposes only + // GB_ASSERT_MSG(!is_type_untyped(tv.type), "%s @ %s\n%s", type_to_string(tv.type), token_pos_to_string(expr_pos), expr_to_string(expr)); + // if (is_type_untyped(tv.type)) { + // gb_printf_err("%s %s\n", token_pos_to_string(expr_pos), expr_to_string(expr)); + // } + // NOTE(bill): Short on constant values return lb_const_value(p->module, tv.type, tv.value); } @@ -11032,7 +11515,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_ast_node(bd, BasicDirective, expr); TokenPos pos = bd->token.pos; - GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(bd->name)); + GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(bd->name.string)); case_end; case_ast_node(i, Implicit, expr); @@ -11070,24 +11553,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { } GB_ASSERT(e->kind != Entity_ProcGroup); - auto *found = map_get(&p->module->values, hash_entity(e)); - if (found) { - auto v = *found; - // NOTE(bill): This is because pointers are already pointers in LLVM - if (is_type_proc(v.type)) { - return v; - } - return lb_emit_load(p, v); - } else if (e != nullptr && e->kind == Entity_Variable) { - return lb_addr_load(p, lb_build_addr(p, expr)); - } - gb_printf_err("Error in: %s\n", token_pos_to_string(i->token.pos)); - String pkg = {}; - if (e->pkg) { - pkg = e->pkg->name; - } - GB_PANIC("nullptr value for expression from identifier: %.*s.%.*s (%p) : %s @ %p", LIT(pkg), LIT(e->token.string), e, type_to_string(e->type), expr); - return {}; + return lb_find_ident(p, m, e, expr); case_end; case_ast_node(de, DerefExpr, expr); @@ -11114,47 +11580,6 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { return lb_build_expr(p, se->call); case_end; - case_ast_node(te, TernaryExpr, expr); - LLVMValueRef incoming_values[2] = {}; - LLVMBasicBlockRef incoming_blocks[2] = {}; - - GB_ASSERT(te->y != nullptr); - lbBlock *then = lb_create_block(p, "if.then"); - lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later - lbBlock *else_ = lb_create_block(p, "if.else"); - - lbValue cond = lb_build_cond(p, te->cond, then, else_); - lb_start_block(p, then); - - Type *type = default_type(type_of_expr(expr)); - - lb_open_scope(p, nullptr); - incoming_values[0] = lb_emit_conv(p, lb_build_expr(p, te->x), type).value; - lb_close_scope(p, lbDeferExit_Default, nullptr); - - lb_emit_jump(p, done); - lb_start_block(p, else_); - - lb_open_scope(p, nullptr); - incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, te->y), type).value; - lb_close_scope(p, lbDeferExit_Default, nullptr); - - lb_emit_jump(p, done); - lb_start_block(p, done); - - lbValue res = {}; - res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), ""); - res.type = type; - - GB_ASSERT(p->curr_block->preds.count >= 2); - incoming_blocks[0] = p->curr_block->preds[0]->block; - incoming_blocks[1] = p->curr_block->preds[1]->block; - - LLVMAddIncoming(res.value, incoming_values, incoming_blocks, 2); - - return res; - case_end; - case_ast_node(te, TernaryIfExpr, expr); LLVMValueRef incoming_values[2] = {}; LLVMBasicBlockRef incoming_blocks[2] = {}; @@ -11213,6 +11638,10 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { lbValue e = lb_build_expr(p, ta->expr); Type *t = type_deref(e.type); if (is_type_union(t)) { + if (ta->ignores[0]) { + // NOTE(bill): This is not needed for optimization levels other than 0 + return lb_emit_union_cast_only_ok_check(p, e, type, pos); + } return lb_emit_union_cast(p, e, type, pos); } else if (is_type_any(t)) { return lb_emit_any_cast(p, e, type, pos); @@ -11358,6 +11787,13 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(se, SliceExpr, expr); + if (is_type_slice(type_of_expr(se->expr))) { + // NOTE(bill): Quick optimization + if (se->high == nullptr && + (se->low == nullptr || lb_is_expr_constant_zero(se->low))) { + return lb_build_expr(p, se->expr); + } + } return lb_addr_load(p, lb_build_addr(p, expr)); case_end; @@ -11436,7 +11872,13 @@ lbValue lb_get_using_variable(lbProcedure *p, Entity *e) { GB_ASSERT(v.value != nullptr); GB_ASSERT_MSG(parent->type == type_deref(v.type), "%s %s", type_to_string(parent->type), type_to_string(v.type)); lbValue ptr = lb_emit_deep_field_gep(p, v, sel); - lb_add_debug_local_variable(p, ptr.value, e->type, e->token); + if (parent->scope) { + if ((parent->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) { + lb_add_debug_local_variable(p, ptr.value, e->type, e->token); + } + } else { + lb_add_debug_local_variable(p, ptr.value, e->type, e->token); + } return ptr; } @@ -11462,11 +11904,14 @@ lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *expr) { return lb_get_soa_variable_addr(p, e); } + if (v.value == nullptr) { - error(expr, "%.*s Unknown value: %.*s, entity: %p %.*s", - LIT(p->name), - LIT(e->token.string), e, LIT(entity_strings[e->kind])); - GB_PANIC("Unknown value"); + return lb_addr(lb_find_value_from_entity(p->module, e)); + + // error(expr, "%.*s Unknown value: %.*s, entity: %p %.*s", + // LIT(p->name), + // LIT(e->token.string), e, LIT(entity_strings[e->kind])); + // GB_PANIC("Unknown value"); } return lb_addr(v); @@ -11813,27 +12258,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { return lb_addr(val); } - if (!is_type_indexable(t)) { - AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr)); - if (found != nullptr) { - if (found->kind == TypeAtomOp_index_get) { - return lb_build_addr(p, found->node); - } else if (found->kind == TypeAtomOp_index_get_ptr) { - return lb_addr(lb_build_expr(p, found->node)); - } else if (found->kind == TypeAtomOp_index_set) { - lbValue ptr = lb_build_addr_ptr(p, ie->expr); - if (deref) { - ptr = lb_emit_load(p, ptr); - } - - lbAddr addr = {lbAddr_AtomOp_index_set}; - addr.addr = ptr; - addr.index_set.index = lb_build_expr(p, ie->index); - addr.index_set.node = found->node; - return addr; - } - } - } GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr)); if (is_type_map(t)) { @@ -11969,6 +12393,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(se, SliceExpr, expr); + lbValue low = lb_const_int(p->module, t_int, 0); lbValue high = {}; @@ -11977,36 +12402,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { bool no_indices = se->low == nullptr && se->high == nullptr; - { - Type *type = base_type(type_of_expr(se->expr)); - if (type->kind == Type_Struct && !is_type_soa_struct(type)) { - TypeAtomOpTable *atom_op_table = type->Struct.atom_op_table; - if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) { - AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr)); - if (found) { - lbValue base = lb_build_expr(p, found->node); - - Type *slice_type = base.type; - lbValue len = lb_slice_len(p, base); - if (high.value == nullptr) high = len; - - if (!no_indices) { - lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); - } - - - lbValue elem = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low); - lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - - lbAddr slice = lb_add_local_generated(p, slice_type, false); - lb_fill_slice(p, slice, elem, new_len); - return slice; - } - } - } - } - - lbAddr addr = lb_build_addr(p, se->expr); lbValue base = lb_addr_load(p, addr); Type *type = base_type(base.type); @@ -12780,8 +13175,16 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { void lb_init_module(lbModule *m, Checker *c) { m->info = &c->info; - m->ctx = LLVMGetGlobalContext(); - m->mod = LLVMModuleCreateWithNameInContext("odin_module", m->ctx); + gbString module_name = gb_string_make(heap_allocator(), "odin_package"); + if (m->pkg) { + module_name = gb_string_appendc(module_name, "-"); + module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len); + } else if (USE_SEPARTE_MODULES) { + module_name = gb_string_appendc(module_name, "-builtin"); + } + + m->ctx = LLVMContextCreate(); + m->mod = LLVMModuleCreateWithNameInContext(module_name ? module_name : "odin_package", m->ctx); // m->debug_builder = nullptr; if (build_context.ODIN_DEBUG) { enum {DEBUG_METADATA_VERSION = 3}; @@ -12808,10 +13211,6 @@ void lb_init_module(lbModule *m, Checker *c) { m->debug_builder = LLVMCreateDIBuilder(m->mod); } - m->state_flags = 0; - m->state_flags |= StateFlag_bounds_check; - - gb_mutex_init(&m->mutex); gbAllocator a = heap_allocator(); map_init(&m->types, a); map_init(&m->llvm_types, a); @@ -12870,6 +13269,7 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) { } gbAllocator ha = heap_allocator(); array_init(&gen->output_object_paths, ha); + array_init(&gen->output_temp_paths, ha); gen->output_base = path_to_full_path(ha, gen->output_base); @@ -12879,8 +13279,34 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) { gen->info = &c->info; - lb_init_module(&gen->module, c); + map_init(&gen->modules, permanent_allocator(), gen->info->packages.entries.count*2); + map_init(&gen->modules_through_ctx, permanent_allocator(), gen->info->packages.entries.count*2); + map_init(&gen->anonymous_proc_lits, heap_allocator(), 1024); + + gb_mutex_init(&gen->mutex); + if (USE_SEPARTE_MODULES) { + for_array(i, gen->info->packages.entries) { + AstPackage *pkg = gen->info->packages.entries[i].value; + + auto m = gb_alloc_item(permanent_allocator(), lbModule); + m->pkg = pkg; + m->gen = gen; + map_set(&gen->modules, hash_pointer(pkg), m); + lb_init_module(m, c); + } + } + + gen->default_module.gen = gen; + map_set(&gen->modules, hash_pointer(nullptr), &gen->default_module); + lb_init_module(&gen->default_module, c); + + + for_array(i, gen->modules.entries) { + lbModule *m = gen->modules.entries[i].value; + LLVMContextRef ctx = LLVMGetModuleContext(m->mod); + map_set(&gen->modules_through_ctx, hash_pointer(ctx), m); + } return true; } @@ -12891,8 +13317,10 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { isize max_len = 7+8+1; u8 *str = cast(u8 *)gb_alloc_array(permanent_allocator(), u8, max_len); - isize len = gb_snprintf(cast(char *)str, max_len, "ggv$%x", m->global_generated_index); - m->global_generated_index++; + + u32 id = cast(u32)gb_atomic32_fetch_add(&m->gen->global_generated_index, 1); + + isize len = gb_snprintf(cast(char *)str, max_len, "ggv$%x", id); String name = make_string(str, len-1); Scope *scope = nullptr; @@ -12901,7 +13329,7 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { g.type = alloc_type_pointer(type); g.value = LLVMAddGlobal(m->mod, lb_type(m, type), cast(char const *)str); if (value.value != nullptr) { - GB_ASSERT(LLVMIsConstant(value.value)); + GB_ASSERT_MSG(LLVMIsConstant(value.value), LLVMPrintValueToString(value.value)); LLVMSetInitializer(g.value, value.value); } else { LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, type))); @@ -12915,17 +13343,12 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { lbValue lb_find_runtime_value(lbModule *m, String const &name) { AstPackage *p = m->info->runtime_package; Entity *e = scope_lookup_current(p->scope, name); - lbValue *found = map_get(&m->values, hash_entity(e)); - GB_ASSERT_MSG(found != nullptr, "Unable to find runtime value '%.*s'", LIT(name)); - lbValue value = *found; - return value; + return lb_find_value_from_entity(m, e); } lbValue lb_find_package_value(lbModule *m, String const &pkg, String const &name) { Entity *e = find_entity_in_pkg(m->info, pkg, name); lbValue *found = map_get(&m->values, hash_entity(e)); - GB_ASSERT_MSG(found != nullptr, "Unable to find value '%.*s.%.*s'", LIT(pkg), LIT(name)); - lbValue value = *found; - return value; + return lb_find_value_from_entity(m, e); } lbValue lb_get_type_info_ptr(lbModule *m, Type *type) { @@ -12940,32 +13363,37 @@ lbValue lb_get_type_info_ptr(lbModule *m, Type *type) { lbValue res = {}; res.type = t_type_info_ptr; - res.value = LLVMConstGEP(lb_global_type_info_data.addr.value, indices, cast(unsigned)gb_count_of(indices)); + res.value = LLVMConstGEP(lb_global_type_info_data_ptr(m).value, indices, cast(unsigned)gb_count_of(indices)); return res; } lbValue lb_type_info_member_types_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_types.addr, lb_global_type_info_member_types_index); lb_global_type_info_member_types_index += cast(i32)count; return offset; } lbValue lb_type_info_member_names_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_names.addr, lb_global_type_info_member_names_index); lb_global_type_info_member_names_index += cast(i32)count; return offset; } lbValue lb_type_info_member_offsets_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_offsets.addr, lb_global_type_info_member_offsets_index); lb_global_type_info_member_offsets_index += cast(i32)count; return offset; } lbValue lb_type_info_member_usings_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_usings.addr, lb_global_type_info_member_usings_index); lb_global_type_info_member_usings_index += cast(i32)count; return offset; } lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_tags.addr, lb_global_type_info_member_tags_index); lb_global_type_info_member_tags_index += cast(i32)count; return offset; @@ -13007,12 +13435,12 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da { // NOTE(bill): Set the type_table slice with the global backing array lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table")); - Type *type = base_type(lb_addr_type(lb_global_type_info_data)); + Type *type = base_type(lb_global_type_info_data_entity->type); GB_ASSERT(is_type_array(type)); LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)}; LLVMValueRef values[2] = { - LLVMConstInBoundsGEP(lb_global_type_info_data.addr.value, indices, gb_count_of(indices)), + LLVMConstInBoundsGEP(lb_global_type_info_data_ptr(m).value, indices, gb_count_of(indices)), LLVMConstInt(lb_type(m, t_int), type->Array.count, true), }; LLVMValueRef slice = llvm_const_named_struct(llvm_addr_type(global_type_table), values, gb_count_of(values)); @@ -13044,7 +13472,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da } lbValue tag = {}; - lbValue ti_ptr = lb_emit_array_epi(p, lb_global_type_info_data.addr, cast(i32)entry_index); + lbValue ti_ptr = lb_emit_array_epi(p, lb_global_type_info_data_ptr(m), cast(i32)entry_index); lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 4); lbValue type_info_flags = lb_const_int(p->module, t_type_info_flags, type_info_flags_of_type(t)); @@ -13447,7 +13875,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr); { - LLVMValueRef vals[6] = {}; + LLVMValueRef vals[7] = {}; isize variant_count = gb_max(0, t->Union.variants.count); lbValue memory_types = lb_type_info_member_types_offset(p, variant_count); @@ -13476,10 +13904,19 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da vals[2] = LLVMConstNull(lb_type(m, t_type_info_ptr)); } - vals[3] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; - vals[4] = lb_const_bool(m, t_bool, t->Union.no_nil).value; - vals[5] = lb_const_bool(m, t_bool, t->Union.maybe).value; + if (is_type_comparable(t) && !is_type_simple_compare(t)) { + vals[3] = lb_get_equal_proc_for_type(m, t).value; + } + + vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; + vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value; + vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value; + for (isize i = 0; i < gb_count_of(vals); i++) { + if (vals[i] == nullptr) { + vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i))); + } + } lbValue res = {}; res.type = type_deref(tag.type); @@ -13691,18 +14128,445 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da } } +struct lbGlobalVariable { + lbValue var; + lbValue init; + DeclInfo *decl; + bool is_initialized; +}; + +lbProcedure *lb_create_startup_type_info(lbModule *m) { + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); + LLVMFinalizeFunctionPassManager(default_function_pass_manager); + + Type *params = alloc_type_tuple(); + Type *results = alloc_type_tuple(); + + Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); + + lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_TYPE_INFO_PROC_NAME), proc_type); + p->is_startup = true; + + lb_begin_procedure_body(p); + + lb_setup_type_info_data(p); + + lb_end_procedure_body(p); + + if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { + gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); + LLVMDumpValue(p->value); + gb_printf_err("\n\n\n\n"); + LLVMVerifyFunction(p->value, LLVMAbortProcessAction); + } + + lb_run_function_pass_manager(default_function_pass_manager, p); + + return p; +} + +lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array<lbGlobalVariable> &global_variables) { // Startup Runtime + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod); + lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level); + LLVMFinalizeFunctionPassManager(default_function_pass_manager); + + Type *params = alloc_type_tuple(); + Type *results = alloc_type_tuple(); + + Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); + + lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type); + p->is_startup = true; + + lb_begin_procedure_body(p); + + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, ""); + + for_array(i, global_variables) { + auto *var = &global_variables[i]; + if (var->is_initialized) { + continue; + } + + lbModule *entity_module = main_module; + + Entity *e = var->decl->entity; + GB_ASSERT(e->kind == Entity_Variable); + e->code_gen_module = entity_module; + + if (var->decl->init_expr != nullptr) { + // gb_printf_err("%s\n", expr_to_string(var->decl->init_expr)); + lbValue init = lb_build_expr(p, var->decl->init_expr); + LLVMValueKind value_kind = LLVMGetValueKind(init.value); + // gb_printf_err("%s %d\n", LLVMPrintValueToString(init.value)); + + if (lb_is_const_or_global(init)) { + if (!var->is_initialized) { + LLVMSetInitializer(var->var.value, init.value); + var->is_initialized = true; + continue; + } + } else { + var->init = init; + } + } + + if (var->init.value != nullptr) { + GB_ASSERT(!var->is_initialized); + Type *t = type_deref(var->var.type); + + if (is_type_any(t)) { + // NOTE(bill): Edge case for 'any' type + Type *var_type = default_type(var->init.type); + lbAddr g = lb_add_global_generated(main_module, var_type, var->init); + lb_addr_store(p, g, var->init); + lbValue gp = lb_addr_get_ptr(p, g); + + lbValue data = lb_emit_struct_ep(p, var->var, 0); + lbValue ti = lb_emit_struct_ep(p, var->var, 1); + lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr)); + lb_emit_store(p, ti, lb_type_info(main_module, var_type)); + } else { + LLVMTypeRef pvt = LLVMTypeOf(var->var.value); + LLVMTypeRef vt = LLVMGetElementType(pvt); + lbValue src0 = lb_emit_conv(p, var->init, t); + LLVMValueRef src = OdinLLVMBuildTransmute(p, src0.value, vt); + LLVMValueRef dst = var->var.value; + LLVMBuildStore(p->builder, src, dst); + } + + var->is_initialized = true; + } + } + + + lb_end_procedure_body(p); + + if (!main_module->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { + gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); + LLVMDumpValue(p->value); + gb_printf_err("\n\n\n\n"); + LLVMVerifyFunction(p->value, LLVMAbortProcessAction); + } + + lb_run_function_pass_manager(default_function_pass_manager, p); + + return p; +} + + +lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) { + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); + LLVMFinalizeFunctionPassManager(default_function_pass_manager); + + Type *params = alloc_type_tuple(); + Type *results = alloc_type_tuple(); + + Type *t_ptr_cstring = alloc_type_pointer(t_cstring); + + String name = str_lit("main"); + if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) { + name = str_lit("mainCRTStartup"); + } else { + array_init(¶ms->Tuple.variables, permanent_allocator(), 2); + params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true); + params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), t_ptr_cstring, false, true); + } + + array_init(&results->Tuple.variables, permanent_allocator(), 1); + results->Tuple.variables[0] = alloc_entity_param(nullptr, blank_token, t_i32, false, true); + + Type *proc_type = alloc_type_proc(nullptr, + params, params->Tuple.variables.count, + results, results->Tuple.variables.count, false, ProcCC_CDecl); + + + lbProcedure *p = lb_create_dummy_procedure(m, name, proc_type); + p->is_startup = true; + + lb_begin_procedure_body(p); + + { // initialize `runtime.args__` + lbValue argc = {LLVMGetParam(p->value, 0), t_i32}; + lbValue argv = {LLVMGetParam(p->value, 1), t_ptr_cstring}; + LLVMSetValueName2(argc.value, "argc", 4); + LLVMSetValueName2(argv.value, "argv", 4); + argc = lb_emit_conv(p, argc, t_int); + lbAddr args = lb_addr(lb_find_runtime_value(p->module, str_lit("args__"))); + lb_fill_slice(p, args, argv, argc); + } + + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, ""); + + if (build_context.command_kind == Command_test) { + Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test")); + Type *array_type = alloc_type_array(t_Internal_Test, m->info->testing_procedures.count); + Type *slice_type = alloc_type_slice(t_Internal_Test); + lbAddr all_tests_array_addr = lb_add_global_generated(p->module, array_type, {}); + lbValue all_tests_array = lb_addr_get_ptr(p, all_tests_array_addr); + + LLVMTypeRef lbt_Internal_Test = lb_type(m, t_Internal_Test); + + LLVMValueRef indices[2] = {}; + indices[0] = LLVMConstInt(lb_type(m, t_i32), 0, false); + + for_array(i, m->info->testing_procedures) { + Entity *testing_proc = m->info->testing_procedures[i]; + String name = testing_proc->token.string; + + String pkg_name = {}; + if (testing_proc->pkg != nullptr) { + pkg_name = testing_proc->pkg->name; + } + lbValue v_pkg = lb_find_or_add_entity_string(m, pkg_name); + lbValue v_name = lb_find_or_add_entity_string(m, name); + lbValue v_proc = lb_find_procedure_value_from_entity(m, testing_proc); + + indices[1] = LLVMConstInt(lb_type(m, t_int), i, false); + + LLVMValueRef vals[3] = {}; + vals[0] = v_pkg.value; + vals[1] = v_name.value; + vals[2] = v_proc.value; + GB_ASSERT(LLVMIsConstant(vals[0])); + GB_ASSERT(LLVMIsConstant(vals[1])); + GB_ASSERT(LLVMIsConstant(vals[2])); + + LLVMValueRef dst = LLVMConstInBoundsGEP(all_tests_array.value, indices, gb_count_of(indices)); + LLVMValueRef src = llvm_const_named_struct(lbt_Internal_Test, vals, gb_count_of(vals)); + + LLVMBuildStore(p->builder, src, dst); + } + + lbAddr all_tests_slice = lb_add_local_generated(p, slice_type, true); + lb_fill_slice(p, all_tests_slice, + lb_array_elem(p, all_tests_array), + lb_const_int(m, t_int, m->info->testing_procedures.count)); + + + lbValue runner = lb_find_package_value(m, str_lit("testing"), str_lit("runner")); + + auto args = array_make<lbValue>(heap_allocator(), 1); + args[0] = lb_addr_load(p, all_tests_slice); + lb_emit_call(p, runner, args); + } else { + lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point); + lb_emit_call(p, entry_point, {}); + } + + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false)); + + lb_end_procedure_body(p); + + if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { + gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); + LLVMDumpValue(p->value); + gb_printf_err("\n\n\n\n"); + LLVMVerifyFunction(p->value, LLVMAbortProcessAction); + } + + lb_run_function_pass_manager(default_function_pass_manager, p); + return p; +} + +String lb_filepath_ll_for_module(lbModule *m) { + String path = m->gen->output_base; + if (m->pkg) { + path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name); + } else if (USE_SEPARTE_MODULES) { + path = concatenate_strings(permanent_allocator(), path, STR_LIT("-builtin")); + } + path = concatenate_strings(permanent_allocator(), path, STR_LIT(".ll")); + + return path; +} +String lb_filepath_obj_for_module(lbModule *m) { + String path = m->gen->output_base; + if (m->pkg) { + path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name); + } + + String ext = {}; + + if (build_context.build_mode == BuildMode_Assembly) { + ext = STR_LIT(".S"); + } else { + switch (build_context.metrics.os) { + case TargetOs_windows: + ext = STR_LIT(".obj"); + break; + case TargetOs_darwin: + case TargetOs_linux: + case TargetOs_essence: + ext = STR_LIT(".o"); + break; + case TargetOs_js: + ext = STR_LIT(".wasm-obj"); + break; + } + } + + return concatenate_strings(permanent_allocator(), path, ext); +} + + +bool lb_is_module_empty(lbModule *m) { + if (LLVMGetFirstFunction(m->mod) == nullptr && + LLVMGetFirstGlobal(m->mod) == nullptr) { + return true; + } + for (auto fn = LLVMGetFirstFunction(m->mod); fn != nullptr; fn = LLVMGetNextFunction(fn)) { + if (LLVMGetFirstBasicBlock(fn) != nullptr) { + return false; + } + } + + for (auto g = LLVMGetFirstGlobal(m->mod); g != nullptr; g = LLVMGetNextGlobal(g)) { + if (LLVMGetLinkage(g) == LLVMExternalLinkage) { + continue; + } + if (!LLVMIsExternallyInitialized(g)) { + return false; + } + } + return true; +} + +struct lbLLVMEmitWorker { + LLVMTargetMachineRef target_machine; + LLVMCodeGenFileType code_gen_file_type; + String filepath_obj; + lbModule *m; +}; + +WORKER_TASK_PROC(lb_llvm_emit_worker_proc) { + GB_ASSERT(MULTITHREAD_OBJECT_GENERATION); + + char *llvm_error = nullptr; + + auto wd = cast(lbLLVMEmitWorker *)data; + + if (LLVMTargetMachineEmitToFile(wd->target_machine, wd->m->mod, cast(char *)wd->filepath_obj.text, wd->code_gen_file_type, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); + } + + return 0; +} + +WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) { + GB_ASSERT(MULTITHREAD_OBJECT_GENERATION); + + auto m = cast(lbModule *)data; + + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(m->mod); + + LLVMInitializeFunctionPassManager(default_function_pass_manager); + LLVMInitializeFunctionPassManager(function_pass_manager_minimal); + LLVMInitializeFunctionPassManager(function_pass_manager_size); + LLVMInitializeFunctionPassManager(function_pass_manager_speed); + + lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); + lb_populate_function_pass_manager_specific(m, function_pass_manager_minimal, 0); + lb_populate_function_pass_manager_specific(m, function_pass_manager_size, 1); + lb_populate_function_pass_manager_specific(m, function_pass_manager_speed, 2); + + LLVMFinalizeFunctionPassManager(default_function_pass_manager); + LLVMFinalizeFunctionPassManager(function_pass_manager_minimal); + LLVMFinalizeFunctionPassManager(function_pass_manager_size); + LLVMFinalizeFunctionPassManager(function_pass_manager_speed); + + + LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMInitializeFunctionPassManager(default_function_pass_manager_without_memcpy); + lb_populate_function_pass_manager(m, default_function_pass_manager_without_memcpy, true, build_context.optimization_level); + LLVMFinalizeFunctionPassManager(default_function_pass_manager_without_memcpy); + + + for_array(i, m->procedures_to_generate) { + lbProcedure *p = m->procedures_to_generate[i]; + if (p->body != nullptr) { // Build Procedure + if (p->flags & lbProcedureFlag_WithoutMemcpyPass) { + lb_run_function_pass_manager(default_function_pass_manager_without_memcpy, p); + } else { + if (p->entity && p->entity->kind == Entity_Procedure) { + switch (p->entity->Procedure.optimization_mode) { + case ProcedureOptimizationMode_None: + case ProcedureOptimizationMode_Minimal: + lb_run_function_pass_manager(function_pass_manager_minimal, p); + break; + case ProcedureOptimizationMode_Size: + lb_run_function_pass_manager(function_pass_manager_size, p); + break; + case ProcedureOptimizationMode_Speed: + lb_run_function_pass_manager(function_pass_manager_speed, p); + break; + default: + lb_run_function_pass_manager(default_function_pass_manager, p); + break; + } + } else { + lb_run_function_pass_manager(default_function_pass_manager, p); + } + } + } + } + + for_array(i, m->equal_procs.entries) { + lbProcedure *p = m->equal_procs.entries[i].value; + lb_run_function_pass_manager(default_function_pass_manager, p); + } + for_array(i, m->hasher_procs.entries) { + lbProcedure *p = m->hasher_procs.entries[i].value; + lb_run_function_pass_manager(default_function_pass_manager, p); + } + + return 0; +} + + +struct lbLLVMModulePassWorkerData { + lbModule *m; + LLVMTargetMachineRef target_machine; +}; + +WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { + GB_ASSERT(MULTITHREAD_OBJECT_GENERATION); + + auto wd = cast(lbLLVMModulePassWorkerData *)data; + + LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager(); + lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level); + LLVMRunPassManager(module_pass_manager, wd->m->mod); + + return 0; +} + + void lb_generate_code(lbGenerator *gen) { #define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0) + #define TIME_SECTION_WITH_LEN(str, len) do { if (build_context.show_more_timings) timings_start_section(&global_timings, make_string((u8 *)str, len)); } while (0) TIME_SECTION("LLVM Initializtion"); - lbModule *m = &gen->module; - LLVMModuleRef mod = gen->module.mod; + isize thread_count = gb_max(build_context.thread_count, 1); + isize worker_count = thread_count-1; + + LLVMBool do_threading = (LLVMIsMultithreaded() && USE_SEPARTE_MODULES && MULTITHREAD_OBJECT_GENERATION && worker_count > 0); + + thread_pool_init(&lb_thread_pool, heap_allocator(), worker_count, "LLVMBackend"); + defer (thread_pool_destroy(&lb_thread_pool)); + + lbModule *default_module = &gen->default_module; CheckerInfo *info = gen->info; auto *min_dep_set = &info->minimum_dependency_set; - LLVMInitializeAllTargetInfos(); LLVMInitializeAllTargets(); LLVMInitializeAllTargetMCs(); @@ -13711,16 +14575,19 @@ void lb_generate_code(lbGenerator *gen) { LLVMInitializeAllDisassemblers(); LLVMInitializeNativeTarget(); - char const *target_triple = alloc_cstring(permanent_allocator(), build_context.metrics.target_triplet); char const *target_data_layout = alloc_cstring(permanent_allocator(), build_context.metrics.target_data_layout); - LLVMSetTarget(mod, target_triple); + for_array(i, gen->modules.entries) { + LLVMSetTarget(gen->modules.entries[i].value->mod, target_triple); + } LLVMTargetRef target = {}; char *llvm_error = nullptr; LLVMGetTargetFromTriple(target_triple, &target, &llvm_error); GB_ASSERT(target != nullptr); + + TIME_SECTION("LLVM Create Target Machine"); LLVMCodeModel code_mode = LLVMCodeModelDefault; @@ -13754,78 +14621,89 @@ void lb_generate_code(lbGenerator *gen) { } // NOTE(bill): Target Machine Creation - LLVMTargetMachineRef target_machine = LLVMCreateTargetMachine( - target, target_triple, llvm_cpu, - llvm_features, - code_gen_level, - LLVMRelocDefault, - code_mode); - defer (LLVMDisposeTargetMachine(target_machine)); - - - LLVMSetModuleDataLayout(mod, LLVMCreateTargetDataLayout(target_machine)); - - if (m->debug_builder) { // Debug Info - for_array(i, info->files.entries) { - AstFile *f = info->files.entries[i].value; - String fullpath = f->fullpath; - String filename = remove_directory_from_path(fullpath); - String directory = directory_from_path(fullpath); - LLVMMetadataRef res = LLVMDIBuilderCreateFile(m->debug_builder, - cast(char const *)filename.text, filename.len, - cast(char const *)directory.text, directory.len); - lb_set_llvm_metadata(m, f, res); - } - - gbString producer = gb_string_make(heap_allocator(), "odin"); - producer = gb_string_append_fmt(producer, " version %.*s", LIT(ODIN_VERSION)); - #ifdef NIGHTLY - producer = gb_string_appendc(producer, "-nightly"); - #endif - #ifdef GIT_SHA - producer = gb_string_append_fmt(producer, "-%s", GIT_SHA); - #endif - - gbString split_name = gb_string_make(heap_allocator(), ""); - - LLVMBool is_optimized = build_context.optimization_level > 0; - AstFile *init_file = m->info->init_package->files[0]; - if (m->info->entry_point && m->info->entry_point->identifier && m->info->entry_point->identifier->file) { - init_file = m->info->entry_point->identifier->file; + // NOTE(bill, 2021-05-04): Target machines must be unique to each module because they are not thread safe + auto target_machines = array_make<LLVMTargetMachineRef>(permanent_allocator(), gen->modules.entries.count); + + for_array(i, gen->modules.entries) { + target_machines[i] = LLVMCreateTargetMachine( + target, target_triple, llvm_cpu, + llvm_features, + code_gen_level, + LLVMRelocDefault, + code_mode); + LLVMSetModuleDataLayout(gen->modules.entries[i].value->mod, LLVMCreateTargetDataLayout(target_machines[i])); + } + + for_array(i, gen->modules.entries) { + lbModule *m = gen->modules.entries[i].value; + if (m->debug_builder) { // Debug Info + for_array(i, info->files.entries) { + AstFile *f = info->files.entries[i].value; + String fullpath = f->fullpath; + String filename = remove_directory_from_path(fullpath); + String directory = directory_from_path(fullpath); + LLVMMetadataRef res = LLVMDIBuilderCreateFile(m->debug_builder, + cast(char const *)filename.text, filename.len, + cast(char const *)directory.text, directory.len); + lb_set_llvm_metadata(m, f, res); + } + + gbString producer = gb_string_make(heap_allocator(), "odin"); + // producer = gb_string_append_fmt(producer, " version %.*s", LIT(ODIN_VERSION)); + // #ifdef NIGHTLY + // producer = gb_string_appendc(producer, "-nightly"); + // #endif + // #ifdef GIT_SHA + // producer = gb_string_append_fmt(producer, "-%s", GIT_SHA); + // #endif + + gbString split_name = gb_string_make(heap_allocator(), ""); + + LLVMBool is_optimized = build_context.optimization_level > 0; + AstFile *init_file = m->info->init_package->files[0]; + if (m->info->entry_point && m->info->entry_point->identifier && m->info->entry_point->identifier->file) { + init_file = m->info->entry_point->identifier->file; + } + + LLVMBool split_debug_inlining = false; + LLVMBool debug_info_for_profiling = false; + + m->debug_compile_unit = LLVMDIBuilderCreateCompileUnit(m->debug_builder, LLVMDWARFSourceLanguageC99, + lb_get_llvm_metadata(m, init_file), + producer, gb_string_length(producer), + is_optimized, "", 0, + 1, split_name, gb_string_length(split_name), + LLVMDWARFEmissionFull, + 0, split_debug_inlining, + debug_info_for_profiling, + "", 0, // sys_root + "", 0 // SDK + ); + GB_ASSERT(m->debug_compile_unit != nullptr); } - - LLVMBool split_debug_inlining = false; - LLVMBool debug_info_for_profiling = false; - - m->debug_compile_unit = LLVMDIBuilderCreateCompileUnit(m->debug_builder, LLVMDWARFSourceLanguageC99, - lb_get_llvm_metadata(m, init_file), - producer, gb_string_length(producer), - is_optimized, "", 0, - 1, split_name, gb_string_length(split_name), - LLVMDWARFEmissionFull, - 0, split_debug_inlining, - debug_info_for_profiling, - "", 0, // sys_root - "", 0 // SDK - ); - GB_ASSERT(m->debug_compile_unit != nullptr); } TIME_SECTION("LLVM Global Variables"); { + lbModule *m = default_module; + { // Add type info data isize max_type_info_count = info->minimum_dependency_type_info_set.entries.count+1; // gb_printf_err("max_type_info_count: %td\n", max_type_info_count); Type *t = alloc_type_array(t_type_info, max_type_info_count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), LB_TYPE_INFO_DATA_NAME); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), LB_TYPE_INFO_DATA_NAME); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lbValue value = {}; value.value = g; value.type = alloc_type_pointer(t); - lb_global_type_info_data = lb_addr(value); + + lb_global_type_info_data_entity = alloc_entity_variable(nullptr, make_token_ident(LB_TYPE_INFO_DATA_NAME), t, EntityState_Resolved); + lb_add_entity(m, lb_global_type_info_data_entity, value); } { // Type info member buffer // NOTE(bill): Removes need for heap allocation by making it global memory @@ -13856,7 +14734,7 @@ void lb_generate_code(lbGenerator *gen) { { char const *name = LB_TYPE_INFO_TYPES_NAME; Type *t = alloc_type_array(t_type_info_ptr, count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_types = lb_addr({g, alloc_type_pointer(t)}); @@ -13865,7 +14743,7 @@ void lb_generate_code(lbGenerator *gen) { { char const *name = LB_TYPE_INFO_NAMES_NAME; Type *t = alloc_type_array(t_string, count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_names = lb_addr({g, alloc_type_pointer(t)}); @@ -13873,7 +14751,7 @@ void lb_generate_code(lbGenerator *gen) { { char const *name = LB_TYPE_INFO_OFFSETS_NAME; Type *t = alloc_type_array(t_uintptr, count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_offsets = lb_addr({g, alloc_type_pointer(t)}); @@ -13882,7 +14760,7 @@ void lb_generate_code(lbGenerator *gen) { { char const *name = LB_TYPE_INFO_USINGS_NAME; Type *t = alloc_type_array(t_bool, count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_usings = lb_addr({g, alloc_type_pointer(t)}); @@ -13891,7 +14769,7 @@ void lb_generate_code(lbGenerator *gen) { { char const *name = LB_TYPE_INFO_TAGS_NAME; Type *t = alloc_type_array(t_string, count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_tags = lb_addr({g, alloc_type_pointer(t)}); @@ -13931,13 +14809,8 @@ void lb_generate_code(lbGenerator *gen) { } } - struct GlobalVariable { - lbValue var; - lbValue init; - DeclInfo *decl; - bool is_initialized; - }; - auto global_variables = array_make<GlobalVariable>(permanent_allocator(), 0, global_variable_max_count); + + auto global_variables = array_make<lbGlobalVariable>(permanent_allocator(), 0, global_variable_max_count); for_array(i, info->variable_init_order) { DeclInfo *d = info->variable_init_order[i]; @@ -13960,8 +14833,9 @@ void lb_generate_code(lbGenerator *gen) { bool is_foreign = e->Variable.is_foreign; bool is_export = e->Variable.is_export; - String name = lb_get_entity_name(m, e); + lbModule *m = &gen->default_module; + String name = lb_get_entity_name(m, e); lbValue g = {}; g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); @@ -13985,6 +14859,7 @@ void lb_generate_code(lbGenerator *gen) { LLVMSetThreadLocalMode(g.value, mode); } if (is_foreign) { + LLVMSetLinkage(g.value, LLVMExternalLinkage); LLVMSetExternallyInitialized(g.value, true); lb_add_foreign_library_path(m, e->Variable.foreign_library); } else { @@ -13993,13 +14868,15 @@ void lb_generate_code(lbGenerator *gen) { if (is_export) { LLVMSetLinkage(g.value, LLVMDLLExportLinkage); LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass); + } else { + if (USE_SEPARTE_MODULES) { + LLVMSetLinkage(g.value, LLVMExternalLinkage); + } else { + LLVMSetLinkage(g.value, LLVMInternalLinkage); + } } - if (e->flags & EntityFlag_Static) { - LLVMSetLinkage(g.value, LLVMInternalLinkage); - } - - GlobalVariable var = {}; + lbGlobalVariable var = {}; var.var = g; var.decl = decl; @@ -14026,13 +14903,14 @@ void lb_generate_code(lbGenerator *gen) { lb_add_entity(m, e, g); lb_add_member(m, name, g); + if (m->debug_builder) { String global_name = e->token.string; if (global_name.len != 0 && global_name != "_") { LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, e->file); LLVMMetadataRef llvm_scope = llvm_file; - LLVMBool local_to_unit = e->flags & EntityFlag_Static; + LLVMBool local_to_unit = LLVMGetLinkage(g.value) == LLVMInternalLinkage; LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); LLVMMetadataRef llvm_decl = nullptr; @@ -14096,6 +14974,10 @@ void lb_generate_code(lbGenerator *gen) { continue; } + lbModule *m = &gen->default_module; + if (USE_SEPARTE_MODULES) { + m = lb_pkg_module(gen, e->pkg); + } String mangled_name = lb_get_entity_name(m, e); @@ -14113,450 +14995,209 @@ void lb_generate_code(lbGenerator *gen) { } - TIME_SECTION("LLVM Registry Initializtion"); - - LLVMPassRegistryRef pass_registry = LLVMGetGlobalPassRegistry(); - - LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(mod); - LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(mod); - LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(mod); - LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(mod); - defer (LLVMDisposePassManager(default_function_pass_manager)); - defer (LLVMDisposePassManager(function_pass_manager_minimal)); - defer (LLVMDisposePassManager(function_pass_manager_size)); - defer (LLVMDisposePassManager(function_pass_manager_speed)); - - LLVMInitializeFunctionPassManager(default_function_pass_manager); - LLVMInitializeFunctionPassManager(function_pass_manager_minimal); - LLVMInitializeFunctionPassManager(function_pass_manager_size); - LLVMInitializeFunctionPassManager(function_pass_manager_speed); - - lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); - lb_populate_function_pass_manager_specific(function_pass_manager_minimal, 0); - lb_populate_function_pass_manager_specific(function_pass_manager_size, 1); - lb_populate_function_pass_manager_specific(function_pass_manager_speed, 2); - - LLVMFinalizeFunctionPassManager(default_function_pass_manager); - LLVMFinalizeFunctionPassManager(function_pass_manager_minimal); - LLVMFinalizeFunctionPassManager(function_pass_manager_size); - LLVMFinalizeFunctionPassManager(function_pass_manager_speed); - - - LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(mod); - defer (LLVMDisposePassManager(default_function_pass_manager_without_memcpy)); - LLVMInitializeFunctionPassManager(default_function_pass_manager_without_memcpy); - lb_populate_function_pass_manager(default_function_pass_manager_without_memcpy, true, build_context.optimization_level); - LLVMFinalizeFunctionPassManager(default_function_pass_manager_without_memcpy); - TIME_SECTION("LLVM Runtime Type Information Creation"); + lbProcedure *startup_type_info = lb_create_startup_type_info(default_module); - lbProcedure *startup_type_info = nullptr; - lbProcedure *startup_runtime = nullptr; - { // Startup Type Info - Type *params = alloc_type_tuple(); - Type *results = alloc_type_tuple(); - - Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); - - lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_TYPE_INFO_PROC_NAME), proc_type); - p->is_startup = true; - startup_type_info = p; - - lb_begin_procedure_body(p); - - lb_setup_type_info_data(p); - - lb_end_procedure_body(p); - - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } - - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - } TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)"); - { // Startup Runtime - Type *params = alloc_type_tuple(); - Type *results = alloc_type_tuple(); - - Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); - - lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type); - p->is_startup = true; - startup_runtime = p; + lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, global_variables); - lb_begin_procedure_body(p); - - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_type_info->type)), startup_type_info->value, nullptr, 0, ""); - - for_array(i, global_variables) { - auto *var = &global_variables[i]; - if (var->is_initialized) { + TIME_SECTION("LLVM Procedure Generation"); + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + for_array(i, m->procedures_to_generate) { + lbProcedure *p = m->procedures_to_generate[i]; + if (p->is_done) { continue; } - - Entity *e = var->decl->entity; - GB_ASSERT(e->kind == Entity_Variable); - - if (var->decl->init_expr != nullptr) { - // gb_printf_err("%s\n", expr_to_string(var->decl->init_expr)); - lbValue init = lb_build_expr(p, var->decl->init_expr); - LLVMValueKind value_kind = LLVMGetValueKind(init.value); - // gb_printf_err("%s %d\n", LLVMPrintValueToString(init.value)); - - if (lb_is_const_or_global(init)) { - if (!var->is_initialized) { - LLVMSetInitializer(var->var.value, init.value); - var->is_initialized = true; - continue; - } - } else { - var->init = init; - } - } - - if (var->init.value != nullptr) { - GB_ASSERT(!var->is_initialized); - Type *t = type_deref(var->var.type); - - if (is_type_any(t)) { - // NOTE(bill): Edge case for 'any' type - Type *var_type = default_type(var->init.type); - lbAddr g = lb_add_global_generated(m, var_type, var->init); - lb_addr_store(p, g, var->init); - lbValue gp = lb_addr_get_ptr(p, g); - - lbValue data = lb_emit_struct_ep(p, var->var, 0); - lbValue ti = lb_emit_struct_ep(p, var->var, 1); - lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr)); - lb_emit_store(p, ti, lb_type_info(m, var_type)); - } else { - LLVMTypeRef pvt = LLVMTypeOf(var->var.value); - LLVMTypeRef vt = LLVMGetElementType(pvt); - lbValue src0 = lb_emit_conv(p, var->init, t); - LLVMValueRef src = OdinLLVMBuildTransmute(p, src0.value, vt); - LLVMValueRef dst = var->var.value; - LLVMBuildStore(p->builder, src, dst); - } - - var->is_initialized = true; + if (p->body != nullptr) { // Build Procedure + m->curr_procedure = p; + lb_begin_procedure_body(p); + lb_build_stmt(p, p->body); + lb_end_procedure_body(p); + p->is_done = true; + m->curr_procedure = nullptr; + } + lb_end_procedure(p); + + // Add Flags + if (p->body != nullptr) { + if (p->name == "memcpy" || p->name == "memmove" || + p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" || + string_starts_with(p->name, str_lit("llvm.memcpy")) || + string_starts_with(p->name, str_lit("llvm.memmove"))) { + p->flags |= lbProcedureFlag_WithoutMemcpyPass; + } + } + + if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { + gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name)); + LLVMDumpValue(p->value); + gb_printf_err("\n\n\n\n"); + String filepath_ll = lb_filepath_ll_for_module(m); + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + } + LLVMVerifyFunction(p->value, LLVMPrintMessageAction); + gb_exit(1); } } - - - lb_end_procedure_body(p); - - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } - - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - - /*{ - LLVMValueRef last_instr = LLVMGetLastInstruction(p->decl_block->block); - for (LLVMValueRef instr = LLVMGetFirstInstruction(p->decl_block->block); - instr != last_instr; - instr = LLVMGetNextInstruction(instr)) { - if (LLVMIsAAllocaInst(instr)) { - LLVMTypeRef type = LLVMGetAllocatedType(instr); - LLVMValueRef sz_val = LLVMSizeOf(type); - GB_ASSERT(LLVMIsConstant(sz_val)); - gb_printf_err(">> 0x%p\n", sz_val); - LLVMTypeRef sz_type = LLVMTypeOf(sz_val); - gb_printf_err(">> %s\n", LLVMPrintTypeToString(sz_type)); - unsigned long long sz = LLVMConstIntGetZExtValue(sz_val); - // long long sz = LLVMConstIntGetSExtValue(sz_val); - gb_printf_err(">> %ll\n", sz); - } - } - }*/ } - String filepath_ll = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".ll")); - TIME_SECTION("LLVM Procedure Generation"); - for_array(i, m->procedures_to_generate) { - lbProcedure *p = m->procedures_to_generate[i]; - if (p->is_done) { - continue; - } - if (p->body != nullptr) { // Build Procedure - m->curr_procedure = p; - lb_begin_procedure_body(p); - lb_build_stmt(p, p->body); - lb_end_procedure_body(p); - p->is_done = true; - m->curr_procedure = nullptr; - } - lb_end_procedure(p); - - // Add Flags - if (p->body != nullptr) { - if (p->name == "memcpy" || p->name == "memmove" || - p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" || - string_starts_with(p->name, str_lit("llvm.memcpy")) || - string_starts_with(p->name, str_lit("llvm.memmove"))) { - p->flags |= lbProcedureFlag_WithoutMemcpyPass; - } - } + if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) { + TIME_SECTION("LLVM main"); + lb_create_main_procedure(default_module, startup_runtime); + } - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name)); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); + if (build_context.ODIN_DEBUG) { + TIME_SECTION("LLVM Debug Info Complete Types and Finalize"); + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (m->debug_builder != nullptr) { + lb_debug_complete_types(m); + LLVMDIBuilderFinalize(m->debug_builder); } - LLVMVerifyFunction(p->value, LLVMPrintMessageAction); - gb_exit(1); } } - if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) { - TIME_SECTION("LLVM DLL main"); - - - Type *params = alloc_type_tuple(); - Type *results = alloc_type_tuple(); - - String name = str_lit("main"); - if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) { - name = str_lit("mainCRTStartup"); - } else { - array_init(¶ms->Tuple.variables, permanent_allocator(), 2); - params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true); - params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), alloc_type_pointer(t_cstring), false, true); - } - - array_init(&results->Tuple.variables, permanent_allocator(), 1); - results->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("_"), t_i32, false, true); - - Type *proc_type = alloc_type_proc(nullptr, - params, params->Tuple.variables.count, - results, results->Tuple.variables.count, false, ProcCC_CDecl); + TIME_SECTION("LLVM Function Pass"); + for_array(i, gen->modules.entries) { + lbModule *m = gen->modules.entries[i].value; + lb_llvm_function_pass_worker_proc(m); + } - lbProcedure *p = lb_create_dummy_procedure(m, name, proc_type); - p->is_startup = true; + TIME_SECTION("LLVM Module Pass"); - lb_begin_procedure_body(p); + for_array(i, gen->modules.entries) { + lbModule *m = gen->modules.entries[i].value; - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, ""); + auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); + wd->m = m; + wd->target_machine = target_machines[i]; - if (build_context.command_kind == Command_test) { - Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test")); - Type *array_type = alloc_type_array(t_Internal_Test, m->info->testing_procedures.count); - Type *slice_type = alloc_type_slice(t_Internal_Test); - lbAddr all_tests_array_addr = lb_add_global_generated(p->module, array_type, {}); - lbValue all_tests_array = lb_addr_get_ptr(p, all_tests_array_addr); + lb_llvm_module_pass_worker_proc(wd); + } - LLVMTypeRef lbt_Internal_Test = lb_type(m, t_Internal_Test); - LLVMValueRef indices[2] = {}; - indices[0] = LLVMConstInt(lb_type(m, t_i32), 0, false); + llvm_error = nullptr; + defer (LLVMDisposeMessage(llvm_error)); - for_array(i, m->info->testing_procedures) { - Entity *testing_proc = m->info->testing_procedures[i]; - String name = testing_proc->token.string; - lbValue *found = map_get(&m->values, hash_entity(testing_proc)); - GB_ASSERT(found != nullptr); + LLVMCodeGenFileType code_gen_file_type = LLVMObjectFile; + if (build_context.build_mode == BuildMode_Assembly) { + code_gen_file_type = LLVMAssemblyFile; + } - String pkg_name = {}; - if (testing_proc->pkg != nullptr) { - pkg_name = testing_proc->pkg->name; + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) { + gb_printf_err("LLVM Error:\n%s\n", llvm_error); + if (build_context.keep_temp_files) { + TIME_SECTION("LLVM Print Module to File"); + String filepath_ll = lb_filepath_ll_for_module(m); + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); + return; } - lbValue v_pkg = lb_find_or_add_entity_string(m, pkg_name); - lbValue v_name = lb_find_or_add_entity_string(m, name); - lbValue v_proc = *found; - - indices[1] = LLVMConstInt(lb_type(m, t_int), i, false); - - LLVMValueRef vals[3] = {}; - vals[0] = v_pkg.value; - vals[1] = v_name.value; - vals[2] = v_proc.value; - GB_ASSERT(LLVMIsConstant(vals[0])); - GB_ASSERT(LLVMIsConstant(vals[1])); - GB_ASSERT(LLVMIsConstant(vals[2])); - - LLVMValueRef dst = LLVMConstInBoundsGEP(all_tests_array.value, indices, gb_count_of(indices)); - LLVMValueRef src = llvm_const_named_struct(lbt_Internal_Test, vals, gb_count_of(vals)); - - LLVMBuildStore(p->builder, src, dst); } + gb_exit(1); + return; + } + } + llvm_error = nullptr; + if (build_context.keep_temp_files || + build_context.build_mode == BuildMode_LLVM_IR) { + TIME_SECTION("LLVM Print Module to File"); - lbAddr all_tests_slice = lb_add_local_generated(p, slice_type, true); - lb_fill_slice(p, all_tests_slice, - lb_array_elem(p, all_tests_array), - lb_const_int(m, t_int, m->info->testing_procedures.count)); + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (lb_is_module_empty(m)) { + continue; + } - lbValue runner = lb_find_package_value(m, str_lit("testing"), str_lit("runner")); + String filepath_ll = lb_filepath_ll_for_module(m); + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); + return; + } + array_add(&gen->output_temp_paths, filepath_ll); - auto args = array_make<lbValue>(heap_allocator(), 1); - args[0] = lb_addr_load(p, all_tests_slice); - lb_emit_call(p, runner, args); - } else { - lbValue *found = map_get(&m->values, hash_entity(entry_point)); - GB_ASSERT(found != nullptr); - lb_emit_call(p, *found, {}); } - - LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false)); - - lb_end_procedure_body(p); - - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); + if (build_context.build_mode == BuildMode_LLVM_IR) { + gb_exit(0); + return; } - - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); } - if (m->debug_builder != nullptr) { - TIME_SECTION("LLVM Debug Info Complete Types"); - lb_debug_complete_types(m); + TIME_SECTION("LLVM Add Foreign Library Paths"); + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + for_array(i, m->info->required_foreign_imports_through_force) { + Entity *e = m->info->required_foreign_imports_through_force[i]; + lb_add_foreign_library_path(m, e); + } - TIME_SECTION("LLVM Print Module to File"); - if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); - return; + if (lb_is_module_empty(m)) { + continue; } - TIME_SECTION("LLVM Debug Info Builder Finalize"); - LLVMDIBuilderFinalize(m->debug_builder); } + TIME_SECTION("LLVM Object Generation"); - TIME_SECTION("LLVM Function Pass"); - { - for_array(i, m->procedures_to_generate) { - lbProcedure *p = m->procedures_to_generate[i]; - if (p->body != nullptr) { // Build Procedure - if (p->flags & lbProcedureFlag_WithoutMemcpyPass) { - LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value); - } else { - if (p->entity && p->entity->kind == Entity_Procedure) { - switch (p->entity->Procedure.optimization_mode) { - case ProcedureOptimizationMode_None: - case ProcedureOptimizationMode_Minimal: - LLVMRunFunctionPassManager(function_pass_manager_minimal, p->value); - break; - case ProcedureOptimizationMode_Size: - LLVMRunFunctionPassManager(function_pass_manager_size, p->value); - break; - case ProcedureOptimizationMode_Speed: - LLVMRunFunctionPassManager(function_pass_manager_speed, p->value); - break; - default: - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - break; - } - } else { - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - } - } + if (do_threading) { + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (lb_is_module_empty(m)) { + continue; } - } - for_array(i, m->equal_procs.entries) { - lbProcedure *p = m->equal_procs.entries[i].value; - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - } - for_array(i, m->hasher_procs.entries) { - lbProcedure *p = m->hasher_procs.entries[i].value; - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - } - } + String filepath_ll = lb_filepath_ll_for_module(m); + String filepath_obj = lb_filepath_obj_for_module(m); + array_add(&gen->output_object_paths, filepath_obj); + array_add(&gen->output_temp_paths, filepath_ll); - TIME_SECTION("LLVM Module Pass"); + auto *wd = gb_alloc_item(heap_allocator(), lbLLVMEmitWorker); + wd->target_machine = target_machines[j]; + wd->code_gen_file_type = code_gen_file_type; + wd->filepath_obj = filepath_obj; + wd->m = m; + thread_pool_add_task(&lb_thread_pool, lb_llvm_emit_worker_proc, wd); + } - LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager(); - defer (LLVMDisposePassManager(module_pass_manager)); - lb_populate_module_pass_manager(target_machine, module_pass_manager, build_context.optimization_level); + thread_pool_start(&lb_thread_pool); + thread_pool_wait_to_process(&lb_thread_pool); + } else { + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (lb_is_module_empty(m)) { + continue; + } - LLVMRunPassManager(module_pass_manager, mod); + String filepath_obj = lb_filepath_obj_for_module(m); + array_add(&gen->output_object_paths, filepath_obj); - llvm_error = nullptr; - defer (LLVMDisposeMessage(llvm_error)); + String short_name = remove_directory_from_path(filepath_obj); + gbString section_name = gb_string_make(heap_allocator(), "LLVM Generate Object: "); + section_name = gb_string_append_length(section_name, short_name.text, short_name.len); - String filepath_obj = {}; - LLVMCodeGenFileType code_gen_file_type = LLVMObjectFile; + TIME_SECTION_WITH_LEN(section_name, gb_string_length(section_name)); - if (build_context.build_mode == BuildMode_Assembly) { - filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".S")); - code_gen_file_type = LLVMAssemblyFile; - } else { - switch (build_context.metrics.os) { - case TargetOs_windows: - filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".obj")); - break; - case TargetOs_darwin: - case TargetOs_linux: - case TargetOs_essence: - filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".o")); - break; - case TargetOs_js: - filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".wasm-obj")); - break; - } - } - - if (LLVMVerifyModule(mod, LLVMReturnStatusAction, &llvm_error)) { - gb_printf_err("LLVM Error:\n%s\n", llvm_error); - if (build_context.keep_temp_files) { - TIME_SECTION("LLVM Print Module to File"); - if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { + if (LLVMTargetMachineEmitToFile(target_machines[j], m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); gb_exit(1); return; } } - gb_exit(1); - return; - } - llvm_error = nullptr; - if (build_context.keep_temp_files || - build_context.build_mode == BuildMode_LLVM_IR) { - TIME_SECTION("LLVM Print Module to File"); - if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); - return; - } - if (build_context.build_mode == BuildMode_LLVM_IR) { - gb_exit(0); - return; - } } - TIME_SECTION("LLVM Object Generation"); - if (LLVMTargetMachineEmitToFile(target_machine, mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); - return; - } - - array_add(&gen->output_object_paths, filepath_obj); - - for_array(i, m->info->required_foreign_imports_through_force) { - Entity *e = m->info->required_foreign_imports_through_force[i]; - lb_add_foreign_library_path(m, e); - } #undef TIME_SECTION } |