From 92f7b7ec831fe8642f9e7cffe24cfb12f5ff2dad Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 6 May 2025 08:34:42 -0600 Subject: [PATCH] wip --- CMakeLists.txt | 9 +- generate.sh | 35 +- src/compiler.cpp | 7 +- src/compiler.h | 485 +- src/emitter.cpp | 7575 +++++++++++++++++++++++++++- src/lib.h | 63 +- src/llvm.cpp | 912 ++-- src/llvm.h | 340 +- src/parser.cpp | 249 +- tests/basic_bool_call.bbb | 16 + tests/basic_shortcircuiting_if.bbb | 12 + 11 files changed, 9016 insertions(+), 687 deletions(-) create mode 100644 tests/basic_bool_call.bbb create mode 100644 tests/basic_shortcircuiting_if.bbb diff --git a/CMakeLists.txt b/CMakeLists.txt index 90d1029..e54a247 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable(bb src/llvm.cpp ) + target_include_directories(bb PUBLIC src) target_compile_definitions(bb PUBLIC $<$:BB_DEBUG=1> @@ -45,5 +46,9 @@ target_link_libraries(bb PUBLIC # ${LLD_WASM} ) -#target_compile_options(bb PRIVATE -fsanitize=address) -#target_link_options(bb PRIVATE -fsanitize=address) +target_compile_options(bb PRIVATE -Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -funsigned-char -fwrapv -fno-strict-aliasing) +if (NOT APPLE) + target_compile_options(bb PRIVATE -Wno-missing-designated-field-initializers) +endif() +# target_compile_options(bb PRIVATE -fsanitize=address) +# target_link_options(bb PRIVATE -fsanitize=address) diff --git a/generate.sh b/generate.sh index 9bba737..b4a7265 100755 --- a/generate.sh +++ b/generate.sh @@ -1,7 +1,34 @@ #!/usr/bin/env bash set -eu -rm -rf build -mkdir build -cd build -cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_LINKER_TYPE=MOLD -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_PREFIX_PATH=/home/david/dev/llvm/install/llvm_20.1.3_x86_64-linux-Release -DCMAKE_COLOR_DIAGNOSTICS=ON +CMAKE_BUILD_TYPE=Debug +LLVM_CMAKE_BUILD_TYPE=Release +BUILD_DIR=build + +BIRTH_NATIVE_OS_STRING=$OSTYPE + +case "$BIRTH_NATIVE_OS_STRING" in + darwin*) BIRTH_OS="macos";; + linux*) BIRTH_OS="linux";; + msys*) BIRTH_OS="windows";; + *) exit 1 +esac + +BIRTH_NATIVE_ARCH_STRING="$(uname -m)" + +case "$BIRTH_NATIVE_ARCH_STRING" in + x86_64) BIRTH_ARCH="x86_64";; + arm64) BIRTH_ARCH="aarch64";; + *) exit 1 +esac + +case "$BIRTH_OS" in + linux) LINKER_TYPE=MOLD;; + *) LINKER_TYPE=DEFAULT;; +esac + + +rm -rf $BUILD_DIR +mkdir $BUILD_DIR +cd $BUILD_DIR +cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_LINKER_TYPE=$LINKER_TYPE -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_PREFIX_PATH=$HOME/dev/llvm/install/llvm_20.1.3_$BIRTH_ARCH-$BIRTH_OS-$LLVM_CMAKE_BUILD_TYPE -DCMAKE_COLOR_DIAGNOSTICS=ON cd .. diff --git a/src/compiler.cpp b/src/compiler.cpp index dbd53e2..4b23cd4 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -60,7 +60,7 @@ fn void compile(Arena* arena, Options options) type_it += 1; auto noreturn_type = type_it; type_it += 1; - assert(type_it - base_type_allocation.pointer == base_allocation_type_count); + assert((u64)(type_it - base_type_allocation.pointer) == base_allocation_type_count); previous->next = void_type; *void_type = { @@ -260,12 +260,14 @@ global_variable String names[] = { string_literal("c_string_to_slice"), string_literal("c_struct_with_array"), string_literal("c_function_pointer"), + string_literal("basic_bool_call"), string_literal("c_abi"), string_literal("string_to_enum"), string_literal("abi_enum_bool"), string_literal("empty_if"), string_literal("else_if"), string_literal("else_if_complicated"), + string_literal("basic_shortcircuiting_if"), string_literal("shortcircuiting_if"), string_literal("field_access_left_assign"), string_literal("for_each"), @@ -291,6 +293,7 @@ global_variable String names[] = { void entry_point(Slice arguments, Slice environment) { + unused(environment); Arena* arena = arena_initialize_default(8 * mb); if (arguments.length < 2) @@ -407,7 +410,7 @@ void entry_point(Slice arguments, Slice environment) .relative_file_path = relative_file_path, .build_mode = build_mode, .has_debug_info = has_debug_info, - .silent = false, + .silent = true, }); // TODO: introduce test diff --git a/src/compiler.h b/src/compiler.h index a4dc6f2..c16e0d0 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -1,8 +1,8 @@ #pragma once #include - -#define report_error() trap_raw() +#include +#define report_error() trap() enum class Command { @@ -38,6 +38,23 @@ fn String build_mode_to_string(BuildMode build_mode) } } +fn bool build_mode_is_optimized(BuildMode build_mode) +{ + switch (build_mode) + { + case BuildMode::debug_none: + case BuildMode::debug: + return false; + case BuildMode::soft_optimize: + case BuildMode::optimize_for_speed: + case BuildMode::optimize_for_size: + case BuildMode::aggressively_optimize_for_speed: + case BuildMode::aggressively_optimize_for_size: + return true; + case BuildMode::count: unreachable(); + } +} + enum class ValueKind { right, @@ -65,6 +82,165 @@ struct Argument; struct Scope; struct MacroDeclaration; +struct DirectAttributes +{ + u32 offset; + u32 alignment; +}; + +struct IndirectAttributes +{ + u32 alignment; + u32 address_space; +}; + +enum class AbiKind : u8 +{ + ignore, + direct, + extend, + indirect, + indirect_aliased, + expand, + coerce_and_expand, + in_alloca, +}; + +struct AbiFlags +{ + AbiKind kind; + bool padding_in_reg; + bool in_alloca_sret; + bool in_alloca_indirect; + bool indirect_by_value; + bool indirect_realign; + bool sret_after_this; + bool in_reg; + bool can_be_flattened; + bool sign_extension; +}; + +struct AbiInformation +{ + Type* semantic_type; + Type* coerce_to_type; + union + { + Type* type; + Type* unpadded_coerce_and_expand_type; + } padding; + u16 padding_argument_index; + union + { + DirectAttributes direct; + IndirectAttributes indirect; + u32 alloca_field_index; + } attributes; + AbiFlags flags; + u16 abi_start; + u16 abi_count; + + inline void set_sret_after_this(bool sret_after_this) + { + assert(flags.kind == AbiKind::indirect); + flags.sret_after_this = sret_after_this; + } + + inline void set_indirect_realign(bool realign) + { + assert(flags.kind == AbiKind::indirect); + flags.indirect_realign = realign; + } + + inline void set_indirect_by_value(bool by_value) + { + assert(flags.kind == AbiKind::indirect); + flags.indirect_by_value = by_value; + } + + inline void set_indirect_align(u32 alignment) + { + assert(flags.kind == AbiKind::indirect || flags.kind == AbiKind::indirect_aliased); + attributes.indirect.alignment = alignment; + } + + inline bool can_have_coerce_to_type() + { + switch (flags.kind) + { + case AbiKind::direct: + case AbiKind::extend: + case AbiKind::coerce_and_expand: + return true; + default: + return false; + } + } + + inline void set_coerce_to_type(Type* coerce_to_type) + { + assert(can_have_coerce_to_type()); + this->coerce_to_type = coerce_to_type; + } + + inline Type* get_coerce_to_type() + { + assert(can_have_coerce_to_type()); + return coerce_to_type; + } + + inline void set_padding_type(Type* padding_type) + { + assert(can_have_padding_type()); + padding = { + .type = padding_type, + }; + } + + inline bool can_have_padding_type() + { + switch (flags.kind) + { + case AbiKind::direct: + case AbiKind::extend: + case AbiKind::indirect: + case AbiKind::indirect_aliased: + case AbiKind::expand: + return true; + default: + return false; + } + } + + inline Type* get_padding_type() + { + return can_have_padding_type() ? padding.type : 0; + } + + inline void set_direct_offset(u32 offset) + { + assert(flags.kind == AbiKind::direct || flags.kind == AbiKind::extend); + attributes.direct.offset = offset; + } + + inline void set_direct_alignment(u32 alignment) + { + assert(flags.kind == AbiKind::direct || flags.kind == AbiKind::extend); + attributes.direct.alignment = alignment; + } + + inline void set_can_be_flattened(bool can_be_flattened) + { + assert(flags.kind == AbiKind::direct); + flags.can_be_flattened = can_be_flattened; + } + + inline bool get_can_be_flattened() + { + return flags.can_be_flattened; + } +}; + struct Target { CPUArchitecture cpu; @@ -87,6 +263,24 @@ enum class CallingConvention count, }; +enum class ResolvedCallingConvention +{ + system_v, + win64, + count, +}; + +fn ResolvedCallingConvention resolve_calling_convention(CallingConvention cc) +{ + switch (cc) + { + case CallingConvention::c: + // TODO: + return ResolvedCallingConvention::system_v; + case CallingConvention::count: unreachable(); + } +} + enum class InlineBehavior { normal, @@ -116,6 +310,8 @@ enum class TypeId alias, union_type, unresolved, + vector, + floating_point, }; struct TypeInteger @@ -124,12 +320,29 @@ struct TypeInteger bool is_signed; }; +struct AbiRegisterCountSystemV +{ + u32 gpr; + u32 sse; +}; + +union AbiRegisterCount +{ + AbiRegisterCountSystemV system_v; +}; + struct TypeFunction { Type* semantic_return_type; Slice semantic_argument_types; CallingConvention calling_convention; bool is_variable_arguments; + // ABI + Slice abi_argument_types; + Type* abi_return_type; + AbiRegisterCount available_registers; + Slice argument_abis; + AbiInformation return_abi; }; struct TypePointer @@ -140,8 +353,9 @@ struct TypePointer struct TypeArray { - u64 element_count; Type* element_type; + u64 element_count; + Type* next; }; struct UnresolvedEnumField @@ -169,6 +383,10 @@ struct TypeEnum { Slice fields; Type* backing_type; + LLVMValueRef enum_to_string_function; + LLVMValueRef string_to_enum_function; + Type* string_to_enum_struct_type; + Global* name_array; u32 line; }; @@ -221,6 +439,13 @@ struct TypeUnion u32 biggest_field; }; +struct LLVMType +{ + LLVMTypeRef abi; + LLVMTypeRef memory; + LLVMMetadataRef debug; +}; + struct Type { union @@ -238,6 +463,7 @@ struct Type TypeId id; String name; Type* next; + LLVMType llvm; }; fn u32 align_bit_count(u32 bit_count) @@ -263,6 +489,10 @@ fn u64 get_byte_size(Type* type) assert(byte_count == 1 || byte_count == 2 || byte_count == 4 || byte_count == 8 || byte_count == 16); return byte_count; } break; + case TypeId::pointer: + { + return 8; + } break; case TypeId::array: { auto element_type = type->array.element_type; @@ -281,7 +511,12 @@ fn u64 get_byte_size(Type* type) auto result = get_byte_size(type->enumerator.backing_type); return result; } break; - default: trap_raw(); + case TypeId::bits: + { + auto result = get_byte_size(type->bits.backing_type); + return result; + } break; + default: trap(); } } @@ -311,7 +546,20 @@ fn u32 get_byte_alignment(Type* type) auto result = get_byte_alignment(type->enumerator.backing_type); return result; } break; - default: trap_raw(); + case TypeId::pointer: + { + return 8; + } break; + case TypeId::bits: + { + auto result = get_byte_alignment(type->bits.backing_type); + return result; + } break; + case TypeId::union_type: + { + return type->union_type.byte_alignment; + } break; + default: trap(); } } @@ -320,7 +568,8 @@ fn u64 get_bit_size(Type* type) switch (type->id) { case TypeId::integer: return type->integer.bit_count; - default: trap_raw(); + case TypeId::enumerator: return get_bit_size(type->enumerator.backing_type); + default: trap(); } } @@ -340,6 +589,7 @@ struct Scope u32 line; u32 column; ScopeKind kind; + LLVMMetadataRef llvm; }; enum class StatementId @@ -396,6 +646,7 @@ struct StatementSwitchClause { Slice values; Block* block; + LLVMBasicBlockRef basic_block; }; struct StatementSwitch @@ -461,7 +712,6 @@ enum class ValueId variable_reference, macro_reference, macro_instantiation, - dereference, call, global, array_initialization, @@ -479,6 +729,8 @@ enum class ValueId zero, select, string_to_enum, + local, + argument, }; struct ValueConstantInteger @@ -487,12 +739,19 @@ struct ValueConstantInteger bool is_signed; }; +struct FunctionLLVM +{ + LLVMBasicBlockRef return_block; + LLVMValueRef return_alloca; +}; + struct ValueFunction { Slice arguments; Scope scope; Block* block; FunctionAttributes attributes; + FunctionLLVM llvm; }; enum class UnaryId @@ -510,6 +769,7 @@ enum class UnaryId int_from_pointer, va_end, bitwise_not, + dereference, }; struct ValueUnary @@ -565,6 +825,7 @@ struct ValueCall { Value* callable; Slice arguments; + Type* function_type; }; struct ValueArrayInitialization @@ -666,6 +927,8 @@ struct MacroInstantiation u32 column; }; +fn bool variable_is_constant(Value* value); + struct Value { union @@ -676,7 +939,6 @@ struct Value ValueBinary binary; Variable* variable_reference; ValueUnaryType unary_type; - Value* dereference; ValueCall call; ValueArrayInitialization array_initialization; ValueArrayExpression array_expression; @@ -694,14 +956,36 @@ struct Value Type* type; ValueId id; ValueKind kind; + LLVMValueRef llvm; bool is_constant() { switch (id) { case ValueId::constant_integer: + case ValueId::enum_literal: return true; - default: trap_raw(); + case ValueId::unary: + case ValueId::binary: + case ValueId::field_access: + case ValueId::array_expression: + case ValueId::call: + return false; + case ValueId::variable_reference: + { + return variable_is_constant(this); + } break; + case ValueId::array_initialization: + { + assert(type); // This asserts that the value type has been analyzed and `is_constant` was properly set + return array_initialization.is_constant; + } break; + case ValueId::aggregate_initialization: + { + assert(type); // This asserts that the value type has been analyzed and `is_constant` was properly set + return aggregate_initialization.is_constant; + } break; + default: trap(); } } }; @@ -717,6 +1001,28 @@ struct Variable u32 column; }; +fn bool variable_is_constant(Value* value) +{ + assert(value->id == ValueId::variable_reference); + auto* variable = value->variable_reference; + + switch (value->kind) + { + case ValueKind::left: + { + switch (variable->scope->kind) + { + case ScopeKind::global: + return true; + default: + return false; + } + } break; + case ValueKind::right: + return false; + } +} + enum class Linkage { internal, @@ -727,6 +1033,7 @@ struct Global { Variable variable; Linkage linkage; + bool emitted; Global* next; }; @@ -742,6 +1049,47 @@ struct Argument u32 index; }; +struct LLVMIntrinsicId +{ + u32 n; +}; + +enum class IntrinsicIndex +{ + trap, + va_start, + va_end, + va_copy, + count, +}; + +global_variable String intrinsic_names[] = { + string_literal("llvm.trap"), + string_literal("llvm.va_start"), + string_literal("llvm.va_end"), + string_literal("llvm.va_copy"), +}; + +static_assert(array_length(intrinsic_names) == (u64)IntrinsicIndex::count); + +struct ModuleLLVM +{ + LLVMContextRef context; + LLVMModuleRef module; + LLVMBuilderRef builder; + LLVMDIBuilderRef di_builder; + LLVMMetadataRef file; + LLVMMetadataRef compile_unit; + LLVMTypeRef pointer_type; + LLVMTypeRef void_type; + LLVMIntrinsicId intrinsic_table[(u64)IntrinsicIndex::count]; + LLVMValueRef memcmp; + LLVMMetadataRef inlined_at; + LLVMBasicBlockRef continue_block; + LLVMBasicBlockRef exit_block; + u32 debug_tag; +}; + struct Module { Arena* arena; @@ -768,6 +1116,7 @@ struct Module MacroDeclaration* current_macro_declaration; MacroInstantiation* current_macro_instantiation; + ModuleLLVM llvm; Scope scope; String name; @@ -788,6 +1137,10 @@ fn Type* integer_type(Module* module, TypeInteger integer) { assert(integer.bit_count); assert(integer.bit_count <= 64 || integer.bit_count == 128); + if (integer.is_signed) + { + assert(integer.bit_count > 1); + } auto index = integer.bit_count == 128 ? (i128_offset + integer.is_signed) : (integer.bit_count - 1 + (64 * integer.is_signed)); auto* result_type = module->first_type + index; assert(result_type->id == TypeId::integer); @@ -806,6 +1159,31 @@ fn Type* noreturn_type(Module* module) return void_type(module) + 1; } +fn Type* uint1(Module* module) +{ + return integer_type(module, { .bit_count = 1, .is_signed = false }); +} + +fn Type* uint8(Module* module) +{ + return integer_type(module, { .bit_count = 8, .is_signed = false }); +} + +fn Type* uint32(Module* module) +{ + return integer_type(module, { .bit_count = 32, .is_signed = false }); +} + +fn Type* uint64(Module* module) +{ + return integer_type(module, { .bit_count = 64, .is_signed = false }); +} + +fn Type* sint32(Module* module) +{ + return integer_type(module, { .bit_count = 32, .is_signed = true }); +} + struct Options { String content; @@ -899,6 +1277,7 @@ fn Type* get_pointer_type(Module* module, Type* element_type) string_literal("&"), element_type->name, }; + auto result = type_allocate_init(module, { .pointer = { .element_type = element_type, @@ -907,6 +1286,17 @@ fn Type* get_pointer_type(Module* module, Type* element_type) .name = arena_join_string(module->arena, array_to_slice(name_parts)), }); + if (last_pointer_type) + { + assert(module->first_pointer_type); + last_pointer_type->pointer.next = result; + } + else + { + assert(!module->first_pointer_type); + module->first_pointer_type = result; + } + return result; } @@ -959,7 +1349,7 @@ fn Type* get_slice_type(Module* module, Type* element_type) }; fields[1] = { .name = string_literal("length"), - .type = integer_type(module, { .bit_count = 64, .is_signed = false }), + .type = uint64(module), .offset = 8, .line = 0, }; @@ -982,7 +1372,7 @@ fn Type* get_slice_type(Module* module, Type* element_type) if (last_slice_type) { - last_slice_type->next = result; + last_slice_type->structure.next = result; } else { @@ -992,5 +1382,78 @@ fn Type* get_slice_type(Module* module, Type* element_type) return result; } +fn String array_name(Module* module, Type* element_type, u64 element_count) +{ + u8 buffer[512]; + auto buffer_slice = String{ .pointer = buffer, .length = array_length(buffer) }; + + u64 i = 0; + + buffer[i] = '['; + i += 1; + + i += format_integer_decimal(buffer_slice(i), element_count); + + buffer[i] = ']'; + i += 1; + + auto element_name = element_type->name; + memcpy(buffer + i, element_name.pointer, element_name.length); + i += element_name.length; + + auto name = arena_duplicate_string(module->arena, buffer_slice(0, i)); + return name; +} + +fn Type* get_array_type(Module* module, Type* element_type, u64 element_count) +{ + assert(element_type); + assert(element_count); + + Type* array_type = module->first_array_type; + + if (array_type) + { + while (1) + { + assert(array_type->id == TypeId::array); + auto* candidate_element_type = array_type->array.element_type; + if (candidate_element_type == element_type) + { + return array_type; + } + + if (!array_type->array.next) + { + break; + } + + array_type = array_type->array.next; + } + } + + Type* last_array_type = array_type; + + auto result = type_allocate_init(module, { + .array = { + .element_type = element_type, + .element_count = element_count, + }, + .id = TypeId::array, + .name = array_name(module, element_type, element_count), + }); + + if (last_array_type) + { + last_array_type->array.next = result; + } + else + { + module->first_array_type = result; + } + + return result; +} + void parse(Module* module); void emit(Module* module); diff --git a/src/emitter.cpp b/src/emitter.cpp index 105e6d8..7031b12 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -1,5 +1,7578 @@ #include -void emit(Module* module) +#include + +enum class EvaluationKind { + scalar, + aggregate, + complex, +}; + +enum class TypeKind +{ + abi, + memory, +}; + +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; + default: + unreachable(); + } +} + +fn bool type_is_aggregate_type_for_abi(Type* type) +{ + auto evaluation_kind = get_evaluation_kind(type); + auto is_member_function_pointer_type = false; // TODO + return evaluation_kind != EvaluationKind::scalar || is_member_function_pointer_type; +} + +fn u64 get_byte_allocation_size(Type* type) +{ + auto size = get_byte_size(type); + auto alignment = get_byte_alignment(type); + auto result = align_forward(size, alignment); + return result; +} + +struct LLVMGlobal +{ + String host_triple; + String host_cpu_model; + String host_cpu_features; +}; + +global_variable LLVMGlobal llvm_global; + +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); + case TypeId::bits: + return type_is_signed(type->bits.backing_type); + case TypeId::pointer: // TODO: pointers should be signed? + return false; + default: unreachable(); + } +} + +fn bool type_is_slice(Type* type) +{ + return type->id == TypeId::structure && type->structure.is_slice; +} + +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 Type* align_integer_type(Module* module, Type* type) +{ + auto bit_count = (u32)get_bit_size(type); + auto abi_bit_count = align_bit_count(bit_count); + bool is_signed = type_is_signed(type); + auto result = integer_type(module, { .bit_count = abi_bit_count, .is_signed = is_signed }); + return result; +} + +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(); + } +} + +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 void emit_value(Module* module, Value* value, TypeKind type_kind); + +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::void_type: + case TypeId::noreturn: + case TypeId::integer: + case TypeId::enumerator: + case TypeId::function: + case TypeId::bits: + case TypeId::union_type: + { + result_type = type; + } break; + case TypeId::structure: + { + if (type->structure.is_slice) + { + auto element_type = resolve_alias(module, type->structure.fields[0].type->pointer.element_type); + result_type = get_slice_type(module, element_type); + } + else + { + result_type = type; + } + } break; + case TypeId::alias: + { + result_type = resolve_alias(module, type->alias.type); + } break; + default: unreachable(); + } + + assert(result_type); + return result_type; +} + +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, +}; + +struct AbiSystemVClassifyResult +{ + AbiSystemVClass r[2]; +}; + + +// AMD64-ABI 3.2.3p2: Rule 4. Each field of an object is +// classified recursively so that always two fields are +// considered. The resulting class is calculated according to +// the classes of the fields in the eightbyte: +// +// (a) If both classes are equal, this is the resulting class. +// +// (b) If one of the classes is NO_CLASS, the resulting class is +// the other class. +// +// (c) If one of the classes is MEMORY, the result is the MEMORY +// class. +// +// (d) If one of the classes is INTEGER, the result is the +// INTEGER. +// +// (e) If one of the classes is X87, X87UP, COMPLEX_X87 class, +// MEMORY is used as class. +// +// (f) Otherwise class SSE is used. + +// Accum should never be memory (we should have returned) or +// ComplexX87 (because this cannot be passed in a structure). +fn AbiSystemVClass abi_system_v_merge_class(AbiSystemVClass accumulator, AbiSystemVClass field) +{ + assert(accumulator != AbiSystemVClass::memory && accumulator != AbiSystemVClass::complex_x87); + + if (accumulator == field || field == AbiSystemVClass::none) + { + return accumulator; + } + + if (field == AbiSystemVClass::memory) + { + return AbiSystemVClass::memory; + } + + if (accumulator == AbiSystemVClass::integer || field == AbiSystemVClass::integer) + { + return AbiSystemVClass::integer; + } + + if (field == AbiSystemVClass::x87 || field == AbiSystemVClass::x87_up || field == AbiSystemVClass::complex_x87 || accumulator == AbiSystemVClass::x87 || accumulator == AbiSystemVClass::x87_up) + { + return AbiSystemVClass::memory; + } + + return AbiSystemVClass::sse; +} + +fn AbiSystemVClassifyResult abi_system_v_classify_post_merge(u64 aggregate_size, AbiSystemVClassifyResult classes) +{ + AbiSystemVClassifyResult result = classes; + + if (result.r[1] == AbiSystemVClass::memory) + { + result.r[0] = AbiSystemVClass::memory; + } + + if (result.r[1] == AbiSystemVClass::x87_up) + { + trap(); + } + + if (aggregate_size > 16 && (result.r[0] != AbiSystemVClass::sse || result.r[1] != AbiSystemVClass::sse_up)) + { + result.r[0] = AbiSystemVClass::memory; + } + + if (result.r[1] == AbiSystemVClass::sse_up && result.r[0] != AbiSystemVClass::sse) + { + result.r[0] = AbiSystemVClass::sse; + } + + return result; +} + +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: + { + u64 offset = 0; + + for (auto& field: type->structure.fields) + { + if (offset >= end) + { + break; + } + + auto field_start = offset < start ? start - offset : 0; + if (!contains_no_user_data(field.type, field_start, end - offset)) + { + return false; + } + offset += get_byte_size(field.type); + } + + return true; + } break; + case TypeId::array: + { + auto element_type = type->array.element_type; + auto element_count = type->array.element_count; + auto element_size = get_byte_size(element_type); + + for (u64 i = 0; i < element_count; i += 1) + { + auto offset = i * element_size; + if (offset >= end) + { + break; + } + + auto element_start = offset < start ? start - offset : 0; + if (!contains_no_user_data(element_type, element_start, end - offset)) + { + return false; + } + } + trap(); + } break; + default: return false; + } + } +} + +fn Field* get_member_at_offset(Type* struct_type, u32 offset) +{ + assert(struct_type->id == TypeId::structure); + + Field* result = 0; + + if (struct_type->structure.byte_size > offset) + { + u32 offset_it = 0; + auto fields = struct_type->structure.fields; + + for (u64 i = 0; i < fields.length; i += 1) + { + auto* field = &fields[i]; + + if (offset_it > offset) + { + break; + } + + result = field; + offset_it = (u32)align_forward(offset_it + get_byte_size(field->type), get_byte_alignment(field->type)); + } + + assert(result); + } + + return result; +} + +fn Type* abi_system_v_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; + case TypeId::pointer: + { + if (offset == 0) + { + return type; + } + else + { + trap(); + } + } break; + case TypeId::structure: + { + auto* field = get_member_at_offset(type, offset); + if (field) + { + auto field_type = field->type; + switch (field_type->id) + { + case TypeId::integer: + case TypeId::enumerator: + { + field_type = align_integer_type(module, field_type); + } break; + default: break; + } + + return abi_system_v_get_integer_type_at_offset(module, field_type, offset - field->offset, source_type, source_offset); + } + else + { + unreachable(); + } + } break; + case TypeId::bits: + { + auto backing_type = type->bits.backing_type; + return abi_system_v_get_integer_type_at_offset(module, backing_type, offset, source_type == type ? backing_type : source_type, source_offset); + } 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; +}; + +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: + { + auto byte_size = get_byte_size(type); + if (byte_size <= 64) + { + if (options.base_offset % get_byte_alignment(type) == 0) + { + auto element_type = type->array.element_type; + auto element_size = get_byte_size(element_type); + + result.r[current_index] = AbiSystemVClass::none; + + u64 vector_size = 16; + + if (byte_size > 16 && (byte_size != get_byte_size(element_type) || byte_size > vector_size)) + { + unreachable(); + } + else + { + auto offset = options.base_offset; + auto element_count = type->array.element_count; + + for (u64 i = 0; i < element_count; i += 1) + { + auto element_classes = abi_system_v_classify_type(element_type, AbiSystemVClassify{ + .base_offset = offset, + .is_variable_argument = options.is_variable_argument, + }); + offset += element_size; + + result.r[0] = abi_system_v_merge_class(result.r[0], element_classes.r[0]); + result.r[1] = abi_system_v_merge_class(result.r[1], element_classes.r[1]); + + if (result.r[0] == AbiSystemVClass::memory || result.r[1] == AbiSystemVClass::memory) + { + break; + } + } + + auto final_result = abi_system_v_classify_post_merge(byte_size, result); + assert(final_result.r[1] != AbiSystemVClass::sse || final_result.r[0] != AbiSystemVClass::sse); + result = final_result; + } + } + } + } break; + case TypeId::structure: + case TypeId::union_type: + { + auto byte_size = type->structure.byte_size; + + if (byte_size <= 64) + { + auto has_variable_array = false; + if (!has_variable_array) + { + result.r[current_index] = AbiSystemVClass::none; + auto is_union = type->id == TypeId::union_type; + + switch (type->id) + { + case TypeId::structure: + { + for (auto& field : type->structure.fields) + { + auto offset = options.base_offset + field.offset; + auto member_type = field.type; + auto member_size = get_byte_size(member_type); + auto member_alignment = get_byte_alignment(member_type); + + u64 native_vector_size = 16; + + auto gt_16 = byte_size > 16 && ((!is_union && byte_size != member_size) || byte_size > native_vector_size); + auto padding = offset % member_alignment != 0; + + if (gt_16 || padding) + { + result.r[0] = AbiSystemVClass::memory; + result = abi_system_v_classify_post_merge(byte_size, result); + return result; + } + + auto member_classes = abi_system_v_classify_type(member_type, { + .base_offset = offset, + .is_variable_argument = options.is_variable_argument, + .is_register_call = options.is_register_call, + }); + + for (u64 i = 0; i < array_length(member_classes.r); i += 1) + { + result.r[i] = abi_system_v_merge_class(result.r[i], member_classes.r[i]); + } + + if (result.r[0] == AbiSystemVClass::memory || result.r[1] == AbiSystemVClass::memory) + { + break; + } + } + + result = abi_system_v_classify_post_merge(byte_size, result); + } break; + case TypeId::union_type: + { + trap(); + } break; + default: unreachable(); + } + } + } + } break; + case TypeId::alias: + return abi_system_v_classify_type(type->alias.type, options); + default: unreachable(); + } + + return result; +} + +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; + case TypeId::structure: + { + LLVMTypeRef llvm_type_buffer[64]; + auto fields = type->structure.fields; + for (u64 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + resolve_type_in_place_memory(module, field.type); + llvm_type_buffer[i] = field.type->llvm.memory; + } + + result = LLVMStructTypeInContext(module->llvm.context, llvm_type_buffer, fields.length, 0); + } break; + case TypeId::bits: + { + auto backing_type = type->bits.backing_type; + resolve_type_in_place_abi(module, backing_type); + result = backing_type->llvm.abi; + } break; + case TypeId::union_type: + { + auto biggest_type = type->union_type.fields[type->union_type.biggest_field].type; + resolve_type_in_place_memory(module, biggest_type); + result = LLVMStructTypeInContext(module->llvm.context, &biggest_type->llvm.memory, 1, 0); + } 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: + case TypeId::structure: + 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; + case TypeId::bits: + { + auto backing_type = type->bits.backing_type; + resolve_type_in_place_memory(module, backing_type); + result = backing_type->llvm.memory; + } break; + case TypeId::union_type: + { + auto biggest_type = type->union_type.fields[type->union_type.biggest_field].type; + resolve_type_in_place_memory(module, biggest_type); + result = LLVMStructTypeInContext(module->llvm.context, &biggest_type->llvm.memory, 1, 0); + } 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; + case TypeId::structure: + { + LLVMDIFlags flags = {}; + auto forward_declaration = LLVMDIBuilderCreateReplaceableCompositeType(module->llvm.di_builder, module->llvm.debug_tag, (char*)type->name.pointer, type->name.length, module->scope.llvm, module->llvm.file, type->structure.line, 0, type->structure.byte_size * 8, type->structure.byte_alignment * 8, flags, (char*)type->name.pointer, type->name.length); + module->llvm.debug_tag += 1; + + LLVMMetadataRef llvm_type_buffer[64]; + + auto fields = type->structure.fields; + for (u64 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place_debug(module, field_type); + auto member_type = LLVMDIBuilderCreateMemberType(module->llvm.di_builder, module->scope.llvm, (char*)field.name.pointer, field.name.length, module->llvm.file, field.line, get_byte_size(field_type) * 8, get_byte_alignment(field_type) * 8, field.offset * 8, flags, field_type->llvm.debug); + llvm_type_buffer[i] = member_type; + } + + auto struct_type = LLVMDIBuilderCreateStructType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->structure.line, type->structure.byte_size * 8, type->structure.byte_alignment * 8, flags, 0, llvm_type_buffer, fields.length, 0, 0, (char*)type->name.pointer, type->name.length); + LLVMMetadataReplaceAllUsesWith(forward_declaration, struct_type); + result = struct_type; + } break; + case TypeId::bits: + { + LLVMMetadataRef llvm_type_buffer[64]; + + auto fields = type->bits.fields; + auto backing_type = type->bits.backing_type->llvm.debug; + LLVMDIFlags flags = {}; + for (u64 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place_debug(module, field_type); + u64 bit_offset = 0; + auto member_type = LLVMDIBuilderCreateBitFieldMemberType(module->llvm.di_builder, module->scope.llvm, (char*)field.name.pointer, field.name.length, module->llvm.file, field.line, get_bit_size(field_type), bit_offset, field.offset, flags, backing_type); + llvm_type_buffer[i] = member_type; + } + + auto size = get_byte_size(type) * 8; + auto alignment = get_byte_alignment(type) * 8; + auto struct_type = LLVMDIBuilderCreateStructType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->bits.line, size, alignment, flags, 0, llvm_type_buffer, fields.length, 0, 0, (char*)type->name.pointer, type->name.length); + result = struct_type; + } break; + case TypeId::union_type: + { + LLVMDIFlags flags = {}; + auto forward_declaration = LLVMDIBuilderCreateReplaceableCompositeType(module->llvm.di_builder, module->llvm.debug_tag, (char*)type->name.pointer, type->name.length, module->scope.llvm, module->llvm.file, type->union_type.line, 0, type->union_type.byte_size * 8, type->union_type.byte_alignment * 8, flags, (char*)type->name.pointer, type->name.length); + module->llvm.debug_tag += 1; + + LLVMMetadataRef llvm_type_buffer[64]; + + auto fields = type->union_type.fields; + for (u64 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place_debug(module, field_type); + auto member_type = LLVMDIBuilderCreateMemberType(module->llvm.di_builder, module->scope.llvm, (char*)field.name.pointer, field.name.length, module->llvm.file, field.line, get_byte_size(field_type) * 8, get_byte_alignment(field_type) * 8, 0, flags, field_type->llvm.debug); + llvm_type_buffer[i] = member_type; + } + + auto union_type = LLVMDIBuilderCreateUnionType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->union_type.line, type->union_type.byte_size * 8, type->union_type.byte_alignment * 8, flags, llvm_type_buffer, fields.length, 0, (char*)type->name.pointer, type->name.length); + LLVMMetadataReplaceAllUsesWith(forward_declaration, union_type); + result = union_type; + } 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, + }, + }; +} + +struct DirectOptions +{ + Type* semantic_type; + Type* type; + Type* padding; + u32 offset; + u32 alignment; + bool can_be_flattened = true; +}; + +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.padding); + result.set_direct_offset(direct.offset); + result.set_direct_alignment(direct.alignment); + result.set_can_be_flattened(direct.can_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; +} + +fn Type* get_anonymous_struct_pair(Module* module, Type* low, Type* high) +{ + Type* pair; + for (pair = module->first_pair_struct_type; pair; pair = pair->structure.next) + { + assert(pair->id == TypeId::structure); + assert(pair->structure.fields.length == 2); + + if (pair->structure.fields[0].type == low && + pair->structure.fields[1].type == high) + { + return pair; + } + + if (!pair->structure.next) + { + break; + } + } + + auto high_alignment = get_byte_alignment(high); + auto alignment = MAX(get_byte_alignment(low), high_alignment); + u64 high_offset = align_forward(get_byte_size(low), high_alignment); + + auto fields = arena_allocate(module->arena, 2); + fields[0] = { + .name = string_literal("low"), + .type = low, + .offset = 0, + .line = 0, + }; + fields[1] = { + .name = string_literal("high"), + .type = high, + .offset = high_offset, + .line = 0, + }; + + auto struct_type = type_allocate_init(module, { + .structure = { + .fields = fields, + .byte_size = high_offset + get_byte_size(high), + .byte_alignment = alignment, + }, + .id = TypeId::structure, + .name = string_literal(""), + }); + + if (pair) + { + assert(module->first_pair_struct_type); + pair->structure.next = struct_type; + } + else + { + assert(!module->first_pair_struct_type); + module->first_pair_struct_type = struct_type; + } + + return struct_type; +} + +fn Type* get_by_value_argument_pair(Module* module, Type* low, Type* high) +{ + auto low_size = get_byte_allocation_size(low); + auto high_alignment = get_byte_alignment(high); + auto high_start = align_forward(low_size, high_alignment); + assert(high_start != 0 && high_start <= 8); + if (high_start != 8) + { + trap(); + } + + auto result = get_anonymous_struct_pair(module, low, high); + return result; +} + + +struct IndirectOptions +{ + Type* semantic_type; + Type* padding_type = 0; + u32 alignment; + bool by_value = true; + bool realign = false; +}; + +fn AbiInformation abi_system_v_get_indirect(IndirectOptions indirect) +{ + auto result = AbiInformation{ + .semantic_type = indirect.semantic_type, + .attributes = { + .indirect = { + .alignment = 0, + .address_space = 0, + }, + }, + .flags = { + .kind = AbiKind::indirect, + }, + }; + + result.set_indirect_align(indirect.alignment); + result.set_indirect_by_value(indirect.by_value); + result.set_indirect_realign(indirect.realign); + result.set_sret_after_this(false); + result.set_padding_type(indirect.padding_type); + + return result; +} + +struct NaturalAlignIndirect +{ + Type* semantic_type; + Type* padding_type = 0; + bool by_value = true; + bool realign = false; +}; + +fn AbiInformation abi_system_v_get_natural_align_indirect(NaturalAlignIndirect natural) +{ + auto alignment = get_byte_alignment(natural.semantic_type); + return abi_system_v_get_indirect({ + .semantic_type = natural.semantic_type, + .padding_type = natural.padding_type, + .alignment = alignment, + .by_value = natural.by_value, + .realign = natural.realign, + }); +} + +fn bool is_illegal_vector_type(Type* type) +{ + switch (type->id) + { + case TypeId::vector: trap(); + default: + return false; + } +} + +fn AbiInformation abi_system_v_get_indirect_result(Module* module, Type* type, u32 free_gpr) +{ + if (!type_is_aggregate_type_for_abi(type) && !is_illegal_vector_type(type) && !is_arbitrary_bit_integer(type)) + { + if (is_promotable_integer_type_for_abi(type)) + { + trap(); + } + else + { + return abi_system_v_get_direct(module, { + .semantic_type = type, + .type = type, + }); + } + } + else + { + auto alignment = MAX(get_byte_alignment(type), 8); + auto size = get_byte_size(type); + + if (free_gpr == 0 && alignment == 8 && size <= 8) + { + trap(); + } + else + { + return abi_system_v_get_indirect({ + .semantic_type = type, + .alignment = alignment, + }); + } + } +} + + +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::none: unreachable(); + case AbiSystemVClass::integer: + { + needed_registers.gpr += 1; + low_type = abi_system_v_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 }; + } + } + } break; + case AbiSystemVClass::memory: + { + return { abi_system_v_get_indirect_result(module, semantic_argument_type, options.available_gpr), needed_registers }; + } break; + default: unreachable(); + } + + Type* high_type = 0; + + switch (high_class) + { + case AbiSystemVClass::none: + break; + case AbiSystemVClass::integer: + { + needed_registers.gpr += 1; + high_type = abi_system_v_get_integer_type_at_offset(module, semantic_argument_type, 8, semantic_argument_type, 8); + + if (low_class == AbiSystemVClass::none) + { + trap(); + } + } break; + default: unreachable(); + } + + Type* result_type = low_type; + if (high_type) + { + result_type = get_by_value_argument_pair(module, low_type, high_type); + } + + return { + abi_system_v_get_direct(module, DirectOptions{ + .semantic_type = semantic_argument_type, + .type = result_type, + }), + needed_registers, + }; +} + +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 + { + argument_abi = abi_system_v_get_indirect_result(module, semantic_argument_type, available_registers->gpr); + } + + 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) + { + for (u64 i = 0; i < coerce_to_type->structure.fields.length; i += 1) + { + auto& field = coerce_to_type->structure.fields[i]; + auto field_type = field.type; + llvm_abi_argument_type_buffer[argument_abi.abi_start + i] = field_type->llvm.abi; + abi_argument_type_buffer[argument_abi.abi_start + i] = field_type; + } + } + 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; + case AbiKind::indirect: + { + auto indirect_type = get_pointer_type(module, argument_abi.semantic_type); + auto abi_index = argument_abi.abi_start; + abi_argument_type_buffer[abi_index] = indirect_type; + resolve_type_in_place(module, indirect_type); + llvm_abi_argument_type_buffer[abi_index] = indirect_type->llvm.abi; + count = 1; + } break; + default: unreachable(); + } + + assert(count); + argument_abi.abi_count = count; + + return argument_abi; +} + +fn AbiInformation abi_system_v_get_indirect_return_result(Type* type) +{ + if (type_is_aggregate_type_for_abi(type)) + { + return abi_system_v_get_natural_align_indirect({ + .semantic_type = type, + }); + } + else + { + trap(); + } +} + +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 = abi_system_v_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)) + { + return abi_system_v_get_extend({ + .semantic_type = semantic_return_type, + .sign = type_is_signed(semantic_return_type), + }); + } + } + } break; + case AbiSystemVClass::memory: + { + return abi_system_v_get_indirect_return_result(semantic_return_type); + } break; + default: unreachable(); + } + + Type* high_type = 0; + + switch (high_class) + { + case AbiSystemVClass::none: + break; + case AbiSystemVClass::integer: + { + u64 high_offset = 8; + high_type = abi_system_v_get_integer_type_at_offset(module, semantic_return_type, high_offset, semantic_return_type, high_offset); + if (low_class == AbiSystemVClass::none) + { + trap(); + } + } break; + + default: unreachable(); + } + + if (high_type) + { + low_type = get_by_value_argument_pair(module, low_type, high_type); + } + + 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; + + u64 total_abi_count = 0; + if (options.return_abi.flags.kind == AbiKind::indirect) + { + auto abi_index = options.return_abi.flags.sret_after_this; + auto argument_attribute = &argument_attributes[abi_index]; + *argument_attribute = { + .semantic_type = options.return_abi.semantic_type->llvm.memory, + .abi_type = options.abi_argument_types[abi_index]->llvm.abi, + .dereferenceable_bytes = 0, + .alignment = get_byte_alignment(options.return_abi.semantic_type), + .no_alias = true, + .non_null = false, + .no_undef = false, + .sign_extend = false, + .zero_extend = false, + .in_reg = options.return_abi.flags.in_reg, + .no_fp_class = {}, + .struct_return = true, + .writable = true, + .dead_on_unwind = true, + .in_alloca = false, + .dereferenceable = false, + .dereferenceable_or_null = false, + .nest = false, + .by_value = false, + .by_reference = false, + .no_capture = false, + }; + total_abi_count += 1; + } + + for (const auto& abi: options.argument_abis) + { + for (auto abi_index = abi.abi_start; abi_index < abi.abi_start + 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, + }; + total_abi_count += 1; + } + } + assert(total_abi_count == options.abi_argument_types.length); + + 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 Type* get_va_list_type(Module* module) +{ + if (!module->va_list_type) + { + auto u32_type = uint32(module); + auto void_pointer = get_pointer_type(module, uint8(module)); + auto fields = arena_allocate(module->arena, 4); + fields[0] = { .name = string_literal("gp_offset"), .type = u32_type, .offset = 0 }; + fields[1] = { .name = string_literal("fp_offset"), .type = u32_type, .offset = 4 }; + fields[2] = { .name = string_literal("overflow_arg_area"), .type = void_pointer, .offset = 8 }; + fields[3] = { .name = string_literal("reg_save_area"), .type = void_pointer, .offset = 16 }; + + auto va_list_struct = type_allocate_init(module, { + .structure = { + .fields = fields, + .byte_size = 24, + .byte_alignment = 16, + }, + .id = TypeId::structure, + .name = string_literal("va_list"), + }); + + module->va_list_type = get_array_type(module, va_list_struct, 1); + } + + assert(module->va_list_type); + + return module->va_list_type; +} + +fn Global* get_enum_name_array_global(Module* module, Type* enum_type) +{ + assert(enum_type->id == TypeId::enumerator); + + if (!enum_type->enumerator.name_array) + { + auto fields = enum_type->enumerator.fields; + auto u8_type = uint8(module); + auto u64_type = uint64(module); + resolve_type_in_place(module, u8_type); + resolve_type_in_place(module, u64_type); + LLVMValueRef name_before = 0; + LLVMValueRef name_constant_buffer[64]; + + for (u32 i = 0; i < fields.length; i += 1) + { + auto null_terminate = true; + auto& field = fields[i]; + auto is_constant = true; + String name_parts[] = { + string_literal("string."), + enum_type->name, + string_literal("."), + field.name, + }; + unsigned address_space = 0; + auto name_global = llvm_module_create_global_variable(module->llvm.module, LLVMArrayType2(u8_type->llvm.abi, field.name.length + null_terminate), is_constant, LLVMInternalLinkage, LLVMConstStringInContext2(module->llvm.context, (char*)field.name.pointer, field.name.length, false), arena_join_string(module->arena, array_to_slice(name_parts)), name_before, LLVMNotThreadLocal, address_space, false); + name_before = name_global; + LLVMValueRef constants[] = { + name_global, + LLVMConstInt(u64_type->llvm.abi, field.name.length, false), + }; + auto slice_constant = LLVMConstStructInContext(module->llvm.context, constants, array_length(constants), false); + name_constant_buffer[i] = slice_constant; + } + + auto slice_type = get_slice_type(module, u8_type); + auto array_element_count = fields.length; + auto name_array = LLVMConstArray2(slice_type->llvm.abi, name_constant_buffer, array_element_count); + auto name_array_type = LLVMArrayType2(slice_type->llvm.abi, array_element_count); + auto is_constant = true; + unsigned address_space = 0; + auto name_array_variable = llvm_module_create_global_variable(module->llvm.module, name_array_type, is_constant, LLVMInternalLinkage, name_array, string_literal("name.array.enum"), name_before, LLVMNotThreadLocal, address_space, false); + LLVMSetAlignment(name_array_variable, get_byte_alignment(slice_type)); + LLVMSetUnnamedAddress(name_array_variable, LLVMGlobalUnnamedAddr); + + auto global_type = get_array_type(module, slice_type, array_element_count); + resolve_type_in_place(module, global_type); + + auto storage_type = get_pointer_type(module, global_type); + resolve_type_in_place(module, storage_type); + + auto global_storage = new_value(module); + *global_storage = { + .type = storage_type, + .id = ValueId::global, + .kind = ValueKind::left, + .llvm = name_array_variable, + }; + + String name_parts[] = { + string_literal("name.array.enum."), + enum_type->name, + }; + + auto global = new_global(module); + *global = { + .variable = { + .storage = global_storage, + .initial_value = 0, + .type = global_type, + .scope = &module->scope, + .name = arena_join_string(module->arena, array_to_slice(name_parts)), + .line = 0, + .column = 0, + }, + .linkage = Linkage::internal, + }; + global->emitted = true; + + enum_type->enumerator.name_array = global; + } + + return enum_type->enumerator.name_array; +} + +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; + case UnaryId::int_from_pointer: + { + analyze_type(module, unary_value, 0); + + auto value_enum_type = unary_value->type; + if (value_enum_type->id != TypeId::pointer) + { + report_error(); + } + + value_type = uint64(module); + typecheck(module, expected_type, value_type); + } break; + case UnaryId::pointer_cast: + { + if (!expected_type) + { + report_error(); + } + + if (expected_type->id != TypeId::pointer) + { + report_error(); + } + + analyze_type(module, unary_value, 0); + auto value_pointer_type = unary_value->type; + if (value_pointer_type == expected_type) + { + report_error(); + } + + if (value_pointer_type->id != TypeId::pointer) + { + report_error(); + } + + value_type = expected_type; + } break; + case UnaryId::enum_name: + { + auto string_type = get_slice_type(module, uint8(module)); + typecheck(module, expected_type, string_type); + analyze_type(module, unary_value, 0); + auto enum_type = unary_value->type; + if (enum_type->id != TypeId::enumerator) + { + report_error(); + } + + auto enum_to_string = enum_type->enumerator.enum_to_string_function; + if (!enum_to_string) + { + auto current_block = LLVMGetInsertBlock(module->llvm.builder); + auto enum_name_array_global = get_enum_name_array_global(module, enum_type); + unused(enum_name_array_global); + unused(current_block); + LLVMTypeRef argument_types[] = { + enum_type->llvm.abi, + }; + auto llvm_function_type = LLVMFunctionType(string_type->llvm.memory, argument_types, array_length(argument_types), false); + String name_parts[] = { + string_literal("enum_to_string."), + enum_type->name, + }; + auto function_name = arena_join_string(module->arena, array_to_slice(name_parts)); + auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, LLVMInternalLinkage, 0, function_name); + LLVMSetFunctionCallConv(llvm_function, LLVMFastCallConv); + + LLVMValueRef llvm_argument; + LLVMGetParams(llvm_function, &llvm_argument); + + auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("entry"), llvm_function); + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + auto alloca = create_alloca(module, { + .type = string_type, + .name = string_literal("retval"), + }); + + auto* return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("return_block"), llvm_function); + auto* else_block = llvm_context_create_basic_block(module->llvm.context, string_literal("else_block"), llvm_function); + + auto enum_fields = enum_type->enumerator.fields; + + auto switch_instruction = LLVMBuildSwitch(module->llvm.builder, llvm_argument, else_block, enum_fields.length); + auto backing_type = enum_type->llvm.abi; + assert(backing_type); + auto u64_type = uint64(module)->llvm.abi; + + for (u64 i = 0; i < enum_fields.length; i += 1) + { + auto& field = enum_fields[i]; + auto* case_block = llvm_context_create_basic_block(module->llvm.context, string_literal("case_block"), llvm_function); + auto case_value = LLVMConstInt(backing_type, field.value, false); + + LLVMAddCase(switch_instruction, case_value, case_block); + LLVMPositionBuilderAtEnd(module->llvm.builder, case_block); + + LLVMValueRef indices[] = { + LLVMConstNull(u64_type), + LLVMConstInt(u64_type, i, false), + }; + + auto case_value_result_pointer = create_gep(module, { + .type = enum_name_array_global->variable.type->llvm.memory, + .pointer = enum_name_array_global->variable.storage->llvm, + .indices = array_to_slice(indices), + }); + + auto case_value_result = create_load(module, { + .type = string_type, + .pointer = case_value_result_pointer, + }); + + create_store(module, { + .source = case_value_result, + .destination = alloca, + .type = string_type, + }); + + LLVMBuildBr(module->llvm.builder, return_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, else_block); + LLVMBuildUnreachable(module->llvm.builder); + + LLVMPositionBuilderAtEnd(module->llvm.builder, return_block); + auto function_result = create_load(module, { + .type = string_type, + .pointer = alloca, + }); + + LLVMBuildRet(module->llvm.builder, function_result); + + if (current_block) + { + LLVMPositionBuilderAtEnd(module->llvm.builder, current_block); + } + + enum_to_string = llvm_function; + enum_type->enumerator.enum_to_string_function = enum_to_string; + } + + assert(enum_to_string); + + value_type = string_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(); + } + + if (expected_type->id != TypeId::integer) + { + report_error(); + } + + auto size = get_byte_size(unary_type); + auto max_value = integer_max_value(unary_type->integer.bit_count, unary_type->integer.is_signed); + if (size > max_value) + { + report_error(); + } + + value_type = expected_type; + } 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]; + analyze_type(module, call_argument, argument_type); + check_types(module, argument_type, call_argument->type); + } + + for (u64 i = semantic_argument_types.length; i < call_arguments.length; i += 1) + { + auto* call_argument = call_arguments[i]; + analyze_type(module, call_argument, 0); + } + + 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: + { + auto values = value->array_initialization.values; + if (expected_type) + { + if (expected_type->array.element_count == 0) + { + expected_type->array.element_count = 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 + { + if (expected_type->array.element_count != values.length) + { + report_error(); + } + } + + bool is_constant = true; + + auto* element_type = expected_type->array.element_type; + for (auto value : 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 + { + if (values.length == 0) + { + report_error(); + } + + Type* expected_type = 0; + bool is_constant = true; + + for (auto value : values) + { + analyze_type(module, value, expected_type); + + is_constant = is_constant && value->is_constant(); + + auto value_type = value->type; + if (expected_type) + { + if (expected_type != value_type) + { + report_error(); + } + } + else + { + assert(value_type); + expected_type = value_type; + } + } + + if (!expected_type) + { + report_error(); + } + + auto element_type = expected_type; + auto element_count = values.length; + + auto array_type = get_array_type(module, element_type, element_count); + value_type = array_type; + + if (value->kind == ValueKind::left) + { + value_type = get_pointer_type(module, array_type); + } + } + } 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: + { + auto slice_type = pointer_element_type; + if (!slice_type->structure.is_slice) + { + report_error(); + } + auto slice_pointer_type = slice_type->structure.fields[0].type; + assert(slice_pointer_type->id == TypeId::pointer); + element_type = slice_pointer_type->pointer.element_type; + } break; + case TypeId::pointer: + { + element_type = pointer_element_type->pointer.element_type; + } 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; + case ValueId::trap: + { + value_type = noreturn_type(module); + } break; + case ValueId::field_access: + { + auto aggregate = value->field_access.aggregate; + auto field_name = value->field_access.field_name; + analyze_type(module, aggregate, 0); + + if (aggregate->kind == ValueKind::right) + { + report_error(); + } + + auto aggregate_type = aggregate->type; + if (aggregate_type->id != TypeId::pointer) + { + report_error(); + } + + auto aggregate_element_type = aggregate_type->pointer.element_type; + Type* real_aggregate_type = aggregate_element_type->id == TypeId::pointer ? aggregate_element_type->pointer.element_type : aggregate_element_type; + auto resolved_aggregate_type = resolve_alias(module, real_aggregate_type); + + switch (resolved_aggregate_type->id) + { + case TypeId::structure: + { + Field* result_field = 0; + auto fields = resolved_aggregate_type->structure.fields; + for (u64 i = 0; i < fields.length; i += 1) + { + auto* field = &fields[i]; + if (field_name.equal(field->name)) + { + result_field = field; + break; + } + } + + if (!result_field) + { + report_error(); + } + + auto field_type = result_field->type; + value_type = value->kind == ValueKind::left ? get_pointer_type(module, field_type) : field_type; + } break; + case TypeId::union_type: + { + UnionField* result_field = 0; + auto fields = resolved_aggregate_type->union_type.fields; + for (u64 i = 0; i < fields.length; i += 1) + { + auto* field = &fields[i]; + if (field_name.equal(field->name)) + { + result_field = field; + break; + } + } + + if (!result_field) + { + report_error(); + } + + auto field_type = result_field->type; + value_type = value->kind == ValueKind::left ? get_pointer_type(module, field_type) : field_type; + } break; + case TypeId::bits: + { + if (value->kind == ValueKind::left) + { + report_error(); + } + + auto fields = resolved_aggregate_type->bits.fields; + u64 i; + for (i = 0; i < fields.length; i += 1) + { + auto field = fields[i]; + if (field_name.equal(field.name)) + { + break; + } + } + + if (i == fields.length) + { + report_error(); + } + + assert(value->kind == ValueKind::right); + + auto field = fields[i]; + value_type = field.type; + } break; + case TypeId::array: + { + if (!field_name.equal(string_literal("length"))) + { + report_error(); + } + + if (expected_type) + { + if (expected_type->id != TypeId::integer) + { + report_error(); + } + + value_type = expected_type; + } + else + { + trap(); + } + } break; + case TypeId::pointer: report_error(); // Double indirection is not allowed + default: report_error(); + } + + assert(value_type); + + typecheck(module, expected_type, value_type); + } break; + case ValueId::slice_expression: + { + auto array_like = value->slice_expression.array_like; + auto start = value->slice_expression.start; + auto end = value->slice_expression.end; + + if (array_like->kind != ValueKind::left) + { + report_error(); + } + + analyze_type(module, array_like, 0); + + auto pointer_type = array_like->type; + if (pointer_type->id != TypeId::pointer) + { + report_error(); + } + + Type* sliceable_type = pointer_type->pointer.element_type; + + Type* element_type = 0; + + switch (sliceable_type->id) + { + case TypeId::pointer: + { + element_type = sliceable_type->pointer.element_type; + } break; + case TypeId::structure: + { + if (!sliceable_type->structure.is_slice) + { + report_error(); + } + auto slice_pointer_type = sliceable_type->structure.fields[0].type; + assert(slice_pointer_type->id == TypeId::pointer); + auto slice_element_type = slice_pointer_type->pointer.element_type; + element_type = slice_element_type; + } break; + case TypeId::array: + { + element_type = sliceable_type->array.element_type; + } break; + default: unreachable(); + } + + assert(element_type); + + auto slice_type = get_slice_type(module, element_type); + typecheck(module, expected_type, slice_type); + + auto index_type = uint64(module); + + Value* indices[] = { start, end }; + + for (auto index : indices) + { + if (index) + { + analyze_type(module, index, index_type); + + if (index->type->id != TypeId::integer) + { + report_error(); + } + } + } + + value_type = slice_type; + } break; + case ValueId::string_literal: + { + auto slice_type = get_slice_type(module, uint8(module)); + typecheck(module, expected_type, slice_type); + value_type = slice_type; + } break; + case ValueId::va_start: + { + auto va_list_type = get_va_list_type(module); + typecheck(module, expected_type, va_list_type); + value_type = va_list_type; + } break; + case ValueId::va_arg: + { + analyze_type(module, value->va_arg.va_list, get_pointer_type(module, get_va_list_type(module))); + value_type = value->va_arg.type; + typecheck(module, expected_type, value_type); + } break; + case ValueId::aggregate_initialization: + { + if (!expected_type) + { + report_error(); + } + + auto resolved_type = resolve_alias(module, expected_type); + value_type = resolved_type; + + assert(!value->aggregate_initialization.is_constant); + bool is_constant = true; + auto values = value->aggregate_initialization.values; + auto names = value->aggregate_initialization.names; + + switch (resolved_type->id) + { + case TypeId::structure: + { + bool is_ordered = true; + auto fields = resolved_type->structure.fields; + + for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + { + auto value = values[initialization_index]; + auto name = names[initialization_index]; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + { + auto& field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + if (declaration_index == fields.length) + { + report_error(); + } + + is_ordered = is_ordered && declaration_index == initialization_index; + + auto field = fields[declaration_index]; + auto declaration_type = field.type; + analyze_type(module, value, declaration_type); + is_constant = is_constant && value->is_constant(); + } + + value->aggregate_initialization.is_constant = is_constant && is_ordered; + } break; + case TypeId::bits: + { + auto fields = resolved_type->bits.fields; + + + assert(values.length == names.length); + + for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + { + auto value = values[initialization_index]; + auto name = names[initialization_index]; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + { + auto& field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + if (declaration_index == fields.length) + { + report_error(); + } + + auto field = fields[declaration_index]; + auto declaration_type = field.type; + analyze_type(module, value, declaration_type); + is_constant = is_constant && value->is_constant(); + } + + value->aggregate_initialization.is_constant = is_constant; + } break; + case TypeId::union_type: + { + if (values.length != 1) + { + report_error(); + } + + auto initialization_value = values[0]; + assert(names.length == 1); + auto initialization_name = names[0]; + + u64 i; + auto fields = resolved_type->union_type.fields; + for (i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + if (initialization_name.equal(field.name)) + { + break; + } + } + + if (i == fields.length) + { + report_error(); + } + + auto field = &fields[i]; + analyze_type(module, initialization_value, field->type); + + value_type = expected_type; + } break; + default: report_error(); + } + } break; + case ValueId::zero: + { + if (!expected_type) + { + report_error(); + } + + if (expected_type->id == TypeId::void_type || expected_type->id == TypeId::noreturn) + { + report_error(); + } + + value_type = expected_type; + } break; + case ValueId::select: + { + auto condition = value->select.condition; + auto true_value = value->select.true_value; + auto false_value = value->select.false_value; + analyze_type(module, condition, 0); + auto is_boolean = false; + analyze_binary_type(module, true_value, false_value, is_boolean, expected_type); + + auto left_type = true_value->type; + auto right_type = false_value->type; + check_types(module, left_type, right_type); + + assert(left_type == right_type); + auto result_type = left_type; + typecheck(module, expected_type, result_type); + + value_type = result_type; + } break; + case ValueId::unreachable: + { + value_type = noreturn_type(module); + } break; + case ValueId::string_to_enum: + { + auto enum_type = value->string_to_enum.type; + auto enum_string_value = value->string_to_enum.string; + if (enum_type->id != TypeId::enumerator) + { + report_error(); + } + + if (!enum_type->enumerator.string_to_enum_function) + { + resolve_type_in_place(module, enum_type); + + auto fields = enum_type->enumerator.fields; + auto array_element_count = fields.length; + + auto insert_block = LLVMGetInsertBlock(module->llvm.builder); + + auto u1_type = uint1(module); + auto u8_type = uint8(module); + auto u64_type = uint64(module); + resolve_type_in_place(module, u1_type); + resolve_type_in_place(module, u8_type); + resolve_type_in_place(module, u64_type); + + auto u64_zero = LLVMConstNull(u64_type->llvm.abi); + + auto enum_alignment = get_byte_alignment(enum_type); + auto enum_size = get_byte_size(enum_type); + auto byte_size = align_forward(enum_size + 1, enum_alignment); + + auto struct_fields = arena_allocate(module->arena, 2); + + struct_fields[0] = { + .name = string_literal("enum_value"), + .type = enum_type, + }; + + struct_fields[1] = { + .name = string_literal("is_valid"), + .type = u1_type, + .offset = enum_size, + }; + + auto struct_type = type_allocate_init(module, { + .structure = { + .fields = struct_fields, + .byte_size = byte_size, + .byte_alignment = enum_alignment, + }, + .id = TypeId::structure, + .name = string_literal("string_to_enum"), + }); + resolve_type_in_place(module, struct_type); + + LLVMTypeRef argument_types[] = { module->llvm.pointer_type, u64_type->llvm.abi }; + auto llvm_function_type = LLVMFunctionType(struct_type->llvm.abi, argument_types, array_length(argument_types), false); + auto slice_struct_type = get_slice_type(module, u8_type); + + String name_parts[] = { + string_literal("string_to_enum."), + enum_type->name, + }; + auto function_name = arena_join_string(module->arena, array_to_slice(name_parts)); + auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, LLVMInternalLinkage, 0, function_name); + LLVMSetFunctionCallConv(llvm_function, LLVMFastCallConv); + + auto name_array_global = get_enum_name_array_global(module, enum_type); + + auto enum_value_type = enum_type->llvm.memory; + + LLVMValueRef value_constant_buffer[64]; + for (u32 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto global_value = LLVMConstInt(enum_value_type, field.value, false); + value_constant_buffer[i] = global_value; + } + + auto value_array = LLVMConstArray2(enum_value_type, value_constant_buffer, array_element_count); + auto value_array_variable_type = LLVMArrayType2(enum_value_type, array_element_count); + auto is_constant = true; + LLVMValueRef before = 0; + LLVMThreadLocalMode thread_local_mode = LLVMNotThreadLocal; + unsigned address_space = 0; + auto externally_initialized = false; + auto value_array_variable = llvm_module_create_global_variable(module->llvm.module, value_array_variable_type, is_constant, LLVMInternalLinkage, value_array, string_literal("value.array.enum"), before, thread_local_mode, address_space, externally_initialized); + LLVMSetAlignment(value_array_variable, enum_alignment); + LLVMSetUnnamedAddress(value_array_variable, LLVMGlobalUnnamedAddr); + + 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"), llvm_function); + auto* loop_entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("loop.entry"), llvm_function); + auto* loop_body_block = llvm_context_create_basic_block(module->llvm.context, string_literal("loop.body"), llvm_function); + auto* loop_exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("loop.exit"), llvm_function); + + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + LLVMValueRef arguments[2]; + LLVMGetParams(llvm_function, arguments); + + auto return_value_alloca = create_alloca(module, { + .type = enum_type, + .name = string_literal("retval"), + }); + + auto return_boolean_alloca = create_alloca(module, { + .type = u8_type, + .name = string_literal("retbool"), + }); + + auto index_alloca = create_alloca(module, { + .type = u64_type, + .name = string_literal("index"), + }); + + create_store(module, { + .source = u64_zero, + .destination = index_alloca, + .type = u64_type, + }); + + auto slice_pointer = arguments[0]; + auto slice_length = arguments[1]; + LLVMBuildBr(module->llvm.builder, loop_entry_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, loop_entry_block); + auto index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + }); + auto loop_compare = LLVMBuildICmp(module->llvm.builder, LLVMIntULT, index_load, LLVMConstInt(u64_type->llvm.abi, array_element_count, false), ""); + LLVMBuildCondBr(module->llvm.builder, loop_compare, loop_body_block, loop_exit_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, loop_body_block); + auto body_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + }); + + LLVMValueRef indices[] = { + u64_zero, + body_index_load, + }; + auto array_element_pointer = create_gep(module, { + .type = name_array_global->variable.type->llvm.memory, + .pointer = name_array_global->variable.storage->llvm, + .indices = array_to_slice(indices), + }); + + auto element_length_pointer = LLVMBuildStructGEP2(module->llvm.builder, slice_struct_type->llvm.abi, array_element_pointer, 1, ""); + auto element_length = create_load(module, { + .type = u64_type, + .pointer = element_length_pointer, + }); + + auto length_comparison = LLVMBuildICmp(module->llvm.builder, LLVMIntEQ, slice_length, element_length, ""); + + auto* length_match_block = llvm_context_create_basic_block(module->llvm.context, string_literal("length.match"), llvm_function); + auto* length_mismatch_block = llvm_context_create_basic_block(module->llvm.context, string_literal("length.mismatch"), llvm_function); + LLVMBuildCondBr(module->llvm.builder, length_comparison, length_match_block, length_mismatch_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, length_match_block); + + auto s32_type = sint32(module); + resolve_type_in_place(module, s32_type); + + LLVMValueRef memcmp = module->llvm.memcmp; + if (!memcmp) + { + memcmp = LLVMGetNamedFunction(module->llvm.module, "memcmp"); + if (!memcmp) + { + LLVMTypeRef arguments[] = { + module->llvm.pointer_type, + module->llvm.pointer_type, + u64_type->llvm.abi, + }; + auto llvm_function_type = LLVMFunctionType(s32_type->llvm.abi, arguments, array_length(arguments), false); + auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, LLVMExternalLinkage, address_space, string_literal("memcmp")); + memcmp = llvm_function; + } + + module->llvm.memcmp = memcmp; + } + + assert(memcmp); + assert(module->llvm.memcmp); + + auto length_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + }); + + LLVMValueRef length_indices[] = { u64_zero, length_index_load }; + auto length_array_element_pointer = create_gep(module, { + .type = name_array_global->variable.type->llvm.memory, + .pointer = name_array_global->variable.storage->llvm, + .indices = array_to_slice(length_indices), + }); + + auto element_pointer_pointer = LLVMBuildStructGEP2(module->llvm.builder, slice_struct_type->llvm.abi, length_array_element_pointer, 0, ""); + auto element_pointer = create_load(module, { + .type = get_pointer_type(module, u8_type), + .pointer = element_pointer_pointer, + }); + + LLVMValueRef memcmp_arguments[] = { + slice_pointer, + element_pointer, + slice_length, + }; + auto memcmp_return_result = LLVMBuildCall2(module->llvm.builder, LLVMGlobalGetValueType(memcmp), memcmp, memcmp_arguments, array_length(memcmp_arguments), ""); + auto content_comparison = LLVMBuildICmp(module->llvm.builder, LLVMIntEQ, memcmp_return_result, LLVMConstNull(s32_type->llvm.abi), ""); + auto* content_match_block = llvm_context_create_basic_block(module->llvm.context, string_literal("content.match"), llvm_function); + LLVMBuildCondBr(module->llvm.builder, content_comparison, content_match_block, length_mismatch_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, content_match_block); + + auto content_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + }); + + LLVMValueRef value_array_indices[] = { + u64_zero, + content_index_load, + }; + auto value_array_element_pointer = create_gep(module, { + .type = value_array_variable_type, + .pointer = value_array_variable, + .indices = array_to_slice(value_array_indices), + }); + + auto enum_value_load = create_load(module, { + .type = enum_type, + .pointer = value_array_element_pointer, + }); + + create_store(module, { + .source = enum_value_load, + .destination = return_value_alloca, + .type = enum_type, + }); + + create_store(module, { + .source = LLVMConstInt(u8_type->llvm.abi, 1, false), + .destination = return_boolean_alloca, + .type = u8_type, + }); + + LLVMBuildBr(module->llvm.builder, return_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, length_mismatch_block); + + auto inc_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + }); + + auto inc = LLVMBuildAdd(module->llvm.builder, inc_index_load, LLVMConstInt(u64_type->llvm.abi, 1, false), ""); + + create_store(module, { + .source = inc, + .destination = index_alloca, + .type = u64_type, + }); + + LLVMBuildBr(module->llvm.builder, loop_entry_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, loop_exit_block); + + create_store(module, { + .source = LLVMConstNull(enum_type->llvm.memory), + .destination = return_value_alloca, + .type = enum_type, + }); + create_store(module, { + .source = LLVMConstNull(u8_type->llvm.abi), + .destination = return_boolean_alloca, + .type = u8_type, + }); + LLVMBuildBr(module->llvm.builder, return_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, return_block); + auto value_load = create_load(module, { + .type = enum_type, + .pointer = return_value_alloca, + .kind = TypeKind::memory, + }); + + auto return_value = LLVMBuildInsertValue(module->llvm.builder, LLVMGetPoison(struct_type->llvm.memory), value_load, 0, ""); + auto bool_load = create_load(module, { + .type = u8_type, + .pointer = return_boolean_alloca, + }); + + return_value = LLVMBuildInsertValue(module->llvm.builder, return_value, bool_load, 1, ""); + + LLVMBuildRet(module->llvm.builder, return_value); + + // End of scope + LLVMPositionBuilderAtEnd(module->llvm.builder, insert_block); + + enum_type->enumerator.string_to_enum_function = llvm_function; + enum_type->enumerator.string_to_enum_struct_type = struct_type; + } + + auto struct_type = enum_type->enumerator.string_to_enum_struct_type; + assert(struct_type); + + typecheck(module, expected_type, struct_type); + + auto string_type = get_slice_type(module, uint8(module)); + + analyze_type(module, enum_string_value, string_type); + value_type = struct_type; + } break; + case ValueId::undefined: + { + if (!expected_type) + { + report_error(); + } + + if (expected_type->id == TypeId::void_type || expected_type->id == TypeId::noreturn) + { + 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 void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind); + +fn bool type_is_integer_backing(Type* type) +{ + switch (type->id) + { + case TypeId::enumerator: + case TypeId::integer: + case TypeId::bits: + case TypeId::pointer: + return true; + default: + return false; + } +} + +struct ValueTypePair +{ + LLVMValueRef value; + Type* type; +}; + +fn ValueTypePair enter_struct_pointer_for_coerced_access(Module* module, LLVMValueRef source_value, Type* source_type, u64 destination_size) +{ + unused(module); + assert(source_type->id == TypeId::structure && source_type->structure.fields.length > 0); + auto first_field_type = source_type->structure.fields[0].type; + auto first_field_size = get_byte_size(first_field_type); + auto source_size = get_byte_size(source_type); + + if (!(first_field_size < destination_size && first_field_size < source_size)) + { + trap(); + } + + return { source_value, source_type }; +} + +fn LLVMValueRef create_coerced_load(Module* module, LLVMValueRef source, Type* source_type, Type* destination_type) +{ + LLVMValueRef result = 0; + + if (type_is_abi_equal(module, source_type, destination_type)) + { + trap(); + } + else + { + auto destination_size = get_byte_size(destination_type); + if (source_type->id == TypeId::structure) + { + auto src = enter_struct_pointer_for_coerced_access(module, source, source_type, destination_size); + source = src.value; + source_type = src.type; + } + + if (type_is_integer_backing(source_type) && type_is_integer_backing(destination_type)) + { + trap(); + } + else + { + auto source_size = get_byte_size(source_type); + + auto is_source_type_scalable = false; + auto is_destination_type_scalable = false; + + if (!is_source_type_scalable && !is_destination_type_scalable && source_size >= destination_size) + { + result = create_load(module, LoadOptions{ .type = destination_type, .pointer = source }); + } + else + { + trap(); + } + } + } + + assert(result); + + return result; +} + +fn void create_coerced_store(Module* module, LLVMValueRef source_value, Type* source_type, LLVMValueRef destination_value, Type* destination_type, u64 destination_size, bool destination_volatile) +{ + unused(destination_volatile); + + auto source_size = get_byte_size(source_type); + + if (!type_is_abi_equal(module, source_type, destination_type)) + { + auto r = enter_struct_pointer_for_coerced_access(module, destination_value, destination_type, source_size); + destination_value = r.value; + destination_type = r.type; + } + + auto is_scalable = false; + + if (is_scalable || source_size <= destination_size) + { + auto destination_alignment = get_byte_alignment(destination_type); + + if (source_type->id == TypeId::integer && destination_type->id == TypeId::pointer && source_size == align_forward(destination_size, destination_alignment)) + { + trap(); + } + else if (source_type->id == TypeId::structure) + { + auto fields = source_type->structure.fields; + for (u32 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto gep = LLVMBuildStructGEP2(module->llvm.builder, source_type->llvm.abi, destination_value, i, ""); + auto field_value = LLVMBuildExtractValue(module->llvm.builder, source_value, i, ""); + create_store(module, { + .source = field_value, + .destination = gep, + .type = field.type, + .alignment = destination_alignment, + }); + } + } + else + { + create_store(module, StoreOptions{ + .source = source_value, + .destination = destination_value, + .type = destination_type, + .alignment = destination_alignment, + }); + } + } + else if (type_is_integer_backing(source_type)) + { + trap(); + } + else + { + trap(); + } +} + +fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, Type* 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]; + LLVMTypeRef llvm_abi_argument_type_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; + + 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: + { + // TODO: handle edge cases: + // - virtual function pointer thunk + // - return alloca already exists + LLVMValueRef pointer = 0; + auto semantic_return_type = return_abi.semantic_type; + if (left_llvm) + { + assert(left_type->pointer.element_type == semantic_return_type); + pointer = left_llvm; + } + else + { + trap(); + } + assert(pointer); + + auto has_sret = return_abi.flags.kind == AbiKind::indirect; + if (has_sret) + { + auto void_ty = void_type(module); + llvm_abi_argument_value_buffer[abi_argument_count] = pointer; + abi_argument_type_buffer[abi_argument_count] = void_ty; + llvm_abi_argument_type_buffer[abi_argument_count] = void_ty->llvm.abi; + abi_argument_count += 1; + llvm_indirect_return_value = pointer; + } + else if (return_abi.flags.kind == AbiKind::in_alloca) + { + trap(); + } + else + { + trap(); + } + } break; + default: break; + } + + auto available_registers = raw_function_type->function.available_registers; + + auto declaration_semantic_argument_count = raw_function_type->function.semantic_argument_types.length; + for (u64 call_argument_index = 0; call_argument_index < call_arguments.length; call_argument_index += 1) + { + auto is_named_argument = call_argument_index < declaration_semantic_argument_count; + auto semantic_call_argument_value = call_arguments[call_argument_index]; + + Type* semantic_argument_type; + AbiInformation argument_abi; + Slice llvm_abi_argument_type_buffer_slice = array_to_slice(llvm_abi_argument_type_buffer); + Slice abi_argument_type_buffer_slice = array_to_slice(abi_argument_type_buffer); + + if (is_named_argument) + { + argument_abi = raw_function_type->function.argument_abis[call_argument_index]; + semantic_argument_type = argument_abi.semantic_type; + } + else + { + semantic_argument_type = semantic_call_argument_value->type; + argument_abi = abi_system_v_classify_argument(module, &available_registers.system_v, llvm_abi_argument_type_buffer_slice, abi_argument_type_buffer_slice, { + .type = semantic_argument_type, + .abi_start = abi_argument_count, + .is_named_argument = false, + }); + } + + if (get_byte_size(semantic_argument_type) > 60 && argument_abi.flags.kind != AbiKind::indirect) + { + trap(); + } + + resolve_type_in_place(module, semantic_argument_type); + + if (is_named_argument) + { + auto llvm_abi_argument_types = llvm_abi_argument_type_buffer_slice(argument_abi.abi_start)(0, argument_abi.abi_count); + auto destination_abi_argument_types = abi_argument_type_buffer_slice(argument_abi.abi_start)(0, argument_abi.abi_count); + auto source_abi_argument_types = raw_function_type->function.abi_argument_types(argument_abi.abi_start)(0, argument_abi.abi_count); + for (u16 i = 0; i < argument_abi.abi_count; i += 1) + { + llvm_abi_argument_types[i] = source_abi_argument_types[i]->llvm.abi; + destination_abi_argument_types[i] = source_abi_argument_types[i]; + } + } + + argument_abi_buffer[call_argument_index] = argument_abi; + + if (argument_abi.padding.type) + { + trap(); + } + + assert(abi_argument_count == argument_abi.abi_start); + auto argument_abi_kind = argument_abi.flags.kind; + switch (argument_abi_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); + + if (coerce_to_type->id != TypeId::structure && type_is_abi_equal(module, semantic_argument_type, coerce_to_type) && argument_abi.attributes.direct.offset == 0) + { + emit_value(module, semantic_call_argument_value, TypeKind::memory); + + auto evaluation_kind = get_evaluation_kind(argument_abi.semantic_type); + Value* v; + switch (evaluation_kind) + { + case EvaluationKind::scalar: v = semantic_call_argument_value; break; + case EvaluationKind::aggregate: trap(); + case EvaluationKind::complex: trap(); + } + + if (!type_is_abi_equal(module, coerce_to_type, v->type)) + { + trap(); + } + + llvm_abi_argument_value_buffer[abi_argument_count] = v->llvm; + abi_argument_count += 1; + } + else + { + if (coerce_to_type->id == TypeId::structure && argument_abi.flags.kind == AbiKind::direct && !argument_abi.flags.can_be_flattened) + { + trap(); + } + + auto evaluation_kind = get_evaluation_kind(semantic_argument_type); + Value* src = 0; + switch (evaluation_kind) + { + case EvaluationKind::scalar: trap(); + case EvaluationKind::aggregate: src = semantic_call_argument_value; break; + case EvaluationKind::complex: trap(); + } + assert(src); + + if (argument_abi.attributes.direct.offset != 0) + { + trap(); + } + + if (coerce_to_type->id == TypeId::structure && argument_abi.flags.kind == AbiKind::direct && argument_abi.flags.can_be_flattened) + { + auto source_type_is_scalable = false; + if (source_type_is_scalable) + { + trap(); + } + else + { + if (src->kind == ValueKind::right) + { + if (src->id == ValueId::variable_reference) + { + src->type = 0; + src->kind = ValueKind::left; + analyze_type(module, src, 0); + } + } + + emit_value(module, semantic_call_argument_value, TypeKind::memory); + auto destination_size = get_byte_size(coerce_to_type); + auto source_size = get_byte_size(argument_abi.semantic_type); + auto alignment = get_byte_alignment(argument_abi.semantic_type); + + LLVMValueRef source = src->llvm; + if (source_size < destination_size) + { + trap(); + } + + auto coerce_fields = coerce_to_type->structure.fields; + + // TODO: + assert(argument_abi.attributes.direct.offset == 0); + + switch (semantic_call_argument_value->kind) + { + case ValueKind::left: + { + for (u32 i = 0; i < (u32)coerce_fields.length; i += 1) + { + auto& field = coerce_fields[i]; + auto gep = LLVMBuildStructGEP2(module->llvm.builder, coerce_to_type->llvm.memory, source, i, ""); + auto maybe_undef = false; + if (maybe_undef) + { + trap(); + } + + auto load = create_load(module, { + .type = field.type, + .pointer = gep, + .alignment = alignment, + }); + llvm_abi_argument_value_buffer[abi_argument_count] = load; + abi_argument_count += 1; + } + } break; + case ValueKind::right: + { + if (type_is_abi_equal(module, coerce_to_type, semantic_argument_type)) + { + for (u32 i = 0; i < (u32)coerce_fields.length; i += 1) + { + llvm_abi_argument_value_buffer[abi_argument_count] = LLVMBuildExtractValue(module->llvm.builder, source, i, ""); + abi_argument_count += 1; + } + } + else + { + switch (semantic_call_argument_value->id) + { + case ValueId::aggregate_initialization: + { + auto is_constant = semantic_call_argument_value->aggregate_initialization.is_constant; + + if (is_constant) + { + 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, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("conststruct"), before, thread_local_mode, address_space, externally_initialized); + LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); + + auto alignment = get_byte_alignment(semantic_argument_type); + LLVMSetAlignment(global, alignment); + + for (u32 i = 0; i < coerce_fields.length; i += 1) + { + auto gep = LLVMBuildStructGEP2(module->llvm.builder, coerce_to_type->llvm.abi, global, i, ""); + auto& field = coerce_fields[i]; + auto maybe_undef = false; + if (maybe_undef) + { + trap(); + } + + auto load = create_load(module, { .type = field.type, .pointer = gep, .alignment = alignment }); + + llvm_abi_argument_value_buffer[abi_argument_count] = load; + abi_argument_count += 1; + } + } + else + { + trap(); + } + } break; + default: trap(); + } + } + } break; + } + } + } + else + { + assert(argument_abi.abi_count == 1); + auto destination_type = coerce_to_type; + + LLVMValueRef v = 0; + switch (src->id) + { + case ValueId::zero: + { + trap(); + } break; + default: + { + if (src->type->id != TypeId::pointer) + { + assert(src->kind == ValueKind::right); + assert(src->type->id == TypeId::structure); + auto type = src->type; + assert(src->kind == ValueKind::right); + src->type = 0; + src->kind = ValueKind::left; + analyze_type(module, src, get_pointer_type(module, type)); + } + + assert(src->type->id == TypeId::pointer); + assert(src->type->llvm.abi == module->llvm.pointer_type); + emit_value(module, src, TypeKind::memory); + + assert(src->type->id == TypeId::pointer); + auto source_type = src->type->pointer.element_type; + assert(source_type == argument_abi.semantic_type); + auto load = create_coerced_load(module, src->llvm, source_type, destination_type); + + auto is_cmse_ns_call = false; + if (is_cmse_ns_call) + { + trap(); + } + + auto maybe_undef = false; + if (maybe_undef) + { + trap(); + } + + v = load; + } break; + } + assert(v); + + llvm_abi_argument_value_buffer[abi_argument_count] = v; + abi_argument_count += 1; + } + } + } break; + case AbiKind::indirect: + case AbiKind::indirect_aliased: + { + auto evaluation_kind = get_evaluation_kind(semantic_argument_type); + auto do_continue = false; + if (evaluation_kind == EvaluationKind::aggregate) + { + auto same_address_space = true; + assert(argument_abi.abi_start >= raw_function_type->function.abi_argument_types.length || same_address_space); + + // TODO: handmade code, may contain bugs + assert(argument_abi.abi_count == 1); + auto abi_argument_type = abi_argument_type_buffer[argument_abi.abi_start]; + + if (abi_argument_type == semantic_call_argument_value->type) + { + trap(); + } + else if (abi_argument_type->id == TypeId::pointer && abi_argument_type->pointer.element_type == semantic_call_argument_value->type) + { + auto is_constant = semantic_call_argument_value->is_constant(); + + if (is_constant) + { + emit_value(module, semantic_call_argument_value, 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, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("conststruct"), before, thread_local_mode, address_space, externally_initialized); + LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); + + auto alignment = get_byte_alignment(semantic_argument_type); + LLVMSetAlignment(global, alignment); + + llvm_abi_argument_value_buffer[abi_argument_count] = global; + abi_argument_count += 1; + do_continue = true; + } + else + { + switch (semantic_call_argument_value->id) + { + case ValueId::variable_reference: + { + auto pointer_type = get_pointer_type(module, semantic_call_argument_value->type); + semantic_call_argument_value->type = 0; + semantic_call_argument_value->kind = ValueKind::left; + analyze_value(module, semantic_call_argument_value, pointer_type, TypeKind::memory); + llvm_abi_argument_value_buffer[abi_argument_count] = semantic_call_argument_value->llvm; + abi_argument_count += 1; + do_continue = true; + } break; + default: + { + trap(); + } break; + } + } + } + else + { + trap(); + } + } + + if (!do_continue) + { + trap(); + } + + } break; + case AbiKind::ignore: unreachable(); + default: unreachable(); + } + + assert(abi_argument_count == argument_abi.abi_start + argument_abi.abi_count); + } + + 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); + Slice argument_abis = { .pointer = argument_abi_buffer, .length = call_arguments.length }; + 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 = argument_abis, + .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: + { + assert(return_abi.semantic_type == noreturn_type(module) || return_abi.semantic_type == void_type(module)); + return llvm_call; + } 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(); + } + } + + // TODO: if + auto fixed_vector_type = false; + if (fixed_vector_type) + { + trap(); + } + + LLVMValueRef coerce_alloca = 0; + + if (left_llvm) + { + assert(left_type->pointer.element_type == return_abi.semantic_type); + coerce_alloca = left_llvm; + } + else + { + coerce_alloca = create_alloca(module, { + .type = return_abi.semantic_type, + .name = string_literal("coerce"), + }); + } + + LLVMValueRef destination_pointer = coerce_alloca; + if (return_abi.attributes.direct.offset != 0) + { + trap(); + } + + auto destination_type = return_abi.semantic_type; + + assert(return_abi.semantic_type->id == TypeId::structure); + if (return_abi.semantic_type->structure.fields.length > 0) + { + auto source_value = llvm_call; + auto source_type = raw_function_type->function.abi_return_type; + auto destination_size = get_byte_size(destination_type); + auto left_destination_size = destination_size - return_abi.attributes.direct.offset; + auto is_destination_volatile = false; + create_coerced_store(module, source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile); + } + else + { + trap(); + } + + assert(coerce_alloca); + if (left_llvm) + { + assert(destination_pointer == left_llvm); + return destination_pointer; + } + else + { + switch (value->kind) + { + case ValueKind::right: return create_load(module, { .type = destination_type, .pointer = destination_pointer }); + case ValueKind::left: trap(); + } + } + } break; + case AbiKind::indirect: + { + assert(llvm_indirect_return_value); + return llvm_indirect_return_value; + } break; + default: unreachable(); + } + } break; + default: unreachable(); + } +} + +fn LLVMValueRef emit_intrinsic_call(Module* module, IntrinsicIndex index, Slice argument_types, Slice argument_values) +{ + auto intrinsic_id = module->llvm.intrinsic_table[(backing_type(IntrinsicIndex))index]; + auto intrinsic_function = LLVMGetIntrinsicDeclaration(module->llvm.module, intrinsic_id.n, argument_types.pointer, argument_types.length); + auto intrinsic_function_type = LLVMIntrinsicGetType(module->llvm.context, intrinsic_id.n, argument_types.pointer, argument_types.length); + auto call = LLVMBuildCall2(module->llvm.builder, intrinsic_function_type, intrinsic_function, argument_values.pointer, argument_values.length, ""); + return call; +} + +fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type, TypeKind type_kind) +{ + unused(left_llvm); + unused(left_type); + unused(type_kind); + + switch (value->id) + { + case ValueId::field_access: + { + auto aggregate = value->field_access.aggregate; + auto field_name = value->field_access.field_name; + + emit_value(module, aggregate, TypeKind::memory); + + assert(aggregate->kind == ValueKind::left); + auto base_type = aggregate->type; + assert(base_type->id == TypeId::pointer); + + auto base_child_type = base_type->pointer.element_type; + auto pointer_type = base_child_type->id == TypeId::pointer ? base_child_type->pointer.element_type : base_type; + assert(pointer_type->id == TypeId::pointer); + auto element_type = pointer_type->pointer.element_type; + resolve_type_in_place(module, element_type); + + auto resolved_element_type = resolve_alias(module, element_type); + + LLVMValueRef v; + if (pointer_type == base_type) + { + v = create_load(module, { + .type = pointer_type, + .pointer = aggregate->llvm, + }); + } + else + { + v = aggregate->llvm; + } + + switch (resolved_element_type->id) + { + case TypeId::structure: + case TypeId::union_type: + { + struct StructLikeFieldAccess + { + Type* type; + u32 field_index; + LLVMTypeRef struct_type; + }; + + StructLikeFieldAccess field_access; + switch (resolved_element_type->id) + { + case TypeId::structure: + { + u32 field_index; + auto fields = resolved_element_type->structure.fields; + auto field_count = (u32)fields.length; + for (field_index = 0; field_index < field_count; field_index += 1) + { + auto& field = fields[field_index]; + if (field_name.equal(field.name)) + { + break; + } + } + + if (field_index == field_count) + { + report_error(); + } + + field_access = { + .type = resolved_element_type->structure.fields[field_index].type, + .field_index = field_index, + .struct_type = resolved_element_type->llvm.memory, + }; + } break; + case TypeId::union_type: + { + auto fields = resolved_element_type->union_type.fields; + u32 field_index; + auto field_count = (u32)fields.length; + for (field_index = 0; field_index < field_count; field_index += 1) + { + auto& field = fields[field_index]; + if (field_name.equal(field.name)) + { + break; + } + } + + if (field_index == field_count) + { + report_error(); + } + + auto field_type = resolved_element_type->union_type.fields[field_index].type; + auto struct_type = LLVMStructTypeInContext(module->llvm.context, &field_type->llvm.memory, 1, false); + + field_access = { + .type = field_type, + .field_index = 0, + .struct_type = struct_type, + }; + } break; + default: unreachable(); + } + + auto gep = LLVMBuildStructGEP2(module->llvm.builder, field_access.struct_type, v, field_access.field_index, ""); + + if (left_llvm) + { + trap(); + } + else + { + switch (value->kind) + { + case ValueKind::left: + { + return gep; + } break; + case ValueKind::right: + { + auto load = create_load(module, { + .type = field_access.type, + .pointer = gep, + }); + return load; + } break; + } + } + } break; + case TypeId::bits: + { + auto fields = resolved_element_type->bits.fields; + u64 i; + for (i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + if (field_name.equal(field.name)) + { + break; + } + } + + assert(i < fields.length); + + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place(module, field_type); + + auto load = create_load(module, { + .type = resolved_element_type, + .pointer = v, + }); + auto shift = LLVMBuildLShr(module->llvm.builder, load, LLVMConstInt(resolved_element_type->llvm.abi, field.offset, false), ""); + auto trunc = LLVMBuildTrunc(module->llvm.builder, shift, field_type->llvm.abi, ""); + if (left_llvm) + { + trap(); + } + + return trunc; + } break; + case TypeId::array: + { + assert(value->field_access.field_name.equal(string_literal("length"))); + auto array_length_type = get_llvm_type(value->type, type_kind); + auto result = LLVMConstInt(array_length_type, resolved_element_type->array.element_count, false); + return result; + } break; + default: unreachable(); + } + } break; + default: unreachable(); + } +} + +struct SliceEmitResult +{ + LLVMValueRef values[2]; +}; + +fn LLVMValueRef emit_slice_result(Module* module, SliceEmitResult slice, LLVMTypeRef slice_type) +{ + auto result = LLVMGetPoison(slice_type); + result = LLVMBuildInsertValue(module->llvm.builder, result, slice.values[0], 0, ""); + result = LLVMBuildInsertValue(module->llvm.builder, result, slice.values[1], 1, ""); + return result; +} + +fn SliceEmitResult emit_slice_expression(Module* module, Value* value) +{ + switch (value->id) + { + case ValueId::slice_expression: + { + auto value_type = value->type; + assert(value_type); + assert(type_is_slice(value_type)); + auto slice_pointer_type = value_type->structure.fields[0].type; + assert(slice_pointer_type->id == TypeId::pointer); + auto slice_element_type = slice_pointer_type->pointer.element_type; + + auto index_type = uint64(module); + resolve_type_in_place(module, index_type); + auto llvm_index_type = index_type->llvm.abi; + auto index_zero = LLVMConstInt(llvm_index_type, 0, 0); + + auto array_like = value->slice_expression.array_like; + auto start = value->slice_expression.start; + auto end = value->slice_expression.end; + + assert(array_like->kind == ValueKind::left); + emit_value(module, array_like, TypeKind::memory); + + auto pointer_type = array_like->type; + assert(pointer_type->id == TypeId::pointer); + auto sliceable_type = pointer_type->pointer.element_type; + bool has_start = start && start->id == ValueId::constant_integer && start->constant_integer.value != 0; + + if (start) + { + emit_value(module, start, TypeKind::memory); + } + + if (end) + { + emit_value(module, end, TypeKind::memory); + } + + switch (sliceable_type->id) + { + case TypeId::pointer: + { + auto element_type = sliceable_type->pointer.element_type; + auto pointer_load = create_load(module, { + .type = sliceable_type, + .pointer = array_like->llvm, + }); + + auto slice_pointer = pointer_load; + if (has_start) + { + LLVMValueRef indices[] = { start->llvm }; + slice_pointer = create_gep(module, { + .type = element_type->llvm.memory, + .pointer = pointer_load, + .indices = array_to_slice(indices), + }); + } + + auto slice_length = end->llvm; + + if (has_start) + { + slice_length = LLVMBuildSub(module->llvm.builder, slice_length, start->llvm, ""); + } + return { slice_pointer, slice_length }; + } break; + case TypeId::structure: + { + assert(sliceable_type->structure.is_slice); + auto slice_load = create_load(module, { + .type = sliceable_type, + .pointer = array_like->llvm, + }); + auto old_slice_pointer = LLVMBuildExtractValue(module->llvm.builder, slice_load, 0, ""); + auto slice_pointer = old_slice_pointer; + + if (has_start) + { + LLVMValueRef indices[] = { start->llvm }; + slice_pointer = create_gep(module, { + .type = slice_element_type->llvm.memory, + .pointer = old_slice_pointer, + .indices = array_to_slice(indices), + }); + } + + auto slice_end = end ? end->llvm : LLVMBuildExtractValue(module->llvm.builder, slice_load, 1, ""); + auto slice_length = slice_end; + if (has_start) + { + slice_length = LLVMBuildSub(module->llvm.builder, slice_end, start->llvm, ""); + } + + return { slice_pointer, slice_length }; + } break; + case TypeId::array: + { + assert(sliceable_type->array.element_type == slice_element_type); + LLVMValueRef slice_pointer = array_like->llvm; + if (has_start) + { + LLVMValueRef indices[] = { index_zero, start->llvm }; + slice_pointer = create_gep(module, { + .type = sliceable_type->llvm.memory, + .pointer = slice_pointer, + .indices = array_to_slice(indices), + }); + } + + LLVMValueRef slice_length = 0; + if (has_start) + { + trap(); + } + else if (end) + { + slice_length = end->llvm; + } + else + { + auto element_count = sliceable_type->array.element_count; + slice_length = LLVMConstInt(llvm_index_type, element_count, 0); + } + + assert(slice_length); + return { slice_pointer, slice_length }; + } break; + default: unreachable(); + } + } break; + default: unreachable(); + } +} + +fn SliceEmitResult emit_string_literal(Module* module, Value* value) +{ + auto resolved_value_type = resolve_alias(module, value->type); + switch (value->id) + { + case ValueId::string_literal: + { + bool null_terminate = true; + auto length = value->string_literal.length; + auto constant_string = LLVMConstStringInContext2(module->llvm.context, (char*)value->string_literal.pointer, length, !null_terminate); + auto u8_type = uint8(module); + resolve_type_in_place(module, u8_type); + auto string_type = LLVMArrayType2(u8_type->llvm.abi, length + null_terminate); + auto is_constant = true; + LLVMValueRef before = 0; + LLVMThreadLocalMode tlm = LLVMNotThreadLocal; + bool externally_initialized = false; + unsigned address_space = 0; + auto global = llvm_module_create_global_variable(module->llvm.module, string_type, is_constant, LLVMInternalLinkage, constant_string, string_literal("conststring"), before, tlm, address_space, externally_initialized); + LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); + auto slice_type = get_slice_type(module, u8_type); + + if (resolved_value_type->id != TypeId::structure) + { + report_error(); + } + + if (!resolved_value_type->structure.is_slice) + { + report_error(); + } + + if (slice_type != resolved_value_type) + { + report_error(); + } + + return { global, LLVMConstNull(slice_type->structure.fields[1].type->llvm.abi) }; + } break; + default: unreachable(); + } +} + +fn void emit_block(Module* module, LLVMBasicBlockRef basic_block) +{ + auto current_basic_block = LLVMGetInsertBlock(module->llvm.builder); + if (current_basic_block) + { + if (!LLVMGetBasicBlockTerminator(current_basic_block)) + { + LLVMBuildBr(module->llvm.builder, basic_block); + } + } + + if (current_basic_block && LLVMGetBasicBlockParent(current_basic_block)) + { + LLVMInsertExistingBasicBlockAfterInsertBlock(module->llvm.builder, basic_block); + } + else + { + LLVMAppendExistingBasicBlock(module->current_function->variable.storage->llvm, basic_block); + + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, basic_block); +} + +fn LLVMValueRef emit_va_arg_from_memory(Module* module, LLVMValueRef va_list_pointer, Type* va_list_struct, Type* argument_type) +{ + assert(va_list_struct->id == TypeId::structure); + auto overflow_arg_area_pointer = LLVMBuildStructGEP2(module->llvm.builder, va_list_struct->llvm.abi, va_list_pointer, 2, ""); + auto overflow_arg_area_type = va_list_struct->structure.fields[2].type; + auto overflow_arg_area = create_load(module, { .type = overflow_arg_area_type, .pointer = overflow_arg_area_pointer }); + if (get_byte_alignment(argument_type) > 8) + { + trap(); + } + + auto argument_type_size = get_byte_size(argument_type); + + auto raw_offset = align_forward(argument_type_size, 8); + auto uint32_type = uint32(module)->llvm.abi; + auto offset = LLVMConstInt(uint32_type, raw_offset, false); + LLVMValueRef indices[] = { + offset, + }; + auto new_overflow_arg_area = create_gep(module, { + .type = uint32_type, + .pointer = overflow_arg_area, + .indices = array_to_slice(indices), + .inbounds = false, + }); + create_store(module, { + .source = new_overflow_arg_area, + .destination = overflow_arg_area_pointer, + .type = overflow_arg_area_type, + }); + return overflow_arg_area; +} + +fn LLVMValueRef emit_va_arg(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type) +{ + switch (value->id) + { + case ValueId::va_arg: + { + auto raw_va_list_type = get_va_list_type(module); + + auto va_list_value = value->va_arg.va_list; + emit_value(module, va_list_value, TypeKind::memory); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + auto zero = LLVMConstNull(u64_type->llvm.memory); + LLVMValueRef gep_indices[] = {zero, zero}; + auto va_list_value_llvm = create_gep(module, { + .type = raw_va_list_type->llvm.memory, + .pointer = va_list_value->llvm, + .indices = array_to_slice(gep_indices), + }); + + auto va_arg_type = value->va_arg.type; + auto r = abi_system_v_classify_argument_type(module, va_arg_type, {}); + auto abi = r.abi; + auto needed_registers = r.needed_registers; + assert(abi.flags.kind != AbiKind::ignore); + + assert(raw_va_list_type->id == TypeId::array); + auto va_list_struct = raw_va_list_type->array.element_type; + LLVMValueRef address = 0; + + if (needed_registers.gpr == 0 && needed_registers.sse == 0) + { + address = emit_va_arg_from_memory(module, va_list_value_llvm, va_list_struct, va_arg_type); + } + else + { + auto va_list_struct_llvm = va_list_struct->llvm.memory; + + LLVMValueRef gpr_offset_pointer = 0; + LLVMValueRef gpr_offset = 0; + if (needed_registers.gpr != 0) + { + gpr_offset_pointer = LLVMBuildStructGEP2(module->llvm.builder, va_list_struct_llvm, va_list_value_llvm, 0, ""); + gpr_offset = create_load(module, { + .type = va_list_struct->structure.fields[0].type, + .pointer = gpr_offset_pointer, + .alignment = 16, + }); + } + else + { + trap(); + } + + auto raw_in_regs = 48 - needed_registers.gpr * 8; + auto u32_type = uint32(module); + resolve_type_in_place(module, u32_type); + auto u32_llvm = u32_type->llvm.memory; + LLVMValueRef in_regs = 0; + if (needed_registers.gpr != 0) + { + in_regs = LLVMConstInt(u32_llvm, raw_in_regs, false); + } + else + { + trap(); + } + + if (needed_registers.gpr != 0) + { + in_regs = LLVMBuildICmp(module->llvm.builder, LLVMIntULE, gpr_offset, in_regs, ""); + } + else + { + trap(); + } + + assert(in_regs); + + LLVMValueRef fp_offset_pointer = 0; + if (needed_registers.sse) + { + trap(); + } + LLVMValueRef fp_offset = 0; + if (needed_registers.sse) + { + trap(); + } + + auto raw_fits_in_fp = 176 - needed_registers.sse * 16; + LLVMValueRef fits_in_fp = 0; + if (needed_registers.sse) + { + trap(); + } + + if (needed_registers.sse && needed_registers.gpr) + { + trap(); + } + + auto* in_reg_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.in_reg"), 0); + auto* in_mem_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.in_mem"), 0); + auto* end_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.end"), 0); + LLVMBuildCondBr(module->llvm.builder, in_regs, in_reg_block, in_mem_block); + + emit_block(module, in_reg_block); + + auto reg_save_area_type = va_list_struct->structure.fields[3].type; + auto reg_save_area = create_load(module, { + .type = reg_save_area_type, + .pointer = LLVMBuildStructGEP2(module->llvm.builder, va_list_struct_llvm, va_list_value_llvm, 3, ""), + .alignment = 16, + }); + + LLVMValueRef register_address = 0; + + if (needed_registers.gpr && needed_registers.sse) + { + trap(); + } + else if (needed_registers.gpr) + { + auto t = reg_save_area_type->pointer.element_type; + resolve_type_in_place(module, t); + + LLVMValueRef indices[] = { gpr_offset }; + register_address = create_gep(module, { + .type = t->llvm.abi, + .pointer = reg_save_area, + .indices = array_to_slice(indices), + .inbounds = false, + }); + if (get_byte_alignment(va_arg_type) > 8) + { + trap(); + } + } + else if (needed_registers.sse == 1) + { + trap(); + } + else if (needed_registers.sse == 2) + { + trap(); + } + else + { + unreachable(); + } + + if (needed_registers.gpr) + { + auto raw_offset = needed_registers.gpr * 8; + auto new_offset = LLVMBuildAdd(module->llvm.builder, gpr_offset, LLVMConstInt(u32_llvm, raw_offset, false), ""); + create_store(module, StoreOptions{ + .source = new_offset, + .destination = gpr_offset_pointer, + .type = u32_type, + }); + } + + if (needed_registers.sse) + { + trap(); + } + + LLVMBuildBr(module->llvm.builder, end_block); + + emit_block(module, in_mem_block); + auto memory_address = emit_va_arg_from_memory(module, va_list_value_llvm, va_list_struct, va_arg_type); + + emit_block(module, end_block); + + LLVMValueRef values[] = { + register_address, + memory_address, + }; + LLVMBasicBlockRef blocks[] = { + in_reg_block, + in_mem_block, + }; + + auto phi = LLVMBuildPhi(module->llvm.builder, module->llvm.pointer_type, ""); + LLVMAddIncoming(phi, values, blocks, array_length(values)); + + address = phi; + + unused(fp_offset_pointer); + unused(fp_offset); + unused(raw_fits_in_fp); + unused(fits_in_fp); + } + + assert(address); + + auto evaluation_kind = get_evaluation_kind(va_arg_type); + + LLVMValueRef result = 0; + + switch (evaluation_kind) + { + case EvaluationKind::scalar: + { + assert(!left_llvm); + assert(!left_type); + result = create_load(module, { + .type = va_arg_type, + .pointer = address, + }); + } break; + case EvaluationKind::aggregate: + { + if (left_llvm) + { + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + u64 memcpy_size = get_byte_size(va_arg_type); + auto alignment = get_byte_alignment(va_arg_type); + LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, address, alignment, LLVMConstInt(u64_type->llvm.abi, memcpy_size, false)); + return left_llvm; + } + else + { + trap(); + } + } break; + case EvaluationKind::complex: + { + trap(); + } break; + } + + assert(result); + return result; + } break; + default: unreachable(); + } +} + +fn LLVMValueRef emit_binary(Module* module, LLVMValueRef left, Type* left_type, LLVMValueRef right, Type* right_type, BinaryId id, Type* resolved_value_type) +{ + switch (resolved_value_type->id) + { + case TypeId::integer: + { + switch (id) + { + case BinaryId::shift_right: + if (resolved_value_type->integer.is_signed) + { + return LLVMBuildAShr(module->llvm.builder, left, right, ""); + } + else + { + return LLVMBuildLShr(module->llvm.builder, left, right, ""); + } + break; + case BinaryId::div: + if (resolved_value_type->integer.is_signed) + { + return LLVMBuildSDiv(module->llvm.builder, left, right, ""); + } + else + { + return LLVMBuildUDiv(module->llvm.builder, left, right, ""); + } + break; + case BinaryId::rem: + if (resolved_value_type->integer.is_signed) + { + return LLVMBuildSRem(module->llvm.builder, left, right, ""); + } + else + { + return LLVMBuildURem(module->llvm.builder, left, right, ""); + } + 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(left_type == right_type); + auto left_signed = type_is_signed(left_type); + auto right_signed = type_is_signed(right_type); + assert(left_signed == right_signed); + auto is_signed = left_signed; + + switch (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; break; + case BinaryId::compare_less: predicate = is_signed ? LLVMIntSLT : LLVMIntULT; break; + case BinaryId::compare_greater_equal: predicate = is_signed ? LLVMIntSGE : LLVMIntUGE; break; + case BinaryId::compare_less_equal: predicate = is_signed ? LLVMIntSLE : LLVMIntULE; break; + default: unreachable(); + } + return LLVMBuildICmp(module->llvm.builder, predicate, left, right, ""); + } break; + case BinaryId::add: return LLVMBuildAdd(module->llvm.builder, left, right, ""); break; + case BinaryId::sub: return LLVMBuildSub(module->llvm.builder, left, right, ""); break; + case BinaryId::mul: return LLVMBuildMul(module->llvm.builder, left, right, ""); break; + case BinaryId::bitwise_and: return LLVMBuildAnd(module->llvm.builder, left, right, ""); break; + case BinaryId::bitwise_or: return LLVMBuildOr(module->llvm.builder, left, right, ""); break; + case BinaryId::bitwise_xor: return LLVMBuildXor(module->llvm.builder, left, right, ""); break; + case BinaryId::shift_left: return LLVMBuildShl(module->llvm.builder, left, right, ""); break; + default: unreachable(); + } + } break; + case TypeId::pointer: + { + auto element_type = resolved_value_type->pointer.element_type; + resolve_type_in_place(module, element_type); + + if (id != BinaryId::add && id != BinaryId::sub) + { + report_error(); + } + + LLVMValueRef index = right; + if (id == BinaryId::sub) + { + index = LLVMBuildNeg(module->llvm.builder, index, ""); + } + + LLVMValueRef indices[] = { index }; + + return create_gep(module, { + .type = element_type->llvm.abi, + .pointer = left, + .indices = array_to_slice(indices), + }); + } break; + default: unreachable(); + } +} + +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; + case ValueId::string_literal: + { + auto string_literal = emit_string_literal(module, right); + auto slice_type = get_slice_type(module, uint8(module)); + + for (u32 i = 0; i < array_length(string_literal.values); i += 1) + { + auto member_pointer = LLVMBuildStructGEP2(module->llvm.builder, slice_type->llvm.abi, left_llvm, i, ""); + auto slice_member_type = slice_type->structure.fields[i].type; + create_store(module, { + .source = string_literal.values[i], + .destination = member_pointer, + .type = slice_member_type, + }); + } + } break; + case ValueId::va_start: + { + assert(resolved_value_type == get_va_list_type(module)); + assert(pointer_type->pointer.element_type == get_va_list_type(module)); + LLVMTypeRef argument_types[] = { + module->llvm.pointer_type, + }; + LLVMValueRef argument_values[] = { + left_llvm, + }; + auto call = emit_intrinsic_call(module, IntrinsicIndex::va_start, array_to_slice(argument_types), array_to_slice(argument_values)); + unused(call); + } break; + case ValueId::aggregate_initialization: + { + auto names = right->aggregate_initialization.names; + auto values = right->aggregate_initialization.values; + auto is_constant = right->aggregate_initialization.is_constant; + auto zero = right->aggregate_initialization.zero; + assert(names.length == values.length); + + if (is_constant) + { + emit_value(module, right, TypeKind::memory); + + LLVMLinkage linkage_type = LLVMInternalLinkage; + LLVMValueRef before = 0; + unsigned address_space = 0; + LLVMThreadLocalMode thread_local_mode = LLVMNotThreadLocal; + 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(value_type); + LLVMSetAlignment(global, alignment); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + u64 memcpy_size = get_byte_size(value_type); + LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, global, alignment, LLVMConstInt(u64_type->llvm.abi, memcpy_size, false)); + } + else + { + switch (resolved_value_type->id) + { + case TypeId::structure: + { + u64 max_field_index = 0; + u64 field_mask = 0; + auto fields = resolved_value_type->structure.fields; + assert(fields.length <= 64); + unused(field_mask); + + for (u32 initialization_index = 0; initialization_index < (u32)values.length; initialization_index += 1) + { + auto name = names[initialization_index]; + auto value = values[initialization_index]; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < (u32)fields.length; declaration_index += 1) + { + auto field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + assert(declaration_index < fields.length); + + field_mask |= 1 << declaration_index; + max_field_index = MAX(max_field_index, declaration_index); + auto& field = fields[declaration_index]; + auto destination_pointer = LLVMBuildStructGEP2(module->llvm.builder, resolved_value_type->llvm.memory, left_llvm, declaration_index, ""); + emit_assignment(module, destination_pointer, get_pointer_type(module, field.type), value); + } + + if (zero) + { + trap(); + } + } break; + case TypeId::union_type: + { + assert(names.length == 1); + assert(values.length == 1); + auto fields = resolved_value_type->union_type.fields; + auto biggest_field_index = resolved_value_type->union_type.biggest_field; + auto& biggest_field = fields[biggest_field_index]; + auto biggest_field_type = fields[biggest_field_index].type; + auto value = values[0]; + auto field_value_type = value->type; + auto field_type_size = get_byte_size(field_value_type); + + LLVMTypeRef struct_type; + + if (type_is_abi_equal(module, field_value_type, biggest_field_type)) + { + struct_type = resolved_value_type->llvm.memory; + } + else + { + struct_type = LLVMStructTypeInContext(module->llvm.context, &field_value_type->llvm.memory, 1, false); + } + + auto destination_pointer = LLVMBuildStructGEP2(module->llvm.builder, struct_type, left_llvm, 0, ""); + auto field_pointer_type = get_pointer_type(module, field_value_type); + unused(biggest_field); + unused(field_type_size); + emit_assignment(module, destination_pointer, field_pointer_type, value); + + auto union_size = resolved_value_type->union_type.byte_size; + if (field_type_size < union_size) + { + trap(); + } + else if (field_type_size > union_size) + { + unreachable(); + } + } break; + default: unreachable(); + } + } + } break; + case ValueId::call: + { + auto result = emit_call(module, right, left_llvm, left_type); + assert(result == left_llvm); + } break; + case ValueId::va_arg: + { + auto result = emit_va_arg(module, right, left_llvm, left_type); + if (result != left_llvm) + { + trap(); + } + } break; + case ValueId::slice_expression: + { + auto slice = emit_slice_expression(module, right); + auto slice_pointer_type = resolved_value_type->structure.fields[0].type; + create_store(module, { + .source = slice.values[0], + .destination = left_llvm, + .type = slice_pointer_type, + }); + + auto slice_length_destination = LLVMBuildStructGEP2(module->llvm.builder, resolved_value_type->llvm.abi, left_llvm, 1, ""); + create_store(module, { + .source = slice.values[1], + .destination = slice_length_destination, + .type = uint64(module), + }); + } break; + case ValueId::zero: + { + auto u8_type = uint8(module); + auto u64_type = uint64(module); + resolve_type_in_place(module, u8_type); + resolve_type_in_place(module, u64_type); + + auto size = get_byte_size(resolved_value_type); + auto alignment = get_byte_alignment(resolved_value_type); + LLVMBuildMemSet(module->llvm.builder, left_llvm, LLVMConstNull(u8_type->llvm.memory), LLVMConstInt(u64_type->llvm.memory, size, false), alignment); + } break; + case ValueId::variable_reference: + { + auto* variable = right->variable_reference; + switch (right->kind) + { + case ValueKind::left: + { + trap(); + } break; + case ValueKind::right: + { + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + auto memcpy_size = get_byte_size(resolved_value_type); + auto alignment = get_byte_alignment(resolved_value_type); + LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, variable->storage->llvm, alignment, LLVMConstInt(u64_type->llvm.abi, memcpy_size, false)); + } break; + } + } break; + case ValueId::string_to_enum: + { + emit_value(module, right, TypeKind::memory); + + auto enum_type = right->string_to_enum.type; + auto s2e_struct_type = enum_type->enumerator.string_to_enum_struct_type; + create_store(module, { + .source = right->llvm, + .destination = left_llvm, + .type = s2e_struct_type, + }); + } break; + case ValueId::undefined: + { + // TODO: do something? + } 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 unary_id = value->unary.id; + auto resolved_unary_type = resolve_alias(module, unary_value->type); + emit_value(module, unary_value, type_kind); + if (unary_id == UnaryId::truncate || unary_id == UnaryId::enum_name) + { + type_kind = TypeKind::abi; + } + 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 (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: + { + if (resolved_value_type == resolved_unary_type) + { + llvm_value = LLVMBuildNot(module->llvm.builder, llvm_unary_value, ""); + } + else + { + switch (resolved_unary_type->id) + { + case TypeId::pointer: + { + llvm_value = LLVMBuildICmp(module->llvm.builder, LLVMIntEQ, llvm_unary_value, LLVMConstNull(resolved_unary_type->llvm.abi), ""); + } break; + default: report_error(); + } + } + } break; + case UnaryId::tilde: + { + trap(); + } break; + case UnaryId::enum_name: + { + assert(type_kind == TypeKind::abi); + auto enum_type = resolved_unary_type; + assert(enum_type->id == TypeId::enumerator); + auto enum_to_string = enum_type->enumerator.enum_to_string_function; + assert(enum_to_string); + auto call = LLVMBuildCall2(module->llvm.builder, LLVMGlobalGetValueType(enum_to_string), enum_to_string, &llvm_unary_value, 1, ""); + LLVMSetInstructionCallConv(call, LLVMFastCallConv); + llvm_value = call; + } 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: + case UnaryId::int_from_enum: + { + llvm_value = llvm_unary_value; + } break; + case UnaryId::int_from_pointer: + { + llvm_value = LLVMBuildPtrToInt(module->llvm.builder, llvm_unary_value, resolved_value_type->llvm.abi, ""); + } break; + case UnaryId::va_end: + { + LLVMTypeRef argument_types[] = { module->llvm.pointer_type }; + LLVMValueRef argument_values[] = { llvm_unary_value }; + llvm_value = emit_intrinsic_call(module, IntrinsicIndex::va_end, array_to_slice(argument_types), array_to_slice(argument_values)); + } break; + case UnaryId::bitwise_not: + { + trap(); + } break; + case UnaryId::dereference: + { + 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, + .kind = type_kind, + }); + 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: + { + assert(resolved_value_type->id == TypeId::integer); + auto constant_integer = LLVMConstInt(resolved_value_type->llvm.abi, get_byte_size(unary_type), false); + llvm_value = constant_integer; + } 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(resolved_value_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: + { + auto binary_id = value->binary.id; + bool is_shorcircuiting = binary_is_shortcircuiting(binary_id); + Value* values[2] = { value->binary.left, value->binary.right }; + + if (is_shorcircuiting) + { + enum class ShortcircuitingOperation + { + boolean_and, + boolean_or, + }; + + ShortcircuitingOperation shorcircuiting_op; + switch (binary_id) + { + case BinaryId::logical_and_shortcircuit: + shorcircuiting_op = ShortcircuitingOperation::boolean_and; + break; + case BinaryId::logical_or_shortcircuit: + shorcircuiting_op = ShortcircuitingOperation::boolean_or; + break; + default: + unreachable(); + } + + auto* left = value->binary.left; + if (left->llvm) + { + assert(false); // TODO: check if this if is really necessary + } + else + { + emit_value(module, left, TypeKind::abi); + } + + auto left_llvm = left->llvm; + + LLVMValueRef left_condition = 0; + + switch (left->type->id) + { + case TypeId::integer: + { + switch (left->type->integer.bit_count) + { + case 1: + left_condition = left_llvm; + break; + default: trap(); + } + } break; + default: trap(); + } + + assert(left_condition); + + auto llvm_function = module->current_function->variable.storage->llvm; + assert(llvm_function); + + auto current_basic_block = LLVMGetInsertBlock(module->llvm.builder); + + auto* right_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.right"), llvm_function); + auto* end_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.end"), llvm_function); + + LLVMBasicBlockRef true_block; + LLVMBasicBlockRef false_block; + + switch (shorcircuiting_op) + { + case ShortcircuitingOperation::boolean_and: + true_block = right_block; + false_block = end_block; + break; + case ShortcircuitingOperation::boolean_or: + true_block = end_block; + false_block = right_block; + break; + } + + LLVMBuildCondBr(module->llvm.builder, left_condition, true_block, false_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, right_block); + + auto* right = value->binary.right; + if (right->llvm) + { + assert(false); // TODO: check if this if is really necessary + } + else + { + emit_value(module, right, TypeKind::abi); + } + + auto right_llvm = right->llvm; + + LLVMValueRef right_condition = 0; + + switch (right->type->id) + { + case TypeId::integer: + { + switch (right->type->integer.bit_count) + { + case 1: + right_condition = right_llvm; + break; + default: trap(); + } + } break; + default: trap(); + } + + assert(right_condition); + + LLVMBuildBr(module->llvm.builder, end_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, end_block); + + auto boolean_type = uint1(module); + resolve_type_in_place(module, boolean_type); + auto boolean = boolean_type->llvm.abi; + + + LLVMValueRef incoming_left = 0; + + switch (shorcircuiting_op) + { + case ShortcircuitingOperation::boolean_and: + incoming_left = LLVMConstNull(boolean); + break; + case ShortcircuitingOperation::boolean_or: + incoming_left = LLVMConstInt(boolean, 1, false); + break; + } + + assert(incoming_left); + + LLVMValueRef incoming_values[] = { + incoming_left, + right_condition, + }; + + LLVMBasicBlockRef blocks[] = { + current_basic_block, + right_block, + }; + static_assert(array_length(incoming_values) == array_length(blocks)); + + auto phi = LLVMBuildPhi(module->llvm.builder, boolean, ""); + LLVMAddIncoming(phi, incoming_values, blocks, array_length(blocks)); + + llvm_value = phi; + + switch (type_kind) + { + case TypeKind::abi: + break; + case TypeKind::memory: + trap(); + } + } + else + { + LLVMValueRef llvm_values[2]; + 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; + } + + llvm_value = emit_binary(module, llvm_values[0], values[0]->type, llvm_values[1], values[1]->type, value->binary.id, resolved_value_type); + } + } 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: + { + auto values = value->array_initialization.values; + auto element_count = values.length; + + if (value->array_initialization.is_constant) + { + assert(value->kind == ValueKind::right); + auto element_type = resolved_value_type->array.element_type; + LLVMValueRef value_buffer[64]; + + resolve_type_in_place(module, element_type); + + for (u64 i = 0; i < element_count; i += 1) + { + auto* v = 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 + { + switch (value->kind) + { + case ValueKind::right: + { + trap(); + } break; + case ValueKind::left: + { + assert(resolved_value_type->id == TypeId::pointer); + auto array_type = resolved_value_type->pointer.element_type; + assert(array_type->id == TypeId::array); + auto alloca = create_alloca(module, { + .type = array_type, + }); + + auto pointer_to_element_type = get_pointer_type(module, array_type->array.element_type); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + auto llvm_u64_type = u64_type->llvm.abi; + auto u64_zero = LLVMConstNull(llvm_u64_type); + + LLVMTypeRef llvm_array_type = array_type->llvm.memory; + + for (u64 i = 0; i < values.length; i += 1) + { + LLVMValueRef indices[] = { + u64_zero, + LLVMConstInt(llvm_u64_type, i, false), + }; + auto alloca_gep = create_gep(module, { + .type = llvm_array_type, + .pointer = alloca, + .indices = array_to_slice(indices), + }); + auto value = values[i]; + emit_assignment(module, alloca_gep, pointer_to_element_type, value); + } + + llvm_value = alloca; + } break; + } + } + } 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: + { + emit_value(module, array_like, TypeKind::memory); + emit_value(module, index, TypeKind::memory); + + 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; + + 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), + }); + auto element_type = array_type->array.element_type; + + switch (value->kind) + { + case ValueKind::left: + llvm_value = gep; + break; + case ValueKind::right: + llvm_value = create_load(module, LoadOptions{ + .type = element_type, + .pointer = gep, + }); + break; + } + } break; + case TypeId::structure: + { + auto slice_type = pointer_element_type; + assert(slice_type->structure.is_slice); + auto slice_pointer_type = slice_type->structure.fields[0].type; + auto slice_element_type = slice_pointer_type->pointer.element_type; + resolve_type_in_place(module, slice_element_type); + + auto pointer_load = create_load(module, { + .type = slice_pointer_type, + .pointer = array_like->llvm, + }); + LLVMValueRef indices[1] = { + index->llvm, + }; + auto gep = create_gep(module, { + .type = slice_element_type->llvm.memory, + .pointer = pointer_load, + .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 = slice_element_type, + .pointer = gep, + }); + break; + } + } break; + case TypeId::pointer: + { + auto element_type = pointer_element_type->pointer.element_type; + // TODO: consider not emitting the and doing straight GEP? + auto pointer_load = create_load(module, { + .type = pointer_element_type, + .pointer = array_like->llvm, + }); + LLVMValueRef indices[] = { index->llvm }; + auto gep = create_gep(module, { + .type = element_type->llvm.memory, + .pointer = pointer_load, + .indices = array_to_slice(indices), + }); + + llvm_value = gep; + + if (value->kind == ValueKind::right) + { + llvm_value = create_load(module, { + .type = element_type, + .pointer = gep, + }); + } + } break; + default: unreachable(); + } + } 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; + case ValueId::trap: + { + auto call = emit_intrinsic_call(module, IntrinsicIndex::trap, {}, {}); + LLVMBuildUnreachable(module->llvm.builder); + LLVMClearInsertionPosition(module->llvm.builder); + llvm_value = call; + } break; + case ValueId::field_access: + { + llvm_value = emit_field_access(module, value, 0, 0, type_kind); + } break; + case ValueId::slice_expression: + { + auto slice = emit_slice_expression(module, value); + llvm_value = emit_slice_result(module, slice, resolved_value_type->llvm.abi); + } break; + case ValueId::va_arg: + { + llvm_value = emit_va_arg(module, value, 0, 0); + } break; + case ValueId::aggregate_initialization: + { + auto names = value->aggregate_initialization.names; + auto values = value->aggregate_initialization.values; + assert(names.length == values.length); + auto is_constant = value->aggregate_initialization.is_constant; + auto zero = value->aggregate_initialization.zero; + + switch (resolved_value_type->id) + { + case TypeId::structure: + { + auto fields = resolved_value_type->structure.fields; + + if (is_constant) + { + LLVMValueRef constant_buffer[64]; + u32 constant_count = (u32)values.length; + + for (u64 i = 0; i < values.length; i += 1) + { + auto* value = values[i]; + emit_value(module, value, TypeKind::memory); + auto llvm_value = value->llvm; + assert(llvm_value); + assert(LLVMIsAConstant(llvm_value)); + constant_buffer[i] = llvm_value; + } + + + if (zero) + { + if (values.length == fields.length) + { + unreachable(); + } + + for (u64 i = values.length; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place(module, field_type); + constant_buffer[i] = LLVMConstNull(field_type->llvm.memory); + constant_count += 1; + } + } + + assert(constant_count == fields.length); + + llvm_value = LLVMConstNamedStruct(get_llvm_type(resolved_value_type, type_kind), constant_buffer, constant_count); + } + else + { + trap(); + } + } break; + case TypeId::union_type: + { + trap(); + } break; + case TypeId::bits: + { + auto fields = resolved_value_type->bits.fields; + Type* backing_type = resolved_value_type->bits.backing_type; + if (is_constant) + { + u64 bits_value = 0; + + for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + { + auto value = values[initialization_index]; + auto name = names[initialization_index]; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + { + auto& field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + if (declaration_index == fields.length) + { + unreachable(); + } + + auto field = fields[declaration_index]; + u64 field_value; + switch (value->id) + { + case ValueId::constant_integer: + { + field_value = value->constant_integer.value; + } break; + default: unreachable(); + } + + bits_value |= field_value << field.offset; + } + + resolve_type_in_place(module, backing_type); + + llvm_value = LLVMConstInt(get_llvm_type(backing_type, type_kind), bits_value, false); + } + else + { + trap(); + } + } break; + default: unreachable(); + } + } break; + case ValueId::zero: + { + llvm_value = LLVMConstNull(get_llvm_type(resolved_value_type, type_kind)); + } break; + case ValueId::select: + { + auto condition = value->select.condition; + auto true_value = value->select.true_value; + auto false_value = value->select.false_value; + emit_value(module, condition, TypeKind::abi); + LLVMValueRef llvm_condition = condition->llvm; + auto condition_type = condition->type; + + switch (condition_type->id) + { + case TypeId::integer: + { + if (condition_type->integer.bit_count != 1) + { + trap(); + } + } break; + default: trap(); + } + + emit_value(module, true_value, type_kind); + emit_value(module, false_value, type_kind); + + llvm_value = LLVMBuildSelect(module->llvm.builder, llvm_condition, true_value->llvm, false_value->llvm, ""); + } break; + case ValueId::unreachable: + { + llvm_value = LLVMBuildUnreachable(module->llvm.builder); + LLVMClearInsertionPosition(module->llvm.builder); + } break; + case ValueId::string_to_enum: + { + auto enum_type = value->string_to_enum.type; + auto string_value = value->string_to_enum.string; + emit_value(module, string_value, TypeKind::memory); + auto llvm_string_value = string_value->llvm; + + auto s2e = enum_type->enumerator.string_to_enum_function; + auto first_field = LLVMBuildExtractValue(module->llvm.builder, llvm_string_value, 0, ""); + auto second_field = LLVMBuildExtractValue(module->llvm.builder, llvm_string_value, 1, ""); + LLVMValueRef fields[] = { + first_field, + second_field, + }; + auto call = LLVMBuildCall2(module->llvm.builder, LLVMGlobalGetValueType(s2e), s2e, fields, array_length(fields), ""); + LLVMSetInstructionCallConv(call, LLVMFastCallConv); + llvm_value = call; + } break; + case ValueId::string_literal: + { + assert(type_is_slice(resolved_value_type)); + auto string_literal = emit_string_literal(module, value); + llvm_value = emit_slice_result(module, string_literal, resolved_value_type->llvm.abi); + } break; + default: unreachable(); + } + + assert(llvm_value); + value->llvm = llvm_value; +} + + +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 LLVMMetadataRef null_expression(Module* module) +{ + return LLVMDIBuilderCreateExpression(module->llvm.di_builder, 0, 0); +} + +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, null_expression(module), 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; + 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; + case StatementId::expression: + { + analyze_value(module, statement->expression, 0, TypeKind::memory); + } break; + case StatementId::while_st: + { + auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.entry"), llvm_function); + LLVMBuildBr(module->llvm.builder, entry_block); + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + auto body_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.body"), llvm_function); + auto continue_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.continue"), llvm_function); + auto exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.exit"), llvm_function); + + auto previous_continue_block = module->llvm.continue_block; + auto previous_exit_block = module->llvm.exit_block; + module->llvm.continue_block = continue_block; + module->llvm.exit_block = exit_block; + + auto condition = statement->while_st.condition; + auto block = statement->while_st.block; + + if (condition->is_constant()) + { + switch (condition->id) + { + case ValueId::constant_integer: + { + if (condition->constant_integer.value == 0) + { + report_error(); + } + } break; + default: unreachable(); + } + + LLVMBuildBr(module->llvm.builder, body_block); + } + else + { + analyze_value(module, condition, 0, TypeKind::abi); + + auto boolean = uint1(module); + + LLVMValueRef llvm_condition = condition->llvm; + auto condition_type = condition->type; + if (condition_type != boolean) + { + switch (condition_type->id) + { + case TypeId::integer: + { + llvm_condition = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, llvm_condition, LLVMConstNull(condition_type->llvm.abi), ""); + } break; + default: unreachable(); + } + } + + LLVMBuildCondBr(module->llvm.builder, llvm_condition, body_block, exit_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, body_block); + + analyze_block(module, block); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + LLVMBuildBr(module->llvm.builder, continue_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, continue_block); + + LLVMBuildBr(module->llvm.builder, entry_block); + + if (llvm_value_use_empty((LLVMValueRef)body_block)) + { + trap(); + } + + if (llvm_value_use_empty((LLVMValueRef)exit_block)) + { + trap(); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); + + module->llvm.continue_block = previous_continue_block; + module->llvm.exit_block = previous_exit_block; + } break; + case StatementId::assignment: + { + auto left = statement->assignment.left; + auto right = statement->assignment.right; + auto id = statement->assignment.id; + analyze_value(module, left, 0, TypeKind::memory); + + auto left_type = left->type; + if (left_type->id != TypeId::pointer) + { + report_error(); + } + auto element_type = left_type->pointer.element_type; + auto left_llvm = left->llvm; + + switch (id) + { + case StatementAssignmentId::assign: + { + analyze_type(module, right, element_type); + emit_assignment(module, left_llvm, left_type, right); + } break; + case StatementAssignmentId::assign_add: + case StatementAssignmentId::assign_sub: + case StatementAssignmentId::assign_mul: + case StatementAssignmentId::assign_div: + case StatementAssignmentId::assign_rem: + case StatementAssignmentId::assign_shift_left: + case StatementAssignmentId::assign_shift_right: + case StatementAssignmentId::assign_and: + case StatementAssignmentId::assign_or: + case StatementAssignmentId::assign_xor: + { + auto evaluation_kind = get_evaluation_kind(element_type); + assert(evaluation_kind == EvaluationKind::scalar); + auto load = create_load(module, { + .type = element_type, + .pointer = left_llvm, + .kind = TypeKind::abi, + }); + analyze_value(module, right, element_type, TypeKind::abi); + auto a = load; + auto b = right->llvm; + + BinaryId binary_id; + switch (id) + { + case StatementAssignmentId::assign: unreachable(); + case StatementAssignmentId::assign_add: binary_id = BinaryId::add; break; + case StatementAssignmentId::assign_sub: binary_id = BinaryId::sub; break; + case StatementAssignmentId::assign_mul: binary_id = BinaryId::mul; break; + case StatementAssignmentId::assign_div: binary_id = BinaryId::div; break; + case StatementAssignmentId::assign_rem: binary_id = BinaryId::rem; break; + case StatementAssignmentId::assign_shift_left: binary_id = BinaryId::shift_left; break; + case StatementAssignmentId::assign_shift_right: binary_id = BinaryId::shift_right; break; + case StatementAssignmentId::assign_and: binary_id = BinaryId::bitwise_and; break; + case StatementAssignmentId::assign_or: binary_id = BinaryId::bitwise_or; break; + case StatementAssignmentId::assign_xor: binary_id = BinaryId::bitwise_xor; break; + } + + auto op = emit_binary(module, a, element_type, b, right->type, binary_id, element_type); + + create_store(module, { + .source = op, + .destination = left_llvm, + .type = element_type, + }); + } break; + } + } break; + case StatementId::switch_st: + { + auto* exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("switch.exit"), llvm_function); + + auto discriminant = statement->switch_st.discriminant; + auto clauses = statement->switch_st.clauses; + analyze_value(module, discriminant, 0, TypeKind::abi); + + auto discriminant_type = discriminant->type; + + switch (discriminant_type->id) + { + case TypeId::enumerator: + { + u32 invalid_clause_index = ~(u32)0; + u32 else_clause_index = invalid_clause_index; + u32 discriminant_case_count = 0; + + for (u64 i = 0; i < clauses.length; i += 1) + { + auto& clause = clauses[i]; + clause.basic_block = llvm_context_create_basic_block(module->llvm.context, clause.values.length == 0 ? string_literal("switch.else_case_block") : string_literal("switch.case_block"), llvm_function); + discriminant_case_count += clause.values.length; + + if (clause.values.length == 0) + { + if (else_clause_index != invalid_clause_index) + { + report_error(); + } + + else_clause_index = i; + } + else + { + for (auto value: clause.values) + { + analyze_value(module, value, discriminant_type, TypeKind::abi); + if (!value->is_constant()) + { + report_error(); + } + } + } + } + + LLVMBasicBlockRef else_block; + if (else_clause_index != invalid_clause_index) + { + else_block = clauses[else_clause_index].basic_block; + } + else + { + else_block = llvm_context_create_basic_block(module->llvm.context, string_literal("switch.else_case_block"), llvm_function); + } + + auto switch_instruction = LLVMBuildSwitch(module->llvm.builder, discriminant->llvm, else_block, discriminant_case_count); + bool all_blocks_terminated = true; + + for (auto& clause : clauses) + { + for (auto value : clause.values) + { + LLVMAddCase(switch_instruction, value->llvm, clause.basic_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, clause.basic_block); + + analyze_block(module, clause.block); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + all_blocks_terminated = false; + LLVMBuildBr(module->llvm.builder, exit_block); + LLVMClearInsertionPosition(module->llvm.builder); + } + } + + if (else_clause_index == invalid_clause_index) + { + LLVMPositionBuilderAtEnd(module->llvm.builder, else_block); + LLVMBuildUnreachable(module->llvm.builder); + LLVMClearInsertionPosition(module->llvm.builder); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); + + if (all_blocks_terminated) + { + LLVMBuildUnreachable(module->llvm.builder); + LLVMClearInsertionPosition(module->llvm.builder); + } + } break; + default: trap(); + } + } break; + case StatementId::for_each: + { + if (module->has_debug_info) + { + auto lexical_block = LLVMDIBuilderCreateLexicalBlock(module->llvm.di_builder, statement->for_each.scope.parent->llvm, module->llvm.file, statement->for_each.scope.line, statement->for_each.scope.column); + statement->for_each.scope.llvm = lexical_block; + } + + auto index_type = uint64(module); + resolve_type_in_place(module, index_type); + auto index_zero = LLVMConstNull(index_type->llvm.abi); + + auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.entry"), llvm_function); + auto* body_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.body"), llvm_function); + auto* continue_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.continue"), llvm_function); + auto* exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.exit"), llvm_function); + + auto previous_continue_block = module->llvm.continue_block; + auto previous_exit_block = module->llvm.exit_block; + module->llvm.continue_block = continue_block; + module->llvm.exit_block = exit_block; + + auto left_values = statement->for_each.left_values; + auto right_values = statement->for_each.right_values; + + switch (statement->for_each.kind) + { + case ForEachKind::slice: + { + assert(left_values.length == right_values.length); + + Local* local = statement->for_each.first_local; + + for (u64 i = 0; i < right_values.length; i += 1, local = local->next) + { + auto kind = left_values[i]; + auto right = right_values[i]; + assert(right->kind == ValueKind::left); + analyze_type(module, right, 0); + + auto pointer_type = right->type; + if (pointer_type->id != TypeId::pointer) + { + report_error(); + } + + auto aggregate_type = pointer_type->pointer.element_type; + + Type* child_type = 0; + + switch (aggregate_type->id) + { + case TypeId::array: + { + child_type = aggregate_type->array.element_type; + } break; + case TypeId::structure: + { + if (!aggregate_type->structure.is_slice) + { + report_error(); + } + child_type = aggregate_type->structure.fields[0].type->pointer.element_type; + } break; + default: trap(); + } + + assert(child_type); + assert(!local->variable.type); + + Type* local_type = 0; + + switch (kind) + { + case ValueKind::left: local_type = get_pointer_type(module, child_type); break; + case ValueKind::right: local_type = child_type; break; + } + + assert(local_type); + + local->variable.type = local_type; + + emit_local_variable(module, local); + emit_value(module, right, TypeKind::memory); + } + + assert(!local); + + LLVMValueRef length_value = 0; + + // TODO: make it right + for (auto value : right_values) + { + auto pointer_type = value->type; + if (pointer_type->id != TypeId::pointer) + { + report_error(); + } + + auto aggregate_type = pointer_type->pointer.element_type; + switch (aggregate_type->id) + { + case TypeId::array: + { + length_value = LLVMConstInt(index_type->llvm.abi, aggregate_type->array.element_count, false); + } break; + case TypeId::structure: + { + assert(aggregate_type->structure.is_slice); + + auto gep = create_gep(module, { + .type = aggregate_type->llvm.abi, + .pointer = value->llvm, + }); + auto load = create_load(module, { + .type = index_type, + .pointer = gep, + }); + length_value = load; + } break; + default: unreachable(); + } + + break; + } + + assert(length_value); + + auto index_alloca = create_alloca(module, { .type = index_type, .name = string_literal("for_each.index") }); + create_store(module, { .source = index_zero, .destination = index_alloca, .type = index_type }); + + LLVMBuildBr(module->llvm.builder, entry_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + auto header_index_load = create_load(module, { .type = index_type, .pointer = index_alloca }); + auto index_compare = LLVMBuildICmp(module->llvm.builder, LLVMIntULT, header_index_load, length_value, ""); + LLVMBuildCondBr(module->llvm.builder, index_compare, body_block, exit_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, body_block); + auto body_index_load = create_load(module, { .type = index_type, .pointer = index_alloca }); + + local = statement->for_each.first_local; + + for (u64 i = 0; i < right_values.length; i += 1, local = local->next) + { + auto kind = left_values[i]; + auto right = right_values[i]; + + auto aggregate_type = right->type->pointer.element_type; + + LLVMValueRef element_pointer_value = 0; + + switch (aggregate_type->id) + { + case TypeId::array: + { + LLVMValueRef indices[] = { + index_zero, + body_index_load, + }; + element_pointer_value = create_gep(module, { + .type = right->type->pointer.element_type->llvm.memory, + .pointer = right->llvm, + .indices = array_to_slice(indices), + }); + } break; + case TypeId::structure: + { + assert(aggregate_type->structure.is_slice); + + auto load = create_load(module, { + .type = aggregate_type, + .pointer = right->llvm, + }); + auto extract_pointer = LLVMBuildExtractValue(module->llvm.builder, load, 0, ""); + + LLVMValueRef indices[] = { + body_index_load, + }; + auto gep = create_gep(module, { + .type = aggregate_type->structure.fields[0].type->pointer.element_type->llvm.memory, + .pointer = extract_pointer, + .indices = array_to_slice(indices), + }); + element_pointer_value = gep; + } break; + default: unreachable(); + } + + assert(element_pointer_value); + + auto local_type = local->variable.type; + + switch (kind) + { + case ValueKind::right: + { + auto evaluation_kind = get_evaluation_kind(local_type); + if (evaluation_kind == EvaluationKind::scalar || (aggregate_type->id == TypeId::structure && aggregate_type->structure.is_slice)) + { + auto load = create_load(module, { + .type = local_type, + .pointer = element_pointer_value, + }); + + create_store(module, { + .source = load, + .destination = local->variable.storage->llvm, + .type = local_type, + }); + } + else + { + trap(); + } + } break; + case ValueKind::left: + { + create_store(module, { + .source = element_pointer_value, + .destination = local->variable.storage->llvm, + .type = local_type, + }); + } break; + } + } + + analyze_statement(module, &statement->for_each.scope, statement->for_each.predicate, last_line, last_column, last_debug_location); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + LLVMBuildBr(module->llvm.builder, continue_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, continue_block); + + auto continue_index_load = create_load(module, { .type = index_type, .pointer = index_alloca }); + auto inc = LLVMBuildAdd(module->llvm.builder, continue_index_load, LLVMConstInt(index_type->llvm.abi, 1, false), ""); + create_store(module, { .source = inc, .destination = index_alloca, .type = index_type }); + + LLVMBuildBr(module->llvm.builder, entry_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); + } break; + case ForEachKind::range: + { + Local* local = statement->for_each.first_local; + // Assert there is only one + assert(local); + assert(!local->next); + assert(!local->variable.type); + + assert(left_values.length == 1); + + if (right_values.length == 2) + { + auto start = right_values[0]; + auto end = right_values[1]; + + Type* local_type = 0; + + switch (start->id) + { + case ValueId::constant_integer: + { + switch (end->id) + { + case ValueId::constant_integer: + { + trap(); + } break; + default: + { + analyze_type(module, end, 0); + auto end_type = end->type; + assert(end_type); + start->type = end_type; + local_type = end_type; + } break; + } + } break; + default: trap(); + } + + assert(local_type); + + for (auto right: right_values) + { + if (!right->type) + { + analyze_type(module, right, local_type); + } + } + + local->variable.type = local_type; + emit_local_variable(module, local); + emit_value(module, start, TypeKind::memory); + + auto index_alloca = local->variable.storage->llvm; + + create_store(module, { + .source = start->llvm, + .destination = index_alloca, + .type = local_type, + }); + + LLVMBuildBr(module->llvm.builder, entry_block); + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + auto header_index_load = create_load(module, { + .type = local_type, + .pointer = index_alloca, + }); + emit_value(module, end, TypeKind::abi); + auto length_value = end->llvm; + auto index_compare = LLVMBuildICmp(module->llvm.builder, LLVMIntULT, header_index_load, length_value, ""); + LLVMBuildCondBr(module->llvm.builder, index_compare, body_block, exit_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, body_block); + analyze_statement(module, &statement->for_each.scope, statement->for_each.predicate, last_line, last_column, last_debug_location); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + LLVMBuildBr(module->llvm.builder, continue_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, continue_block); + + auto continue_index_load = create_load(module, { + .type = local_type, + .pointer = index_alloca, + }); + + auto inc = LLVMBuildAdd(module->llvm.builder, continue_index_load, LLVMConstInt(local_type->llvm.abi, 1, false), ""); + create_store(module, { + .source = inc, + .destination = index_alloca, + .type = local_type, + }); + + LLVMBuildBr(module->llvm.builder, entry_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); + } + else + { + // TODO: case for reverse range + trap(); + } + } break; + } + + // END OF SCOPE + module->llvm.continue_block = previous_continue_block; + module->llvm.exit_block = previous_exit_block; + } break; + case StatementId::break_st: + { + auto exit_block = module->llvm.exit_block; + if (!exit_block) + { + report_error(); + } + + LLVMBuildBr(module->llvm.builder, exit_block); + LLVMClearInsertionPosition(module->llvm.builder); + } break; + case StatementId::continue_st: + { + auto continue_block = module->llvm.continue_block; + if (!continue_block) + { + report_error(); + } + + LLVMBuildBr(module->llvm.builder, continue_block); + LLVMClearInsertionPosition(module->llvm.builder); + } 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) + { + if (global->emitted) + { + continue; + } + + 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), + }, + }; + auto semantic_return_type = function_type->semantic_return_type; + function_type->return_abi = abi_system_classify_return_type(module, resolve_alias(module, 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) + { + assert(!function_type->return_abi.flags.sret_after_this); + function_type->available_registers.system_v.gpr -= 1; + auto indirect_type = get_pointer_type(module, function_type->return_abi.semantic_type); + resolve_type_in_place(module, indirect_type); + + auto abi_index = abi_argument_type_count; + abi_argument_type_buffer[abi_index] = indirect_type; + llvm_abi_argument_type_buffer[abi_index] = indirect_type->llvm.abi; + abi_argument_type_count += 1; + } + + 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); + + 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: + { + auto indirect_argument_index = function_type->return_abi.flags.sret_after_this; + if (function_type->return_abi.flags.sret_after_this) + { + trap(); + } + + global->variable.storage->function.llvm.return_alloca = llvm_abi_arguments[indirect_argument_index]; + + if (!function_type->return_abi.flags.indirect_by_value) + { + 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 + { + auto alloca = create_alloca(module, { + .type = argument_abi.semantic_type, + .name = argument->variable.name, + }); + create_store(module, { + .source = first_argument, + .destination = alloca, + .type = argument_abi.semantic_type, + }); + + semantic_argument_storage = alloca; + } + } + else + { + auto is_fixed_vector_type = false; + if (is_fixed_vector_type) + { + trap(); + } + + if (coerce_to_type->id == TypeId::structure && coerce_to_type->structure.fields.length > 1 && argument_abi.flags.kind == AbiKind::direct && !argument_abi.flags.can_be_flattened) + { + auto contains_homogeneous_scalable_vector_types = false; + if (contains_homogeneous_scalable_vector_types) + { + trap(); + } + } + + auto alloca = create_alloca(module, { .type = argument_abi.semantic_type }); + LLVMValueRef pointer; + Type* pointer_type; + if (argument_abi.attributes.direct.offset > 0) + { + trap(); + } + else + { + pointer = alloca; + pointer_type = argument_abi.semantic_type; + } + + if (coerce_to_type->id == TypeId::structure && coerce_to_type->structure.fields.length > 1 && argument_abi.flags.kind == AbiKind::direct && argument_abi.flags.can_be_flattened) + { + auto struct_size = get_byte_size(coerce_to_type); + auto pointer_element_size = get_byte_size(pointer_type); + auto is_scalable = false; + + if (is_scalable) + { + trap(); + } + else + { + auto source_size = struct_size; + auto destination_size = pointer_element_size; + auto address_alignment = get_byte_alignment(argument_abi.semantic_type); + + LLVMValueRef address; + if (source_size <= destination_size) + { + address = alloca; + } + else + { + address = create_alloca(module, { .type = coerce_to_type, .name = string_literal("coerce"), .alignment = address_alignment }); + } + + assert(coerce_to_type->structure.fields.length == argument_abi.abi_count); + + resolve_type_in_place(module, coerce_to_type); + + for (u64 i = 0; i < coerce_to_type->structure.fields.length; i += 1) + { + auto gep = LLVMBuildStructGEP2(module->llvm.builder, coerce_to_type->llvm.abi, address, i, ""); + create_store(module, { + .source = argument_abi_arguments[i], + .destination = gep, + .type = coerce_to_type->structure.fields[i].type, + }); + } + + if (source_size > destination_size) + { + unused(pointer); + trap(); + } + } + } + + semantic_argument_storage = alloca; + } + } break; + case AbiKind::indirect: + { + assert(argument_abi.abi_count == 1); + auto evaluation_kind = get_evaluation_kind(argument_abi.semantic_type); + switch (evaluation_kind) + { + default: + { + if (argument_abi.flags.indirect_realign || argument_abi.flags.kind == AbiKind::indirect_aliased) + { + trap(); + } + + auto use_indirect_debug_address = !argument_abi.flags.indirect_by_value; + if (use_indirect_debug_address) + { + trap(); + } + + auto llvm_argument = argument_abi_arguments[0]; + semantic_argument_storage = llvm_argument; + } break; + case EvaluationKind::scalar: 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 + { + emit_block(module, return_block); + } + } + 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) + { + emit_block(module, return_block); + } + } + + 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 (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 + { + LLVMValueRef source = 0; + if (function_type->return_abi.attributes.direct.offset == 0) + { + source = return_alloca; + } + else + { + trap(); + } + assert(source); + + auto source_type = function_type->return_abi.semantic_type; + auto destination_type = coerce_to_type; + auto result = create_coerced_load(module, source, source_type, destination_type); + return_value = result; + } + } break; + case AbiKind::indirect: + { + auto evaluation_kind = get_evaluation_kind(function_type->return_abi.semantic_type); + switch (evaluation_kind) + { + case EvaluationKind::scalar: trap(); + case EvaluationKind::aggregate: break; + case EvaluationKind::complex: trap(); + } + } break; + default: unreachable(); + } + + LLVMBuildRet(module->llvm.builder, return_value); + } + + // END OF SCOPE + module->current_function = 0; + } + } break; + case ValueId::global: + { + analyze_value(module, global->variable.initial_value, global->variable.type, TypeKind::memory); + + auto initial_value_type = global->variable.initial_value->type; + + if (!global->variable.type) + { + global->variable.type = initial_value_type; + } + + auto global_type = global->variable.type; + + if (global_type != initial_value_type) + { + report_error(); + } + + resolve_type_in_place(module, global_type); + + bool is_constant = false; + LLVMLinkage linkage; + switch (global->linkage) + { + case Linkage::internal: linkage = LLVMInternalLinkage; break; + case Linkage::external: linkage = LLVMExternalLinkage; break; + } + + LLVMValueRef before = 0; + LLVMThreadLocalMode thread_local_mode = LLVMNotThreadLocal; + unsigned address_space = 0; + bool externally_initialized = false; + + auto global_llvm = llvm_module_create_global_variable(module->llvm.module, global_type->llvm.memory, is_constant, linkage, global->variable.initial_value->llvm, global->variable.name, before, thread_local_mode, address_space, externally_initialized); + auto alignment = get_byte_alignment(global_type); + LLVMSetAlignment(global_llvm, alignment); + global->variable.storage->llvm = global_llvm; + global->variable.storage->type = get_pointer_type(module, global_type); + + if (module->has_debug_info) + { + auto name = global->variable.name; + auto linkage_name = name; + auto local_to_unit = global->linkage == Linkage::internal; + auto global_debug = LLVMDIBuilderCreateGlobalVariableExpression(module->llvm.di_builder, module->scope.llvm, (char*)name.pointer, name.length, (char*)linkage_name.pointer, linkage_name.length, module->llvm.file, global->variable.line, global_type->llvm.debug, local_to_unit, null_expression(module), 0, alignment * 8); + LLVMGlobalSetMetadata(global_llvm, 0, global_debug); + } + } 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); + } +} diff --git a/src/lib.h b/src/lib.h index 6281273..d5a8c8c 100644 --- a/src/lib.h +++ b/src/lib.h @@ -8,6 +8,7 @@ #define breakpoint() __builtin_debugtrap() #define string_literal_length(s) (sizeof(s) - 1) #define string_literal(s) ((String){ .pointer = (u8*)(s), .length = string_literal_length(s), }) +#define split_string_literal(s) (char*)(s), string_literal_length(s) #define offsetof(S, f) __builtin_offsetof(S, f) #define array_length(arr) sizeof(arr) / sizeof((arr)[0]) @@ -16,9 +17,9 @@ #define backing_type(E) __underlying_type(E) #define unreachable_raw() __builtin_unreachable() -#define trap_raw() __builtin_trap() +#define trap() __builtin_trap() #if BB_DEBUG -#define unreachable() trap_raw() +#define unreachable() trap() #else #define unreachable() unreachable_raw() #endif @@ -300,14 +301,32 @@ fn void* os_reserve(void* base, u64 size, ProtectionFlags protection, MapFlags m .read = protection.read, .write = protection.write, .execute = protection.execute, + .sem = 0, + ._ = 0, }; auto map_flags = MAP { .type = map.priv ? MAP::Type::priv : MAP::Type::shared, + .fixed = 0, .anonymous = map.anonymous, + .bit32 = 0, + ._0 = 0, + .grows_down = 0, + ._1 = 0, + .deny_write = 0, + .executable = 0, + .locked = 0, .no_reserve = map.no_reserve, .populate = map.populate, + .non_block = 0, + .stack = 0, + .huge_tlb = 0, + .sync = 0, + .fixed_no_replace = 0, + ._2 = 0, + .uninitialized = 0, + ._3 = 0, }; auto* address = mmap(base, size, protection_flags, map_flags, -1, 0); @@ -323,6 +342,8 @@ fn void os_commit(void* address, u64 size, ProtectionFlags protection) .read = protection.read, .write = protection.write, .execute = protection.execute, + .sem = 0, + ._ = 0, }; auto result = mprotect(address, size, protection_flags); assert(!result); @@ -616,7 +637,7 @@ fn String file_read(Arena* arena, String file_path) { if (os_is_debugger_present()) { - trap_raw(); + trap(); } exit(1); } @@ -639,3 +660,39 @@ fn u64 next_power_of_two(u64 n) n += 1; return n; } + +fn u8 format_integer_decimal(String buffer, u64 v) +{ + u8 byte_count = 0; + auto value = v; + + if (value != 0) + { + u8 reverse_buffer[64]; + u8 reverse_index = 0; + + while (value != 0) + { + auto digit_value = (u8)(value % 10); + auto ascii_character = digit_value + '0'; + value /= 10; + reverse_buffer[reverse_index] = ascii_character; + reverse_index += 1; + } + + while (reverse_index != 0) + { + reverse_index -= 1; + buffer[byte_count] = reverse_buffer[reverse_index]; + byte_count += 1; + } + } + else + { + buffer[0] = '0'; + byte_count = 1; + } + + return byte_count; +} + diff --git a/src/llvm.cpp b/src/llvm.cpp index 60324b0..fcd66dc 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -26,97 +26,119 @@ #include "lld/Common/CommonLinkerContext.h" -using namespace llvm; - -fn StringRef string_ref(String string) +fn llvm::StringRef string_ref(String string) { - return StringRef((char*)string.pointer, string.length); + return llvm::StringRef((char*)string.pointer, string.length); } -EXPORT Module* llvm_context_create_module(LLVMContext& context, String name) +EXPORT LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String name) { - return new Module(string_ref(name), context); + auto module = new llvm::Module(string_ref(name), *llvm::unwrap(context)); + return wrap(module); } -EXPORT unsigned llvm_integer_type_get_bit_count(const IntegerType& integer_type) +EXPORT unsigned llvm_integer_type_get_bit_count(const llvm::IntegerType& integer_type) { auto result = integer_type.getBitWidth(); return result; } -EXPORT GlobalVariable* llvm_module_create_global_variable(Module& module, Type* type, bool is_constant, GlobalValue::LinkageTypes linkage_type, Constant* initial_value, String name, GlobalVariable* before, GlobalValue::ThreadLocalMode thread_local_mode, unsigned address_space, bool externally_initialized) +EXPORT LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMValueRef before, LLVMThreadLocalMode thread_local_mode, unsigned address_space, bool externally_initialized) { - auto* global = new GlobalVariable(module, type, is_constant, linkage_type, initial_value, string_ref(name), before, thread_local_mode, address_space, externally_initialized); - return global; + llvm::GlobalValue::LinkageTypes linkage; + switch (linkage_type) + { + case LLVMExternalLinkage: linkage = llvm::GlobalValue::ExternalLinkage; break; + case LLVMInternalLinkage: linkage = llvm::GlobalValue::InternalLinkage; break; + default: trap(); + } + + llvm::GlobalValue::ThreadLocalMode tlm; + switch (thread_local_mode) + { + case LLVMNotThreadLocal: tlm = llvm::GlobalValue::NotThreadLocal; break; + default: trap(); + } + auto* global = new llvm::GlobalVariable(*llvm::unwrap(module), llvm::unwrap(type), is_constant, linkage, llvm::unwrap(initial_value), string_ref(name), before ? llvm::unwrap(before) : 0, tlm, address_space, externally_initialized); + return wrap(global); } -EXPORT void llvm_global_variable_add_debug_info(GlobalVariable& global, DIGlobalVariableExpression* debug_global_variable) +EXPORT void llvm_global_variable_add_debug_info(llvm::GlobalVariable& global, llvm::DIGlobalVariableExpression* debug_global_variable) { global.addDebugInfo(debug_global_variable); } -EXPORT void llvm_global_variable_delete(GlobalVariable* global) +EXPORT void llvm_global_variable_delete(llvm::GlobalVariable* global) { delete global; } -EXPORT void llvm_subprogram_replace_type(DISubprogram& subprogram, DISubroutineType* subroutine_type) +EXPORT void llvm_subprogram_replace_type(llvm::DISubprogram& subprogram, llvm::DISubroutineType* subroutine_type) { subprogram.replaceType(subroutine_type); } -EXPORT Function* llvm_module_create_function(Module* module, FunctionType* function_type, GlobalValue::LinkageTypes linkage_type, unsigned address_space, String name) +EXPORT LLVMValueRef llvm_module_create_function(LLVMModuleRef module, LLVMTypeRef function_type, LLVMLinkage linkage_type, unsigned address_space, String name) { - auto* function = Function::Create(function_type, linkage_type, address_space, string_ref(name), module); - return function; + llvm::GlobalValue::LinkageTypes llvm_linkage_type; + switch (linkage_type) + { + case LLVMExternalLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::ExternalLinkage; break; + case LLVMAvailableExternallyLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::AvailableExternallyLinkage; break; + case LLVMLinkOnceAnyLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::LinkOnceAnyLinkage; break; + case LLVMLinkOnceODRLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::LinkOnceODRLinkage; break; + case LLVMWeakAnyLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::WeakAnyLinkage; break; + case LLVMWeakODRLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::WeakODRLinkage; break; + case LLVMAppendingLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::AppendingLinkage; break; + case LLVMInternalLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::InternalLinkage; break; + case LLVMPrivateLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::PrivateLinkage; break; + case LLVMExternalWeakLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::ExternalWeakLinkage; break; + case LLVMCommonLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::CommonLinkage; break; + default: + trap(); + } + auto* function = llvm::Function::Create(llvm::unwrap(function_type), llvm_linkage_type, address_space, string_ref(name), llvm::unwrap(module)); + return wrap(function); } -EXPORT StructType* llvm_context_create_forward_declared_struct_type(LLVMContext& context, String name) +EXPORT LLVMBasicBlockRef llvm_context_create_basic_block(LLVMContextRef context, String name, LLVMValueRef parent_function) { - auto* struct_type = StructType::create(context, string_ref(name)); - return struct_type; + auto* basic_block = llvm::BasicBlock::Create(*llvm::unwrap(context), string_ref(name), parent_function ? llvm::unwrap(parent_function) : 0); + return wrap(basic_block); } -EXPORT StructType* llvm_context_create_struct_type(LLVMContext& context, Type** type_pointer, size_t type_count, String name, bool is_packed) +EXPORT bool llvm_value_has_one_use(LLVMValueRef value) { - auto types = ArrayRef(type_pointer, type_count); - auto* struct_type = StructType::create(context, types, string_ref(name), is_packed); - return struct_type; -} - -EXPORT StructType* llvm_context_get_struct_type(LLVMContext& context, Type** type_pointer, size_t type_count, bool is_packed) -{ - auto types = ArrayRef(type_pointer, type_count); - auto* struct_type = StructType::get(context, types, is_packed); - return struct_type; -} - -EXPORT BasicBlock* llvm_context_create_basic_block(LLVMContext& context, String name, Function* parent) -{ - auto* basic_block = BasicBlock::Create(context, string_ref(name), parent); - return basic_block; -} - -EXPORT bool llvm_value_has_one_use(Value& value) -{ - auto result = value.hasOneUse(); + auto v = llvm::unwrap(value); + auto result = v->hasOneUse(); return result; } -EXPORT Value* llvm_basic_block_user_begin(BasicBlock* basic_block) +EXPORT LLVMValueRef llvm_basic_block_user_begin(LLVMBasicBlockRef basic_block) { - Value* value = *basic_block->user_begin(); - return value; + llvm::Value* value = *llvm::unwrap(basic_block)->user_begin(); + return wrap(value); } -EXPORT void llvm_basic_block_delete(BasicBlock* basic_block) +EXPORT void llvm_basic_block_delete(LLVMBasicBlockRef basic_block) { - delete basic_block; + delete llvm::unwrap(basic_block); } -EXPORT BranchInst* llvm_value_to_branch(Value* value) +EXPORT llvm::BranchInst* llvm_value_to_branch(llvm::Value* value) { - auto* result = dyn_cast(value); + auto* result = dyn_cast(value); return result; } @@ -124,7 +146,7 @@ EXPORT BranchInst* llvm_value_to_branch(Value* value) // for something immediately preceding the IP. Sometimes this can // happen with how we generate implicit-returns; it can also happen // with noreturn cleanups. -fn StoreInst* get_store_if_valid(User* user, Value* return_alloca, Type* element_type) +fn llvm::StoreInst* get_store_if_valid(llvm::User* user, llvm::Value* return_alloca, llvm::Type* element_type) { auto *SI = dyn_cast(user); if (!SI || SI->getPointerOperand() != return_alloca || @@ -142,62 +164,65 @@ fn StoreInst* get_store_if_valid(User* user, Value* return_alloca, Type* element // copy of static llvm::StoreInst *findDominatingStoreToReturnValue(CodeGenFunction &CGF) { // in clang/lib/CodeGen/CGCall.cpp:3526 in LLVM 19 -EXPORT StoreInst* llvm_find_return_value_dominating_store(IRBuilder<>& builder, Value* return_alloca, Type* element_type) +EXPORT LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LLVMValueRef ra, LLVMTypeRef et) { - // Check if a User is a store which pointerOperand is the ReturnValue. - // We are looking for stores to the ReturnValue, not for stores of the - // ReturnValue to some other location. - if (!return_alloca->hasOneUse()) { - llvm::BasicBlock *IP = builder.GetInsertBlock(); - if (IP->empty()) return nullptr; + auto builder = llvm::unwrap(b); + auto return_alloca = llvm::unwrap(ra); + auto element_type = llvm::unwrap(et); + // Check if a User is a store which pointerOperand is the ReturnValue. + // We are looking for stores to the ReturnValue, not for stores of the + // ReturnValue to some other location. + if (!return_alloca->hasOneUse()) { + llvm::BasicBlock *IP = builder->GetInsertBlock(); + if (IP->empty()) return nullptr; - // Look at directly preceding instruction, skipping bitcasts and lifetime - // markers. - for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) { - if (isa(&I)) - continue; - if (auto *II = dyn_cast(&I)) - if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end) - continue; + // Look at directly preceding instruction, skipping bitcasts and lifetime + // markers. + for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) { + if (isa(&I)) + continue; + if (auto *II = dyn_cast(&I)) + if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end) + continue; - return get_store_if_valid(&I, return_alloca, element_type); + return wrap(get_store_if_valid(&I, return_alloca, element_type)); + } + return nullptr; } - return nullptr; - } - llvm::StoreInst *store = get_store_if_valid(return_alloca->user_back(), return_alloca, element_type); - if (!store) return nullptr; + llvm::StoreInst *store = get_store_if_valid(return_alloca->user_back(), return_alloca, element_type); + if (!store) return nullptr; - // Now do a first-and-dirty dominance check: just walk up the - // single-predecessors chain from the current insertion point. - llvm::BasicBlock *StoreBB = store->getParent(); - llvm::BasicBlock *IP = builder.GetInsertBlock(); - llvm::SmallPtrSet SeenBBs; - while (IP != StoreBB) { - if (!SeenBBs.insert(IP).second || !(IP = IP->getSinglePredecessor())) - return nullptr; - } + // Now do a first-and-dirty dominance check: just walk up the + // single-predecessors chain from the current insertion point. + llvm::BasicBlock *StoreBB = store->getParent(); + llvm::BasicBlock *IP = builder->GetInsertBlock(); + llvm::SmallPtrSet SeenBBs; + while (IP != StoreBB) { + if (!SeenBBs.insert(IP).second || !(IP = IP->getSinglePredecessor())) + return nullptr; + } - // Okay, the store's basic block dominates the insertion point; we - // can do our thing. - return store; + // Okay, the store's basic block dominates the insertion point; we + // can do our thing. + return wrap(store); } -EXPORT bool llvm_value_use_empty(Value& value) +EXPORT bool llvm_value_use_empty(LLVMValueRef value) { - return value.use_empty(); + return llvm::unwrap(value)->use_empty(); } -EXPORT bool llvm_basic_block_is_empty(BasicBlock& basic_block) +EXPORT bool llvm_basic_block_is_empty(LLVMBasicBlockRef basic_block) { - return basic_block.empty(); + return llvm::unwrap(basic_block)->empty(); } -EXPORT AllocaInst* llvm_builder_create_alloca(IRBuilder<>& builder, Type* type, unsigned address_space, String name) +EXPORT LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef b, LLVMTypeRef type, unsigned address_space, u32 alignment, String name) { - const DataLayout &data_layout = builder.GetInsertBlock()->getDataLayout(); - Align alignment = data_layout.getABITypeAlign(type); - return builder.Insert(new AllocaInst(type, address_space, 0, alignment), string_ref(name)); + auto& builder = *llvm::unwrap(b); + auto llvm_alignment = llvm::Align(alignment); + return wrap(builder.Insert(new llvm::AllocaInst(llvm::unwrap(type), address_space, 0, llvm_alignment), string_ref(name))); } enum class BBLLVMAttributeFramePointerKind : u8 @@ -263,445 +288,312 @@ enum class BBLLVMFPClassTest : unsigned AllFlags = Nan | Inf | Finite, }; -enum class BBLLVMUWTableKind +fn llvm::AttributeSet build_argument_attributes(LLVMContextRef context, BBLLVMArgumentAttributes* attributes) { - None = 0, ///< No unwind table requested - Sync = 1, ///< "Synchronous" unwind tables - Async = 2, ///< "Asynchronous" unwind tables (instr precise) - Default = 2, -}; + llvm::AttrBuilder builder(*llvm::unwrap(context)); -struct BBLLVMArgumentAttributes -{ - Type* semantic_type; - Type* abi_type; - u64 dereferenceable_bytes; - u32 alignment; - u32 no_alias:1; - u32 non_null:1; - u32 no_undef:1; - u32 sign_extend:1; - u32 zero_extend:1; - u32 in_reg:1; - u32 no_fp_class:10; - u32 struct_return:1; - u32 writable:1; - u32 dead_on_unwind:1; - u32 in_alloca:1; - u32 dereferenceable:1; - u32 dereferenceable_or_null:1; - u32 nest:1; - u32 by_value:1; - u32 by_reference:1; - u32 no_capture:1; - u32 _:6; -}; - -static_assert(sizeof(BBLLVMArgumentAttributes) == 2 * sizeof(Type*) + 2 * sizeof(u64)); - -fn AttributeSet build_argument_attributes(LLVMContext& context, const BBLLVMArgumentAttributes& attributes) -{ - AttrBuilder builder(context); - - if (attributes.alignment) + if (attributes->alignment) { - builder.addAlignmentAttr(attributes.alignment); + builder.addAlignmentAttr(attributes->alignment); } - if (attributes.no_alias) + if (attributes->no_alias) { - builder.addAttribute(Attribute::NoAlias); + builder.addAttribute(llvm::Attribute::NoAlias); } - if (attributes.non_null) + if (attributes->non_null) { - builder.addAttribute(Attribute::NonNull); + builder.addAttribute(llvm::Attribute::NonNull); } - if (attributes.no_undef) + if (attributes->no_undef) { - builder.addAttribute(Attribute::NoUndef); + builder.addAttribute(llvm::Attribute::NoUndef); } - if (attributes.sign_extend) + if (attributes->sign_extend) { - builder.addAttribute(Attribute::SExt); + builder.addAttribute(llvm::Attribute::SExt); } - if (attributes.zero_extend) + if (attributes->zero_extend) { - builder.addAttribute(Attribute::ZExt); + builder.addAttribute(llvm::Attribute::ZExt); } - if (attributes.in_reg) + if (attributes->in_reg) { - builder.addAttribute(Attribute::InReg); + builder.addAttribute(llvm::Attribute::InReg); } - if (attributes.no_fp_class) + if (attributes->no_fp_class) { __builtin_trap(); // TODO } - if (attributes.struct_return) + if (attributes->struct_return) { - builder.addStructRetAttr(attributes.semantic_type); + builder.addStructRetAttr(llvm::unwrap(attributes->semantic_type)); } - if (attributes.writable) + if (attributes->writable) { - builder.addAttribute(Attribute::Writable); + builder.addAttribute(llvm::Attribute::Writable); } - if (attributes.dead_on_unwind) + if (attributes->dead_on_unwind) { - builder.addAttribute(Attribute::DeadOnUnwind); + builder.addAttribute(llvm::Attribute::DeadOnUnwind); } - if (attributes.in_alloca) + if (attributes->in_alloca) { __builtin_trap(); // TODO } - if (attributes.dereferenceable) + if (attributes->dereferenceable) { - builder.addDereferenceableAttr(attributes.dereferenceable_bytes); + builder.addDereferenceableAttr(attributes->dereferenceable_bytes); } - if (attributes.dereferenceable_or_null) + if (attributes->dereferenceable_or_null) { - builder.addDereferenceableOrNullAttr(attributes.dereferenceable_bytes); + builder.addDereferenceableOrNullAttr(attributes->dereferenceable_bytes); } - if (attributes.nest) + if (attributes->nest) { - builder.addAttribute(Attribute::Nest); + builder.addAttribute(llvm::Attribute::Nest); } - if (attributes.by_value) + if (attributes->by_value) { - builder.addByValAttr(attributes.semantic_type); + builder.addByValAttr(llvm::unwrap(attributes->semantic_type)); } - if (attributes.by_reference) + if (attributes->by_reference) { - builder.addByRefAttr(attributes.semantic_type); + builder.addByRefAttr(llvm::unwrap(attributes->semantic_type)); } - if (attributes.no_capture) + if (attributes->no_capture) { - builder.addAttribute(Attribute::NoCapture); + builder.addAttribute(llvm::Attribute::NoCapture); } - auto attribute_set = AttributeSet::get(context, builder); + auto attribute_set = llvm::AttributeSet::get(*llvm::unwrap(context), builder); return attribute_set; } -struct BBLLVMFunctionAttributesFlags0 +inline fn BBLLVMAttributeList llvm_attribute_list_to_abi(llvm::AttributeList list) { - u64 noreturn:1; - u64 cmse_ns_call:1; - u64 nounwind:1; - u64 returns_twice:1; - u64 cold:1; - u64 hot:1; - u64 no_duplicate:1; - u64 convergent:1; - u64 no_merge:1; - u64 will_return:1; - u64 no_caller_saved_registers:1; - u64 no_cf_check:1; - u64 no_callback:1; - u64 alloc_size:1; - u64 uniform_work_group_size:1; - u64 aarch64_pstate_sm_body:1; - u64 aarch64_pstate_sm_enabled:1; - u64 aarch64_pstate_sm_compatible:1; - u64 aarch64_preserves_za:1; - u64 aarch64_in_za:1; - u64 aarch64_out_za:1; - u64 aarch64_inout_za:1; - u64 aarch64_preserves_zt0:1; - u64 aarch64_in_zt0:1; - u64 aarch64_out_zt0:1; - u64 aarch64_inout_zt0:1; - u64 optimize_for_size:1; - u64 min_size:1; - u64 no_red_zone:1; - u64 indirect_tls_seg_refs:1; - u64 no_implicit_floats:1; - u64 sample_profile_suffix_elision_policy:1; - u64 memory_none:1; - u64 memory_readonly:1; - u64 memory_inaccessible_or_arg_memory_only:1; - u64 memory_arg_memory_only:1; - u64 strict_fp:1; - u64 no_inline:1; - u64 always_inline:1; - u64 guard_no_cf:1; + static_assert(sizeof(llvm::AttributeList) == sizeof(uintptr_t)); + static_assert(sizeof(BBLLVMAttributeList) == sizeof(uintptr_t)); - // TODO: branch protection function attributes - // TODO: cpu features + return list.getRawPointer(); +} - // Call-site begin - u64 call_no_builtins:1; - - u64 definition_frame_pointer_kind:2; - u64 definition_less_precise_fpmad:1; - u64 definition_null_pointer_is_valid:1; - u64 definition_no_trapping_fp_math:1; - u64 definition_no_infs_fp_math:1; - u64 definition_no_nans_fp_math:1; - u64 definition_approx_func_fp_math:1; - u64 definition_unsafe_fp_math:1; - u64 definition_use_soft_float:1; - u64 definition_no_signed_zeroes_fp_math:1; - u64 definition_stack_realignment:1; - u64 definition_backchain:1; - u64 definition_split_stack:1; - u64 definition_speculative_load_hardening:1; - u64 definition_zero_call_used_registers:4; - // TODO: denormal builtins - u64 definition_non_lazy_bind:1; - u64 definition_cmse_nonsecure_entry:1; - u64 definition_unwind_table_kind:2; -}; - -static_assert(sizeof(BBLLVMFunctionAttributesFlags0) == sizeof(u64)); - -struct BBLLVMFunctionAttributesFlags1 +inline fn llvm::AttributeList abi_attribute_list_to_llvm(BBLLVMAttributeList list) { - u64 definition_disable_tail_calls:1; - u64 definition_stack_protect_strong:1; - u64 definition_stack_protect:1; - u64 definition_stack_protect_req:1; - u64 definition_aarch64_new_za:1; - u64 definition_aarch64_new_zt0:1; - u64 definition_optimize_none:1; - u64 definition_naked:1; - u64 definition_inline_hint:1; - u64 _:55; -}; + static_assert(sizeof(llvm::AttributeList) == sizeof(uintptr_t)); + static_assert(sizeof(BBLLVMAttributeList) == sizeof(uintptr_t)); + auto attribute_list = *(llvm::AttributeList*)&list; + return attribute_list; +} -static_assert(sizeof(BBLLVMFunctionAttributesFlags1) == sizeof(u64)); - -struct BBLLVMFunctionAttributes +EXPORT BBLLVMAttributeList llvm_attribute_list_build(LLVMContextRef context, BBLLVMAttributeListOptions* attributes, bool call_site) { - String prefer_vector_width; - String stack_protector_buffer_size; - String definition_probe_stack; - String definition_stack_probe_size; + llvm::AttrBuilder function_attribute_builder(*llvm::unwrap(context)); - BBLLVMFunctionAttributesFlags0 flags0; - BBLLVMFunctionAttributesFlags1 flags1; -}; - -static_assert(sizeof(BBLLVMFunctionAttributes) == 10 * sizeof(u64)); - -struct BBLLVMAttributeList -{ - BBLLVMFunctionAttributes function; - BBLLVMArgumentAttributes return_; - const BBLLVMArgumentAttributes* argument_pointer; - u64 argument_count; -}; - -static_assert(sizeof(BBLLVMAttributeList) == sizeof(BBLLVMFunctionAttributes) + sizeof(BBLLVMArgumentAttributes) + sizeof(void*) + sizeof(u64)); - -typedef void* BBLLVMAttributeListHandle; - -EXPORT BBLLVMAttributeListHandle llvm_attribute_list_build(LLVMContext& context, const BBLLVMAttributeList& attributes, bool call_site) -{ - AttrBuilder function_attribute_builder(context); - - if (attributes.function.prefer_vector_width.length) + if (attributes->function.prefer_vector_width.length) { - function_attribute_builder.addAttribute("prefer-vector-width", string_ref(attributes.function.prefer_vector_width)); + function_attribute_builder.addAttribute("prefer-vector-width", string_ref(attributes->function.prefer_vector_width)); } - if (attributes.function.stack_protector_buffer_size.length) + if (attributes->function.stack_protector_buffer_size.length) { - function_attribute_builder.addAttribute("stack-protector-buffer-size", string_ref(attributes.function.stack_protector_buffer_size)); + function_attribute_builder.addAttribute("stack-protector-buffer-size", string_ref(attributes->function.stack_protector_buffer_size)); } - if (attributes.function.flags0.noreturn) + if (attributes->function.flags0.noreturn) { - function_attribute_builder.addAttribute(Attribute::NoReturn); + function_attribute_builder.addAttribute(llvm::Attribute::NoReturn); } - if (attributes.function.flags0.cmse_ns_call) + if (attributes->function.flags0.cmse_ns_call) { function_attribute_builder.addAttribute("cmse_nonsecure_call"); } - if (attributes.function.flags0.nounwind) + if (attributes->function.flags0.nounwind) { - function_attribute_builder.addAttribute(Attribute::NoUnwind); + function_attribute_builder.addAttribute(llvm::Attribute::NoUnwind); } - if (attributes.function.flags0.returns_twice) + if (attributes->function.flags0.returns_twice) { - function_attribute_builder.addAttribute(Attribute::ReturnsTwice); + function_attribute_builder.addAttribute(llvm::Attribute::ReturnsTwice); } - if (attributes.function.flags0.cold) + if (attributes->function.flags0.cold) { - function_attribute_builder.addAttribute(Attribute::Cold); + function_attribute_builder.addAttribute(llvm::Attribute::Cold); } - if (attributes.function.flags0.hot) + if (attributes->function.flags0.hot) { - function_attribute_builder.addAttribute(Attribute::Hot); + function_attribute_builder.addAttribute(llvm::Attribute::Hot); } - if (attributes.function.flags0.no_duplicate) + if (attributes->function.flags0.no_duplicate) { - function_attribute_builder.addAttribute(Attribute::NoDuplicate); + function_attribute_builder.addAttribute(llvm::Attribute::NoDuplicate); } - if (attributes.function.flags0.convergent) + if (attributes->function.flags0.convergent) { - function_attribute_builder.addAttribute(Attribute::Convergent); + function_attribute_builder.addAttribute(llvm::Attribute::Convergent); } - if (attributes.function.flags0.no_merge) + if (attributes->function.flags0.no_merge) { - function_attribute_builder.addAttribute(Attribute::NoMerge); + function_attribute_builder.addAttribute(llvm::Attribute::NoMerge); } - if (attributes.function.flags0.will_return) + if (attributes->function.flags0.will_return) { - function_attribute_builder.addAttribute(Attribute::WillReturn); + function_attribute_builder.addAttribute(llvm::Attribute::WillReturn); } - if (attributes.function.flags0.no_caller_saved_registers) + if (attributes->function.flags0.no_caller_saved_registers) { function_attribute_builder.addAttribute("no-caller-saved-registers"); } - if (attributes.function.flags0.no_cf_check) + if (attributes->function.flags0.no_cf_check) { - function_attribute_builder.addAttribute(Attribute::NoCfCheck); + function_attribute_builder.addAttribute(llvm::Attribute::NoCfCheck); } - if (attributes.function.flags0.no_callback) + if (attributes->function.flags0.no_callback) { - function_attribute_builder.addAttribute(Attribute::NoCallback); + function_attribute_builder.addAttribute(llvm::Attribute::NoCallback); } - if (attributes.function.flags0.alloc_size) + if (attributes->function.flags0.alloc_size) { __builtin_trap(); // TODO } - if (attributes.function.flags0.uniform_work_group_size) + if (attributes->function.flags0.uniform_work_group_size) { __builtin_trap(); // TODO } - if (attributes.function.flags0.aarch64_pstate_sm_body) + if (attributes->function.flags0.aarch64_pstate_sm_body) { function_attribute_builder.addAttribute("aarch64_pstate_sm_body"); } - if (attributes.function.flags0.aarch64_pstate_sm_enabled) + if (attributes->function.flags0.aarch64_pstate_sm_enabled) { function_attribute_builder.addAttribute("aarch64_pstate_sm_enabled"); } - if (attributes.function.flags0.aarch64_pstate_sm_compatible) + if (attributes->function.flags0.aarch64_pstate_sm_compatible) { function_attribute_builder.addAttribute("aarch64_pstate_sm_compatible"); } - if (attributes.function.flags0.aarch64_preserves_za) + if (attributes->function.flags0.aarch64_preserves_za) { function_attribute_builder.addAttribute("aarch64_preserves_za"); } - if (attributes.function.flags0.aarch64_in_za) + if (attributes->function.flags0.aarch64_in_za) { function_attribute_builder.addAttribute("aarch64_in_za"); } - if (attributes.function.flags0.aarch64_out_za) + if (attributes->function.flags0.aarch64_out_za) { function_attribute_builder.addAttribute("aarch64_out_za"); } - if (attributes.function.flags0.aarch64_inout_za) + if (attributes->function.flags0.aarch64_inout_za) { function_attribute_builder.addAttribute("aarch64_inout_za"); } - if (attributes.function.flags0.aarch64_preserves_zt0) + if (attributes->function.flags0.aarch64_preserves_zt0) { function_attribute_builder.addAttribute("aarch64_preserves_zt0"); } - if (attributes.function.flags0.aarch64_in_zt0) + if (attributes->function.flags0.aarch64_in_zt0) { function_attribute_builder.addAttribute("aarch64_in_zt0"); } - if (attributes.function.flags0.aarch64_out_zt0) + if (attributes->function.flags0.aarch64_out_zt0) { function_attribute_builder.addAttribute("aarch64_out_zt0"); } - if (attributes.function.flags0.aarch64_inout_zt0) + if (attributes->function.flags0.aarch64_inout_zt0) { function_attribute_builder.addAttribute("aarch64_inout_zt0"); } - if (attributes.function.flags0.optimize_for_size) + if (attributes->function.flags0.optimize_for_size) { - function_attribute_builder.addAttribute(Attribute::OptimizeForSize); + function_attribute_builder.addAttribute(llvm::Attribute::OptimizeForSize); } - if (attributes.function.flags0.min_size) + if (attributes->function.flags0.min_size) { - function_attribute_builder.addAttribute(Attribute::MinSize); + function_attribute_builder.addAttribute(llvm::Attribute::MinSize); } - if (attributes.function.flags0.no_red_zone) + if (attributes->function.flags0.no_red_zone) { - function_attribute_builder.addAttribute(Attribute::NoRedZone); + function_attribute_builder.addAttribute(llvm::Attribute::NoRedZone); } - if (attributes.function.flags0.indirect_tls_seg_refs) + if (attributes->function.flags0.indirect_tls_seg_refs) { function_attribute_builder.addAttribute("indirect-tls-seg-refs"); } - if (attributes.function.flags0.no_implicit_floats) + if (attributes->function.flags0.no_implicit_floats) { - function_attribute_builder.addAttribute(Attribute::NoImplicitFloat); + function_attribute_builder.addAttribute(llvm::Attribute::NoImplicitFloat); } - if (attributes.function.flags0.sample_profile_suffix_elision_policy) + if (attributes->function.flags0.sample_profile_suffix_elision_policy) { function_attribute_builder.addAttribute("sample-profile-suffix-elision-policy", "selected"); } - if (attributes.function.flags0.memory_none) + if (attributes->function.flags0.memory_none) { function_attribute_builder.addMemoryAttr(llvm::MemoryEffects::none()); } - if (attributes.function.flags0.memory_readonly) + if (attributes->function.flags0.memory_readonly) { function_attribute_builder.addMemoryAttr(llvm::MemoryEffects::readOnly()); } - if (attributes.function.flags0.memory_inaccessible_or_arg_memory_only) + if (attributes->function.flags0.memory_inaccessible_or_arg_memory_only) { function_attribute_builder.addMemoryAttr(llvm::MemoryEffects::inaccessibleOrArgMemOnly()); } - if (attributes.function.flags0.memory_arg_memory_only) + if (attributes->function.flags0.memory_arg_memory_only) { - Attribute attribute = function_attribute_builder.getAttribute(Attribute::Memory); + llvm::Attribute attribute = function_attribute_builder.getAttribute(llvm::Attribute::Memory); function_attribute_builder.addMemoryAttr(attribute.getMemoryEffects() | llvm::MemoryEffects::argMemOnly()); } @@ -711,25 +603,25 @@ EXPORT BBLLVMAttributeListHandle llvm_attribute_list_build(LLVMContext& context, if (call_site) { - if (attributes.function.flags0.call_no_builtins) + if (attributes->function.flags0.call_no_builtins) { - function_attribute_builder.addAttribute(Attribute::NoBuiltin); + function_attribute_builder.addAttribute(llvm::Attribute::NoBuiltin); } } else { - if (attributes.function.definition_probe_stack.length) + if (attributes->function.definition_probe_stack.length) { - function_attribute_builder.addAttribute("probe-stack", string_ref(attributes.function.definition_probe_stack)); + function_attribute_builder.addAttribute("probe-stack", string_ref(attributes->function.definition_probe_stack)); } - if (attributes.function.definition_stack_probe_size.length) + if (attributes->function.definition_stack_probe_size.length) { - function_attribute_builder.addAttribute("stack-probe-size", string_ref(attributes.function.definition_stack_probe_size)); + function_attribute_builder.addAttribute("stack-probe-size", string_ref(attributes->function.definition_stack_probe_size)); } - StringRef frame_pointer_kind_name; - switch ((BBLLVMAttributeFramePointerKind) attributes.function.flags0.definition_frame_pointer_kind) + llvm::StringRef frame_pointer_kind_name; + switch ((BBLLVMAttributeFramePointerKind) attributes->function.flags0.definition_frame_pointer_kind) { case BBLLVMAttributeFramePointerKind::None: frame_pointer_kind_name = "none"; break; case BBLLVMAttributeFramePointerKind::Reserved: frame_pointer_kind_name = "reserved"; break; @@ -738,184 +630,184 @@ EXPORT BBLLVMAttributeListHandle llvm_attribute_list_build(LLVMContext& context, } function_attribute_builder.addAttribute("frame-pointer", frame_pointer_kind_name); - if (attributes.function.flags0.definition_less_precise_fpmad) + if (attributes->function.flags0.definition_less_precise_fpmad) { function_attribute_builder.addAttribute("less-precise-fp-mad", "true"); } - if (attributes.function.flags0.definition_null_pointer_is_valid) + if (attributes->function.flags0.definition_null_pointer_is_valid) { - function_attribute_builder.addAttribute(Attribute::NullPointerIsValid); + function_attribute_builder.addAttribute(llvm::Attribute::NullPointerIsValid); } - if (attributes.function.flags0.definition_no_trapping_fp_math) + if (attributes->function.flags0.definition_no_trapping_fp_math) { function_attribute_builder.addAttribute("no-trapping-math", "true"); } - if (attributes.function.flags0.definition_no_infs_fp_math) + if (attributes->function.flags0.definition_no_infs_fp_math) { function_attribute_builder.addAttribute("no-infs-fp-math", "true"); } - if (attributes.function.flags0.definition_no_nans_fp_math) + if (attributes->function.flags0.definition_no_nans_fp_math) { function_attribute_builder.addAttribute("no-nans-fp-math", "true"); } - if (attributes.function.flags0.definition_approx_func_fp_math) + if (attributes->function.flags0.definition_approx_func_fp_math) { function_attribute_builder.addAttribute("approx-func-fp-math", "true"); } - if (attributes.function.flags0.definition_unsafe_fp_math) + if (attributes->function.flags0.definition_unsafe_fp_math) { function_attribute_builder.addAttribute("unsafe-fp-math", "true"); } - if (attributes.function.flags0.definition_use_soft_float) + if (attributes->function.flags0.definition_use_soft_float) { function_attribute_builder.addAttribute("use-soft-float", "true"); } - if (attributes.function.flags0.definition_no_signed_zeroes_fp_math) + if (attributes->function.flags0.definition_no_signed_zeroes_fp_math) { function_attribute_builder.addAttribute("no-signed-zeros-fp-math", "true"); } - if (attributes.function.flags0.definition_stack_realignment) + if (attributes->function.flags0.definition_stack_realignment) { function_attribute_builder.addAttribute("stackrealign"); } - if (attributes.function.flags0.definition_backchain) + if (attributes->function.flags0.definition_backchain) { function_attribute_builder.addAttribute("backchain"); } - if (attributes.function.flags0.definition_split_stack) + if (attributes->function.flags0.definition_split_stack) { function_attribute_builder.addAttribute("split-stack"); } - if (attributes.function.flags0.definition_speculative_load_hardening) + if (attributes->function.flags0.definition_speculative_load_hardening) { function_attribute_builder.addAttribute("split-stack"); } - if (attributes.function.flags0.definition_zero_call_used_registers) + if (attributes->function.flags0.definition_zero_call_used_registers != ZeroCallUsedRegsKind::all) { __builtin_trap(); // TODO } // TODO: denormal builtins - if (attributes.function.flags0.definition_non_lazy_bind) + if (attributes->function.flags0.definition_non_lazy_bind) { - function_attribute_builder.addAttribute(Attribute::NonLazyBind); + function_attribute_builder.addAttribute(llvm::Attribute::NonLazyBind); } - if (attributes.function.flags0.definition_cmse_nonsecure_entry) + if (attributes->function.flags0.definition_cmse_nonsecure_entry) { function_attribute_builder.addAttribute("cmse_nonsecure_entry"); } - UWTableKind unwind_table_kind; - switch ((BBLLVMUWTableKind)attributes.function.flags0.definition_unwind_table_kind) + llvm::UWTableKind unwind_table_kind; + switch ((BBLLVMUWTableKind)attributes->function.flags0.definition_unwind_table_kind) { - case BBLLVMUWTableKind::None: unwind_table_kind = UWTableKind::None; break; - case BBLLVMUWTableKind::Sync: unwind_table_kind = UWTableKind::Sync; break; - case BBLLVMUWTableKind::Async: unwind_table_kind = UWTableKind::Async; break; + case BBLLVMUWTableKind::None: unwind_table_kind = llvm::UWTableKind::None; break; + case BBLLVMUWTableKind::Sync: unwind_table_kind = llvm::UWTableKind::Sync; break; + case BBLLVMUWTableKind::Async: unwind_table_kind = llvm::UWTableKind::Async; break; } function_attribute_builder.addUWTableAttr(unwind_table_kind); - if (attributes.function.flags1.definition_disable_tail_calls) + if (attributes->function.flags1.definition_disable_tail_calls) { function_attribute_builder.addAttribute("disable-tail-calls", "true"); } - if (attributes.function.flags1.definition_stack_protect_strong) + if (attributes->function.flags1.definition_stack_protect_strong) { - function_attribute_builder.addAttribute(Attribute::StackProtectStrong); + function_attribute_builder.addAttribute(llvm::Attribute::StackProtectStrong); } - if (attributes.function.flags1.definition_stack_protect) + if (attributes->function.flags1.definition_stack_protect) { - function_attribute_builder.addAttribute(Attribute::StackProtect); + function_attribute_builder.addAttribute(llvm::Attribute::StackProtect); } - if (attributes.function.flags1.definition_stack_protect_req) + if (attributes->function.flags1.definition_stack_protect_req) { - function_attribute_builder.addAttribute(Attribute::StackProtectReq); + function_attribute_builder.addAttribute(llvm::Attribute::StackProtectReq); } - if (attributes.function.flags1.definition_aarch64_new_za) + if (attributes->function.flags1.definition_aarch64_new_za) { function_attribute_builder.addAttribute("aarch64_new_za"); } - if (attributes.function.flags1.definition_aarch64_new_zt0) + if (attributes->function.flags1.definition_aarch64_new_zt0) { function_attribute_builder.addAttribute("aarch64_new_zt0"); } - if (attributes.function.flags1.definition_optimize_none) + if (attributes->function.flags1.definition_optimize_none) { - function_attribute_builder.addAttribute(Attribute::OptimizeNone); + function_attribute_builder.addAttribute(llvm::Attribute::OptimizeNone); } - if (attributes.function.flags1.definition_naked) + if (attributes->function.flags1.definition_naked) { - function_attribute_builder.addAttribute(Attribute::Naked); + function_attribute_builder.addAttribute(llvm::Attribute::Naked); } - if (attributes.function.flags1.definition_inline_hint) + if (attributes->function.flags1.definition_inline_hint) { - function_attribute_builder.addAttribute(Attribute::InlineHint); + function_attribute_builder.addAttribute(llvm::Attribute::InlineHint); } } - auto function_attributes = AttributeSet::get(context, function_attribute_builder); + auto function_attributes = llvm::AttributeSet::get(*llvm::unwrap(context), function_attribute_builder); - auto return_attributes = build_argument_attributes(context, attributes.return_); + auto return_attributes = build_argument_attributes(context, &attributes->return_); - AttributeSet argument_attribute_buffer[128]; - assert(attributes.argument_count < array_length(argument_attribute_buffer)); + llvm::AttributeSet argument_attribute_buffer[128]; + assert(attributes->argument_count < array_length(argument_attribute_buffer)); - for (u64 i = 0; i < attributes.argument_count; i += 1) + for (u64 i = 0; i < attributes->argument_count; i += 1) { - auto attribute_set = build_argument_attributes(context, attributes.argument_pointer[i]); + auto attribute_set = build_argument_attributes(context, &attributes->argument_pointer[i]); argument_attribute_buffer[i] = attribute_set; } - ArrayRef argument_attributes = ArrayRef(argument_attribute_buffer, attributes.argument_count); + llvm::ArrayRef argument_attributes = llvm::ArrayRef(argument_attribute_buffer, attributes->argument_count); - auto attribute_list = AttributeList::get(context, function_attributes, return_attributes, argument_attributes); + auto attribute_list = llvm::AttributeList::get(*llvm::unwrap(context), function_attributes, return_attributes, argument_attributes); - static_assert(sizeof(AttributeList) == sizeof(uintptr_t)); - - return *(BBLLVMAttributeListHandle*)&attribute_list; + return llvm_attribute_list_to_abi(attribute_list); } -EXPORT bool llvm_instruction_is_call_base(Instruction* instruction) +EXPORT bool llvm_instruction_is_call_base(llvm::Instruction* instruction) { - return isa(instruction); + return isa(instruction); } -EXPORT void llvm_function_set_attributes(Function& function, BBLLVMAttributeListHandle attribute_list_handle) +EXPORT void llvm_function_set_attributes(LLVMValueRef f, BBLLVMAttributeList attribute_list_handle) { - auto attribute_list = *(AttributeList*)&attribute_list_handle; - function.setAttributes(attribute_list); + auto* function = llvm::unwrap(f); + auto attribute_list = abi_attribute_list_to_llvm(attribute_list_handle); + function->setAttributes(attribute_list); } -EXPORT void llvm_call_base_set_attributes(CallBase& call, BBLLVMAttributeListHandle attribute_list_handle) +EXPORT void llvm_call_base_set_attributes(LLVMValueRef call_value, BBLLVMAttributeList attribute_list_handle) { - auto attribute_list = *(AttributeList*)&attribute_list_handle; - call.setAttributes(attribute_list); + auto call = llvm::unwrap(call_value); + auto attribute_list = abi_attribute_list_to_llvm(attribute_list_handle); + call->setAttributes(attribute_list); } -fn String stream_to_string(raw_string_ostream& stream) +fn String stream_to_string(llvm::raw_string_ostream& stream) { // No need to call stream.flush(); because it's string-based stream.flush(); @@ -933,46 +825,47 @@ fn String stream_to_string(raw_string_ostream& stream) return String{ result, length }; } -EXPORT String llvm_function_to_string(Function& function) +EXPORT String llvm_function_to_string(llvm::Function& function) { std::string buffer; - raw_string_ostream os(buffer); + llvm::raw_string_ostream os(buffer); function.print(os); os.flush(); auto result = stream_to_string(os); return result; } -EXPORT bool llvm_function_verify(Function& function, String* error_message) +EXPORT bool llvm_function_verify(LLVMValueRef function_value, String* error_message) { std::string message_buffer; - raw_string_ostream message_stream(message_buffer); + llvm::raw_string_ostream message_stream(message_buffer); + auto& function = *llvm::unwrap(function_value); bool result = verifyFunction(function, &message_stream); - auto size = message_stream.str().size(); *error_message = stream_to_string(message_stream); // We invert the condition because LLVM conventions are just stupid return !result; } -EXPORT bool llvm_module_verify(const Module& module, String* error_message) +EXPORT bool llvm_module_verify(LLVMModuleRef m, String* error_message) { std::string message_buffer; - raw_string_ostream message_stream(message_buffer); + llvm::raw_string_ostream message_stream(message_buffer); - bool result = verifyModule(module, &message_stream); + const auto& module = *llvm::unwrap(m); + bool result = llvm::verifyModule(module, &message_stream); *error_message = stream_to_string(message_stream); // We invert the condition because LLVM conventions are just stupid return !result; } -EXPORT String llvm_module_to_string(Module* module) +EXPORT String llvm_module_to_string(LLVMModuleRef module) { std::string buffer; - raw_string_ostream stream(buffer); - module->print(stream, 0); + llvm::raw_string_ostream stream(buffer); + llvm::unwrap(module)->print(stream, 0); return stream_to_string(stream); } @@ -1000,9 +893,9 @@ EXPORT String llvm_host_cpu_name() EXPORT String llvm_host_cpu_features() { - SubtargetFeatures Features; + llvm::SubtargetFeatures Features; #if LLVM_VERSION_MAJOR >= 19 - auto host_cpu_features = sys::getHostCPUFeatures(); + auto host_cpu_features = llvm::sys::getHostCPUFeatures(); #else StringMap host_cpu_features; if (!sys::getHostCPUFeatures(host_cpu_features)) { @@ -1279,50 +1172,50 @@ struct BBLLVMTargetMachineCreate static_assert(sizeof(BBLLVMTargetMachineCreate) == 192); static_assert(BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT == 4); -EXPORT TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachineCreate& create, String* error_message) +EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachineCreate& create, String* error_message) { std::string error_message_string; - const Target* target = TargetRegistry::lookupTarget(string_ref(create.target_triple), error_message_string); + const llvm::Target* target = llvm::TargetRegistry::lookupTarget(string_ref(create.target_triple), error_message_string); - TargetMachine* target_machine; + llvm::TargetMachine* target_machine; if (target) { - std::optional code_model; + std::optional code_model; switch (create.code_model) { case BBLLVMCodeModel::none: code_model = std::nullopt; break; - case BBLLVMCodeModel::tiny: code_model = CodeModel::Tiny; break; - case BBLLVMCodeModel::small: code_model = CodeModel::Small; break; - case BBLLVMCodeModel::kernel: code_model = CodeModel::Kernel; break; - case BBLLVMCodeModel::medium: code_model = CodeModel::Medium; break; - case BBLLVMCodeModel::large: code_model = CodeModel::Large; break; + case BBLLVMCodeModel::tiny: code_model = llvm::CodeModel::Tiny; break; + case BBLLVMCodeModel::small: code_model = llvm::CodeModel::Small; break; + case BBLLVMCodeModel::kernel: code_model = llvm::CodeModel::Kernel; break; + case BBLLVMCodeModel::medium: code_model = llvm::CodeModel::Medium; break; + case BBLLVMCodeModel::large: code_model = llvm::CodeModel::Large; break; } - std::optional relocation_model; + std::optional relocation_model; switch (create.relocation_model) { case BBLLVMRelocationModel::default_relocation: relocation_model = std::nullopt; break; - case BBLLVMRelocationModel::static_relocation: relocation_model = Reloc::Static; break; - case BBLLVMRelocationModel::pic: relocation_model = Reloc::PIC_; break; - case BBLLVMRelocationModel::dynamic_no_pic: relocation_model = Reloc::DynamicNoPIC; break; - case BBLLVMRelocationModel::ropi: relocation_model = Reloc::ROPI; break; - case BBLLVMRelocationModel::rwpi: relocation_model = Reloc::RWPI; break; - case BBLLVMRelocationModel::ropi_rwpi: relocation_model = Reloc::ROPI_RWPI; break; + case BBLLVMRelocationModel::static_relocation: relocation_model = llvm::Reloc::Static; break; + case BBLLVMRelocationModel::pic: relocation_model = llvm::Reloc::PIC_; break; + case BBLLVMRelocationModel::dynamic_no_pic: relocation_model = llvm::Reloc::DynamicNoPIC; break; + case BBLLVMRelocationModel::ropi: relocation_model = llvm::Reloc::ROPI; break; + case BBLLVMRelocationModel::rwpi: relocation_model = llvm::Reloc::RWPI; break; + case BBLLVMRelocationModel::ropi_rwpi: relocation_model = llvm::Reloc::ROPI_RWPI; break; } - CodeGenOptLevel optimization_level; + llvm::CodeGenOptLevel optimization_level; switch (create.optimization_level) { - case BBLLVMCodeGenerationOptimizationLevel::none: optimization_level = CodeGenOptLevel::None; break; - case BBLLVMCodeGenerationOptimizationLevel::less: optimization_level = CodeGenOptLevel::Less; break; - case BBLLVMCodeGenerationOptimizationLevel::normal: optimization_level = CodeGenOptLevel::Default; break; - case BBLLVMCodeGenerationOptimizationLevel::aggressive: optimization_level = CodeGenOptLevel::Aggressive; break; + case BBLLVMCodeGenerationOptimizationLevel::none: optimization_level = llvm::CodeGenOptLevel::None; break; + case BBLLVMCodeGenerationOptimizationLevel::less: optimization_level = llvm::CodeGenOptLevel::Less; break; + case BBLLVMCodeGenerationOptimizationLevel::normal: optimization_level = llvm::CodeGenOptLevel::Default; break; + case BBLLVMCodeGenerationOptimizationLevel::aggressive: optimization_level = llvm::CodeGenOptLevel::Aggressive; break; } // INFO: This calls the default constructor, so all LLVM defaults are set and we only override what we control - TargetOptions target_options; + llvm::TargetOptions target_options; target_options.UnsafeFPMath = create.target_options.unsafe_fp_math; target_options.NoInfsFPMath = create.target_options.no_infs_fp_math; @@ -1341,16 +1234,16 @@ EXPORT TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachineCreate auto global_isel_abort_mode = (BBLLVMGlobalISelAbortMode)create.target_options.global_isel_abort_mode; switch (global_isel_abort_mode) { - case BBLLVMGlobalISelAbortMode::disable: target_options.GlobalISelAbort = GlobalISelAbortMode::Disable; break; - case BBLLVMGlobalISelAbortMode::enable: target_options.GlobalISelAbort = GlobalISelAbortMode::Enable; break; - case BBLLVMGlobalISelAbortMode::disable_with_diag: target_options.GlobalISelAbort = GlobalISelAbortMode::DisableWithDiag; break; + case BBLLVMGlobalISelAbortMode::disable: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::Disable; break; + case BBLLVMGlobalISelAbortMode::enable: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::Enable; break; + case BBLLVMGlobalISelAbortMode::disable_with_diag: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::DisableWithDiag; break; } auto swift_async_frame_pointer = (BBLLVMSwiftAsyncFramePointerMode)create.target_options.swift_async_frame_pointer; switch (swift_async_frame_pointer) { - case BBLLVMSwiftAsyncFramePointerMode::deployment_based: target_options.SwiftAsyncFramePointer = SwiftAsyncFramePointerMode::DeploymentBased; break; - case BBLLVMSwiftAsyncFramePointerMode::always: target_options.SwiftAsyncFramePointer = SwiftAsyncFramePointerMode::Always; break; - case BBLLVMSwiftAsyncFramePointerMode::never: target_options.SwiftAsyncFramePointer = SwiftAsyncFramePointerMode::Never; break; + case BBLLVMSwiftAsyncFramePointerMode::deployment_based: target_options.SwiftAsyncFramePointer = llvm::SwiftAsyncFramePointerMode::DeploymentBased; break; + case BBLLVMSwiftAsyncFramePointerMode::always: target_options.SwiftAsyncFramePointer = llvm::SwiftAsyncFramePointerMode::Always; break; + case BBLLVMSwiftAsyncFramePointerMode::never: target_options.SwiftAsyncFramePointer = llvm::SwiftAsyncFramePointerMode::Never; break; } target_options.UseInitArray = create.target_options.use_init_array; @@ -1382,10 +1275,10 @@ EXPORT TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachineCreate auto bb_sections = (BBLLVMBasicBlockSection) create.target_options.bb_sections; switch (bb_sections) { - case BBLLVMBasicBlockSection::all: target_options.BBSections = BasicBlockSection::All; break; - case BBLLVMBasicBlockSection::list: target_options.BBSections = BasicBlockSection::List; break; - case BBLLVMBasicBlockSection::preset: target_options.BBSections = BasicBlockSection::Preset; break; - case BBLLVMBasicBlockSection::none: target_options.BBSections = BasicBlockSection::None; break; + case BBLLVMBasicBlockSection::all: target_options.BBSections = llvm::BasicBlockSection::All; break; + case BBLLVMBasicBlockSection::list: target_options.BBSections = llvm::BasicBlockSection::List; break; + case BBLLVMBasicBlockSection::preset: target_options.BBSections = llvm::BasicBlockSection::Preset; break; + case BBLLVMBasicBlockSection::none: target_options.BBSections = llvm::BasicBlockSection::None; break; } target_options.EmitCallSiteInfo = create.target_options.emit_call_site_information; @@ -1405,57 +1298,57 @@ EXPORT TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachineCreate auto float_abi = (BBLLVMFloatAbi) create.target_options.float_abi; switch (float_abi) { - case BBLLVMFloatAbi::normal: target_options.FloatABIType = FloatABI::Default; break; - case BBLLVMFloatAbi::soft: target_options.FloatABIType = FloatABI::Soft; break; - case BBLLVMFloatAbi::hard: target_options.FloatABIType = FloatABI::Hard; break; + case BBLLVMFloatAbi::normal: target_options.FloatABIType = llvm::FloatABI::Default; break; + case BBLLVMFloatAbi::soft: target_options.FloatABIType = llvm::FloatABI::Soft; break; + case BBLLVMFloatAbi::hard: target_options.FloatABIType = llvm::FloatABI::Hard; break; } auto thread_model = (BBLLVMThreadModel) create.target_options.thread_model; switch (thread_model) { - case BBLLVMThreadModel::posix: target_options.ThreadModel = ThreadModel::POSIX; break; - case BBLLVMThreadModel::single: target_options.ThreadModel = ThreadModel::Single; break; + case BBLLVMThreadModel::posix: target_options.ThreadModel = llvm::ThreadModel::POSIX; break; + case BBLLVMThreadModel::single: target_options.ThreadModel = llvm::ThreadModel::Single; break; } auto fp_op_fusion_mode = (BBLLVMFPOpFusion) create.target_options.fp_op_fusion_mode; switch (fp_op_fusion_mode) { - case BBLLVMFPOpFusion::fast: target_options.AllowFPOpFusion = FPOpFusion::Fast; break; - case BBLLVMFPOpFusion::standard: target_options.AllowFPOpFusion = FPOpFusion::Standard; break; - case BBLLVMFPOpFusion::strict: target_options.AllowFPOpFusion = FPOpFusion::Strict; break; + case BBLLVMFPOpFusion::fast: target_options.AllowFPOpFusion = llvm::FPOpFusion::Fast; break; + case BBLLVMFPOpFusion::standard: target_options.AllowFPOpFusion = llvm::FPOpFusion::Standard; break; + case BBLLVMFPOpFusion::strict: target_options.AllowFPOpFusion = llvm::FPOpFusion::Strict; break; } auto eabi_version = (BBLLVMEAbi) create.target_options.eabi_version; switch (eabi_version) { - case BBLLVMEAbi::unknown: target_options.EABIVersion = EABI::Unknown; break; - case BBLLVMEAbi::normal: target_options.EABIVersion = EABI::Default; break; - case BBLLVMEAbi::eabi4: target_options.EABIVersion = EABI::EABI4; break; - case BBLLVMEAbi::eabi5: target_options.EABIVersion = EABI::EABI5; break; - case BBLLVMEAbi::gnu: target_options.EABIVersion = EABI::GNU; break; + case BBLLVMEAbi::unknown: target_options.EABIVersion = llvm::EABI::Unknown; break; + case BBLLVMEAbi::normal: target_options.EABIVersion = llvm::EABI::Default; break; + case BBLLVMEAbi::eabi4: target_options.EABIVersion = llvm::EABI::EABI4; break; + case BBLLVMEAbi::eabi5: target_options.EABIVersion = llvm::EABI::EABI5; break; + case BBLLVMEAbi::gnu: target_options.EABIVersion = llvm::EABI::GNU; break; } auto debugger_kind = (BBLLVMDebuggerKind) create.target_options.debugger_kind; switch (debugger_kind) { - case BBLLVMDebuggerKind::normal: target_options.DebuggerTuning = DebuggerKind::Default; break; - case BBLLVMDebuggerKind::gdb: target_options.DebuggerTuning = DebuggerKind::GDB; break; - case BBLLVMDebuggerKind::lldb: target_options.DebuggerTuning = DebuggerKind::LLDB; break; - case BBLLVMDebuggerKind::sce: target_options.DebuggerTuning = DebuggerKind::SCE; break; - case BBLLVMDebuggerKind::dbx: target_options.DebuggerTuning = DebuggerKind::DBX; break; + case BBLLVMDebuggerKind::normal: target_options.DebuggerTuning = llvm::DebuggerKind::Default; break; + case BBLLVMDebuggerKind::gdb: target_options.DebuggerTuning = llvm::DebuggerKind::GDB; break; + case BBLLVMDebuggerKind::lldb: target_options.DebuggerTuning = llvm::DebuggerKind::LLDB; break; + case BBLLVMDebuggerKind::sce: target_options.DebuggerTuning = llvm::DebuggerKind::SCE; break; + case BBLLVMDebuggerKind::dbx: target_options.DebuggerTuning = llvm::DebuggerKind::DBX; break; } auto exception_handling = (BBLLVMExceptionHandling) create.target_options.exception_handling; switch (exception_handling) { - case BBLLVMExceptionHandling::none: target_options.ExceptionModel = ExceptionHandling::None; break; - case BBLLVMExceptionHandling::dwarf_cfi: target_options.ExceptionModel = ExceptionHandling::DwarfCFI; break; - case BBLLVMExceptionHandling::setjmp_longjmp: target_options.ExceptionModel = ExceptionHandling::SjLj; break; - case BBLLVMExceptionHandling::arm: target_options.ExceptionModel = ExceptionHandling::ARM; break; - case BBLLVMExceptionHandling::win_eh: target_options.ExceptionModel = ExceptionHandling::WinEH; break; - case BBLLVMExceptionHandling::wasm: target_options.ExceptionModel = ExceptionHandling::Wasm; break; - case BBLLVMExceptionHandling::aix: target_options.ExceptionModel = ExceptionHandling::AIX; break; - case BBLLVMExceptionHandling::zos: target_options.ExceptionModel = ExceptionHandling::ZOS; break; + case BBLLVMExceptionHandling::none: target_options.ExceptionModel = llvm::ExceptionHandling::None; break; + case BBLLVMExceptionHandling::dwarf_cfi: target_options.ExceptionModel = llvm::ExceptionHandling::DwarfCFI; break; + case BBLLVMExceptionHandling::setjmp_longjmp: target_options.ExceptionModel = llvm::ExceptionHandling::SjLj; break; + case BBLLVMExceptionHandling::arm: target_options.ExceptionModel = llvm::ExceptionHandling::ARM; break; + case BBLLVMExceptionHandling::win_eh: target_options.ExceptionModel = llvm::ExceptionHandling::WinEH; break; + case BBLLVMExceptionHandling::wasm: target_options.ExceptionModel = llvm::ExceptionHandling::Wasm; break; + case BBLLVMExceptionHandling::aix: target_options.ExceptionModel = llvm::ExceptionHandling::AIX; break; + case BBLLVMExceptionHandling::zos: target_options.ExceptionModel = llvm::ExceptionHandling::ZOS; break; } target_options.LoopAlignment = create.target_options.loop_alignment; @@ -1520,26 +1413,26 @@ EXPORT TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachineCreate auto emit_dwarf_unwind = (BBLLVMEmitDwarfUnwindType) create.target_options.mc.emit_dwarf_unwind; switch (emit_dwarf_unwind) { - case BBLLVMEmitDwarfUnwindType::always: target_options.MCOptions.EmitDwarfUnwind = EmitDwarfUnwindType::Always; break; - case BBLLVMEmitDwarfUnwindType::no_compact_unwind: target_options.MCOptions.EmitDwarfUnwind = EmitDwarfUnwindType::NoCompactUnwind; break; - case BBLLVMEmitDwarfUnwindType::normal: target_options.MCOptions.EmitDwarfUnwind = EmitDwarfUnwindType::Default; break; + case BBLLVMEmitDwarfUnwindType::always: target_options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::Always; break; + case BBLLVMEmitDwarfUnwindType::no_compact_unwind: target_options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::NoCompactUnwind; break; + case BBLLVMEmitDwarfUnwindType::normal: target_options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::Default; break; } auto use_dwarf_directory = (BBLLVMDwarfDirectory) create.target_options.mc.use_dwarf_directory; switch (use_dwarf_directory) { - case BBLLVMDwarfDirectory::disable: target_options.MCOptions.MCUseDwarfDirectory = MCTargetOptions::DwarfDirectory::DisableDwarfDirectory; break; - case BBLLVMDwarfDirectory::enable: target_options.MCOptions.MCUseDwarfDirectory = MCTargetOptions::DwarfDirectory::EnableDwarfDirectory; break; - case BBLLVMDwarfDirectory::normal: target_options.MCOptions.MCUseDwarfDirectory = MCTargetOptions::DwarfDirectory::DefaultDwarfDirectory; break; + case BBLLVMDwarfDirectory::disable: target_options.MCOptions.MCUseDwarfDirectory = llvm::MCTargetOptions::DwarfDirectory::DisableDwarfDirectory; break; + case BBLLVMDwarfDirectory::enable: target_options.MCOptions.MCUseDwarfDirectory = llvm::MCTargetOptions::DwarfDirectory::EnableDwarfDirectory; break; + case BBLLVMDwarfDirectory::normal: target_options.MCOptions.MCUseDwarfDirectory = llvm::MCTargetOptions::DwarfDirectory::DefaultDwarfDirectory; break; } #if LLVM_VERSION_MAJOR >= 19 auto debug_compression_type = (BBLLVMDebugCompressionType) create.target_options.mc.debug_compression_type; switch (debug_compression_type) { - case BBLLVMDebugCompressionType::none: target_options.MCOptions.CompressDebugSections = DebugCompressionType::None; break; - case BBLLVMDebugCompressionType::zlib: target_options.MCOptions.CompressDebugSections = DebugCompressionType::Zlib; break; - case BBLLVMDebugCompressionType::zstd: target_options.MCOptions.CompressDebugSections = DebugCompressionType::Zstd; break; + case BBLLVMDebugCompressionType::none: target_options.MCOptions.CompressDebugSections = llvm::DebugCompressionType::None; break; + case BBLLVMDebugCompressionType::zlib: target_options.MCOptions.CompressDebugSections = llvm::DebugCompressionType::Zlib; break; + case BBLLVMDebugCompressionType::zstd: target_options.MCOptions.CompressDebugSections = llvm::DebugCompressionType::Zstd; break; } #endif @@ -1562,11 +1455,11 @@ EXPORT TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachineCreate return target_machine; } -EXPORT void llvm_module_set_target(Module& module, TargetMachine& target_machine) +EXPORT void llvm_module_set_target(llvm::Module& module, llvm::TargetMachine& target_machine) { module.setDataLayout(target_machine.createDataLayout()); auto& triple_string = target_machine.getTargetTriple().getTriple(); - module.setTargetTriple(StringRef(triple_string)); + module.setTargetTriple(llvm::StringRef(triple_string)); } enum class BBLLVMOptimizationLevel : u8 @@ -1599,12 +1492,12 @@ struct BBLLVMOptimizationPipelineOptions static_assert(sizeof(BBLLVMOptimizationPipelineOptions) == sizeof(u64)); static_assert(BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 51); -EXPORT void llvm_module_run_optimization_pipeline(Module& module, TargetMachine& target_machine, BBLLVMOptimizationPipelineOptions options) +EXPORT void llvm_module_run_optimization_pipeline(llvm::Module& module, llvm::TargetMachine& target_machine, BBLLVMOptimizationPipelineOptions options) { // TODO: PGO // TODO: CS profile - PipelineTuningOptions pipeline_tuning_options; + llvm::PipelineTuningOptions pipeline_tuning_options; pipeline_tuning_options.LoopUnrolling = options.loop_unrolling; pipeline_tuning_options.LoopInterleaving = options.loop_interleaving; pipeline_tuning_options.LoopVectorization = options.loop_vectorization; @@ -1615,24 +1508,25 @@ EXPORT void llvm_module_run_optimization_pipeline(Module& module, TargetMachine& // TODO: instrumentation - LoopAnalysisManager loop_analysis_manager; - FunctionAnalysisManager function_analysis_manager; - CGSCCAnalysisManager cgscc_analysis_manager; - ModuleAnalysisManager module_analysis_manager; + llvm::LoopAnalysisManager loop_analysis_manager; + llvm::FunctionAnalysisManager function_analysis_manager; + llvm::CGSCCAnalysisManager cgscc_analysis_manager; + llvm::ModuleAnalysisManager module_analysis_manager; - PassBuilder pass_builder(&target_machine, pipeline_tuning_options); + llvm::PassBuilder pass_builder(&target_machine, pipeline_tuning_options); if (options.assignment_tracking && options.debug_info != 0) { - pass_builder.registerPipelineStartEPCallback([&](ModulePassManager& MPM, OptimizationLevel Level) { - MPM.addPass(AssignmentTrackingPass()); + pass_builder.registerPipelineStartEPCallback([&](llvm::ModulePassManager& MPM, llvm::OptimizationLevel Level) { + unused(Level); + MPM.addPass(llvm::AssignmentTrackingPass()); }); } - Triple target_triple = target_machine.getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 + llvm::Triple target_triple = target_machine.getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 // TODO: add library (?) - std::unique_ptr TLII(llvm::driver::createTLII(target_triple, driver::VectorLibrary::NoLibrary)); - function_analysis_manager.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); + std::unique_ptr TLII(llvm::driver::createTLII(target_triple, llvm::driver::VectorLibrary::NoLibrary)); + function_analysis_manager.registerPass([&] { return llvm::TargetLibraryAnalysis(*TLII); }); pass_builder.registerModuleAnalyses(module_analysis_manager); pass_builder.registerCGSCCAnalyses(cgscc_analysis_manager); @@ -1640,25 +1534,25 @@ EXPORT void llvm_module_run_optimization_pipeline(Module& module, TargetMachine& pass_builder.registerLoopAnalyses(loop_analysis_manager); pass_builder.crossRegisterProxies(loop_analysis_manager, function_analysis_manager, cgscc_analysis_manager, module_analysis_manager); - ModulePassManager module_pass_manager; + llvm::ModulePassManager module_pass_manager; if (options.verify_module) { - module_pass_manager.addPass(VerifierPass()); + module_pass_manager.addPass(llvm::VerifierPass()); } bool thin_lto = false; bool lto = false; - OptimizationLevel optimization_level; + llvm::OptimizationLevel optimization_level; switch ((BBLLVMOptimizationLevel)options.optimization_level) { - case BBLLVMOptimizationLevel::O0: optimization_level = OptimizationLevel::O0; break; - case BBLLVMOptimizationLevel::O1: optimization_level = OptimizationLevel::O1; break; - case BBLLVMOptimizationLevel::O2: optimization_level = OptimizationLevel::O2; break; - case BBLLVMOptimizationLevel::O3: optimization_level = OptimizationLevel::O3; break; - case BBLLVMOptimizationLevel::Os: optimization_level = OptimizationLevel::Os; break; - case BBLLVMOptimizationLevel::Oz: optimization_level = OptimizationLevel::Oz; break; + case BBLLVMOptimizationLevel::O0: optimization_level = llvm::OptimizationLevel::O0; break; + case BBLLVMOptimizationLevel::O1: optimization_level = llvm::OptimizationLevel::O1; break; + case BBLLVMOptimizationLevel::O2: optimization_level = llvm::OptimizationLevel::O2; break; + case BBLLVMOptimizationLevel::O3: optimization_level = llvm::OptimizationLevel::O3; break; + case BBLLVMOptimizationLevel::Os: optimization_level = llvm::OptimizationLevel::Os; break; + case BBLLVMOptimizationLevel::Oz: optimization_level = llvm::OptimizationLevel::Oz; break; } // TODO: thin lto post-link @@ -1707,12 +1601,12 @@ enum class BBLLVMCodeGenerationPipelineResult : u8 failed_to_add_emit_passes = 2, }; -EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(Module& module, TargetMachine& target_machine, BBLLVMCodeGenerationPipelineOptions options) +EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(llvm::Module& module, llvm::TargetMachine& target_machine, BBLLVMCodeGenerationPipelineOptions options) { // We still use the legacy PM to run the codegen pipeline since the new PM // does not work with the codegen pipeline. // FIXME: make the new PM work with the codegen pipeline. - legacy::PassManager CodeGenPasses; + llvm::legacy::PassManager CodeGenPasses; #if LLVM_VERSION_MAJOR >= 19 if (options.optimize_when_possible) { @@ -1720,7 +1614,7 @@ EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeli } #endif - raw_pwrite_stream* dwarf_object_file = 0; + llvm::raw_pwrite_stream* dwarf_object_file = 0; if (options.output_dwarf_file_path.length) { __builtin_trap(); @@ -1728,19 +1622,19 @@ EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeli if (options.optimize_when_possible) { - Triple target_triple = target_machine.getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 + llvm::Triple target_triple = target_machine.getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 // TODO: add library (?) - std::unique_ptr TLII(llvm::driver::createTLII(target_triple, driver::VectorLibrary::NoLibrary)); - CodeGenPasses.add(new TargetLibraryInfoWrapperPass(*TLII)); + std::unique_ptr TLII(llvm::driver::createTLII(target_triple, llvm::driver::VectorLibrary::NoLibrary)); + CodeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*TLII)); } - std::unique_ptr stream; + std::unique_ptr stream; if (options.output_file_path.length) { std::error_code error_code; - stream = std::make_unique(string_ref(options.output_file_path), error_code, sys::fs::OF_None); + stream = std::make_unique(string_ref(options.output_file_path), error_code, llvm::sys::fs::OF_None); if (error_code) { @@ -1752,12 +1646,12 @@ EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeli stream = std::make_unique(); } - CodeGenFileType file_type; + llvm::CodeGenFileType file_type; switch ((BBLLVMCodeGenerationFileType)options.code_generation_file_type) { - case BBLLVMCodeGenerationFileType::assembly_file: file_type = CodeGenFileType::AssemblyFile; break; - case BBLLVMCodeGenerationFileType::object_file: file_type = CodeGenFileType::ObjectFile; break; - case BBLLVMCodeGenerationFileType::null: file_type = CodeGenFileType::Null; break; + case BBLLVMCodeGenerationFileType::assembly_file: file_type = llvm::CodeGenFileType::AssemblyFile; break; + case BBLLVMCodeGenerationFileType::object_file: file_type = llvm::CodeGenFileType::ObjectFile; break; + case BBLLVMCodeGenerationFileType::null: file_type = llvm::CodeGenFileType::Null; break; } auto disable_verify = !options.verify_module; diff --git a/src/llvm.h b/src/llvm.h index 8ab155a..14b494b 100644 --- a/src/llvm.h +++ b/src/llvm.h @@ -1,46 +1,318 @@ -#include +#pragma once -namespace llvm +#include +#include +#include +#include +#include +#include + +enum class BBLLVMUWTableKind : u64 { - class Type; - class Value; -} + None = 0, ///< No unwind table requested + Sync = 1, ///< "Synchronous" unwind tables + Async = 2, ///< "Asynchronous" unwind tables (instr precise) + Default = 2, +}; + +enum class BBLLVMFramePointerKind : u64 +{ + none = 0, + reserved = 1, + non_leaf = 2, + all = 3, +}; + +enum class ZeroCallUsedRegsKind : u64 +{ + all = 0, + skip = 1 << 0, + only_used = 1 << 1, + only_gpr = 1 << 2, + only_arg = 1 << 3, + used_gpr_arg = only_used | only_gpr | only_arg, + used_gpr = only_used | only_gpr, + used_arg = only_used | only_arg, + used = only_used, + all_gpr_arg = only_gpr | only_arg, + all_gpr = only_gpr, + all_arg = only_arg, +}; + +struct BBLLVMFunctionAttributesFlags0 +{ + u64 noreturn:1; + u64 cmse_ns_call:1; + u64 nounwind:1; + u64 returns_twice:1; + u64 cold:1; + u64 hot:1; + u64 no_duplicate:1; + u64 convergent:1; + u64 no_merge:1; + u64 will_return:1; + u64 no_caller_saved_registers:1; + u64 no_cf_check:1; + u64 no_callback:1; + u64 alloc_size:1; + u64 uniform_work_group_size:1; + u64 aarch64_pstate_sm_body:1; + u64 aarch64_pstate_sm_enabled:1; + u64 aarch64_pstate_sm_compatible:1; + u64 aarch64_preserves_za:1; + u64 aarch64_in_za:1; + u64 aarch64_out_za:1; + u64 aarch64_inout_za:1; + u64 aarch64_preserves_zt0:1; + u64 aarch64_in_zt0:1; + u64 aarch64_out_zt0:1; + u64 aarch64_inout_zt0:1; + u64 optimize_for_size:1; + u64 min_size:1; + u64 no_red_zone:1; + u64 indirect_tls_seg_refs:1; + u64 no_implicit_floats:1; + u64 sample_profile_suffix_elision_policy:1; + u64 memory_none:1; + u64 memory_readonly:1; + u64 memory_inaccessible_or_arg_memory_only:1; + u64 memory_arg_memory_only:1; + u64 strict_fp:1; + u64 no_inline:1; + u64 always_inline:1; + u64 guard_no_cf:1; + + // TODO: branch protection function attributes + // TODO: cpu features + + // Call-site begin + u64 call_no_builtins:1; + + BBLLVMFramePointerKind definition_frame_pointer_kind:2; + u64 definition_less_precise_fpmad:1; + u64 definition_null_pointer_is_valid:1; + u64 definition_no_trapping_fp_math:1; + u64 definition_no_infs_fp_math:1; + u64 definition_no_nans_fp_math:1; + u64 definition_approx_func_fp_math:1; + u64 definition_unsafe_fp_math:1; + u64 definition_use_soft_float:1; + u64 definition_no_signed_zeroes_fp_math:1; + u64 definition_stack_realignment:1; + u64 definition_backchain:1; + u64 definition_split_stack:1; + u64 definition_speculative_load_hardening:1; + ZeroCallUsedRegsKind definition_zero_call_used_registers:4; + // TODO: denormal builtins + u64 definition_non_lazy_bind:1; + u64 definition_cmse_nonsecure_entry:1; + BBLLVMUWTableKind definition_unwind_table_kind:2; +}; + +static_assert(sizeof(BBLLVMFunctionAttributesFlags0) == sizeof(u64)); + +struct BBLLVMFunctionAttributesFlags1 +{ + u64 definition_disable_tail_calls:1; + u64 definition_stack_protect_strong:1; + u64 definition_stack_protect:1; + u64 definition_stack_protect_req:1; + u64 definition_aarch64_new_za:1; + u64 definition_aarch64_new_zt0:1; + u64 definition_optimize_none:1; + u64 definition_naked:1; + u64 definition_inline_hint:1; + u64 _:55; +}; + +static_assert(sizeof(BBLLVMFunctionAttributesFlags1) == sizeof(u64)); + +struct BBLLVMFunctionAttributes +{ + String prefer_vector_width; + String stack_protector_buffer_size; + String definition_probe_stack; + String definition_stack_probe_size; + + BBLLVMFunctionAttributesFlags0 flags0; + BBLLVMFunctionAttributesFlags1 flags1; +}; + +static_assert(sizeof(BBLLVMFunctionAttributes) == 10 * sizeof(u64)); + +struct BBLLVMArgumentAttributes +{ + LLVMTypeRef semantic_type; + LLVMTypeRef abi_type; + u64 dereferenceable_bytes; + u32 alignment; + u32 no_alias:1; + u32 non_null:1; + u32 no_undef:1; + u32 sign_extend:1; + u32 zero_extend:1; + u32 in_reg:1; + u32 no_fp_class:10; + u32 struct_return:1; + u32 writable:1; + u32 dead_on_unwind:1; + u32 in_alloca:1; + u32 dereferenceable:1; + u32 dereferenceable_or_null:1; + u32 nest:1; + u32 by_value:1; + u32 by_reference:1; + u32 no_capture:1; + u32 _:6; +}; + +static_assert(sizeof(BBLLVMArgumentAttributes) == 2 * sizeof(LLVMTypeRef) + 2 * sizeof(u64)); + +struct BBLLVMAttributeListOptions +{ + BBLLVMFunctionAttributes function; + BBLLVMArgumentAttributes return_; + BBLLVMArgumentAttributes* argument_pointer; + u64 argument_count; +}; + +static_assert(sizeof(BBLLVMAttributeListOptions) == sizeof(BBLLVMFunctionAttributes) + sizeof(BBLLVMArgumentAttributes) + sizeof(void*) + sizeof(u64)); + +typedef void* BBLLVMAttributeList; + +enum class DwarfEmissionKind +{ + none, + full, + line_tables_only, +}; + +enum class DwarfType +{ + void_type = 0x0, + address = 0x1, + boolean = 0x2, + complex_float = 0x3, + float_type = 0x4, + signed_type = 0x5, + signed_char = 0x6, + unsigned_type = 0x7, + unsigned_char = 0x8, + + // DWARF 3. + imaginary_float = 0x9, + packed_decimal = 0xa, + numeric_string = 0xb, + edited = 0xc, + signed_fixed = 0xd, + unsigned_fixed = 0xe, + decimal_float = 0xf, + + // DWARF 4. + UTF = 0x10, + + // DWARF 5. + UCS = 0x11, + ASCII = 0x12, + + // HP extensions. + HP_float80 = 0x80, // Floating-point (80 bit). + HP_complex_float80 = 0x81, // Complex floating-point (80 bit). + HP_float128 = 0x82, // Floating-point (128 bit). + HP_complex_float128 = 0x83, // Complex fp (128 bit). + HP_floathpintel = 0x84, // Floating-point (82 bit IA64). + HP_imaginary_float80 = 0x85, + HP_imaginary_float128 = 0x86, + HP_VAX_float = 0x88, // F or G floating. + HP_VAX_float_d = 0x89, // D floating. + HP_packed_decimal = 0x8a, // Cobol. + HP_zoned_decimal = 0x8b, // Cobol. + HP_edited = 0x8c, // Cobol. + HP_signed_fixed = 0x8d, // Cobol. + HP_unsigned_fixed = 0x8e, // Cobol. + HP_VAX_complex_float = 0x8f, // F or G floating complex. + HP_VAX_complex_float_d = 0x90, // D floating complex. +}; + +enum class DIFlagsVisibility : u32 +{ + none = 0, + private_ = 1, + protected_ = 2, + public_ = 3, +}; + +enum class DIFlagsInheritance : u32 +{ + none = 0, + single_ = 1, + multiple_ = 2, + virtual_ = 3, +}; + +struct DIFlags +{ + DIFlagsVisibility visibility:2; + u32 forward_declaration:1; + u32 apple_block:1; + u32 block_by_ref_struct:1; + u32 virtual_:1; + u32 artificial:1; + u32 explicit_:1; + u32 prototyped:1; + u32 objective_c_class_complete:1; + u32 object_pointer:1; + u32 vector:1; + u32 static_member:1; + u32 lvalue_reference:1; + u32 rvalue_reference:1; + u32 reserved:1; + DIFlagsInheritance inheritance:2; + u32 introduced_virtual:1; + u32 bit_field:1; + u32 no_return:1; + u32 type_pass_by_value:1; + u32 type_pass_by_reference:1; + u32 enum_class:1; + u32 thunk:1; + u32 non_trivial:1; + u32 big_endian:1; + u32 little_endian:1; + u32 all_calls_described:1; + u32 _:3; +}; + +static_assert(sizeof(DIFlags) == sizeof(u32)); + fn bool llvm_initialized = false; -extern "C" void LLVMInitializeX86TargetInfo(); -extern "C" void LLVMInitializeX86Target(); -extern "C" void LLVMInitializeX86TargetMC(); -extern "C" void LLVMInitializeX86AsmPrinter(); -extern "C" void LLVMInitializeX86AsmParser(); -extern "C" void LLVMInitializeX86Disassembler(); - extern "C" String llvm_default_target_triple(); extern "C" String llvm_host_cpu_name(); extern "C" String llvm_host_cpu_features(); -struct LLVMGlobal -{ - String host_triple; - String host_cpu_model; - String host_cpu_features; -}; +extern "C" LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String name); -global_variable LLVMGlobal llvm_global; +extern "C" LLVMValueRef llvm_module_create_function(LLVMModuleRef module, LLVMTypeRef function_type, LLVMLinkage linkage_type, unsigned address_space, String name); +extern "C" void llvm_function_set_attributes(LLVMValueRef function, BBLLVMAttributeList attribute_list); -fn void initialize_all() -{ - assert(!llvm_initialized); - LLVMInitializeX86TargetInfo(); - LLVMInitializeX86Target(); - LLVMInitializeX86TargetMC(); - LLVMInitializeX86AsmPrinter(); - LLVMInitializeX86AsmParser(); - LLVMInitializeX86Disassembler(); +extern "C" LLVMBasicBlockRef llvm_context_create_basic_block(LLVMContextRef context, String name, LLVMValueRef parent_function); + +extern "C" LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMValueRef before, LLVMThreadLocalMode thread_local_mode, unsigned address_space, bool externally_initialized); + +extern "C" LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef builder, LLVMTypeRef type, unsigned address_space, u32 alignment, String name); +extern "C" bool llvm_value_has_one_use(LLVMValueRef value); +extern "C" LLVMValueRef llvm_basic_block_user_begin(LLVMBasicBlockRef basic_block); +extern "C" void llvm_basic_block_delete(LLVMBasicBlockRef basic_block); +extern "C" bool llvm_basic_block_is_empty(LLVMBasicBlockRef basic_block); +extern "C" void llvm_function_set_attributes(LLVMValueRef f, BBLLVMAttributeList attribute_list_handle); +extern "C" void llvm_call_base_set_attributes(LLVMValueRef call_value, BBLLVMAttributeList attribute_list_handle); + +extern "C" BBLLVMAttributeList llvm_attribute_list_build(LLVMContextRef context, BBLLVMAttributeListOptions* attributes, bool call_site); +extern "C" LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LLVMValueRef ra, LLVMTypeRef et); +extern "C" bool llvm_value_use_empty(LLVMValueRef value); +extern "C" bool llvm_function_verify(LLVMValueRef function_value, String* error_message); +extern "C" bool llvm_module_verify(LLVMModuleRef m, String* error_message); + +extern "C" String llvm_module_to_string(LLVMModuleRef module); - llvm_global = { - .host_triple = llvm_default_target_triple(), - .host_cpu_model = llvm_host_cpu_name(), - .host_cpu_features = llvm_host_cpu_features(), - }; -} diff --git a/src/parser.cpp b/src/parser.cpp index e10167b..c8b7eac 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -309,22 +309,22 @@ fn bool is_hexadecimal_alpha_upper(u8 ch) fn bool is_hexadecimal_alpha(u8 ch) { - return is_hexadecimal_alpha_lower(ch) | is_hexadecimal_alpha_upper(ch); + return is_hexadecimal_alpha_lower(ch) || is_hexadecimal_alpha_upper(ch); } fn bool is_hexadecimal(u8 ch) { - return is_decimal(ch) | is_hexadecimal_alpha(ch); + return is_decimal(ch) || is_hexadecimal_alpha(ch); } fn bool is_identifier_start(u8 ch) { - return (is_lower(ch) | is_upper(ch)) | (ch == '_'); + return (is_lower(ch) || is_upper(ch)) || (ch == '_'); } fn bool is_identifier(u8 ch) { - return is_identifier_start(ch) | is_decimal(ch); + return is_identifier_start(ch) || is_decimal(ch); } fn u32 get_line(Module* module) @@ -530,64 +530,6 @@ fn u64 parse_integer_decimal_assume_valid(String string) fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder); -fn u8 format_integer_decimal(String buffer, u64 v) -{ - u8 byte_count = 0; - auto value = v; - - if (value != 0) - { - u8 reverse_buffer[64]; - u8 reverse_index = 0; - - while (value != 0) - { - auto digit_value = (u8)(value % 10); - auto ascii_character = digit_value + '0'; - value /= 10; - reverse_buffer[reverse_index] = ascii_character; - reverse_index += 1; - } - - while (reverse_index != 0) - { - reverse_index -= 1; - buffer[byte_count] = reverse_buffer[reverse_index]; - byte_count += 1; - } - } - else - { - buffer[0] = '0'; - byte_count = 1; - } - - return byte_count; -} - -fn String array_name(Module* module, Type* element_type, u64 element_count) -{ - u8 buffer[512]; - auto buffer_slice = String{ .pointer = buffer, .length = array_length(buffer) }; - - u64 i = 0; - - buffer[i] = left_bracket; - i += 1; - - i += format_integer_decimal(buffer_slice(i), element_count); - - buffer[i] = right_bracket; - i += 1; - - auto element_name = element_type->name; - memcpy(buffer + i, element_name.pointer, element_name.length); - i += element_name.length; - - auto name = arena_duplicate_string(module->arena, buffer_slice(0, i)); - return name; -} - fn Type* parse_type(Module* module, Scope* scope) { auto start_character = module->content[module->offset]; @@ -729,8 +671,8 @@ fn Type* parse_type(Module* module, Scope* scope) assert(!length_value); auto result = type_allocate_init(module, { .array = { - .element_count = 0, .element_type = element_type, + .element_count = 0, }, .id = TypeId::array, .name = string_literal(""), @@ -747,15 +689,8 @@ fn Type* parse_type(Module* module, Scope* scope) assert(element_count != 0); - auto result = type_allocate_init(module, { - .array = { - .element_count = element_count, - .element_type = element_type, - }, - .id = TypeId::array, - .name = array_name(module, element_type, element_count), - }); - return result; + auto array_type = get_array_type(module, element_type, element_count); + return array_type; } } } @@ -886,7 +821,7 @@ fn u8 escape_character(u8 ch) { switch (ch) { - default: trap_raw(); + default: trap(); } } @@ -984,7 +919,7 @@ fn Token tokenize(Module* module) } else { - trap_raw(); + trap(); } } break; case '<': @@ -1100,12 +1035,11 @@ fn Token tokenize(Module* module) auto ch = module->content[module->offset]; if (ch == '"') { - module->offset += 1; break; } else if (ch == '\\') { - trap_raw(); + trap(); } else { @@ -1114,6 +1048,7 @@ fn Token tokenize(Module* module) } auto end = module->offset; + module->offset += 1; auto string_literal = module->content(start, end); token = { @@ -1369,7 +1304,7 @@ fn Value* reference_identifier(Module* module, Scope* current_scope, String iden { assert(scope->parent); auto function = scope_to_function(scope); - for (auto argument: function->arguments) + for (auto& argument: function->arguments) { if (identifier.equal(argument.variable.name)) { @@ -1416,7 +1351,7 @@ fn Value* reference_identifier(Module* module, Scope* current_scope, String iden { if (identifier.equal(constant_argument.name)) { - trap_raw(); + trap(); } } @@ -1431,7 +1366,7 @@ fn Value* reference_identifier(Module* module, Scope* current_scope, String iden } break; case ScopeKind::macro_instantiation: { - trap_raw(); + trap(); } break; } @@ -1520,7 +1455,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) { case ValueIntrinsic::align_of: { - trap_raw(); + trap(); } break; case ValueIntrinsic::enum_name: case ValueIntrinsic::extend: @@ -1679,7 +1614,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) } break; case ValueIntrinsic::va_copy: { - trap_raw(); + trap(); } break; case ValueIntrinsic::count: unreachable(); } @@ -1834,11 +1769,11 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) return result; } -fn Precedence get_token_precedence(TokenId id) +fn Precedence get_token_precedence(Token token) { Precedence precedence; - switch (id) + switch (token.id) { case TokenId::none: unreachable(); case TokenId::comma: @@ -1862,6 +1797,23 @@ fn Precedence get_token_precedence(TokenId id) case TokenId::assign_ampersand: precedence = Precedence::assignment; break; + case TokenId::operator_keyword: // TODO: check if any other operator that is not bitwise is added + { + switch (token.operator_keyword) + { + case OperatorKeyword::and_op: + case OperatorKeyword::or_op: + precedence = Precedence::bitwise; + break; + case OperatorKeyword::and_op_shortcircuit: + precedence = Precedence::boolean_and; + break; + case OperatorKeyword::or_op_shortcircuit: + precedence = Precedence::boolean_or; + break; + case OperatorKeyword::count: unreachable(); + } + } break; case TokenId::compare_equal: case TokenId::compare_not_equal: case TokenId::compare_less: @@ -1870,7 +1822,6 @@ fn Precedence get_token_precedence(TokenId id) case TokenId::compare_greater_equal: precedence = Precedence::comparison; break; - case TokenId::operator_keyword: // TODO: check if any other operator that is not bitwise is added case TokenId::ampersand: case TokenId::bar: case TokenId::caret: @@ -1895,7 +1846,7 @@ fn Precedence get_token_precedence(TokenId id) case TokenId::dot: precedence = Precedence::postfix; break; - default: trap_raw(); + default: trap(); } return precedence; @@ -1966,12 +1917,21 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder) case TokenId::operator_keyword: // Binary { - auto precedence = get_token_precedence(token.id); + auto precedence = get_token_precedence(token); assert(precedence != Precedence::assignment); BinaryId id; switch (token.id) { + case TokenId::operator_keyword: + switch (token.operator_keyword) + { + case OperatorKeyword::and_op: id = BinaryId::logical_and; break; + case OperatorKeyword::or_op: id = BinaryId::logical_or; break; + case OperatorKeyword::and_op_shortcircuit: id = BinaryId::logical_and_shortcircuit; break; + case OperatorKeyword::or_op_shortcircuit: id = BinaryId::logical_or_shortcircuit; break; + case OperatorKeyword::count: unreachable(); + } break; case TokenId::plus: id = BinaryId::add; break; case TokenId::dash: id = BinaryId::sub; break; case TokenId::asterisk: id = BinaryId::mul; break; @@ -1988,15 +1948,6 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder) case TokenId::compare_less_equal: id = BinaryId::compare_less_equal; break; case TokenId::compare_greater: id = BinaryId::compare_greater; break; case TokenId::compare_greater_equal: id = BinaryId::compare_greater_equal; break; - case TokenId::operator_keyword: switch (token.operator_keyword) - { - case OperatorKeyword::and_op: id = BinaryId::logical_and; break; - case OperatorKeyword::or_op: id = BinaryId::logical_or; break; - case OperatorKeyword::and_op_shortcircuit: id = BinaryId::logical_and_shortcircuit; break; - case OperatorKeyword::or_op_shortcircuit: id = BinaryId::logical_or_shortcircuit; break; - case OperatorKeyword::count: unreachable(); - break; - } break; default: unreachable(); } @@ -2018,8 +1969,11 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder) { result = new_value(module); *result = { - .dereference = left, - .id = ValueId::dereference, + .unary = { + .value = left, + .id = UnaryId::dereference, + }, + .id = ValueId::unary, .kind = ValueKind::right, }; } break; @@ -2116,7 +2070,7 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder) { case ConstantArgumentId::value: { - trap_raw(); // TODO + trap(); // TODO } break; case ConstantArgumentId::type: { @@ -2188,6 +2142,7 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder) .index = index, }, .id = ValueId::array_expression, + .kind = builder.kind, }; } else @@ -2208,6 +2163,7 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder) .start = start_value, .end = end_value, }, + .id = ValueId::slice_expression, }; } } @@ -2244,7 +2200,7 @@ fn Value* parse_precedence_left(Module* module, Scope* scope, ValueBuilder build { auto checkpoint = get_checkpoint(module); auto token = tokenize(module); - auto token_precedence = get_token_precedence(token.id); + auto token_precedence = get_token_precedence(token); if (token_precedence == Precedence::assignment) { token_precedence = builder.allow_assignment_operators ? Precedence::assignment : Precedence::none; @@ -2283,6 +2239,53 @@ fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder) fn Block* parse_block(Module* module, Scope* parent_scope); +fn void print_value(Value* value, u32 identation) +{ + unused(identation); + for (u32 i = 0; i < identation; i += 1) + { + print(string_literal(" ")); + } + + switch (value->id) + { + case ValueId::binary: + { + switch (value->binary.id) + { + case BinaryId::compare_not_equal: + { + print(string_literal("!=")); + } break; + case BinaryId::logical_and_shortcircuit: + { + print(string_literal("and?")); + } break; + case BinaryId::logical_or_shortcircuit: + { + print(string_literal("or?")); + } break; + default: unreachable(); + } + print(string_literal("\n")); + + print_value(value->binary.left, identation + 1); + print_value(value->binary.right, identation + 1); + } break; + case ValueId::variable_reference: + { + print(value->variable_reference->name); + } break; + case ValueId::constant_integer: + { + print(string_literal("constant_integer")); + } break; + default: unreachable(); + } + + print(string_literal("\n")); +} + fn Statement* parse_statement(Module* module, Scope* scope) { bool require_semicolon = true; @@ -2394,7 +2397,7 @@ fn Statement* parse_statement(Module* module, Scope* scope) { case StatementStartKeyword::underscore_st: { - trap_raw(); + trap(); } break; case StatementStartKeyword::return_st: { @@ -2462,7 +2465,7 @@ fn Statement* parse_statement(Module* module, Scope* scope) .right_values = {}, .predicate = 0, .scope = { - .parent = scope, + .parent = parent_scope, .line = statement_line, .column = statement_column, .kind = ScopeKind::for_each, @@ -2511,7 +2514,7 @@ fn Statement* parse_statement(Module* module, Scope* scope) } else { - trap_raw(); + trap(); } skip_space(module); @@ -2741,7 +2744,7 @@ fn Statement* parse_statement(Module* module, Scope* scope) case TokenId::assign_ampersand: id = StatementAssignmentId::assign_and; break; case TokenId::assign_bar: id = StatementAssignmentId::assign_or; break; case TokenId::assign_caret: id = StatementAssignmentId::assign_xor; break; - default: trap_raw(); + default: trap(); } skip_space(module); @@ -2759,7 +2762,7 @@ fn Statement* parse_statement(Module* module, Scope* scope) } else { - trap_raw(); + trap(); } } break; } @@ -2804,6 +2807,10 @@ fn Block* parse_block(Module* module, Scope* parent_scope) } auto* statement = parse_statement(module, scope); + if (!block->first_statement) + { + block->first_statement = statement; + } if (current_statement) { @@ -2951,8 +2958,6 @@ void parse(Module* module) skip_space(module); - bool is_global_keyword = false; - enum class GlobalKeyword { bits, @@ -3099,16 +3104,13 @@ void parse(Module* module) skip_space(module); expect_character(module, left_brace); - u64 highest_value = 0; - u64 lowest_value = ~(u64)0; - u64 field_count = 0; String name_buffer[64]; - Value* value_buffer[64]; u64 int_value_buffer[64]; bool is_resolved = true; bool implicit_value = false; + unused(implicit_value); while (1) { @@ -3122,11 +3124,11 @@ void parse(Module* module) field_count += 1; auto field_name = parse_identifier(module); + name_buffer[field_index] = field_name; skip_space(module); u64 field_integer_value = field_index; - Value* field_value = 0; if (consume_character_if_match(module, '=')) { @@ -3142,17 +3144,17 @@ void parse(Module* module) { field_integer_value = field_value->constant_integer.value; } break; - default: trap_raw(); + default: trap(); } } else { - trap_raw(); + trap(); } } else { - trap_raw(); + trap(); } } else @@ -3163,6 +3165,8 @@ void parse(Module* module) } } + int_value_buffer[field_index] = field_integer_value; + skip_space(module); consume_character_if_match(module, ','); } @@ -3171,11 +3175,12 @@ void parse(Module* module) { auto fields = arena_allocate(module->arena, field_count); u64 highest_value = 0; - auto lowest_value = ~(u64)0; + // auto lowest_value = ~(u64)0; for (u64 i = 0; i < field_count; i += 1) { auto value = int_value_buffer[i]; + highest_value = MAX(highest_value, value); fields[i] = { .name = name_buffer[i], .value = value, @@ -3204,7 +3209,7 @@ void parse(Module* module) } else { - trap_raw(); + trap(); } } break; case GlobalKeyword::function: @@ -3412,7 +3417,7 @@ void parse(Module* module) .line = line, .column = 0, }, - .index = i, + .index = i + 1, }; } @@ -3436,6 +3441,8 @@ void parse(Module* module) { Type* type_argument_buffer[64]; u64 type_argument_count = 0; + unused(type_argument_buffer); + unused(type_argument_count); ConstantArgument constant_argument_buffer[64]; u64 constant_argument_count = 0; @@ -3463,7 +3470,7 @@ void parse(Module* module) if (has_value) { - trap_raw(); // TODO + trap(); // TODO } else { @@ -3520,7 +3527,7 @@ void parse(Module* module) if (module->last_macro_declaration) { assert(module->first_macro_declaration); - trap_raw(); + trap(); } else { @@ -3568,7 +3575,7 @@ void parse(Module* module) .line = argument_line, .column = argument_column, }, - .index = argument_index, + .index = argument_index + 1, }; argument_count += 1; @@ -3601,7 +3608,7 @@ void parse(Module* module) Type* struct_type; if (forward_declaration) { - trap_raw(); + trap(); } else { diff --git a/tests/basic_bool_call.bbb b/tests/basic_bool_call.bbb new file mode 100644 index 0000000..7b9b28d --- /dev/null +++ b/tests/basic_bool_call.bbb @@ -0,0 +1,16 @@ +require = fn (ok: u1) void +{ + if (!ok) #trap(); +} + +bb_bool = fn (x: u8) void +{ + require(#truncate(x)); +} + +[export] main = fn [cc(c)] () s32 +{ + bb_bool(1); + return 0; +} + diff --git a/tests/basic_shortcircuiting_if.bbb b/tests/basic_shortcircuiting_if.bbb new file mode 100644 index 0000000..c938530 --- /dev/null +++ b/tests/basic_shortcircuiting_if.bbb @@ -0,0 +1,12 @@ +[export] main = fn [cc(c)] (argument_count: u32) s32 +{ + >a: s32 = 0; + if (argument_count != 3 and? argument_count != 2) + { + return 0; + } + else + { + return 1; + } +}