#include #include struct LLVMGlobal { String host_triple; String host_cpu_model; String host_cpu_features; }; global_variable LLVMGlobal llvm_global; fn void llvm_initialize_all_raw() { assert(!llvm_initialized); LLVMInitializeX86TargetInfo(); LLVMInitializeX86Target(); LLVMInitializeX86TargetMC(); LLVMInitializeX86AsmPrinter(); LLVMInitializeX86AsmParser(); LLVMInitializeX86Disassembler(); llvm_global = { .host_triple = llvm_default_target_triple(), .host_cpu_model = llvm_host_cpu_name(), .host_cpu_features = llvm_host_cpu_features(), }; } fn void llvm_initialize_all() { if (!llvm_initialized) { llvm_initialize_all_raw(); } } fn bool is_arbitrary_bit_integer(Type* type) { switch (type->id) { case TypeId::integer: switch (type->integer.bit_count) { case 8: case 16: case 32: case 64: case 128: return false; default: return true; } break; case TypeId::unresolved: unreachable(); case TypeId::bits: return is_arbitrary_bit_integer(type->bits.backing_type); case TypeId::enumerator: return is_arbitrary_bit_integer(type->enumerator.backing_type); default: return false; } } fn u64 integer_max_value(u32 bit_count, bool is_signed) { auto max_value = bit_count == 64 ? ~(u64)0 : ((u64)1 << (bit_count - is_signed)) - 1; return max_value; } fn void dump_module(Module* module) { print(llvm_module_to_string(module->llvm.module)); } fn bool type_is_signed(Type* type) { switch (type->id) { case TypeId::integer: return type->integer.is_signed; case TypeId::enumerator: return type_is_signed(type->enumerator.backing_type); default: unreachable(); } } enum class EvaluationKind { scalar, aggregate, complex, }; enum class TypeKind { abi, memory, }; fn LLVMCallConv llvm_calling_convention(CallingConvention calling_convention) { LLVMCallConv cc; switch (calling_convention) { case CallingConvention::c: cc = LLVMCCallConv; break; case CallingConvention::count: unreachable(); } return cc; } fn Type* resolve_alias(Module* module, Type* type) { Type* result_type = 0; switch (type->id) { case TypeId::pointer: { auto* element_type = type->pointer.element_type; auto* resolved_element_type = resolve_alias(module, element_type); result_type = get_pointer_type(module, resolved_element_type); } break; case TypeId::array: { auto* element_type = type->array.element_type; auto element_count = type->array.element_count; assert(element_count); auto* resolved_element_type = resolve_alias(module, element_type); result_type = get_array_type(module, resolved_element_type, element_count); } break; case TypeId::integer: case TypeId::enumerator: { result_type = type; } break; default: unreachable(); } assert(result_type); return result_type; } fn EvaluationKind get_evaluation_kind(Type* type) { switch (type->id) { case TypeId::void_type: case TypeId::noreturn: case TypeId::forward_declaration: case TypeId::unresolved: case TypeId::function: case TypeId::alias: unreachable(); case TypeId::integer: case TypeId::pointer: case TypeId::bits: case TypeId::enumerator: return EvaluationKind::scalar; case TypeId::array: case TypeId::structure: case TypeId::union_type: return EvaluationKind::aggregate; } } fn void llvm_initialize(Module* module) { llvm_initialize_all(); auto context = LLVMContextCreate(); auto m = llvm_context_create_module(context, module->name); auto builder = LLVMCreateBuilderInContext(context); LLVMDIBuilderRef di_builder = 0; LLVMMetadataRef di_compile_unit = 0; LLVMMetadataRef di_file = 0; if (module->has_debug_info) { di_builder = LLVMCreateDIBuilder(m); auto last_slash = string_last_character(module->path, '/'); if (last_slash == string_no_match) { report_error(); } auto directory = module->path(0, last_slash); auto file_name = module->path(last_slash + 1); di_file = LLVMDIBuilderCreateFile(di_builder, (char*)file_name.pointer, file_name.length, (char*)directory.pointer, directory.length); auto producer_name = string_literal("bloat buster"); auto is_optimized = build_mode_is_optimized(module->build_mode); auto flags = string_literal(""); u32 runtime_version = 0; auto split_name = string_literal(""); auto sysroot = string_literal(""); auto sdk = string_literal(""); di_compile_unit = LLVMDIBuilderCreateCompileUnit(di_builder, LLVMDWARFSourceLanguageC17, di_file, (char*)producer_name.pointer, producer_name.length, is_optimized, (char*)flags.pointer, flags.length, runtime_version, (char*)split_name.pointer, split_name.length, LLVMDWARFEmissionFull, 0, 0, is_optimized, (char*)sysroot.pointer, sysroot.length, (char*)sdk.pointer, sdk.length); module->scope.llvm = di_compile_unit; } module->llvm = { .context = context, .module = m, .builder = builder, .di_builder = di_builder, .file = di_file, .compile_unit = di_compile_unit, .pointer_type = LLVMPointerTypeInContext(context, 0), .void_type = LLVMVoidTypeInContext(context), }; for (u64 i = 0; i < (u64)IntrinsicIndex::count; i += 1) { String name = intrinsic_names[i]; module->llvm.intrinsic_table[i].n = LLVMLookupIntrinsicID((char*)name.pointer, name.length); } } enum class AbiSystemVClass { none, integer, sse, sse_up, x87, x87_up, complex_x87, memory, }; fn bool contains_no_user_data(Type* type, u64 start, u64 end) { unused(end); if (get_byte_size(type) <= start) { return true; } else { switch (type->id) { case TypeId::structure: { trap(); } break; case TypeId::array: { trap(); } break; default: return false; } } } fn Type* get_integer_type_at_offset(Module* module, Type* type, u32 offset, Type* source_type, u32 source_offset) { switch (type->id) { case TypeId::integer: { auto bit_count = type->integer.bit_count; switch (bit_count) { case 64: return type; case 32: case 16: case 8: { assert(offset == 0); auto start = source_offset + get_byte_size(type); auto end = source_offset + 8; if (contains_no_user_data(source_type, start, end)) { return type; } } break; default: { auto original_byte_count = get_byte_size(type); assert(original_byte_count != source_offset); auto byte_count = MIN(original_byte_count - source_offset, 8); auto bit_count = byte_count * 8; auto result_type = integer_type(module, { .bit_count = (u32)bit_count, .is_signed = false }); return result_type; } break; } } break; default: unreachable(); } auto source_size = get_byte_size(source_type); auto byte_count = source_size - source_offset; u32 bit_count = byte_count > 8 ? 64 : byte_count * 8; auto result = integer_type(module, { .bit_count = bit_count, .is_signed = false }); return result; } struct AbiSystemVClassify { u64 base_offset; bool is_variable_argument; bool is_register_call; }; struct AbiSystemVClassifyResult { AbiSystemVClass r[2]; }; fn AbiSystemVClassifyResult abi_system_v_classify_type(Type* type, AbiSystemVClassify options) { AbiSystemVClassifyResult result = {}; auto is_memory = options.base_offset >= 8; auto current_index = is_memory; auto not_current_index = !is_memory; assert(current_index != not_current_index); result.r[current_index] = AbiSystemVClass::memory; switch (type->id) { case TypeId::void_type: case TypeId::noreturn: result.r[current_index] = AbiSystemVClass::none; break; case TypeId::bits: return abi_system_v_classify_type(type->bits.backing_type, options); case TypeId::enumerator: return abi_system_v_classify_type(type->enumerator.backing_type, options); case TypeId::pointer: result.r[current_index] = AbiSystemVClass::integer; break; case TypeId::integer: { if (type->integer.bit_count <= 64) { result.r[current_index] = AbiSystemVClass::integer; } else if (type->integer.bit_count == 128) { trap(); } else { report_error(); } } break; case TypeId::array: { trap(); } break; case TypeId::structure: { trap(); } break; case TypeId::alias: return abi_system_v_classify_type(type->alias.type, options); default: unreachable(); } return result; } fn bool is_integral_or_enumeration_type(Type* type) { switch (type->id) { case TypeId::alias: return is_integral_or_enumeration_type(type->alias.type); case TypeId::integer: case TypeId::bits: return true; case TypeId::structure: return false; default: unreachable(); } } fn bool is_promotable_integer_type_for_abi(Type* type) { switch (type->id) { case TypeId::integer: return type->integer.bit_count < 32; case TypeId::bits: return is_promotable_integer_type_for_abi(type->bits.backing_type); case TypeId::alias: return is_promotable_integer_type_for_abi(type->alias.type); default: unreachable(); } } struct DirectOptions { Type* semantic_type; Type* type; Type* padding; u32 offset; u32 alignment; bool cannot_be_flattened; }; fn void resolve_type_in_place_memory(Module* module, Type* type); fn void resolve_type_in_place_abi(Module* module, Type* type) { if (!type->llvm.abi) { LLVMTypeRef result = 0; switch (type->id) { case TypeId::void_type: case TypeId::noreturn: result = module->llvm.void_type; break; case TypeId::integer: result = LLVMIntTypeInContext(module->llvm.context, type->integer.bit_count); break; case TypeId::pointer: result = module->llvm.pointer_type; break; case TypeId::array: { auto* element_type = type->array.element_type; auto element_count = type->array.element_count; assert(element_count); resolve_type_in_place_memory(module, element_type); auto array_type = LLVMArrayType2(element_type->llvm.memory, element_count); result = array_type; } break; case TypeId::enumerator: { auto backing_type = type->enumerator.backing_type; resolve_type_in_place_abi(module, backing_type); result = backing_type->llvm.abi; } break; default: unreachable(); } assert(result); type->llvm.abi = result; } } fn void resolve_type_in_place_memory(Module* module, Type* type) { if (!type->llvm.memory) { resolve_type_in_place_abi(module, type); LLVMTypeRef result = 0; switch (type->id) { case TypeId::void_type: case TypeId::noreturn: case TypeId::pointer: case TypeId::array: result = type->llvm.abi; break; case TypeId::integer: { auto byte_size = get_byte_size(type); auto bit_count = byte_size * 8; result = LLVMIntTypeInContext(module->llvm.context, bit_count); } break; case TypeId::enumerator: { auto backing_type = type->enumerator.backing_type; resolve_type_in_place_memory(module, backing_type); result = backing_type->llvm.memory; } break; default: unreachable(); } assert(result); type->llvm.memory = result; if (type->id == TypeId::bits) { assert(type->llvm.memory == type->llvm.abi); } } } fn void resolve_type_in_place_debug(Module* module, Type* type) { if (module->has_debug_info) { if (!type->llvm.debug) { LLVMMetadataRef result = 0; switch (type->id) { case TypeId::void_type: case TypeId::noreturn: { result = LLVMDIBuilderCreateBasicType(module->llvm.di_builder, (char*)type->name.pointer, type->name.length, 0, (u32)DwarfType::void_type, type->id == TypeId::noreturn ? LLVMDIFlagNoReturn : LLVMDIFlagZero); } break; case TypeId::integer: { DwarfType dwarf_type = type->integer.bit_count == 1 ? DwarfType::boolean : (type->integer.is_signed ? DwarfType::signed_type : DwarfType::unsigned_type); LLVMDIFlags flags = {}; result = LLVMDIBuilderCreateBasicType(module->llvm.di_builder, (char*)type->name.pointer, type->name.length, type->integer.bit_count, (u32)dwarf_type, flags); } break; case TypeId::pointer: { resolve_type_in_place_debug(module, type->pointer.element_type); if (type->llvm.debug) { trap(); } else { result = LLVMDIBuilderCreatePointerType(module->llvm.di_builder, type->pointer.element_type->llvm.debug, 64, 64, 0, (char*)type->name.pointer, type->name.length); } } break; case TypeId::array: { auto array_element_type = type->array.element_type; auto array_element_count = type->array.element_count; assert(array_element_count); resolve_type_in_place_debug(module, array_element_type); auto bit_alignment = get_byte_alignment(type) * 8; auto array_type = LLVMDIBuilderCreateArrayType(module->llvm.di_builder, array_element_count, bit_alignment, array_element_type->llvm.debug, 0, 0); result = array_type; } break; case TypeId::enumerator: { auto backing_type = type->enumerator.backing_type; resolve_type_in_place_debug(module, backing_type); LLVMMetadataRef field_buffer[64]; for (u64 i = 0; i < type->enumerator.fields.length; i += 1) { auto& field = type->enumerator.fields[i]; auto enum_field = LLVMDIBuilderCreateEnumerator(module->llvm.di_builder, (char*)field.name.pointer, field.name.length, field.value, type_is_signed(backing_type)); field_buffer[i] = enum_field; } result = LLVMDIBuilderCreateEnumerationType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->enumerator.line, get_bit_size(type), get_byte_alignment(type) * 8, field_buffer, type->enumerator.fields.length, backing_type->llvm.debug); } break; default: unreachable(); } assert(result); type->llvm.debug = result; } } } fn void resolve_type_in_place(Module* module, Type* type) { resolve_type_in_place_abi(module, type); resolve_type_in_place_memory(module, type); resolve_type_in_place_debug(module, type); } fn bool type_is_abi_equal(Module* module, Type* a, Type* b) { resolve_type_in_place(module, a); resolve_type_in_place(module, b); bool result = a == b; if (!result) { result = a->llvm.abi == b->llvm.abi; } return result; } fn AbiInformation abi_system_v_get_ignore(Module* module, Type* semantic_type) { resolve_type_in_place(module, semantic_type); return { .semantic_type = semantic_type, .flags = { .kind = AbiKind::ignore, }, }; } fn AbiInformation abi_system_v_get_direct(Module* module, DirectOptions direct) { AbiInformation result = { .semantic_type = direct.semantic_type, .flags = { .kind = AbiKind::direct, }, }; resolve_type_in_place(module, direct.semantic_type); resolve_type_in_place(module, direct.type); if (unlikely(direct.padding)) { resolve_type_in_place(module, direct.padding); } result.set_coerce_to_type(direct.type); result.set_padding_type(direct.type); result.set_direct_offset(direct.offset); result.set_direct_alignment(direct.alignment); result.set_can_be_flattened(!direct.cannot_be_flattened); return result; } struct ExtendOptions { Type* semantic_type; Type* type; bool sign; }; fn AbiInformation abi_system_v_get_extend(ExtendOptions options) { assert(is_integral_or_enumeration_type(options.semantic_type)); AbiInformation result = { .semantic_type = options.semantic_type, .flags = { .kind = AbiKind::extend, }, }; result.set_coerce_to_type(options.type ? options.type : options.semantic_type); result.set_padding_type(0); result.set_direct_offset(0); result.set_direct_alignment(0); result.flags.sign_extension = options.sign; return result; } struct AbiSystemVClassifyArgumentTypeOptions { u32 available_gpr; bool is_named_argument; bool is_reg_call; }; struct AbiSystemVClassifyArgumentTypeResult { AbiInformation abi; AbiRegisterCountSystemV needed_registers; }; fn AbiSystemVClassifyArgumentTypeResult abi_system_v_classify_argument_type(Module* module, Type* semantic_argument_type, AbiSystemVClassifyArgumentTypeOptions options) { auto classify_result = abi_system_v_classify_type(semantic_argument_type, AbiSystemVClassify{ .base_offset = 0, .is_variable_argument = !options.is_named_argument, .is_register_call = options.is_reg_call, }); auto low_class = classify_result.r[0]; auto high_class = classify_result.r[1]; AbiRegisterCountSystemV needed_registers = {}; Type* low_type = 0; switch (low_class) { case AbiSystemVClass::integer: { needed_registers.gpr += 1; low_type = get_integer_type_at_offset(module, semantic_argument_type, 0, semantic_argument_type, 0); if (high_class == AbiSystemVClass::none && low_type->id == TypeId::integer) { // TODO: if enumerator if (is_integral_or_enumeration_type(semantic_argument_type) && is_promotable_integer_type_for_abi(semantic_argument_type)) { return { abi_system_v_get_extend({ .semantic_type = semantic_argument_type, .sign = type_is_signed(semantic_argument_type), }), needed_registers }; } } trap(); } break; default: unreachable(); } trap(); } struct AbiSystemVClassifyArgumentOptions { Type* type; u16 abi_start; bool is_reg_call = false; bool is_named_argument; }; fn AbiInformation abi_system_v_classify_argument(Module* module, AbiRegisterCountSystemV* available_registers, Slice llvm_abi_argument_type_buffer, Slice abi_argument_type_buffer, AbiSystemVClassifyArgumentOptions options) { auto semantic_argument_type = options.type; if (options.is_reg_call) { trap(); } auto result = abi_system_v_classify_argument_type(module, semantic_argument_type, { .available_gpr = available_registers->gpr, .is_named_argument = options.is_named_argument, .is_reg_call = options.is_reg_call, }); auto abi = result.abi; auto needed_registers = result.needed_registers; AbiInformation argument_abi; if (available_registers->gpr >= needed_registers.gpr && available_registers->sse >= needed_registers.sse) { available_registers->gpr -= needed_registers.gpr; available_registers->sse -= needed_registers.sse; argument_abi = abi; } else { trap(); } if (argument_abi.get_padding_type()) { trap(); } argument_abi.abi_start = options.abi_start; u16 count = 0; switch (argument_abi.flags.kind) { case AbiKind::direct: case AbiKind::extend: { auto coerce_to_type = argument_abi.get_coerce_to_type(); resolve_type_in_place(module, coerce_to_type); auto is_flattened_struct = argument_abi.flags.kind == AbiKind::direct && argument_abi.get_can_be_flattened() && coerce_to_type->id == TypeId::structure; count = is_flattened_struct ? coerce_to_type->structure.fields.length : 1; if (is_flattened_struct) { trap(); } else { llvm_abi_argument_type_buffer[argument_abi.abi_start] = coerce_to_type->llvm.abi; abi_argument_type_buffer[argument_abi.abi_start] = coerce_to_type; } } break; default: unreachable(); } assert(count); argument_abi.abi_count = count; return argument_abi; } fn AbiInformation abi_system_classify_return_type(Module* module, Type* semantic_return_type) { auto type_classes = abi_system_v_classify_type(semantic_return_type, {}); auto low_class = type_classes.r[0]; auto high_class = type_classes.r[1]; assert(high_class != AbiSystemVClass::memory || low_class == AbiSystemVClass::memory); assert(high_class != AbiSystemVClass::sse_up || low_class == AbiSystemVClass::sse); Type* low_type = 0; switch (low_class) { case AbiSystemVClass::none: { if (high_class == AbiSystemVClass::none) { return abi_system_v_get_ignore(module, semantic_return_type); } trap(); } break; case AbiSystemVClass::integer: { low_type = get_integer_type_at_offset(module, semantic_return_type, 0, semantic_return_type, 0); if (high_class == AbiSystemVClass::none && low_type->id == TypeId::integer) { if (semantic_return_type->id == TypeId::enumerator) { trap(); } if (is_integral_or_enumeration_type(semantic_return_type) && is_promotable_integer_type_for_abi(semantic_return_type)) { trap(); } } } break; default: unreachable(); } Type* high_type = 0; switch (high_class) { case AbiSystemVClass::none: break; case AbiSystemVClass::integer: { trap(); } break; default: unreachable(); } if (high_type) { trap(); } auto result = abi_system_v_get_direct(module, { .semantic_type = semantic_return_type, .type = low_type, }); return result; } struct AttributeBuildOptions { AbiInformation return_abi; Slice argument_abis; Slice abi_argument_types; Type* abi_return_type; FunctionAttributes attributes; bool call_site; }; struct AllocaOptions { Type* type; String name = string_literal(""); u32 alignment; }; fn LLVMValueRef create_alloca(Module* module, AllocaOptions options) { auto abi_type = options.type; resolve_type_in_place(module, abi_type); u32 alignment; if (options.alignment) { alignment = options.alignment; } else { alignment = get_byte_alignment(abi_type); } auto alloca = llvm_builder_create_alloca(module->llvm.builder, abi_type->llvm.memory, 0, alignment, options.name); return alloca; } struct StoreOptions { LLVMValueRef source; LLVMValueRef destination; Type* type; u32 alignment; }; fn void create_store(Module* module, StoreOptions options) { assert(options.source); assert(options.destination); assert(options.type); auto resolved_type = resolve_alias(module, options.type); resolve_type_in_place(module, resolved_type); LLVMValueRef source_value; LLVMTypeRef memory_type = resolved_type->llvm.memory; if (resolved_type->llvm.abi == memory_type) { source_value = options.source; } else { source_value = LLVMBuildIntCast2(module->llvm.builder, options.source, memory_type, type_is_signed(resolved_type), ""); } u32 alignment; if (options.alignment) { alignment = options.alignment; } else { alignment = get_byte_alignment(resolved_type); } auto store = LLVMBuildStore(module->llvm.builder, source_value, options.destination); LLVMSetAlignment(store, alignment); } struct LoadOptions { Type* type; LLVMValueRef pointer; u32 alignment; TypeKind kind; }; fn LLVMValueRef create_load(Module* module, LoadOptions options) { resolve_type_in_place(module, options.type); u32 alignment; if (options.alignment) { alignment = options.alignment; } else { alignment = get_byte_alignment(options.type); } auto result = LLVMBuildLoad2(module->llvm.builder, options.type->llvm.memory, options.pointer, ""); LLVMSetAlignment(result, alignment); switch (options.kind) { case TypeKind::abi: { if (options.type->llvm.memory == options.type->llvm.abi) { break; } else { result = LLVMBuildIntCast2(module->llvm.builder, result, options.type->llvm.abi, type_is_signed(options.type), ""); } } break; case TypeKind::memory: break; } return result; } struct GEPOptions { LLVMTypeRef type; LLVMValueRef pointer; Slice indices; bool inbounds = true; }; fn LLVMValueRef create_gep(Module* module, GEPOptions options) { auto* gep_function = options.inbounds ? &LLVMBuildInBoundsGEP2 : &LLVMBuildGEP2; auto gep = gep_function(module->llvm.builder, options.type, options.pointer, options.indices.pointer, (u32)options.indices.length, ""); return gep; } fn BBLLVMAttributeList build_attribute_list(Module* module, AttributeBuildOptions options) { resolve_type_in_place(module, options.return_abi.semantic_type); BBLLVMAttributeListOptions attributes = {}; attributes.return_ = { .semantic_type = options.return_abi.semantic_type->llvm.memory, .abi_type = options.abi_return_type->llvm.abi, .dereferenceable_bytes = 0, .alignment = 0, .no_alias = false, .non_null = false, .no_undef = false, .sign_extend = options.return_abi.flags.kind == AbiKind::extend and options.return_abi.flags.sign_extension, .zero_extend = options.return_abi.flags.kind == AbiKind::extend and !options.return_abi.flags.sign_extension, .in_reg = false, .no_fp_class = 0, // TODO: this is a struct .struct_return = false, .writable = false, .dead_on_unwind = false, .in_alloca = false, .dereferenceable = false, .dereferenceable_or_null = false, .nest = false, .by_value = false, .by_reference = false, .no_capture = false, }; BBLLVMArgumentAttributes argument_attribute_buffer[128]; Slice argument_attributes = { .pointer = argument_attribute_buffer, .length = options.abi_argument_types.length }; attributes.argument_pointer = argument_attributes.pointer; attributes.argument_count = argument_attributes.length; if (options.return_abi.flags.kind == AbiKind::indirect) { trap(); } for (auto& abi: options.argument_abis) { for (auto abi_index = abi.abi_start; abi_index < abi.abi_count; abi_index += 1) { auto& attributes = argument_attributes[abi_index]; resolve_type_in_place(module, abi.semantic_type); auto abi_type = options.abi_argument_types[abi_index]; resolve_type_in_place(module, abi_type); attributes = { .semantic_type = abi.semantic_type->llvm.memory, .abi_type = abi_type->llvm.abi, .dereferenceable_bytes = 0, .alignment = (u32)(abi.flags.kind == AbiKind::indirect ? 8 : 0), .no_alias = false, .non_null = false, .no_undef = false, .sign_extend = abi.flags.kind == AbiKind::extend and abi.flags.sign_extension, .zero_extend = abi.flags.kind == AbiKind::extend and !abi.flags.sign_extension, .in_reg = abi.flags.in_reg, .no_fp_class = {}, .struct_return = false, .writable = false, .dead_on_unwind = false, .in_alloca = false, .dereferenceable = false, .dereferenceable_or_null = false, .nest = false, .by_value = abi.flags.indirect_by_value, .by_reference = false, .no_capture = false, }; } } attributes.function = { .prefer_vector_width = {}, .stack_protector_buffer_size = {}, .definition_probe_stack = {}, .definition_stack_probe_size = {}, .flags0 = { .noreturn = options.return_abi.semantic_type == noreturn_type(module), .cmse_ns_call = false, .nounwind = true, .returns_twice = false, .cold = false, .hot = false, .no_duplicate = false, .convergent = false, .no_merge = false, .will_return = false, .no_caller_saved_registers = false, .no_cf_check = false, .no_callback = false, .alloc_size = false, // TODO .uniform_work_group_size = false, .aarch64_pstate_sm_body = false, .aarch64_pstate_sm_enabled = false, .aarch64_pstate_sm_compatible = false, .aarch64_preserves_za = false, .aarch64_in_za = false, .aarch64_out_za = false, .aarch64_inout_za = false, .aarch64_preserves_zt0 = false, .aarch64_in_zt0 = false, .aarch64_out_zt0 = false, .aarch64_inout_zt0 = false, .optimize_for_size = false, .min_size = false, .no_red_zone = false, .indirect_tls_seg_refs = false, .no_implicit_floats = false, .sample_profile_suffix_elision_policy = false, .memory_none = false, .memory_readonly = false, .memory_inaccessible_or_arg_memory_only = false, .memory_arg_memory_only = false, .strict_fp = false, .no_inline = options.attributes.inline_behavior == InlineBehavior::no_inline, .always_inline = options.attributes.inline_behavior == InlineBehavior::always_inline, .guard_no_cf = false, // TODO: branch protection function attributes // TODO: cpu features // CALL-SITE ATTRIBUTES .call_no_builtins = false, // DEFINITION-SITE ATTRIBUTES .definition_frame_pointer_kind = module->has_debug_info ? BBLLVMFramePointerKind::all : BBLLVMFramePointerKind::none, .definition_less_precise_fpmad = false, .definition_null_pointer_is_valid = false, .definition_no_trapping_fp_math = false, .definition_no_infs_fp_math = false, .definition_no_nans_fp_math = false, .definition_approx_func_fp_math = false, .definition_unsafe_fp_math = false, .definition_use_soft_float = false, .definition_no_signed_zeroes_fp_math = false, .definition_stack_realignment = false, .definition_backchain = false, .definition_split_stack = false, .definition_speculative_load_hardening = false, .definition_zero_call_used_registers = ZeroCallUsedRegsKind::all, // TODO: denormal builtins .definition_non_lazy_bind = false, .definition_cmse_nonsecure_entry = false, .definition_unwind_table_kind = BBLLVMUWTableKind::None, }, .flags1 = { .definition_disable_tail_calls = false, .definition_stack_protect_strong = false, .definition_stack_protect = false, .definition_stack_protect_req = false, .definition_aarch64_new_za = false, .definition_aarch64_new_zt0 = false, .definition_optimize_none = false, .definition_naked = !options.call_site and options.attributes.naked, .definition_inline_hint = !options.call_site and options.attributes.inline_behavior == InlineBehavior::inline_hint, }, }; auto attribute_list = llvm_attribute_list_build(module->llvm.context, &attributes, options.call_site); return attribute_list; } fn void check_types(Module* module, Type* expected, Type* source) { assert(expected); assert(source); if (expected != source) { auto resolved_expected = resolve_alias(module, expected); auto resolved_source = resolve_alias(module, source); if (resolved_expected != resolved_source) { auto is_dst_p_and_source_int = resolved_expected->id == TypeId::pointer && resolved_source->id == TypeId::integer; if (!is_dst_p_and_source_int) { report_error(); } } } } fn void typecheck(Module* module, Type* expected, Type* source) { if (expected) { check_types(module, expected, source); } } fn bool unary_is_boolean(UnaryId id) { switch (id) { case UnaryId::exclamation: return true; case UnaryId::minus: case UnaryId::plus: case UnaryId::ampersand: case UnaryId::tilde: case UnaryId::enum_name: case UnaryId::extend: case UnaryId::truncate: case UnaryId::pointer_cast: case UnaryId::int_from_enum: case UnaryId::int_from_pointer: case UnaryId::va_end: case UnaryId::bitwise_not: case UnaryId::dereference: return false; } } fn bool binary_is_boolean(BinaryId id) { switch (id) { case BinaryId::add: case BinaryId::sub: case BinaryId::mul: case BinaryId::div: case BinaryId::rem: case BinaryId::bitwise_and: case BinaryId::bitwise_or: case BinaryId::bitwise_xor: case BinaryId::shift_left: case BinaryId::shift_right: return false; case BinaryId::compare_equal: case BinaryId::compare_not_equal: case BinaryId::compare_greater: case BinaryId::compare_less: case BinaryId::compare_greater_equal: case BinaryId::compare_less_equal: case BinaryId::logical_and: case BinaryId::logical_or: case BinaryId::logical_and_shortcircuit: case BinaryId::logical_or_shortcircuit: return true; } } fn bool binary_is_shortcircuiting(BinaryId id) { switch (id) { case BinaryId::logical_and_shortcircuit: case BinaryId::logical_or_shortcircuit: return true; default: return false; } } fn void analyze_type(Module* module, Value* value, Type* expected_type); fn void analyze_binary_type(Module* module, Value* left, Value* right, bool is_boolean, Type* expected_type) { auto left_constant = left->is_constant(); auto right_constant = right->is_constant(); if (!expected_type) { if (left_constant && right_constant) { if (!left->type && !right->type) { auto are_string_literal = left->id == ValueId::string_literal && right->id == ValueId::string_literal; if (are_string_literal) { expected_type = get_slice_type(module, uint8(module)); } else { report_error(); } } } } if (is_boolean || !expected_type) { if (left_constant) { analyze_type(module, right, 0); analyze_type(module, left, right->type); } else { analyze_type(module, left, 0); analyze_type(module, right, left->type); } } else if (!is_boolean && expected_type) { analyze_type(module, left, expected_type); analyze_type(module, right, expected_type); } else { report_error(); // TODO: this might not be an error necessarily? } assert(left->type); assert(right->type); } fn void analyze_type(Module* module, Value* value, Type* expected_type) { assert(!value->type); assert(!value->llvm); if (expected_type && expected_type->id == TypeId::unresolved) { trap(); } Type* value_type = 0; switch (value->id) { case ValueId::constant_integer: { if (!expected_type) { report_error(); } resolve_type_in_place(module, expected_type); auto* resolved_type = resolve_alias(module, expected_type); switch (resolved_type->id) { case TypeId::integer: { if (value->constant_integer.is_signed) { if (resolved_type->integer.is_signed) { report_error(); } trap(); } else { auto max_value = integer_max_value(resolved_type->integer.bit_count, resolved_type->integer.is_signed); if (value->constant_integer.value > max_value) { report_error(); } value_type = expected_type; } } break; case TypeId::pointer: value_type = uint64(module); break; default: trap(); } typecheck(module, expected_type, value_type); } break; case ValueId::unary: { auto unary_id = value->unary.id; auto unary_value = value->unary.value; switch (unary_id) { case UnaryId::extend: { if (!expected_type) { report_error(); } auto extended_value = unary_value; analyze_type(module, extended_value, 0); auto source = extended_value->type; assert(source); auto source_bit_size = get_bit_size(source); auto expected_bit_size = get_bit_size(expected_type); if (source_bit_size > expected_bit_size) { report_error(); } else if (source_bit_size == expected_bit_size && type_is_signed(source) == type_is_signed(expected_type)) { report_error(); } value_type = expected_type; } break; case UnaryId::truncate: { if (!expected_type) { report_error(); } analyze_type(module, unary_value, 0); auto expected_bit_size = get_bit_size(expected_type); auto source_bit_size = get_bit_size(unary_value->type); if (expected_bit_size >= source_bit_size) { report_error(); } value_type = expected_type; } break; case UnaryId::dereference: { analyze_type(module, unary_value, 0); if (value->kind == ValueKind::left) { report_error(); } auto pointer_type = unary_value->type; assert(pointer_type->id == TypeId::pointer); auto dereference_type = pointer_type->pointer.element_type; typecheck(module, expected_type, dereference_type); value_type = dereference_type; } break; case UnaryId::int_from_enum: { analyze_type(module, unary_value, 0); auto value_enum_type = unary_value->type; if (value_enum_type->id != TypeId::enumerator) { report_error(); } auto backing_type = value_enum_type->enumerator.backing_type; typecheck(module, expected_type, backing_type); value_type = backing_type; } break; default: { auto is_boolean = unary_is_boolean(unary_id); if (is_boolean) { analyze_type(module, unary_value, 0); value_type = uint1(module); } else { analyze_type(module, unary_value, expected_type); value_type = unary_value->type; } typecheck(module, expected_type, value_type); } break; } } break; case ValueId::unary_type: { auto unary_type = value->unary_type.type; auto unary_type_id = value->unary_type.id; switch (unary_type_id) { case UnaryTypeId::byte_size: { if (!expected_type) { report_error(); } trap(); } break; case UnaryTypeId::integer_max: { if (unary_type->id != TypeId::integer) { report_error(); } if (expected_type) { if (expected_type->id != TypeId::integer) { report_error(); } } value_type = expected_type ? expected_type : unary_type; typecheck(module, expected_type, value_type); } break; } } break; case ValueId::binary: { auto is_boolean = binary_is_boolean(value->binary.id); analyze_binary_type(module, value->binary.left, value->binary.right, is_boolean, expected_type); check_types(module, value->binary.left->type, value->binary.right->type); value_type = is_boolean ? uint1(module) : value->binary.left->type; } break; case ValueId::variable_reference: { switch (value->kind) { case ValueKind::left: value_type = value->variable_reference->storage->type; break; case ValueKind::right: value_type = value->variable_reference->type; break; } assert(value_type); typecheck(module, expected_type, value_type); } break; case ValueId::call: { auto call = &value->call; auto callable = call->callable; analyze_type(module, callable, 0); Type* function_type = 0; switch (callable->id) { case ValueId::variable_reference: { auto variable_type = callable->variable_reference->type; switch (variable_type->id) { case TypeId::function: function_type = variable_type; break; case TypeId::pointer: { auto* element_type = variable_type->pointer.element_type; switch (element_type->id) { case TypeId::function: function_type = element_type; break; default: report_error(); } } break; default: report_error(); } } break; default: report_error(); } assert(function_type); call->function_type = function_type; auto semantic_argument_types = function_type->function.semantic_argument_types; auto call_arguments = call->arguments; if (function_type->function.is_variable_arguments) { if (call_arguments.length < semantic_argument_types.length) { report_error(); } } else { if (call_arguments.length != semantic_argument_types.length) { report_error(); } } for (u64 i = 0; i < semantic_argument_types.length; i += 1) { auto* argument_type = semantic_argument_types[i]; auto* call_argument = call_arguments[i]; unused(argument_type); unused(call_argument); trap(); } for (u64 i = semantic_argument_types.length; i < call_arguments.length; i += 1) { trap(); } auto semantic_return_type = function_type->function.semantic_return_type; typecheck(module, expected_type, semantic_return_type); value_type = semantic_return_type; } break; case ValueId::array_initialization: { if (expected_type) { if (expected_type->array.element_count == 0) { expected_type->array.element_count = value->array_initialization.values.length; assert(expected_type->name.equal(string_literal(""))); expected_type->name = array_name(module, expected_type->array.element_type, expected_type->array.element_count); } else { trap(); } bool is_constant = true; auto* element_type = expected_type->array.element_type; for (auto value : value->array_initialization.values) { analyze_type(module, value, element_type); is_constant = is_constant && value->is_constant(); } value->array_initialization.is_constant = is_constant; if (value->kind == ValueKind::left) // TODO: possible? { report_error(); } value_type = expected_type; } else { trap(); } } break; case ValueId::array_expression: { analyze_type(module, value->array_expression.index, uint64(module)); auto array_like = value->array_expression.array_like; array_like->kind = ValueKind::left; analyze_type(module, array_like, 0); assert(array_like->kind == ValueKind::left); auto array_like_type = array_like->type; if (array_like_type->id != TypeId::pointer) { report_error(); } auto pointer_element_type = array_like_type->pointer.element_type; Type* element_type = 0; switch (pointer_element_type->id) { case TypeId::array: { element_type = pointer_element_type->array.element_type; } break; case TypeId::structure: { trap(); } break; case TypeId::pointer: { trap(); } break; default: report_error(); } assert(element_type); value_type = element_type; if (value->kind == ValueKind::left) { value_type = get_pointer_type(module, element_type); } typecheck(module, expected_type, value_type); } break; case ValueId::enum_literal: { if (!expected_type) { report_error(); } if (expected_type->id != TypeId::enumerator) { report_error(); } value_type = expected_type; } break; default: unreachable(); } assert(value_type); value->type = value_type; } fn LLVMTypeRef get_llvm_type(Type* type, TypeKind type_kind) { switch (type_kind) { case TypeKind::abi: return type->llvm.abi; case TypeKind::memory: return type->llvm.memory; } } fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type) { unused(left_llvm); unused(left_type); switch (value->id) { case ValueId::call: { auto call = &value->call; auto raw_function_type = call->function_type; auto callable = call->callable; auto call_arguments = call->arguments; LLVMValueRef llvm_callable = 0; switch (callable->id) { case ValueId::variable_reference: { auto variable = callable->variable_reference; auto variable_type = variable->type; switch (variable_type->id) { case TypeId::pointer: { auto element_type = variable_type->pointer.element_type; switch (element_type->id) { case TypeId::function: { llvm_callable = create_load(module, LoadOptions{ .type = get_pointer_type(module, raw_function_type), .pointer = variable->storage->llvm, }); } break; default: report_error(); } } break; case TypeId::function: llvm_callable = variable->storage->llvm; break; default: report_error(); } } break; default: report_error(); } assert(llvm_callable); LLVMValueRef llvm_abi_argument_value_buffer[64]; Type* abi_argument_type_buffer[64]; AbiInformation argument_abi_buffer[64]; u16 abi_argument_count = 0; bool uses_in_alloca = false; if (uses_in_alloca) { trap(); } LLVMValueRef llvm_indirect_return_value = 0; unused(llvm_indirect_return_value); auto& return_abi = raw_function_type->function.return_abi; auto return_abi_kind = return_abi.flags.kind; switch (return_abi_kind) { case AbiKind::indirect: case AbiKind::in_alloca: case AbiKind::coerce_and_expand: { trap(); } break; default: break; } auto available_registers = raw_function_type->function.available_registers; unused(available_registers); for (u64 call_argument_index = 0; call_argument_index < call_arguments.length; call_argument_index += 1) { trap(); } auto declaration_abi_argument_count = raw_function_type->function.abi_argument_types.length; if (raw_function_type->function.is_variable_arguments) { assert(abi_argument_count >= declaration_abi_argument_count); } else { assert(abi_argument_count == declaration_abi_argument_count); } assert(raw_function_type->llvm.abi); auto llvm_call = LLVMBuildCall2(module->llvm.builder, raw_function_type->llvm.abi, llvm_callable, llvm_abi_argument_value_buffer, abi_argument_count, ""); auto attribute_list = build_attribute_list(module, { .return_abi = return_abi, .argument_abis = { .pointer = argument_abi_buffer, .length = call_arguments.length }, .abi_argument_types = { .pointer = abi_argument_type_buffer, .length = abi_argument_count }, .abi_return_type = raw_function_type->function.abi_return_type, .attributes = {}, .call_site = true, }); LLVMSetInstructionCallConv(llvm_call, llvm_calling_convention(raw_function_type->function.calling_convention)); llvm_call_base_set_attributes(llvm_call, attribute_list); switch (return_abi_kind) { case AbiKind::ignore: { trap(); } break; case AbiKind::direct: case AbiKind::extend: { auto coerce_to_type = return_abi.get_coerce_to_type(); if (type_is_abi_equal(module, return_abi.semantic_type, coerce_to_type) && return_abi.attributes.direct.offset == 0) { auto evaluation_kind = get_evaluation_kind(coerce_to_type); switch (evaluation_kind) { case EvaluationKind::scalar: return llvm_call; case EvaluationKind::aggregate: break; case EvaluationKind::complex: unreachable(); } } trap(); } break; case AbiKind::indirect: { trap(); } break; default: unreachable(); } } break; default: unreachable(); } } fn void emit_value(Module* module, Value* value, TypeKind type_kind) { assert(value->type); assert(!value->llvm); auto resolved_value_type = resolve_alias(module, value->type); resolve_type_in_place(module, resolved_value_type); auto must_be_constant = !module->current_function && !module->current_macro_instantiation; LLVMValueRef llvm_value = 0; switch (value->id) { case ValueId::constant_integer: { auto llvm_integer_type = get_llvm_type(resolved_value_type, type_kind); llvm_value = LLVMConstInt(llvm_integer_type, value->constant_integer.value, value->constant_integer.is_signed); } break; case ValueId::unary: { auto unary_value = value->unary.value; assert(!unary_value->llvm); auto resolved_unary_type = resolve_alias(module, unary_value->type); emit_value(module, unary_value, type_kind); auto destination_type = get_llvm_type(resolved_value_type, type_kind); assert(destination_type); auto llvm_unary_value = unary_value->llvm; assert(llvm_unary_value); switch (value->unary.id) { case UnaryId::minus: { if (value->unary.value->is_constant()) { llvm_value = LLVMConstNeg(llvm_unary_value); } else { llvm_value = LLVMBuildNeg(module->llvm.builder, llvm_unary_value, ""); } } break; case UnaryId::plus: { trap(); } break; case UnaryId::ampersand: { assert(resolved_value_type == resolved_unary_type); llvm_value = llvm_unary_value; } break; case UnaryId::exclamation: { trap(); } break; case UnaryId::tilde: { trap(); } break; case UnaryId::enum_name: { trap(); } break; case UnaryId::extend: { assert(resolved_unary_type->id == TypeId::integer); if (resolved_unary_type->integer.is_signed) { llvm_value = LLVMBuildSExt(module->llvm.builder, llvm_unary_value, destination_type, ""); } else { llvm_value = LLVMBuildZExt(module->llvm.builder, llvm_unary_value, destination_type, ""); } } break; case UnaryId::truncate: { if (type_kind != TypeKind::abi) { assert(resolved_value_type->llvm.abi == resolved_value_type->llvm.memory); } llvm_value = LLVMBuildTrunc(module->llvm.builder, llvm_unary_value, destination_type, ""); } break; case UnaryId::pointer_cast: { trap(); } break; case UnaryId::int_from_enum: { llvm_value = llvm_unary_value; } break; case UnaryId::int_from_pointer: { trap(); } break; case UnaryId::va_end: { trap(); } break; case UnaryId::bitwise_not: { trap(); } break; case UnaryId::dereference: { if (type_kind != TypeKind::memory) { trap(); } switch (value->kind) { case ValueKind::right: { auto pointer_type = unary_value->type; assert(pointer_type->id == TypeId::pointer); auto child_type = pointer_type->pointer.element_type; assert(child_type == resolved_value_type); auto load = create_load(module, LoadOptions{ .type = child_type, .pointer = unary_value->llvm, }); llvm_value = load; } break; case ValueKind::left: trap(); } } break; } } break; case ValueId::unary_type: { auto unary_type = value->unary_type.type; auto unary_type_id = value->unary_type.id; resolve_type_in_place(module, unary_type); switch (unary_type_id) { case UnaryTypeId::byte_size: { trap(); } break; case UnaryTypeId::integer_max: { assert(unary_type->id == TypeId::integer); auto is_signed = unary_type->integer.is_signed; auto max_value = integer_max_value(unary_type->integer.bit_count, is_signed); auto constant_integer = LLVMConstInt(resolved_value_type->llvm.abi, max_value, is_signed); llvm_value = constant_integer; } break; } } break; case ValueId::binary: { bool is_shorcircuiting = binary_is_shortcircuiting(value->binary.id); if (is_shorcircuiting) { trap(); } else { LLVMValueRef llvm_values[2]; Value* values[2] = { value->binary.left, value->binary.right }; for (u64 i = 0; i < array_length(values); i += 1) { auto* binary_value = values[i]; if (binary_value->llvm) { assert(false); // TODO: check if this if is really necessary } else { emit_value(module, binary_value, TypeKind::abi); } llvm_values[i] = binary_value->llvm; } switch (resolved_value_type->id) { case TypeId::integer: { switch (value->binary.id) { case BinaryId::add: llvm_value = LLVMBuildAdd(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break; case BinaryId::sub: llvm_value = LLVMBuildSub(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break; case BinaryId::mul: llvm_value = LLVMBuildMul(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break; case BinaryId::bitwise_and: llvm_value = LLVMBuildAnd(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break; case BinaryId::bitwise_or: llvm_value = LLVMBuildOr(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break; case BinaryId::bitwise_xor: llvm_value = LLVMBuildXor(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break; case BinaryId::shift_left: llvm_value = LLVMBuildShl(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break; case BinaryId::shift_right: if (resolved_value_type->integer.is_signed) { llvm_value = LLVMBuildAShr(module->llvm.builder, llvm_values[0], llvm_values[1], ""); } else { llvm_value = LLVMBuildLShr(module->llvm.builder, llvm_values[0], llvm_values[1], ""); } break; case BinaryId::div: if (resolved_value_type->integer.is_signed) { llvm_value = LLVMBuildSDiv(module->llvm.builder, llvm_values[0], llvm_values[1], ""); } else { llvm_value = LLVMBuildUDiv(module->llvm.builder, llvm_values[0], llvm_values[1], ""); } break; case BinaryId::rem: if (resolved_value_type->integer.is_signed) { llvm_value = LLVMBuildSRem(module->llvm.builder, llvm_values[0], llvm_values[1], ""); } else { llvm_value = LLVMBuildURem(module->llvm.builder, llvm_values[0], llvm_values[1], ""); } break; case BinaryId::compare_equal: case BinaryId::compare_not_equal: case BinaryId::compare_greater: case BinaryId::compare_less: case BinaryId::compare_greater_equal: case BinaryId::compare_less_equal: { LLVMIntPredicate predicate; assert(values[0]->type == values[1]->type); auto left_signed = type_is_signed(values[0]->type); auto right_signed = type_is_signed(values[1]->type); assert(left_signed == right_signed); auto is_signed = left_signed; switch (value->binary.id) { case BinaryId::compare_equal: predicate = LLVMIntEQ; break; case BinaryId::compare_not_equal: predicate = LLVMIntNE; break; case BinaryId::compare_greater: predicate = is_signed ? LLVMIntSGT : LLVMIntUGT; case BinaryId::compare_less: predicate = is_signed ? LLVMIntSLT : LLVMIntULT; case BinaryId::compare_greater_equal: predicate = is_signed ? LLVMIntSGE : LLVMIntUGE; case BinaryId::compare_less_equal: predicate = is_signed ? LLVMIntSLE : LLVMIntULE; default: unreachable(); } llvm_value = LLVMBuildICmp(module->llvm.builder, predicate, llvm_values[0], llvm_values[1], ""); } break; default: unreachable(); } } break; default: unreachable(); } } } break; case ValueId::variable_reference: { auto* variable = value->variable_reference; auto resolved_variable_value_type = resolve_alias(module, variable->type); auto resolved_variable_pointer_type = resolve_alias(module, variable->storage->type); switch (value->kind) { case ValueKind::left: { if (resolved_variable_pointer_type == resolved_value_type) { llvm_value = variable->storage->llvm; } else { trap(); } } break; case ValueKind::right: { if (resolved_variable_value_type != resolved_value_type) { report_error(); } if (must_be_constant) { if (variable->scope->kind != ScopeKind::global) { report_error(); } trap(); } else { assert(get_byte_size(resolved_value_type) <= 16); auto evaluation_kind = get_evaluation_kind(resolved_value_type); switch (evaluation_kind) { case EvaluationKind::scalar: case EvaluationKind::aggregate: { llvm_value = create_load(module, { .type = resolved_value_type, .pointer = variable->storage->llvm, }); } break; case EvaluationKind::complex: trap(); } } } break; } } break; case ValueId::call: { auto call = emit_call(module, value, 0, 0); llvm_value = call; } break; case ValueId::array_initialization: { if (value->array_initialization.is_constant) { assert(value->kind == ValueKind::right); auto element_type = resolved_value_type->array.element_type; auto element_count = value->array_initialization.values.length; LLVMValueRef value_buffer[64]; resolve_type_in_place(module, element_type); for (u64 i = 0; i < element_count; i += 1) { auto* v = value->array_initialization.values[i]; emit_value(module, v, TypeKind::memory); value_buffer[i] = v->llvm; } auto constant_array = LLVMConstArray2(element_type->llvm.memory, value_buffer, element_count); llvm_value = constant_array; } else { trap(); } } break; case ValueId::array_expression: { auto* array_like = value->array_expression.array_like; auto* index = value->array_expression.index; switch (array_like->kind) { case ValueKind::left: { auto array_like_type = array_like->type; assert(array_like_type->id == TypeId::pointer); auto pointer_element_type = array_like_type->pointer.element_type; switch (pointer_element_type->id) { case TypeId::array: { auto array_type = pointer_element_type; emit_value(module, array_like, TypeKind::memory); emit_value(module, index, TypeKind::memory); auto uint64_type = uint64(module); resolve_type_in_place(module, uint64_type); auto zero_index = LLVMConstNull(uint64_type->llvm.abi); LLVMValueRef indices[] = { zero_index, index->llvm }; auto gep = create_gep(module, { .type = array_type->llvm.memory, .pointer = array_like->llvm, .indices = array_to_slice(indices), }); switch (value->kind) { case ValueKind::left: llvm_value = gep; break; case ValueKind::right: llvm_value = create_load(module, LoadOptions{ .type = array_type->array.element_type, .pointer = gep, }); break; } } break; case TypeId::structure: { trap(); } break; case TypeId::pointer: { trap(); } break; default: report_error(); } } break; case ValueKind::right: { trap(); } break; } } break; case ValueId::enum_literal: { assert(resolved_value_type->id == TypeId::enumerator); auto enum_name = value->enum_literal; bool found = false; u64 i; for (i = 0; i < resolved_value_type->enumerator.fields.length; i += 1) { auto& field = resolved_value_type->enumerator.fields[i]; if (enum_name.equal(field.name)) { found = true; break; } } if (!found) { report_error(); } auto& field = resolved_value_type->enumerator.fields[i]; auto llvm_type = get_llvm_type(resolved_value_type, type_kind); llvm_value = LLVMConstInt(llvm_type, field.value, type_is_signed(resolved_value_type)); } break; default: unreachable(); } assert(llvm_value); value->llvm = llvm_value; } fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, Value* right) { assert(!right->llvm); auto pointer_type = left_type; auto value_type = right->type; assert(pointer_type); assert(value_type); resolve_type_in_place(module, pointer_type); resolve_type_in_place(module, value_type); auto resolved_pointer_type = resolve_alias(module, pointer_type); auto resolved_value_type = resolve_alias(module, value_type); assert(resolved_pointer_type->id == TypeId::pointer); assert(resolved_pointer_type->pointer.element_type == resolved_value_type); auto type_kind = TypeKind::memory; auto evaluation_kind = get_evaluation_kind(resolved_value_type); switch (evaluation_kind) { case EvaluationKind::scalar: { emit_value(module, right, type_kind); create_store(module, { .source = right->llvm, .destination = left_llvm, .type = resolved_value_type, }); } break; case EvaluationKind::aggregate: { switch (right->id) { case ValueId::array_initialization: { if (right->array_initialization.is_constant) { emit_value(module, right, TypeKind::memory); bool is_constant = true; LLVMLinkage linkage_type = LLVMInternalLinkage; LLVMValueRef before = 0; LLVMThreadLocalMode thread_local_mode = {}; u32 address_space = 0; bool externally_initialized = false; auto global = llvm_module_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("constarray"), before, thread_local_mode, address_space, externally_initialized); LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); auto alignment = get_byte_alignment(resolved_value_type); LLVMSetAlignment(global, alignment); auto uint64_type = uint64(module); resolve_type_in_place(module, uint64_type); auto element_type = resolved_value_type->array.element_type; auto element_count = resolved_value_type->array.element_count; assert(right->array_initialization.values.length == element_count); u64 memcpy_size = get_byte_size(element_type) * element_count; LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, global, alignment, LLVMConstInt(uint64_type->llvm.abi, memcpy_size, false)); } else { trap(); } } break; default: unreachable(); } } break; default: unreachable(); } } fn void emit_local_storage(Module* module, Variable* variable) { assert(!variable->storage); auto value_type = variable->type; resolve_type_in_place(module, value_type); auto pointer_type = get_pointer_type(module, value_type); auto storage = new_value(module); auto alloca = create_alloca(module, { .type = value_type, .name = variable->name, }); *storage = Value{ .type = pointer_type, .id = ValueId::local, .llvm = alloca, }; variable->storage = storage; } fn void emit_local_variable(Module* module, Local* local) { emit_local_storage(module, &local->variable); assert(local->variable.storage); if (module->has_debug_info) { auto debug_type = local->variable.type->llvm.debug; assert(debug_type); bool always_preserve = true; LLVMDIFlags flags = {}; auto scope = local->variable.scope->llvm; auto bit_alignment = get_byte_alignment(local->variable.storage->type->pointer.element_type) * 8; auto local_variable = LLVMDIBuilderCreateAutoVariable(module->llvm.di_builder, scope, (char*)local->variable.name.pointer, local->variable.name.length, module->llvm.file, local->variable.line, debug_type, always_preserve, flags, bit_alignment); auto debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, local->variable.line, local->variable.column, scope, module->llvm.inlined_at); LLVMSetCurrentDebugLocation2(module->llvm.builder, debug_location); auto basic_block = LLVMGetInsertBlock(module->llvm.builder); assert(basic_block); LLVMDIBuilderInsertDeclareRecordAtEnd(module->llvm.di_builder, local->variable.storage->llvm, local_variable, LLVMDIBuilderCreateExpression(module->llvm.di_builder, 0, 0), debug_location, basic_block); } } fn void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind) { analyze_type(module, value, expected_type); emit_value(module, value, type_kind); } fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u32* last_line, u32* last_column, LLVMMetadataRef* last_debug_location); fn void analyze_block(Module* module, Block* block) { if (module->has_debug_info) { auto lexical_block = LLVMDIBuilderCreateLexicalBlock(module->llvm.di_builder, block->scope.parent->llvm, module->llvm.file, block->scope.line, block->scope.column); block->scope.llvm = lexical_block; } u32 last_line = 0; u32 last_column = 0; LLVMMetadataRef last_debug_location = 0; for (auto* statement = block->first_statement; statement; statement = statement->next) { analyze_statement(module, &block->scope, statement, &last_line, &last_column, &last_debug_location); } } fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u32* last_line, u32* last_column, LLVMMetadataRef* last_debug_location) { Global* parent_function_global; if (module->current_function) { parent_function_global = module->current_function; } else if (module->current_macro_instantiation) { parent_function_global = module->current_macro_instantiation->instantiation_function; } else { report_error(); } auto* llvm_function = parent_function_global->variable.storage->llvm; assert(llvm_function); if (module->has_debug_info) { if (statement->line != *last_line || statement->column != *last_column) { auto new_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, statement->line, statement->column, scope->llvm, module->llvm.inlined_at); *last_debug_location = new_location; LLVMSetCurrentDebugLocation2(module->llvm.builder, new_location); *last_line = statement->line; *last_column = statement->column; } } switch (statement->id) { case StatementId::return_st: { if (module->current_function) { auto& function_type = parent_function_global->variable.storage->type->pointer.element_type->function; auto& return_abi = function_type.return_abi; auto return_value = statement->return_st; switch (return_abi.semantic_type->id) { case TypeId::void_type: { if (return_value) { report_error(); } } break; case TypeId::noreturn: { report_error(); } break; default: { if (module->has_debug_info) { LLVMSetCurrentDebugLocation2(module->llvm.builder, *last_debug_location); } auto return_alloca = module->current_function->variable.storage->function.llvm.return_alloca; if (!return_alloca) { report_error(); } if (!return_value) { report_error(); } analyze_type(module, return_value, return_abi.semantic_type); auto pointer_type = get_pointer_type(module, return_abi.semantic_type); emit_assignment(module, return_alloca, pointer_type, return_value); } break; } auto return_block = module->current_function->variable.storage->function.llvm.return_block; LLVMBuildBr(module->llvm.builder, return_block); LLVMClearInsertionPosition(module->llvm.builder); } else if (module->current_macro_instantiation) { trap(); } else { report_error(); } } break; case StatementId::local: { auto local = statement->local; auto expected_type = local->variable.type; assert(!local->variable.storage); analyze_type(module, local->variable.initial_value, expected_type); local->variable.type = expected_type ? expected_type : local->variable.initial_value->type; assert(local->variable.type); if (expected_type) { assert(expected_type == local->variable.type); } emit_local_variable(module, local); emit_assignment(module, local->variable.storage->llvm, local->variable.storage->type, local->variable.initial_value); } break; case StatementId::if_st: { auto* taken_block = llvm_context_create_basic_block(module->llvm.context, string_literal("if.taken"), llvm_function); auto* not_taken_block = llvm_context_create_basic_block(module->llvm.context, string_literal("if.not_taken"), llvm_function); auto* exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("if.exit"), llvm_function); auto condition = statement->if_st.condition; breakpoint(); analyze_value(module, condition, 0, TypeKind::abi); auto condition_type = condition->type; LLVMValueRef llvm_condition = 0; switch (condition_type->id) { case TypeId::integer: { llvm_condition = condition->llvm; if (condition_type->integer.bit_count != 1) { llvm_condition = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, llvm_condition, LLVMConstNull(condition_type->llvm.abi), ""); } } break; default: report_error(); } assert(llvm_condition); LLVMBuildCondBr(module->llvm.builder, llvm_condition, taken_block, not_taken_block); LLVMPositionBuilderAtEnd(module->llvm.builder, taken_block); analyze_statement(module, scope, statement->if_st.if_statement, last_line, last_column, last_debug_location); if (LLVMGetInsertBlock(module->llvm.builder)) { LLVMBuildBr(module->llvm.builder, exit_block); } LLVMPositionBuilderAtEnd(module->llvm.builder, not_taken_block); auto else_statement = statement->if_st.else_statement; if (else_statement) { analyze_statement(module, scope, else_statement, last_line, last_column, last_debug_location); } if (LLVMGetInsertBlock(module->llvm.builder)) { LLVMBuildBr(module->llvm.builder, exit_block); } LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); } break; case StatementId::block: { analyze_block(module, statement->block); } break; default: unreachable(); } } fn void emit_debug_argument(Module* module, Argument* argument, LLVMBasicBlockRef basic_block) { assert(module->has_debug_info); resolve_type_in_place(module, argument->variable.type); bool always_preserve = true; LLVMDIFlags flags = {}; LLVMMetadataRef scope = argument->variable.scope->llvm; auto parameter_variable = LLVMDIBuilderCreateParameterVariable(module->llvm.di_builder, scope, (char*)argument->variable.name.pointer, argument->variable.name.length, argument->index, module->llvm.file, argument->variable.line, argument->variable.type->llvm.debug, always_preserve, flags); auto inlined_at = module->llvm.inlined_at; auto debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, argument->variable.line, argument->variable.column, scope, inlined_at); LLVMDIBuilderInsertDeclareRecordAtEnd(module->llvm.di_builder, argument->variable.storage->llvm, parameter_variable, LLVMDIBuilderCreateExpression(module->llvm.di_builder, 0, 0), debug_location, basic_block); } void emit(Module* module) { llvm_initialize(module); for (auto* global = module->first_global; global; global = global->next) { switch (global->variable.storage->id) { case ValueId::function: case ValueId::external_function: { auto function_type = &global->variable.storage->type->pointer.element_type->function; auto semantic_argument_count = function_type->semantic_argument_types.length; function_type->argument_abis = arena_allocate(module->arena, semantic_argument_count); auto resolved_calling_convention = resolve_calling_convention(function_type->calling_convention); auto is_reg_call = resolved_calling_convention == ResolvedCallingConvention::system_v && false; // TODO: regcall calling convention LLVMTypeRef llvm_abi_argument_type_buffer[64]; switch (resolved_calling_convention) { case ResolvedCallingConvention::system_v: { function_type->available_registers = { .system_v = { .gpr = (u32)(is_reg_call ? 11 : 6), .sse = (u32)(is_reg_call ? 16 : 8), }, }; function_type->return_abi = abi_system_classify_return_type(module, function_type->semantic_return_type); auto return_abi_kind = function_type->return_abi.flags.kind; Type* abi_argument_type_buffer[64]; u16 abi_argument_type_count = 0; Type* abi_return_type; switch (return_abi_kind) { case AbiKind::direct: case AbiKind::extend: { abi_return_type = function_type->return_abi.coerce_to_type; } break; case AbiKind::ignore: case AbiKind::indirect: { abi_return_type = void_type(module); } break; default: unreachable(); // TODO } assert(abi_return_type); function_type->abi_return_type = abi_return_type; resolve_type_in_place(module, abi_return_type); if (function_type->return_abi.flags.kind == AbiKind::indirect) { trap(); } for (u64 i = 0; i < semantic_argument_count; i += 1) { auto& abi = function_type->argument_abis[i]; auto semantic_argument_type = function_type->semantic_argument_types[i]; auto is_named_argument = i < semantic_argument_count; assert(is_named_argument); abi = abi_system_v_classify_argument(module, &function_type->available_registers.system_v, array_to_slice(llvm_abi_argument_type_buffer), array_to_slice(abi_argument_type_buffer), { .type = semantic_argument_type, .abi_start = abi_argument_type_count, .is_named_argument = is_named_argument, }); abi_argument_type_count += abi.abi_count; } auto abi_argument_types = new_type_array(module, abi_argument_type_count); memcpy(abi_argument_types.pointer, abi_argument_type_buffer, sizeof(abi_argument_type_buffer[0]) * abi_argument_type_count); function_type->abi_argument_types = abi_argument_types; } break; case ResolvedCallingConvention::win64: { report_error(); } break; case ResolvedCallingConvention::count: unreachable(); } auto llvm_function_type = LLVMFunctionType(function_type->abi_return_type->llvm.abi, llvm_abi_argument_type_buffer, (u32)function_type->abi_argument_types.length, function_type->is_variable_arguments); LLVMMetadataRef subroutine_type = 0; if (module->has_debug_info) { LLVMMetadataRef debug_argument_type_buffer[64]; Slice debug_argument_types = { .pointer = debug_argument_type_buffer, .length = function_type->argument_abis.length + 1 + function_type->is_variable_arguments }; debug_argument_types[0] = function_type->return_abi.semantic_type->llvm.debug; assert(debug_argument_types[0]); auto debug_argument_type_slice = debug_argument_types(1)(0, function_type->argument_abis.length); for (u64 i = 0; i < function_type->argument_abis.length; i += 1) { auto& argument_abi = function_type->argument_abis[i]; auto* debug_argument_type = &debug_argument_type_slice[i]; *debug_argument_type = argument_abi.semantic_type->llvm.debug; assert(*debug_argument_type); } if (function_type->is_variable_arguments) { auto void_ty = void_type(module); assert(void_ty->llvm.debug); debug_argument_types[function_type->argument_abis.length + 1] = void_ty->llvm.debug; } LLVMDIFlags flags = {}; subroutine_type = LLVMDIBuilderCreateSubroutineType(module->llvm.di_builder, module->llvm.file, debug_argument_types.pointer, (u32)debug_argument_types.length, flags); } global->variable.storage->type->pointer.element_type->llvm.abi = llvm_function_type; global->variable.storage->type->pointer.element_type->llvm.debug = subroutine_type; LLVMLinkage llvm_linkage_type; switch (global->linkage) { case Linkage::internal: llvm_linkage_type = LLVMInternalLinkage; break; case Linkage::external: llvm_linkage_type = LLVMExternalLinkage; break; } unsigned address_space = 0; auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, llvm_linkage_type, address_space, global->variable.name); global->variable.storage->llvm = llvm_function; LLVMCallConv cc; switch (function_type->calling_convention) { case CallingConvention::c: cc = LLVMCCallConv; break; case CallingConvention::count: unreachable(); } LLVMSetFunctionCallConv(llvm_function, cc); assert(global->variable.storage->id == ValueId::function); auto attribute_list = build_attribute_list(module, { .return_abi = function_type->return_abi, .argument_abis = function_type->argument_abis, .abi_argument_types = function_type->abi_argument_types, .abi_return_type = function_type->abi_return_type, .attributes = global->variable.storage->function.attributes, .call_site = false, }); llvm_function_set_attributes(llvm_function, attribute_list); LLVMMetadataRef subprogram = 0; auto is_definition = global->variable.storage->id == ValueId::function; if (module->has_debug_info) { auto is_local_to_unit = global->linkage == Linkage::internal; auto line = global->variable.line; auto scope_line = line + 1; LLVMDIFlags flags = {}; auto is_optimized = build_mode_is_optimized(module->build_mode); subprogram = LLVMDIBuilderCreateFunction(module->llvm.di_builder, module->scope.llvm, (char*)global->variable.name.pointer, global->variable.name.length, (char*)global->variable.name.pointer, global->variable.name.length, module->llvm.file, line, subroutine_type, is_local_to_unit, is_definition, scope_line, flags, is_optimized); LLVMSetSubprogram(llvm_function, subprogram); } if (is_definition) { global->variable.storage->function.scope.llvm = subprogram; module->current_function = global; LLVMValueRef llvm_abi_argument_buffer[64]; Slice llvm_abi_arguments = { .pointer = llvm_abi_argument_buffer, .length = function_type->abi_argument_types.length }; LLVMGetParams(llvm_function, llvm_abi_argument_buffer); auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("entry"), llvm_function); auto return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("return_block"), 0); global->variable.storage->function.llvm.return_block = return_block; LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); LLVMSetCurrentDebugLocation2(module->llvm.builder, 0); auto return_abi_kind = function_type->return_abi.flags.kind; switch (return_abi_kind) { case AbiKind::indirect: { trap(); } break; case AbiKind::in_alloca: { trap(); } break; default: { auto alloca = create_alloca(module, { .type = function_type->return_abi.semantic_type, .name = string_literal("retval"), }); global->variable.storage->function.llvm.return_alloca = alloca; } break; case AbiKind::ignore: break; } auto arguments = global->variable.storage->function.arguments; auto argument_abis = function_type->argument_abis; assert(arguments.length == argument_abis.length); for (u64 i = 0; i < semantic_argument_count; i += 1) { auto* argument = &arguments[i]; auto& argument_abi = argument_abis[i]; auto argument_abi_arguments = llvm_abi_arguments(argument_abi.abi_start)(0, argument_abi.abi_count); LLVMValueRef semantic_argument_storage = 0; switch (argument_abi.flags.kind) { case AbiKind::direct: case AbiKind::extend: { auto first_argument = argument_abi_arguments[0]; auto coerce_to_type = argument_abi.get_coerce_to_type(); if (coerce_to_type->id != TypeId::structure && type_is_abi_equal(module, coerce_to_type, argument_abi.semantic_type) && argument_abi.attributes.direct.offset == 0) { assert(argument_abi.abi_count == 1); auto is_promoted = false; auto v = first_argument; if (coerce_to_type->llvm.abi != LLVMTypeOf(v)) { trap(); } if (is_promoted) { trap(); } // TODO: this we can get rid of because we handle all of this inside `create_alloca`, load, stores, etc if (is_arbitrary_bit_integer(argument_abi.semantic_type)) { auto bit_count = (u32)get_bit_size(argument_abi.semantic_type); auto abi_bit_count = align_bit_count(bit_count); bool is_signed = type_is_signed(argument_abi.semantic_type); auto destination_type = integer_type(module, { .bit_count = abi_bit_count, .is_signed = is_signed }); auto alloca = create_alloca(module, { .type = destination_type, .name = argument->variable.name, }); LLVMValueRef result; if (bit_count < abi_bit_count) { if (is_signed) { result = LLVMBuildSExt(module->llvm.builder, first_argument, destination_type->llvm.memory, ""); } else { result = LLVMBuildZExt(module->llvm.builder, first_argument, destination_type->llvm.memory, ""); } } else { trap(); } create_store(module, { .source = result, .destination = alloca, .type = destination_type, }); semantic_argument_storage = alloca; } else { trap(); } } else { trap(); } } break; default: unreachable(); } assert(semantic_argument_storage); auto storage = new_value(module); auto value_type = argument->variable.type; *storage = { .type = get_pointer_type(module, value_type), .id = ValueId::argument, .llvm = semantic_argument_storage, }; argument->variable.storage = storage; if (module->has_debug_info) { emit_debug_argument(module, argument, entry_block); } } analyze_block(module, global->variable.storage->function.block); auto* current_basic_block = LLVMGetInsertBlock(module->llvm.builder); if (current_basic_block) { assert(!LLVMGetBasicBlockTerminator(current_basic_block)); if (llvm_basic_block_is_empty(current_basic_block) || llvm_value_use_empty((LLVMValueRef)current_basic_block)) { LLVMReplaceAllUsesWith((LLVMValueRef)return_block, (LLVMValueRef)current_basic_block); llvm_basic_block_delete(return_block); } else { trap(); } } else { bool is_reachable = false; if (llvm_value_has_one_use((LLVMValueRef)return_block)) { auto user = llvm_basic_block_user_begin(return_block); is_reachable = LLVMIsABranchInst(user) && !LLVMIsConditional(user) && LLVMGetSuccessor(user, 0) == return_block; if (is_reachable) { LLVMPositionBuilderAtEnd(module->llvm.builder, LLVMGetInstructionParent(user)); LLVMInstructionEraseFromParent(user); llvm_basic_block_delete(return_block); } } if (!is_reachable) { trap(); } } if (module->has_debug_info) { LLVMSetCurrentDebugLocation2(module->llvm.builder, 0); auto subprogram = LLVMGetSubprogram(llvm_function); LLVMDIBuilderFinalizeSubprogram(module->llvm.di_builder, subprogram); } if (function_type->return_abi.semantic_type == noreturn_type(module) || global->variable.storage->function.attributes.naked) { LLVMBuildUnreachable(module->llvm.builder); } else if (function_type->return_abi.semantic_type == void_type(module)) { LLVMBuildRetVoid(module->llvm.builder); } else { LLVMValueRef return_value = 0; switch (return_abi_kind) { case AbiKind::direct: case AbiKind::extend: { auto return_alloca = global->variable.storage->function.llvm.return_alloca; auto coerce_to_type = function_type->return_abi.get_coerce_to_type(); auto return_semantic_type = function_type->return_abi.semantic_type; if (coerce_to_type->id != TypeId::structure && type_is_abi_equal(module, coerce_to_type, return_semantic_type) && function_type->return_abi.attributes.direct.offset == 0) { auto store = llvm_find_return_value_dominating_store(module->llvm.builder, return_alloca, return_semantic_type->llvm.abi); if (store) { return_value = LLVMGetOperand(store, 0); auto alloca = LLVMGetOperand(store, 1); assert(alloca == return_alloca); LLVMInstructionEraseFromParent(store); assert(llvm_value_use_empty(alloca)); LLVMInstructionEraseFromParent(alloca); } else { return_value = create_load(module, LoadOptions{ .type = return_semantic_type, .pointer = return_alloca, }); } } else { trap(); } } break; case AbiKind::indirect: { trap(); } break; default: unreachable(); } LLVMBuildRet(module->llvm.builder, return_value); } // END OF SCOPE module->current_function = 0; } } break; case ValueId::global: { trap(); } break; default: report_error(); } } if (module->has_debug_info) { LLVMDIBuilderFinalize(module->llvm.di_builder); } String verification_error_message = {}; if (!llvm_module_verify(module->llvm.module, &verification_error_message)) { dump_module(module); print(string_literal("\n==========================\nLLVM VERIFICATION ERROR\n==========================\n")); print(verification_error_message); fail(); } if (!module->silent) { dump_module(module); } }