#include #include 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; default: unreachable(); } } enum class EvaluationKind { scalar, aggregate, complex, }; enum class TypeKind { abi, memory, }; fn Type* resolve_alias(Module* module, Type* type) { Type* result_type = 0; switch (type->id) { case TypeId::integer: { result_type = type; } break; 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; 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); auto 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, 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) { if (get_byte_size(type) <= start) { return true; } else { switch (type->id) { case TypeId::structure: { trap_raw(); } break; case TypeId::array: { trap_raw(); } 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: { trap_raw(); } break; } } break; default: unreachable(); } auto source_size = get_byte_size(source_type); auto byte_count = source_size - source_offset; auto bit_count = byte_count > 8 ? 64 : byte_count * 8; auto result = integer_type(module, { .bit_count = 64, .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_raw(); } else { report_error(); } } break; case TypeId::array: { trap_raw(); } break; case TypeId::structure: { trap_raw(); } 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_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: { trap_raw(); } 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: 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; 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::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_raw(); } else { result = LLVMDIBuilderCreatePointerType(module->llvm.di_builder, type->pointer.element_type->llvm.debug, 64, 64, 0, (char*)type->name.pointer, type->name.length); } } 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_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; } 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: { trap_raw(); } 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_raw(); } if (is_integral_or_enumeration_type(semantic_return_type) && is_promotable_integer_type_for_abi(semantic_return_type)) { trap_raw(); } } } break; default: unreachable(); } Type* high_type = 0; switch (high_class) { case AbiSystemVClass::none: break; case AbiSystemVClass::integer: { trap_raw(); } break; default: unreachable(); } if (high_type) { trap_raw(); } 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; bool is_valid_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.is_valid_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; bool is_valid_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.is_valid_alignment) { alignment = options.alignment; } else { alignment = get_byte_alignment(resolved_type); } auto store = LLVMBuildStore(module->llvm.builder, source_value, options.destination); LLVMSetAlignment(store, alignment); } 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]; auto argument_attributes = Slice{ .pointer = argument_attribute_buffer, .length = options.abi_argument_types.length }; if (options.return_abi.flags.kind == AbiKind::indirect) { trap_raw(); } for (auto& abi: options.argument_abis) { trap_raw(); } 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: 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, integer_type(module, { .bit_count = 8, .is_signed = false })); } else { report_error(); } } } } if (is_boolean || !expected_type) { trap_raw(); } 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_raw(); } 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_raw(); } else { auto bit_count = resolved_type->integer.bit_count; auto max_value = bit_count == 64 ? ~(u64)0 : ((u64)1 << (bit_count - resolved_type->integer.is_signed)) - 1; if (value->constant_integer.value > max_value) { report_error(); } value_type = expected_type; } } break; case TypeId::pointer: value_type = integer_type(module, { .bit_count = 64, .is_signed = false }); break; default: trap_raw(); } typecheck(module, expected_type, value_type); } break; case ValueId::unary: { auto is_boolean = unary_is_boolean(value->unary.id); if (is_boolean) { trap_raw(); } else { analyze_type(module, value->unary.value, expected_type); value_type = value->unary.value->type; typecheck(module, expected_type, value_type); } } 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 ? integer_type(module, { .bit_count = 1, .is_signed = false }) : 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; 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 void emit_value(Module* module, Value* value, TypeKind type_kind) { assert(value->type); assert(!value->llvm); auto resolved_type = resolve_alias(module, value->type); resolve_type_in_place(module, resolved_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_type, type_kind); llvm_value = LLVMConstInt(llvm_integer_type, value->constant_integer.value, value->constant_integer.is_signed); } break; case ValueId::unary: { switch (value->unary.id) { case UnaryId::minus: { LLVMValueRef llvm_unary_value; if (value->unary.value->llvm) { assert(false); llvm_unary_value = value->unary.value->llvm; } else { emit_value(module, value->unary.value, type_kind); llvm_unary_value = value->unary.value->llvm; } if (value->unary.value->is_constant()) { llvm_value = LLVMConstNeg(llvm_unary_value); } else { trap_raw(); } } break; case UnaryId::plus: { trap_raw(); } break; case UnaryId::ampersand: { trap_raw(); } break; case UnaryId::exclamation: { trap_raw(); } break; case UnaryId::tilde: { trap_raw(); } break; case UnaryId::enum_name: { trap_raw(); } break; case UnaryId::extend: { trap_raw(); } break; case UnaryId::truncate: { trap_raw(); } break; case UnaryId::pointer_cast: { trap_raw(); } break; case UnaryId::int_from_enum: { trap_raw(); } break; case UnaryId::int_from_pointer: { trap_raw(); } break; case UnaryId::va_end: { trap_raw(); } break; case UnaryId::bitwise_not: { trap_raw(); } break; } } break; case ValueId::binary: { bool is_shorcircuiting = binary_is_shortcircuiting(value->binary.id); if (is_shorcircuiting) { trap_raw(); } 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]; LLVMValueRef llvm_value; 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_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_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_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_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; default: unreachable(); } } break; default: unreachable(); } } } 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; 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_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_raw(); } 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; default: unreachable(); } } 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); } } 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; function_type->argument_abis = arena_allocate(module->arena, function_type->semantic_argument_types.length); 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_raw(); } auto required_argument_count = function_type->semantic_argument_types.length; for (auto abi: function_type->argument_abis) { trap_raw(); } 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; LinkageType llvm_linkage_type; switch (global->linkage) { case Linkage::internal: llvm_linkage_type = LinkageType::internal; break; case Linkage::external: llvm_linkage_type = LinkageType::external; 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); } if (is_definition) { global->variable.storage->function.scope.llvm = subprogram; module->current_function = global; 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_raw(); } break; case AbiKind::in_alloca: { trap_raw(); } 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; } for (auto& argument : global->variable.storage->function.arguments) { trap_raw(); } analyze_block(module, global->variable.storage->function.block); auto* insert_block = LLVMGetInsertBlock(module->llvm.builder); if (insert_block) { trap_raw(); } 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_raw(); } } 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(); if (coerce_to_type->id != TypeId::structure && type_is_abi_equal(module, coerce_to_type, function_type->return_abi.semantic_type) && function_type->return_abi.attributes.direct.offset == 0) { auto store = llvm_find_return_value_dominating_store(module->llvm.builder, return_alloca, function_type->return_abi.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 { trap_raw(); } } else { trap_raw(); } } break; case AbiKind::indirect: { trap_raw(); } break; default: unreachable(); } LLVMBuildRet(module->llvm.builder, return_value); } // END OF SCOPE module->current_function = 0; } } break; case ValueId::global: { trap_raw(); } 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(verification_error_message); print(string_literal("\n")); } }