From eb88c61f883acb0402c05fb855d927d75bcd8994 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 4 Jun 2025 21:25:30 -0600 Subject: [PATCH] Compile first executable with self-hosted compiler --- .gitignore | 1 + src/compiler.bbb | 2697 ++++++++++++++++++++++++++++++++++++- src/compiler.cpp | 10 +- src/compiler.hpp | 179 ++- src/emitter.cpp | 2320 +++++++++++++++++-------------- src/llvm.cpp | 922 +------------ src/llvm.hpp | 499 +------ src/parser.cpp | 647 ++++++--- tests/bool_pair.bbb | 18 + tests/enum_debug_info.bbb | 38 + tests/min_max.bbb | 10 + tests/return_array.bbb | 22 + 12 files changed, 4691 insertions(+), 2672 deletions(-) create mode 100644 tests/bool_pair.bbb create mode 100644 tests/enum_debug_info.bbb create mode 100644 tests/min_max.bbb create mode 100644 tests/return_array.bbb diff --git a/.gitignore b/.gitignore index d96d990..f2c1307 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ imgui.ini /.zig-cache/ /zig-out/ /bb-cache/ +/self-hosted-bb-cache/ diff --git a/src/compiler.bbb b/src/compiler.bbb index cf68319..b6dda3d 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -97,6 +97,19 @@ align_forward = fn (value: u64, alignment: u64) u64 return result; } +next_power_of_two = fn (n: u64) u64 +{ + n -= 1; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n |= n >> 32; + n += 1; + return n; +} + string_no_match = #integer_max(u64); c_string_length = fn (c_string: &u8) u64 @@ -342,6 +355,24 @@ os_file_read = fn (fd: File, buffer: []u8, byte_count: u64) void } } +os_file_write_partially = fn (fd: File, pointer: &u8, length: u64) u64 +{ + >result = write(fd, pointer, length); + assert(result > 0); + return #extend(result); +} + +os_file_write = fn (fd: File, buffer: []u8) void +{ + >total_written_byte_count: u64 = 0; + + while (total_written_byte_count < buffer.length) + { + >written_byte_count = os_file_write_partially(fd, buffer.pointer + total_written_byte_count, buffer.length - total_written_byte_count); + total_written_byte_count += written_byte_count; + } +} + os_path_absolute_stack = fn (buffer: []u8, relative_file_path: &u8) []u8 { >syscall_result = realpath(relative_file_path, buffer.pointer); @@ -504,6 +535,11 @@ file_read = fn (arena: &Arena, path: []u8) []u8 return result; } +print = fn (string: []u8) void +{ + os_file_write(1, string); +} + path_absolute = fn (arena: &Arena, relative_file_path: &u8) []u8 { >buffer: [4096]u8 = undefined; @@ -531,6 +567,126 @@ fail = fn () noreturn exit(1); } +LLVMContext = opaque; +LLVMModule = opaque; + +LLVMBuilder = opaque; + +LLVMValue = opaque; +LLVMUse = opaque; +LLVMType = opaque; +LLVMBasicBlock = opaque; + +LLVMIntrinsicId = typealias u32; + +LLVMAttributeId = typealias u32; +LLVMAttribute = opaque; + +LLVMMetadata = opaque; +LLVMDIBuilder = opaque; + +LLVMTarget = opaque; +LLVMTargetDataLayout = opaque; +LLVMTargetMachine = opaque; +LLVMTargetMachineOptions = opaque; + +LLVMIntrinsicIndex = enum u32 +{ + "llvm.smax", + "llvm.smin", + "llvm.trap", + "llvm.umax", + "llvm.umin", + "llvm.va_start", + "llvm.va_end", + "llvm.va_copy", +} + +LLVMAttributeIndex = enum u32 +{ + align, + alwaysinline, + byval, + dead_on_unwind, + inlinehint, + inreg, + naked, + noalias, + noinline, + noreturn, + nounwind, + signext, + sret, + writable, + zeroext, +} + +LLVMLinkage = enum u32 +{ + external, // Externally visible function available_externally, + link_once_any, // Keep one copy of function when linking (inline)*/ + link_once_odr, // Same, but only replaced by something + + link_once_odr_auto_hide, // Obsolete + weak_any, // Keep one copy of function when linking (weak) */ + weak_odr, // Same, but only replaced by something equivalent + appending, // Special purpose, only applies to global arrays */ + internal, // Rename collisions when linking (static functions) */ + private, // Like Internal, but omit from symbol table */ + dll_import, // Obsolete */ + dll_export, // Obsolete */ + external_weak,// ExternalWeak linkage description */ + ghost, // Obsolete */ + common, // Tentative definitions */ + linker_private, // Like Private, but linker removes. */ + linker_private_weak // Like LinkerPrivate, but is weak. */ +} + +LLVMCallingConvention = enum +{ + c = 0, + fast = 8, + cold = 9, + ghc = 10, + hi_pe = 11, + any_reg = 13, + preserve_most = 14, + preserve_all = 15, + swift = 16, + cxx_fast_tls = 17, + x86_stdcall = 64, + x86_fastcall = 65, + arm_apcs = 66, + arm_aapcs = 67, + arm_aapcs_vfp = 68, + msp430_intr = 69, + x86_this_call = 70, + ptx_kernel = 71, + ptx_device = 72, + spir_function = 75, + spir_kernel = 76, + intel_oclbi = 77, + x86_64_system_v = 78, + win64 = 79, + x86_vector_call = 80, + hhvm = 81, + hhvm_c = 82, + x86_intr = 83, + avr_intr = 84, + avr_signal = 85, + avr_builtin = 86, + amdgpu_vs = 87, + amdgpu_gs = 88, + amdgpu_ps = 89, + amdgpu_cs = 90, + amdgpu_kernel = 91, + x86_regcall = 92, + amdgpu_hs = 93, + msp430_builtin = 94, + amdgpu_ls = 95, + amdgpu_es = 96 +} + CompilerCommand = enum { compile, @@ -548,6 +704,15 @@ BuildMode = enum aggressively_optimize_for_size, } +build_mode_is_optimized = fn (build_mode: BuildMode) u1 +{ + switch (build_mode) + { + .debug_none, .debug => { return 0; }, + else => { return 1; }, + } +} + CompileFile = struct { relative_file_path: []u8, @@ -556,7 +721,7 @@ CompileFile = struct silent: u1, } -base_cache_dir = "bb-cache"; +base_cache_dir = "self-hosted-bb-cache"; CPUArchitecture = enum { @@ -593,7 +758,11 @@ CompileOptions = struct executable: []u8, name: []u8, objects: [][]u8, - libraries: [][]u8, + library_directories: [][]u8, + library_names: [][]u8, + library_paths: [][]u8, + link_libc: u1, + link_libcpp: u1, target: Target, build_mode: BuildMode, has_debug_info: u1, @@ -623,6 +792,7 @@ Type = struct; Value = struct; Local = struct; Block = struct; +Module = struct; ScopeKind = enum { @@ -644,6 +814,7 @@ Scope = struct { types: TypeList, parent: &Scope, + llvm: &LLVMMetadata, line: u32, column: u32, kind: ScopeKind, @@ -710,7 +881,7 @@ TypeId = enum TypeInteger = struct { - bit_count: u32, + bit_count: u64, signed: u1, } @@ -731,18 +902,181 @@ AbiRegisterCount = union system_v: AbiRegisterCountSystemV, }; -AbiInformation = struct +AbiInformationPadding = union { - foo: u32, + type: &Type, + unpadded_coerce_and_expand_type: &Type, } -TypeFunction = struct +AbiInformationDirectAttributes = struct +{ + offset: u32, + alignment: u32, +} + +AbiInformationIndirectAttributes = struct +{ + alignment: u32, + address_space: u32, +} + +AbiInformationAttributes = union +{ + direct: AbiInformationDirectAttributes, + indirect: AbiInformationIndirectAttributes, + alloca_field_index: u32, +} + +AbiKind = enum +{ + ignore, + direct, + extend, + indirect, + indirect_aliased, + expand, + coerce_and_expand, + in_alloca, +} + +AbiFlags = struct +{ + kind: AbiKind, + padding_in_reg: u1, + in_alloca_sret: u1, + in_alloca_indirect: u1, + indirect_by_value: u1, + indirect_realign: u1, + sret_after_this: u1, + in_reg: u1, + can_be_flattened: u1, + sign_extension: u1, +} + +AbiInformation = struct +{ + semantic_type: &Type, + coerce_to_type: &Type, + padding: AbiInformationPadding, + padding_argument_index: u16, + attributes: AbiInformationAttributes, + flags: AbiFlags, + abi_start: u16, + abi_count: u16, +} + +abi_set_sret_after_this = fn (abi_information: &AbiInformation, value: u1) void +{ + assert(abi_information.flags.kind == .indirect); + abi_information.flags.sret_after_this = value; +} + +abi_set_indirect_realign = fn (abi_information: &AbiInformation, value: u1) void +{ + assert(abi_information.flags.kind == .indirect); + abi_information.flags.indirect_realign = value; +} + +abi_set_indirect_by_value = fn (abi_information: &AbiInformation, value: u1) void +{ + assert(abi_information.flags.kind == .indirect); + abi_information.flags.indirect_by_value = value; +} + +abi_set_indirect_align = fn (abi_information: &AbiInformation, value: u32) void +{ + assert(abi_information.flags.kind == .indirect); + abi_information.attributes.indirect.alignment = value; +} + +abi_can_have_coerce_to_type = fn (abi_information: &AbiInformation) u1 +{ + switch (abi_information.flags.kind) + { + .direct, .extend, .coerce_and_expand => + { + return 1; + }, + else => + { + return 0; + }, + } +} + +abi_set_coerce_to_type = fn (abi_information: &AbiInformation, type: &Type) void +{ + assert(abi_can_have_coerce_to_type(abi_information)); + abi_information.coerce_to_type = type; +} + +abi_get_coerce_to_type = fn (abi_information: &AbiInformation) &Type +{ + assert(abi_can_have_coerce_to_type(abi_information)); + return abi_information.coerce_to_type; +} + +abi_can_have_padding_type = fn (abi_information: &AbiInformation) u1 +{ + switch (abi_information.flags.kind) + { + .direct, .extend, .indirect, .indirect_aliased, .expand => + { + return 1; + }, + else => + { + return 0; + } + } +} + +abi_set_padding_type = fn (abi_information: &AbiInformation, type: &Type) void +{ + assert(abi_can_have_padding_type(abi_information)); + abi_information.padding = { + .type = type, + }; +} + +abi_get_padding_type = fn (abi_information: &AbiInformation) &Type +{ + return #select(abi_can_have_padding_type(abi_information), abi_information.padding.type, zero); +} + +abi_set_direct_offset = fn (abi_information: &AbiInformation, offset: u32) void +{ + assert(abi_information.flags.kind == .direct or abi_information.flags.kind == .extend); + abi_information.attributes.direct.offset = offset; +} + +abi_set_direct_alignment = fn (abi_information: &AbiInformation, alignment: u32) void +{ + assert(abi_information.flags.kind == .direct or abi_information.flags.kind == .extend); + abi_information.attributes.direct.alignment = alignment; +} + +abi_set_can_be_flattened = fn (abi_information: &AbiInformation, value: u1) void +{ + assert(abi_information.flags.kind == .direct); + abi_information.flags.can_be_flattened = value; +} + +abi_get_can_be_flattened = fn (abi_information: &AbiInformation) u1 +{ + return abi_information.flags.can_be_flattened; +} + +TypeFunctionBase = struct { semantic_return_type: &Type, semantic_argument_types: []&Type, calling_convention: CallingConvention, - is_variable_arguments: u1, + is_variable_argument: u1, +} +TypeFunctionAbi = struct +{ abi_argument_types: []&Type, abi_return_type: &Type, available_registers: AbiRegisterCount, @@ -750,6 +1084,13 @@ TypeFunction = struct return_abi: AbiInformation, } +TypeFunction = struct +{ + base: TypeFunctionBase, + abi: TypeFunctionAbi, + next: &Type, +} + TypeContent = union { integer: TypeInteger, @@ -757,6 +1098,13 @@ TypeContent = union pointer: TypePointer, } +TypeLLVM = struct +{ + abi: &LLVMType, + memory: &LLVMType, + debug: &LLVMMetadata, +} + Type = struct { content: TypeContent, @@ -764,20 +1112,175 @@ Type = struct name: []u8, next: &Type, scope: &Scope, + llvm: TypeLLVM, +} + +type_is_signed = fn (type: &Type) u1 +{ + switch (type.id) + { + .integer => + { + return type.content.integer.signed; + }, + else => + { + #trap(); + }, + } +} + +EvaluationKind = enum +{ + scalar, + aggregate, + complex, +} + +get_evaluation_kind = fn (type: &Type) EvaluationKind +{ + switch (type.id) + { + .integer, + .pointer, + .bits, + .enum, + => + { + return .scalar; + }, + .array, + .struct, + .union, + .enum_array, + => + { + return .aggregate; + }, + else => + { + unreachable; + }, + } +} + +integer_max_value = fn (bit_count: u64, signed: u1) u64 +{ + >value: u64 = #select(bit_count == 64, ~0, (1 << (bit_count - #extend(signed))) - 1); + return value; +} + +align_bit_count = fn (bit_count: u64) u64 +{ + >aligned_bit_count = #max(next_power_of_two(bit_count), 8); + assert(aligned_bit_count % 8 == 0); + return aligned_bit_count; +} + +aligned_byte_count_from_bit_count = fn (bit_count: u64) u64 +{ + >aligned_bit_count = align_bit_count(bit_count); + return aligned_bit_count / 8; +} + +get_byte_size = fn (type: &Type) u64 +{ + switch (type.id) + { + .integer => + { + >byte_count = aligned_byte_count_from_bit_count(type.content.integer.bit_count); + assert(byte_count == 1 or byte_count == 2 or byte_count == 4 or byte_count == 8 or byte_count == 16); + return byte_count; + }, + else => + { + #trap(); + }, + } +} + +get_byte_alignment = fn (type: &Type) u32 +{ + switch (type.id) + { + .integer => + { + >aligned_byte_count: u32 = #truncate(aligned_byte_count_from_bit_count(type.content.integer.bit_count)); + assert(aligned_byte_count == 1 or aligned_byte_count == 2 or aligned_byte_count == 4 or aligned_byte_count == 8 or aligned_byte_count == 16); + return aligned_byte_count; + }, + else => + { + #trap(); + }, + } +} + +is_integral_or_enumeration_type = fn (type: &Type) u1 +{ + switch (type.id) + { + .integer, .bits, .enum => + { + return 1; + }, + .array, .struct => + { + return 0; + }, + else => + { + unreachable; + }, + } +} + +is_promotable_integer_type_for_abi = fn (type: &Type) u1 +{ + switch (type.id) + { + .integer => + { + return type.content.integer.bit_count < 32; + }, + else => + { + #trap(); + } + } +} + +resolve_type_in_place_abi = fn (module: &Module, type: &Type) void; +resolve_type_in_place_memory = fn (module: &Module, type: &Type) void; +resolve_type_in_place_debug = fn (module: &Module, type: &Type) void; + +resolve_type_in_place = fn (module: &Module, type: &Type) void +{ + resolve_type_in_place_abi(module, type); + resolve_type_in_place_memory(module, type); + resolve_type_in_place_debug(module, type); } ValueId = enum { infer_or_ignore, - external_function, + forward_declared_function, function, constant_integer, + global, } ValueConstantInteger = struct { value: u64, - is_signed: u1, + signed: u1, +} + +ValueFunctionLLVM = struct +{ + return_block: &LLVMBasicBlock, + return_alloca: &LLVMValue, } ValueFunction = struct @@ -785,6 +1288,7 @@ ValueFunction = struct arguments: []Argument, scope: Scope, block: &Block, + llvm: ValueFunctionLLVM, attributes: FunctionAttributes, } @@ -806,6 +1310,7 @@ Value = struct type: &Type, id: ValueId, kind: ValueKind, + llvm: &LLVMValue, } i128_offset: u64 = 64 * 2; @@ -821,31 +1326,6 @@ MacroInstantiation = struct foo: u32, } -LLVMContext = opaque; -LLVMModule = opaque; - -LLVMBuilder = opaque; - -LLVMValue = opaque; -LLVMType = opaque; -LLVMBasicBlock = opaque; -LLVMIntrinsicId = typealias u32; - -LLVMMetadata = opaque; -LLVMDIBuilder = opaque; - -LLVMTarget = opaque; -LLVMTargetMachine = opaque; -LLVMTargetMachineOptions = opaque; - -LLVMIntrinsicIndex = enum -{ - trap, - va_start, - va_end, - va_copy, -} - [extern] LLVMInitializeX86TargetInfo = fn [cc(c)] () void; [extern] LLVMInitializeX86Target = fn [cc(c)] () void; [extern] LLVMInitializeX86TargetMC = fn [cc(c)] () void; @@ -882,7 +1362,7 @@ llvm_initialize_targets = fn () void } } -LLVMCodeGenerationOptimizationLevel = enum +LLVMCodeGenerationOptimizationLevel = enum u32 { none = 0, less = 1, @@ -890,16 +1370,325 @@ LLVMCodeGenerationOptimizationLevel = enum aggressive = 3, } +LLVMDwarfSourceLanguage = enum u32 +{ + C89, + C, + Ada83, + C_plus_plus, + Cobol74, + Cobol85, + Fortran77, + Fortran90, + Pascal83, + Modula2, + // New in DWARF v3: + Java, + C99, + Ada95, + Fortran95, + PLI, + ObjC, + ObjC_plus_plus, + UPC, + D, + // New in DWARF v4: + Python, + // New in DWARF v5: + OpenCL, + Go, + Modula3, + Haskell, + C_plus_plus_03, + C_plus_plus_11, + OCaml, + Rust, + C11, + Swift, + Julia, + Dylan, + C_plus_plus_14, + Fortran03, + Fortran08, + RenderScript, + BLISS, + Kotlin, + Zig, + Crystal, + C_plus_plus_17, + C_plus_plus_20, + C17, + Fortran18, + Ada2005, + Ada2012, + HIP, + Assembly, + C_sharp, + Mojo, + GLSL, + GLSL_ES, + HLSL, + OpenCL_CPP, + CPP_for_OpenCL, + SYCL, + Ruby, + Move, + Hylo, + Metal, + + // Vendor extensions: + Mips_Assembler, + GOOGLE_RenderScript, + BORLAND_Delphi +} + +LLVMDwarfEmissionKind = enum u32 +{ + none, + full, + line_tables_only, +} + +LLVMDwarfTypeEncoding = enum +{ + void = 0x0, + address = 0x1, + boolean = 0x2, + complex_float = 0x3, + float = 0x4, + signed = 0x5, + signed_char = 0x6, + unsigned = 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. +} + +LLVMDIFlagsVisibility = enum u2 +{ + none = 0, + private = 1, + protected = 2, + public = 3, +} + +LLVMDIFlagsInheritance = enum u2 +{ + none = 0, + single = 1, + multiple = 2, + virtual = 3, +} + +LLVMDIFlags = bits u32 +{ + visibility: LLVMDIFlagsVisibility, + forward_declaration: u1, + apple_block: u1, + block_by_ref_struct: u1, + virtual: u1, + artificial: u1, + explicit: u1, + prototyped: u1, + objective_c_class_complete: u1, + object_pointer: u1, + vector: u1, + static_member: u1, + lvalue_reference: u1, + rvalue_reference: u1, + _: u1, + inheritance: LLVMDIFlagsInheritance, + introduced_virtual: u1, + bit_field: u1, + noreturn: u1, + type_pass_by_value: u1, + type_pass_by_reference: u1, + enum_class: u1, + thunk: u1, + non_trivial: u1, + big_endian: u1, + little_endian: u1, + all_calls_described: u1, + _: u3, +} + +LLVMOptimizationLevel = enum u3 +{ + O0 = 0, + O1 = 1, + O2 = 2, + O3 = 3, + Os = 4, + Oz = 5, +} + +LLVMOptimizationOptions = bits u64 +{ + optimization_level: LLVMOptimizationLevel, + debug_info: u1, + loop_unrolling: u1, + loop_interleaving: u1, + loop_vectorization: u1, + slp_vectorization: u1, + merge_functions: u1, + call_graph_profile: u1, + unified_lto: u1, + assignment_tracking: u1, + verify_module: u1, + reserved: u51, +} + +LLVMCodeGenerationFileType = enum u8 +{ + assembly = 0, + object = 1, + null = 2, +} + +LLVMCodeGenerationOptions = struct +{ + output_dwarf_file_path: []u8, + output_file_path: []u8, + file_type: LLVMCodeGenerationFileType, + optimize_when_possible: u8, + verify_module: u8, +} + +LLVMCodeGenerationResult = enum u8 +{ + success = 0, + failed_to_create_file = 1, + failed_to_emit_passes = 2, +} + [extern] LLVMContextCreate = fn [cc(c)] () &LLVMContext; [extern] llvm_context_create_module = fn (context: &LLVMContext, name: []u8) &LLVMModule; [extern] LLVMCreateBuilderInContext = fn (context: &LLVMContext) &LLVMBuilder; -[extern] LLVMCreateTargetMachineOptions = fn () &LLVMTargetMachineOptions; -[extern] LLVMTargetMachineOptionsSetCPU = fn (target_machine_options: &LLVMTargetMachineOptions, cpu: &u8) void; -[extern] LLVMTargetMachineOptionsSetFeatures = fn (target_machine_options: &LLVMTargetMachineOptions, features: &u8) void; -[extern] LLVMTargetMachineOptionsSetCodeGenOptLevel = fn (target_machine_options: &LLVMTargetMachineOptions, optimization_level: LLVMCodeGenerationOptimizationLevel) void; -[extern] LLVMGetTargetFromTriple = fn (target_triple: &u8, target_pointer: &&LLVMTarget, error_message_pointer: &&u8) s32; -[extern] LLVMCreateTargetMachineWithOptions = fn (target: &LLVMTarget, target_triple: &u8, target_machine_options: &LLVMTargetMachineOptions) &LLVMTargetMachine; +[extern] LLVMVoidTypeInContext = fn [cc(c)] (context: &LLVMContext) &LLVMType; +[extern] LLVMPointerTypeInContext = fn [cc(c)] (context: &LLVMContext, address_space: u32) &LLVMType; +[extern] LLVMIntTypeInContext = fn [cc(c)] (context: &LLVMContext, bit_count: u32) &LLVMType; +[extern] LLVMFunctionType = fn [cc(c)] (return_type: &LLVMType, argument_pointer: &&LLVMType, argument_count: u32, is_variable_argument: s32) &LLVMType; + +[extern] LLVMConstInt = fn [cc(c)] (type: &LLVMType, value: u64, is_signed: s32) &LLVMValue; + +[extern] LLVMCreateDIBuilder = fn (module: &LLVMModule) &LLVMDIBuilder; +[extern] LLVMDIBuilderCreateFile = fn (di_builder: &LLVMDIBuilder, file_pointer: &u8, file_length: u64, directory_pointer: &u8, directory_length: u64) &LLVMMetadata; +[extern] LLVMDIBuilderCreateCompileUnit = fn (di_builder: &LLVMDIBuilder, language: LLVMDwarfSourceLanguage, file: &LLVMMetadata, producer_name_pointer: &u8, producer_name_length: u64, is_optimized: s32, flags_pointer: &u8, flags_length: u64, runtime_version: u32, split_name_pointer: &u8, split_name_length: u64, emission_kind: LLVMDwarfEmissionKind, dwo_id: u32, split_debug_inlining: s32, debug_info_for_profiling: s32, sysroot_pointer: &u8, sysroot_length: u64, sdk_pointer: &u8, sdk_length: u64) &LLVMMetadata; + +[extern] LLVMDIBuilderCreateBasicType = fn [cc(c)] (di_builder: &LLVMDIBuilder, name_pointer: &u8, name_length: u64, bit_size: u64, dwarf_encoding: LLVMDwarfTypeEncoding, flags: LLVMDIFlags) &LLVMMetadata; +[extern] LLVMDIBuilderCreatePointerType = fn [cc(c)] (di_builder: &LLVMDIBuilder, element_type: &LLVMMetadata, bit_size: u64, bit_alignment: u32, address_space: u32, name_pointer: &u8, name_length: u64) &LLVMMetadata; +[extern] LLVMDIBuilderCreateSubroutineType = fn [cc(c)] (di_builder: &LLVMDIBuilder, file: &LLVMMetadata, argument_type_pointer: &&LLVMMetadata, argument_type_count: u32, flags: LLVMDIFlags) &LLVMMetadata; + +[extern] LLVMDIBuilderCreateFunction = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, linkage_name_pointer: &u8, linkage_name_length: u64, file: &LLVMMetadata, line: u32, type: &LLVMMetadata, is_local_to_unit: s32, is_definition: s32, scope_line: u32, flags: LLVMDIFlags, is_optimized: s32) &LLVMMetadata; +[extern] LLVMDIBuilderFinalizeSubprogram = fn [cc(c)] (di_builder: &LLVMDIBuilder, subprogram: &LLVMMetadata) void; + +[extern] LLVMDIBuilderCreateLexicalBlock = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, file: &LLVMMetadata, line: u32, column: u32) &LLVMMetadata; +[extern] LLVMDIBuilderCreateDebugLocation = fn [cc(c)] (context: &LLVMContext, line: u32, column: u32, scope: &LLVMMetadata, inlined_at: &LLVMMetadata) &LLVMMetadata; +[extern] LLVMDIBuilderFinalize = fn [cc(c)] (di_builder: &LLVMDIBuilder) void; + +[extern] LLVMSetSubprogram = fn [cc(c)] (function: &LLVMValue, subprogram: &LLVMMetadata) void; +[extern] LLVMGetSubprogram = fn [cc(c)] (function: &LLVMValue) &LLVMMetadata; + +[extern] LLVMLookupIntrinsicID = fn [cc(c)] (name_pointer: &u8, name_length: u64) LLVMIntrinsicId; + +[extern] LLVMGetEnumAttributeKindForName = fn [cc(c)] (name_pointer: &u8, name_length: u64) LLVMAttributeId; + +[extern] LLVMCreateEnumAttribute = fn [cc(c)] (context: &LLVMContext, attribute_id: LLVMAttributeId, value: u64) &LLVMAttribute; +[extern] LLVMCreateTypeAttribute = fn [cc(c)] (context: &LLVMContext, attribute_id: LLVMAttributeId, type: &LLVMType) &LLVMAttribute; +[extern] LLVMCreateStringAttribute = fn [cc(c)] (context: &LLVMContext, attribute_key_pointer: &u8, attribute_key_length: u64, attribute_value_pointer: &u8, attribute_value_length: u64) &LLVMAttribute; +[extern] LLVMAddAttributeAtIndex = fn [cc(c)] (value: &LLVMValue, index: u32, attribute: &LLVMAttribute) void; + +[extern] LLVMGetParams = fn [cc(c)] (function: &LLVMValue, parameter_pointer: &&LLVMValue) void; + +[extern] llvm_context_create_basic_block = fn [cc(c)] (context: &LLVMContext, name: []u8, parent_function: &LLVMValue) &LLVMBasicBlock; +[extern] llvm_builder_create_alloca = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, address_space: u32, alignment: u32, name: []u8) &LLVMValue; +[extern] LLVMBuildStore = fn [cc(c)] (builder: &LLVMBuilder, source: &LLVMValue, destination: &LLVMValue) &LLVMValue; +[extern] LLVMBuildLoad2 = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, pointer: &LLVMValue, name: &u8) &LLVMValue; +[extern] LLVMPositionBuilderAtEnd = fn [cc(c)] (builder: &LLVMBuilder, basic_block: &LLVMBasicBlock) void; +[extern] LLVMClearInsertionPosition = fn [cc(c)] (builder: &LLVMBuilder) void; +[extern] LLVMSetCurrentDebugLocation2 = fn [cc(c)] (builder: &LLVMBuilder, debug_location: &LLVMMetadata) void; +[extern] LLVMBuildRet = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue) &LLVMValue; +[extern] LLVMBuildRetVoid = fn [cc(c)] (builder: &LLVMBuilder) &LLVMValue; +[extern] LLVMBuildBr = fn [cc(c)] (builder: &LLVMBuilder, target_block: &LLVMBasicBlock) &LLVMValue; +[extern] LLVMBuildIntCast2 = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, signed: s32, name: &u8) &LLVMValue; +[extern] LLVMGetInsertBlock = fn [cc(c)] (builder: &LLVMBuilder) &LLVMBasicBlock; +[extern] LLVMGetBasicBlockTerminator = fn [cc(c)] (basic_block: &LLVMBasicBlock) &LLVMValue; +[extern] LLVMGetBasicBlockParent = fn [cc(c)] (basic_block: &LLVMBasicBlock) &LLVMValue; + +[extern] LLVMInstructionEraseFromParent = fn [cc(c)] (value: &LLVMValue) void; +[extern] LLVMGetOperand = fn [cc(c)] (value: &LLVMValue, index: u32) &LLVMValue; +[extern] LLVMGetFirstUse = fn [cc(c)] (value: &LLVMValue) &LLVMUse; +[extern] LLVMGetNextUse = fn [cc(c)] (use: &LLVMUse) &LLVMUse; +[extern] LLVMGetUser = fn [cc(c)] (use: &LLVMUse) &LLVMValue; + +[extern] LLVMIsABranchInst = fn [cc(c)] (value: &LLVMValue) &LLVMValue; +[extern] LLVMIsConditional = fn [cc(c)] (value: &LLVMValue) s32; +[extern] LLVMGetSuccessor = fn [cc(c)] (value: &LLVMValue, index: u32) &LLVMBasicBlock; + +[extern] LLVMSetAlignment = fn [cc(c)] (value: &LLVMValue, alignment: u32) void; + +[extern] llvm_find_return_value_dominating_store = fn [cc(c)] (builder: &LLVMBuilder, return_alloca: &LLVMValue, element_type: &LLVMType) &LLVMValue; +[extern] llvm_module_verify = fn [cc(c)] (module: &LLVMModule, error_message: &[]u8) u1; +[extern] llvm_module_to_string = fn [cc(c)] (module: &LLVMModule) []u8; + +[extern] LLVMCreateTargetMachineOptions = fn [cc(c)] () &LLVMTargetMachineOptions; +[extern] LLVMTargetMachineOptionsSetCPU = fn [cc(c)] (target_machine_options: &LLVMTargetMachineOptions, cpu: &u8) void; +[extern] LLVMTargetMachineOptionsSetFeatures = fn [cc(c)] (target_machine_options: &LLVMTargetMachineOptions, features: &u8) void; +[extern] LLVMTargetMachineOptionsSetCodeGenOptLevel = fn [cc(c)] (target_machine_options: &LLVMTargetMachineOptions, optimization_level: LLVMCodeGenerationOptimizationLevel) void; +[extern] LLVMGetTargetFromTriple = fn [cc(c)] (target_triple: &u8, target_pointer: &&LLVMTarget, error_message_pointer: &&u8) s32; +[extern] LLVMCreateTargetMachineWithOptions = fn [cc(c)] (target: &LLVMTarget, target_triple: &u8, target_machine_options: &LLVMTargetMachineOptions) &LLVMTargetMachine; +[extern] LLVMCreateTargetDataLayout = fn [cc(c)] (target_machine: &LLVMTargetMachine) &LLVMTargetDataLayout; +[extern] LLVMSetModuleDataLayout = fn [cc(c)] (module: &LLVMModule, target_data_layout: &LLVMTargetDataLayout) void; +[extern] LLVMSetTarget = fn [cc(c)] (module: &LLVMModule, target_triple: &u8) void; + +[extern] llvm_module_create_function = fn [cc(c)] (module: &LLVMModule, function_type: &LLVMType, linkage_type: LLVMLinkage, address_space: u32, name: []u8) &LLVMValue; +[extern] LLVMSetFunctionCallConv = fn [cc(c)] (function: &LLVMValue, calling_convention: LLVMCallingConvention) void; + +[extern] llvm_module_run_optimization_pipeline = fn [cc(c)] (module: &LLVMModule, target_machine: &LLVMTargetMachine, options: &LLVMOptimizationOptions) void; +[extern] llvm_module_run_code_generation_pipeline = fn [cc(c)] (module: &LLVMModule, target_machine: &LLVMTargetMachine, options: &LLVMCodeGenerationOptions) LLVMCodeGenerationResult; + +LLDResult = struct +{ + stdout: []u8, + stderr: []u8, + success: u1, +} +[extern] lld_elf_link = fn [cc(c)] (argument_pointer: &&u8, argument_count: u64, exit_early: u1, disable_output: u1) LLDResult; + +default_address_space: u32 = 0; ModuleLLVM = struct { @@ -908,10 +1697,12 @@ ModuleLLVM = struct builder: &LLVMBuilder, di_builder: &LLVMDIBuilder, file: &LLVMMetadata, - compile_unit: &LLVMMetadata, + target_machine: &LLVMTargetMachine, + target_data_layout: &LLVMTargetDataLayout, pointer_type: &LLVMType, void_type: &LLVMType, intrinsic_table: enum_array[LLVMIntrinsicIndex](LLVMIntrinsicId), + attribute_table: enum_array[LLVMAttributeIndex](LLVMAttributeId), memcmp: &LLVMValue, inlined_at: &LLVMMetadata, continue_block: &LLVMBasicBlock, @@ -951,7 +1742,11 @@ Module = struct path: []u8, executable: []u8, objects: [][]u8, - libraries: [][]u8, + library_directories: [][]u8, + library_names: [][]u8, + library_paths: [][]u8, + link_libc: u1, + link_libcpp: u1, target: Target, build_mode: BuildMode, @@ -1118,7 +1913,7 @@ integer_type = fn (module: &Module, integer: TypeInteger) &Type { assert(integer.bit_count != 0); assert(integer.bit_count <= 64); - >index = #select(integer.bit_count == 128, i128_offset + #extend(integer.signed), #extend(integer.bit_count) - 1 + 64 * #extend(integer.signed)); + >index = #select(integer.bit_count == 128, i128_offset + #extend(integer.signed), integer.bit_count - 1 + 64 * #extend(integer.signed)); >result = module.scope.types.first + index; assert(result.id == .integer); assert(result.content.integer.bit_count == integer.bit_count); @@ -1126,6 +1921,11 @@ integer_type = fn (module: &Module, integer: TypeInteger) &Type return result; } +uint64 = fn (module: &Module) &Type +{ + return integer_type(module, { .bit_count = 64, .signed = 0 }); +} + void_type = fn (module: &Module) &Type { return module.scope.types.first + void_offset; @@ -1580,7 +2380,7 @@ parse_type = fn (module: &Module, scope: &Scope) &Type } } - >result = integer_type(module, { .bit_count = #truncate(bit_count), .signed = is_signed }); + >result = integer_type(module, { .bit_count = bit_count, .signed = is_signed }); return result; } else @@ -1981,7 +2781,7 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value .content = { .constant_integer = { .value = integer_value, - .is_signed = 0, + .signed = 0, }, }, .id = .constant_integer, @@ -2461,7 +3261,7 @@ parse = fn (module: &Module) void { >calling_convention: CallingConvention = .c; >function_attributes: FunctionAttributes = zero; - >is_variable_arguments: u1 = 0; + >is_variable_argument: u1 = 0; if (consume_character_if_match(module, left_bracket)) { @@ -2577,10 +3377,12 @@ parse = fn (module: &Module) void >function_type = new_type(module, { .content = { .function = { - .semantic_return_type = return_type, - .semantic_argument_types = argument_types, - .calling_convention = calling_convention, - .is_variable_arguments = is_variable_arguments, + .base = { + .semantic_return_type = return_type, + .semantic_argument_types = argument_types, + .calling_convention = calling_convention, + .is_variable_argument = is_variable_argument, + }, zero, }, }, @@ -2592,7 +3394,7 @@ parse = fn (module: &Module) void >storage = new_value(module); storage.& = { - .id = .external_function, + .id = .forward_declared_function, .type = get_pointer_type(module, function_type), zero, // TODO? kind = .left, @@ -2679,6 +3481,1228 @@ parse = fn (module: &Module) void } } +resolve_alias = fn (module: &Module, type: &Type) &Type +{ + >result: &Type = zero; + + switch (type.id) + { + .void, + .integer, + => + { + result = type; + }, + .pointer => + { + >element_type = type.content.pointer.element_type; + >resolved_element_type = resolve_alias(module, element_type); + if (element_type == resolved_element_type) + { + result = type; + } + else + { + result = get_pointer_type(module, resolved_element_type); + } + }, + else => + { + #trap(); + }, + } + + assert(result != zero); + + return result; +} + +resolve_type_in_place_abi = fn (module: &Module, type: &Type) void +{ + if (!type.llvm.abi) + { + >result: &LLVMType = zero; + + switch (type.id) + { + .void, .noreturn => + { + result = module.llvm.void_type; + }, + .integer => + { + >bit_count = type.content.integer.bit_count; + assert(bit_count <= #integer_max(u32)); + result = LLVMIntTypeInContext(module.llvm.context, #truncate(bit_count)); + }, + .pointer, + .opaque, + => + { + result = module.llvm.pointer_type; + }, + else => + { + #trap(); + }, + } + + assert(result != zero); + type.llvm.abi = result; + } +} + +resolve_type_in_place_memory = fn (module: &Module, type: &Type) void +{ + if (!type.llvm.memory) + { + resolve_type_in_place_abi(module, type); + + >result: &LLVMType = zero; + + switch (type.id) + { + .void, + .noreturn, + .pointer, + .opaque, + .array, + .struct, + .enum_array, + => + { + result = type.llvm.abi; + }, + .integer => + { + >byte_size = get_byte_size(type); + >bit_count = byte_size * 8; + result = LLVMIntTypeInContext(module.llvm.context, #truncate(bit_count)); + }, + else => + { + #trap(); + }, + } + + assert(result != zero); + type.llvm.memory = result; + } +} + +resolve_type_in_place_debug = fn (module: &Module, type: &Type) void +{ + if (module.has_debug_info) + { + if (!type.llvm.debug) + { + >result: &LLVMMetadata = zero; + + switch (type.id) + { + .void, .noreturn => + { + >bit_size: u64 = 0; + >dwarf_encoding: LLVMDwarfTypeEncoding = .void; + >flags: LLVMDIFlags = { + .noreturn = type.id == .noreturn, + zero, + }; + result = LLVMDIBuilderCreateBasicType(module.llvm.di_builder, type.name.pointer, type.name.length, bit_size, dwarf_encoding, flags); + }, + .integer => + { + >bit_count = type.content.integer.bit_count; + >dwarf_encoding: LLVMDwarfTypeEncoding = #select(type.content.integer.signed, .signed, .unsigned); + dwarf_encoding = #select(bit_count == 1, .boolean, dwarf_encoding); + >flags: LLVMDIFlags = zero; + result = LLVMDIBuilderCreateBasicType(module.llvm.di_builder, type.name.pointer, type.name.length, bit_count, dwarf_encoding, flags); + }, + .pointer => + { + >element_type = type.content.pointer.element_type; + resolve_type_in_place_debug(module, element_type); + + result = type.llvm.debug; + + if (!result) + { + result = LLVMDIBuilderCreatePointerType(module.llvm.di_builder, element_type.llvm.debug, 64, 64, default_address_space, type.name.pointer, type.name.length); + } + }, + else => + { + #trap(); + }, + } + + assert(result != zero); + type.llvm.debug = result; + } + } +} + +AbiSystemVClass = enum +{ + none, + integer, + sse, + sse_up, + x87, + x87_up, + complex_x87, + memory, +} + +AbiSystemVClassifyArgument = struct +{ + base_offset: u64, + is_variable_argument: u1, + is_register_call: u1, +} + +abi_system_v_classify_type = fn (type: &Type, options: AbiSystemVClassifyArgument) [2]AbiSystemVClass +{ + >result: [2]AbiSystemVClass = zero; + + >is_memory = options.base_offset >= 8; + >current_index: u64 = #extend(is_memory); + >not_current_index: u64 = #extend(!is_memory); + assert(current_index != not_current_index); + result[current_index] = .memory; + + switch (type.id) + { + .void, .noreturn => + { + result[current_index] = .none; + }, + .integer => + { + >bit_count = type.content.integer.bit_count; + if (bit_count <= 64) + { + result[current_index] = .integer; + } + else if (bit_count == 128) + { + #trap(); + } + else + { + report_error(); + } + }, + else => + { + #trap(); + }, + } + + return result; +} + +contains_no_user_data = fn (type: &Type, start: u64, end: u64) u1 +{ + >byte_size = get_byte_size(type); + >result = byte_size <= start; + + if (!result) + { + switch (type.id) + { + .struct => + { + result = 1; + #trap(); + }, + .array, .enum_array => + { + result = 1; + #trap(); + }, + else => {}, + } + } + + return result; +} + +abi_system_v_get_integer_type_at_offset = fn (module: &Module, type: &Type, offset: u64, source_type: &Type, source_offset: u64) &Type +{ + switch (type.id) + { + .integer => + { + if (offset == 0) + { + >bit_count = type.content.integer.bit_count; + >start = source_offset + get_byte_size(type); + >end = source_offset + 8; + + if (bit_count == 64) + { + return type; + } + + if (bit_count == 32 or bit_count == 16 or bit_count == 8) + { + if (contains_no_user_data(source_type, start, end)) + { + return type; + } + } + } + }, + } + + >source_size = get_byte_size(source_type); + assert(source_size != source_offset); + >byte_count = source_size - source_offset; + >bit_count = #select(byte_count > 8, 64, byte_count * 8); + >result = integer_type(module, { .bit_count = bit_count, .signed = 0 }); + return result; +} + +AbiSystemVDirect = struct +{ + semantic_type: &Type, + type: &Type, + padding: &Type, + offset: u32, + alignment: u32, + cannot_be_flattened: u1, +} + +abi_system_v_get_direct = fn (module: &Module, direct: AbiSystemVDirect) AbiInformation +{ + >result: AbiInformation = { + .semantic_type = direct.semantic_type, + .flags = { + .kind = .direct, + zero, + }, + zero, + }; + + resolve_type_in_place(module, direct.semantic_type); + resolve_type_in_place(module, direct.type); + + if (direct.padding) + { + resolve_type_in_place(module, direct.padding); + } + + abi_set_coerce_to_type(&result, direct.type); + abi_set_padding_type(&result, direct.padding); + abi_set_direct_offset(&result, direct.offset); + abi_set_direct_alignment(&result, direct.alignment); + abi_set_can_be_flattened(&result, !direct.cannot_be_flattened); + + return result; +} + +abi_system_v_classify_return_type = fn (module: &Module, semantic_return_type: &Type) AbiInformation +{ + >classes = abi_system_v_classify_type(semantic_return_type, zero); + assert(classes[1] != .memory or classes[0] == .memory); + assert(classes[1] != .sse_up or classes[0] == .sse); + + >low_type: &Type = zero; + + switch (classes[0]) + { + .none => + { + if (classes[1] == .none) + { + #trap(); + } + else + { + report_error(); + } + }, + .integer => + { + low_type = abi_system_v_get_integer_type_at_offset(module, semantic_return_type, 0, semantic_return_type, 0); + + if (classes[1] == .none and low_type.id == .integer) + { + if (is_integral_or_enumeration_type(semantic_return_type) and? is_promotable_integer_type_for_abi(semantic_return_type)) + { + #trap(); + } + } + }, + else => + { + #trap(); + }, + } + + >high_type: &Type = zero; + + switch (classes[1]) + { + .none => {}, + .integer => + { + >high_offset: u64 = 8; + high_type = abi_system_v_get_integer_type_at_offset(module, semantic_return_type, high_offset, semantic_return_type, high_offset); + + if (classes[0] == .none) + { + #trap(); + } + }, + else => + { + #trap(); + }, + } + + if (high_type) + { + #trap(); + } + + >result = abi_system_v_get_direct(module, { + .semantic_type = semantic_return_type, + .type = low_type, + zero, + }); + + return result; +} + +LLVMAttributeCallback = typealias fn [cc(c)] (&LLVMValue, u32, &LLVMAttribute) void; + +add_enum_attribute = fn (module: &Module, attribute_index: LLVMAttributeIndex, attribute_value: u64, add_callback: &LLVMAttributeCallback, value: &LLVMValue, index: u32) &LLVMAttribute +{ + >attribute = LLVMCreateEnumAttribute(module.llvm.context, module.llvm.attribute_table[attribute_index], attribute_value); + add_callback(value, index, attribute); +} + +add_type_attribute = fn (module: &Module, attribute_index: LLVMAttributeIndex, type: &LLVMType, add_callback: &LLVMAttributeCallback, value: &LLVMValue, index: u32) &LLVMAttribute +{ + >attribute = LLVMCreateTypeAttribute(module.llvm.context, module.llvm.attribute_table[attribute_index], type); + add_callback(value, index, attribute); +} + +add_string_attribute = fn (module: &Module, attribute_key: []u8, attribute_value: []u8, add_callback: &LLVMAttributeCallback, value: &LLVMValue, index: u32) &LLVMAttribute +{ + >attribute = LLVMCreateStringAttribute(module.llvm.context, attribute_key.pointer, attribute_key.length, attribute_value.pointer, attribute_value.length); + add_callback(value, index, attribute); +} + +LLVMAttributes = struct +{ + alignment: u32, + sign_extend: u1, + zero_extend: u1, + no_alias: u1, + in_reg: u1, + sret: u1, + writable: u1, + dead_on_unwind: u1, + by_value: u1, +} + +add_value_attribute = fn (module: &Module, value: &LLVMValue, index: u32, add_callback: &LLVMAttributeCallback, semantic_type: &LLVMType, abi_type: &LLVMType, attributes: LLVMAttributes) void +{ + assert(value != zero); + assert(semantic_type != zero); + assert(abi_type != zero); + + if (attributes.alignment) + { + add_enum_attribute(module, .align, #extend(attributes.alignment), add_callback, value, index); + } + + if (attributes.sign_extend) + { + add_enum_attribute(module, .signext, 0, add_callback, value, index); + } + + if (attributes.zero_extend) + { + add_enum_attribute(module, .zeroext, 0, add_callback, value, index); + } + + if (attributes.no_alias) + { + add_enum_attribute(module, .noalias, 0, add_callback, value, index); + } + + if (attributes.in_reg) + { + add_enum_attribute(module, .inreg, 0, add_callback, value, index); + } + + if (attributes.sret) + { + add_type_attribute(module, .sret, semantic_type, add_callback, value, index); + } + + if (attributes.writable) + { + add_enum_attribute(module, .writable, 0, add_callback, value, index); + } + + if (attributes.dead_on_unwind) + { + add_enum_attribute(module, .dead_on_unwind, 0, add_callback, value, index); + } + + if (attributes.by_value) + { + add_type_attribute(module, .byval, semantic_type, add_callback, value, index); + } +} + +AttributeBuildOptions = struct +{ + return_abi: AbiInformation, + argument_abis: []AbiInformation, + abi_argument_types: []&Type, + abi_return_type: &Type, + attributes: FunctionAttributes, +} + +emit_attributes = fn (module: &Module, value: &LLVMValue, add_callback: &LLVMAttributeCallback, options: AttributeBuildOptions) void +{ + >return_abi = &options.return_abi; + >semantic_return_type = return_abi.semantic_type; + resolve_type_in_place(module, semantic_return_type); + >abi_return_type = options.abi_return_type; + resolve_type_in_place(module, abi_return_type); + + add_value_attribute(module, value, 0, add_callback, semantic_return_type.llvm.memory, abi_return_type.llvm.abi, { + .alignment = 0, + .sign_extend = return_abi.flags.kind == .extend and return_abi.flags.sign_extension, + .zero_extend = return_abi.flags.kind == .extend and !return_abi.flags.sign_extension, + .no_alias = 0, + .in_reg = 0, + .sret = 0, + .writable = 0, + .dead_on_unwind = 0, + .by_value = 0, + }); + + >total_abi_count: u64 = 0; + + if (return_abi.flags.kind == .indirect) + { + >abi = return_abi; + >abi_index = abi.flags.sret_after_this; + + >abi_type = options.abi_argument_types[abi_index]; + resolve_type_in_place(module, abi_type); + + add_value_attribute(module, value, #extend(abi_index + 1), add_callback, semantic_return_type.llvm.memory, abi_type.llvm.abi, { + .alignment = get_byte_alignment(semantic_return_type), + .sign_extend = 0, + .zero_extend = 0, + .no_alias = 1, + .in_reg = abi.flags.in_reg, + .sret = 1, + .writable = 1, + .dead_on_unwind = 1, + .by_value = 0, + }); + + total_abi_count += 1; + } + + for (&abi: options.argument_abis) + { + resolve_type_in_place(module, abi.semantic_type); + + for (abi_index: abi.abi_start..abi.abi_start + abi.abi_count) + { + >abi_type = options.abi_argument_types[abi_index]; + resolve_type_in_place(module, abi_type); + + add_value_attribute(module, value, #extend(abi_index + 1), add_callback, semantic_return_type.llvm.memory, abi_type.llvm.abi, { + .alignment = #select(abi.flags.kind == .indirect, 8, 0), + .sign_extend = abi.flags.kind == .extend and abi.flags.sign_extension, + .zero_extend = abi.flags.kind == .extend and !abi.flags.sign_extension, + .no_alias = 0, + .in_reg = abi.flags.in_reg, + .sret = 0, + .writable = 0, + .dead_on_unwind = 0, + .by_value = abi.flags.indirect_by_value, + }); + + total_abi_count += 1; + } + } + + assert(total_abi_count == options.abi_argument_types.length); + + >index: u32 = ~0; + + >is_noreturn = semantic_return_type == noreturn_type(module); + if (is_noreturn) + { + add_enum_attribute(module, .noreturn, 0, add_callback, value, index); + } + + >nounwind: u1 = 1; + if (nounwind) + { + add_enum_attribute(module, .nounwind, 0, add_callback, value, index); + } + + >no_inline = options.attributes.inline_behavior == .no_inline; + if (no_inline) + { + add_enum_attribute(module, .noinline, 0, add_callback, value, index); + } + + >always_inline = options.attributes.inline_behavior == .always_inline; + if (always_inline) + { + add_enum_attribute(module, .alwaysinline, 0, add_callback, value, index); + } + + if (module.has_debug_info) + { + add_string_attribute(module, "frame-pointer", "all", add_callback, value, index); + } + + >is_definition = add_callback == &LLVMAddAttributeAtIndex; + if (is_definition) + { + if (options.attributes.naked) + { + add_enum_attribute(module, .naked, 0, add_callback, value, index); + } + + if (options.attributes.inline_behavior == .inline_hint) + { + add_enum_attribute(module, .inlinehint, 0, add_callback, value, index); + } + } +} + +ResolvedCallingConvention = enum +{ + system_v, + win64, +} + +AllocaOptions = struct +{ + type: &Type, + name: []u8, + alignment: u32, +} + +create_alloca = fn (module: &Module, options: AllocaOptions) &LLVMValue +{ + >abi_type = options.type; + resolve_type_in_place(module, abi_type); + + >alignment = options.alignment; + if (alignment == 0) + { + alignment = get_byte_alignment(abi_type); + } + + >alloca = llvm_builder_create_alloca(module.llvm.builder, abi_type.llvm.memory, default_address_space, alignment, options.name); + return alloca; +} + +StoreOptions = struct +{ + source: &LLVMValue, + destination: &LLVMValue, + type: &Type, + alignment: u32, +} + +create_store = fn (module: &Module, options: StoreOptions) void +{ + assert(options.source != zero); + assert(options.destination != zero); + assert(options.type != zero); + + >resolved_type = resolve_alias(module, options.type); + resolve_type_in_place(module, resolved_type); + >memory_type = resolved_type.llvm.memory; + + >source_value: &LLVMValue = options.source; + + if (resolved_type.llvm.abi != memory_type) + { + source_value = LLVMBuildIntCast2(module.llvm.builder, source_value, memory_type, #extend(type_is_signed(resolved_type)), ""); + } + + >alignment = options.alignment; + if (alignment == 0) + { + alignment = get_byte_alignment(resolved_type); + } + + >store = LLVMBuildStore(module.llvm.builder, source_value, options.destination); + LLVMSetAlignment(store, alignment); +} + +TypeKind = enum +{ + abi, + memory, +} + +memory_to_abi = fn (module: &Module, value: &LLVMValue, type: &Type) &LLVMValue +{ + >result = value; + if (type.llvm.memory != type.llvm.abi) + { + result = LLVMBuildIntCast2(module.llvm.builder, result, type.llvm.abi, #extend(type_is_signed(type)), ""); + } + return result; +} + +LoadOptions = struct +{ + type: &Type, + pointer: &LLVMValue, + alignment: u32, + kind: TypeKind, +} + +create_load = fn (module: &Module, options: LoadOptions) &LLVMValue +{ + resolve_type_in_place(module, options.type); + >alignment = options.alignment; + if (alignment == 0) + { + alignment = get_byte_alignment(options.type); + } + + >result = LLVMBuildLoad2(module.llvm.builder, options.type.llvm.memory, options.pointer, ""); + LLVMSetAlignment(result, alignment); + + switch (options.kind) + { + .abi => + { + result = memory_to_abi(module, result, options.type); + }, + .memory => {}, + } + + return result; +} + +check_types = fn (module: &Module, expected: &Type, source: &Type) void +{ + assert(expected != zero); + assert(source != zero); + + if (expected != source) + { + >resolved_expected = resolve_alias(module, expected); + >resolved_source = resolve_alias(module, source); + + if (resolved_expected != resolved_source) + { + >is_dst_p_and_source_int = resolved_expected.id == .pointer and resolved_source.id == .integer; + + if (!is_dst_p_and_source_int) + { + report_error(); + } + } + } +} + +typecheck = fn (module: &Module, expected: &Type, source: &Type) void +{ + if (expected) + { + check_types(module, expected, source); + } +} + +TypeAnalysis = struct +{ + indexing_type: &Type, + must_be_constant: u1, +} + +analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysis: TypeAnalysis) void +{ + assert(!value.type); + assert(!value.llvm); + + if (expected_type != zero and expected_type.id == .unresolved) + { + #trap(); + } + + >value_type: &Type = zero; + + switch (value.id) + { + .constant_integer => + { + if (!expected_type) + { + if (analysis.indexing_type) + { + expected_type = uint64(module); + } + } + + if (!expected_type) + { + report_error(); + } + + resolve_type_in_place(module, expected_type); + >resolved_type = resolve_alias(module, expected_type); + + >value_constant = value.content.constant_integer.value; + >value_is_signed = value.content.constant_integer.signed; + + switch (resolved_type.id) + { + .integer => + { + >type_bit_count = resolved_type.content.integer.bit_count; + >type_is_signed = resolved_type.content.integer.signed; + + if (value_is_signed) + { + if (type_is_signed) + { + report_error(); + } + + #trap(); + } + else + { + >max_value = integer_max_value(type_bit_count, type_is_signed); + if (value_constant > max_value) + { + report_error(); + } + + value_type = expected_type; + } + }, + .pointer => + { + value_type = uint64(module); + }, + else => + { + report_error(); + }, + } + + typecheck(module, expected_type, value_type); + }, + else => + { + #trap(); + }, + } + + assert(value_type != zero); + value.type = value_type; +} + +get_llvm_type = fn (type: &Type, kind: TypeKind) &LLVMType +{ + switch (kind) + { + .abi => + { + return type.llvm.abi; + }, + .memory => + { + return type.llvm.memory; + }, + } +} + +emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_constant: u1) void +{ + assert(value.type != zero); + assert(!value.llvm); + + >resolved_value_type = resolve_alias(module, value.type); + resolve_type_in_place(module, resolved_value_type); + + >must_be_constant = expect_constant or (!module.current_function and !module.current_macro_instantiation); + + >llvm_value: &LLVMValue = zero; + + switch (value.id) + { + .constant_integer => + { + >llvm_integer_type = get_llvm_type(resolved_value_type, type_kind); + llvm_value = LLVMConstInt(llvm_integer_type, value.content.constant_integer.value, #extend(value.content.constant_integer.signed)); + }, + else => + { + #trap(); + }, + } + + assert(llvm_value != zero); + value.llvm = llvm_value; +} + +emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, right: &Value) void +{ + assert(right.llvm == zero); + + >pointer_type = left_type; + >value_type = right.type; + + assert(pointer_type != zero); + assert(value_type != zero); + resolve_type_in_place(module, pointer_type); + resolve_type_in_place(module, value_type); + + >resolved_pointer_type = resolve_alias(module, pointer_type); + >resolved_value_type = resolve_alias(module, value_type); + assert(resolved_pointer_type.id == .pointer); + assert(resolved_pointer_type.content.pointer.element_type == resolved_value_type); + + >type_kind: TypeKind = .memory; + + >evaluation_kind = get_evaluation_kind(resolved_value_type); + + switch (evaluation_kind) + { + .scalar => + { + emit_value(module, right, type_kind, 0); + create_store(module, { + .source = right.llvm, + .destination = left_llvm, + .type = resolved_value_type, + zero, + }); + }, + .aggregate => + { + #trap(); + }, + .complex => + { + #trap(); + }, + } +} + +analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) void +{ + >parent_function_global: &Global = undefined; + if (module.current_function) + { + parent_function_global = module.current_function; + assert(parent_function_global != zero); + } + else if (module.current_macro_instantiation) + { + #trap(); + } + else + { + report_error(); + } + + >llvm_function = parent_function_global.variable.storage.llvm; + assert(llvm_function != zero); + + >statement_location: &LLVMMetadata = zero; + if (module.has_debug_info) + { + statement_location = LLVMDIBuilderCreateDebugLocation(module.llvm.context, statement.line, statement.column, scope.llvm, module.llvm.inlined_at); + LLVMSetCurrentDebugLocation2(module.llvm.builder, statement_location); + } + + switch (statement.id) + { + .return => + { + >return_value = statement.content.return; + + if (module.current_function) + { + assert(!module.current_macro_instantiation); + >function_type = &parent_function_global.variable.storage.type.content.pointer.element_type.content.function; + >return_abi = &function_type.abi.return_abi; + + switch (return_abi.semantic_type.id) + { + .void => + { + if (return_value) + { + report_error(); + } + }, + .noreturn => + { + report_error(); + }, + else => + { + if (!return_value) + { + report_error(); + } + + if (module.has_debug_info) + { + LLVMSetCurrentDebugLocation2(module.llvm.builder, statement_location); + } + + >return_alloca = parent_function_global.variable.storage.content.function.llvm.return_alloca; + assert(return_alloca != zero); + + >return_type = return_abi.semantic_type; + analyze_type(module, return_value, return_type, zero); + + >pointer_type = get_pointer_type(module, return_type); + emit_assignment(module, return_alloca, pointer_type, return_value); + }, + } + + >return_block = parent_function_global.variable.storage.content.function.llvm.return_block; + assert(return_block != zero); + LLVMBuildBr(module.llvm.builder, return_block); + LLVMClearInsertionPosition(module.llvm.builder); + } + else if (module.current_macro_instantiation) + { + #trap(); + } + else + { + report_error(); + } + }, + else => + { + #trap(); + }, + } +} + +analyze_block = fn (module: &Module, block: &Block) void +{ + if (module.has_debug_info) + { + >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; + } + + >statement = block.first_statement; + + while (statement) + { + analyze_statement(module, &block.scope, statement); + + statement = statement.next; + } +} + +emit_block = fn (module: &Module, basic_block: &LLVMBasicBlock) void +{ + >current_basic_block = LLVMGetInsertBlock(module.llvm.builder); + if (current_basic_block) + { + if (!LLVMGetBasicBlockTerminator(current_basic_block)) + { + LLVMBuildBr(module.llvm.builder, basic_block); + } + } + + assert(LLVMGetBasicBlockParent(basic_block) != zero); + LLVMPositionBuilderAtEnd(module.llvm.builder, basic_block); +} + +type_is_abi_equal = fn (module: &Module, a: &Type, b: &Type) u1 +{ + resolve_type_in_place(module, a); + resolve_type_in_place(module, b); + + >result = a == b; + if (!result) + { + result = a.llvm.abi == b.llvm.abi; + } + + return result; +} + +LLVMObjectGenerate = struct +{ + path: []u8, + optimization_level: LLVMOptimizationLevel, + run_optimization_passes: u1, + has_debug_info: u1, +} + +generate_object = fn (module: &LLVMModule, target_machine: &LLVMTargetMachine, options: LLVMObjectGenerate) LLVMCodeGenerationResult +{ + if (options.run_optimization_passes) + { + >prefer_speed = options.optimization_level == .O2 or options.optimization_level == .O3; + >options: LLVMOptimizationOptions = { + .optimization_level = options.optimization_level, + .debug_info = options.has_debug_info, + .loop_unrolling = prefer_speed, + .loop_interleaving = prefer_speed, + .loop_vectorization = prefer_speed, + .slp_vectorization = prefer_speed, + .merge_functions = prefer_speed, + .call_graph_profile = 0, + .unified_lto = 0, + .assignment_tracking = options.has_debug_info, + .verify_module = 1, + zero, + }; + llvm_module_run_optimization_pipeline(module, target_machine, &options); + } + + >code_generation_options: LLVMCodeGenerationOptions = { + .output_file_path = options.path, + .file_type = .object, + .optimize_when_possible = #extend(options.optimization_level > .O0), + .verify_module = 1, + zero, + }; + + >result = llvm_module_run_code_generation_pipeline(module, target_machine, &code_generation_options); + return result; +} + +ArgumentBuilder = struct +{ + buffer: [128]&u8, + count: u64, +} + +add_argument = fn (builder: &ArgumentBuilder, argument: []u8) void +{ + assert(argument.pointer[argument.length] == 0); + >index = builder.count; + assert(index < builder.buffer.length); + builder.buffer[index] = argument.pointer; + builder.count = index + 1; +} + +flush_arguments = fn (builder: &ArgumentBuilder) []&u8 +{ + >argument_count = builder.count; + assert(argument_count < builder.buffer.length); + builder.buffer[argument_count] = zero; + return builder.buffer[..argument_count]; +} + +link = fn (module: &Module) void +{ + >arena = module.arena; + >builder: ArgumentBuilder = zero; + add_argument(&builder, "ld.lld"); + add_argument(&builder, "--error-limit=0"); + add_argument(&builder, "-o"); + add_argument(&builder, module.executable); + + for (library_directory: module.library_directories) + { + add_argument(&builder, arena_join_string(arena, [ "-L", library_directory ][..])); + } + + >candidate_library_paths: [_][]u8 = [ + "/usr/lib", + "/usr/lib/x86_64-linux-gnu", + ]; + + >i: u64 = 0; + >scrt1_directory_path: []u8 = zero; + >scrt1_object_path: []u8 = zero; + for (directory_path: candidate_library_paths) + { + >object_path = arena_join_string(arena, [ directory_path, "/Scrt1.o" ][..]); + >file = os_file_open(object_path.pointer, { .read = 1, zero }, zero); + if (file >= 0) + { + os_file_close(file); + scrt1_directory_path = directory_path; + scrt1_object_path = object_path; + break; + } + } + + if (scrt1_object_path.length == 0) + { + report_error(); + } + + add_argument(&builder, arena_join_string(arena, [ "-L", scrt1_directory_path ][..])); + add_argument(&builder, "-L/usr/lib/gcc/x86_64-pc-linux-gnu/15.1.1"); + add_argument(&builder, "-L/usr/lib/gcc/x86_64-linux-gnu/13"); + + for (object: module.objects) + { + add_argument(&builder, object); + } + + for (library_path: module.library_paths) + { + add_argument(&builder, library_path); + } + + for (library_name: module.library_names) + { + add_argument(&builder, arena_join_string(arena, [ "-l", library_name ][..])); + } + + if (module.link_libcpp) + { + add_argument(&builder, "-lstdc++"); + } + + >link_libc: u1 = 1; + >dynamic_linker: u1 = 1; + + if (dynamic_linker) + { + add_argument(&builder, "-dynamic-linker"); + add_argument(&builder, "/usr/lib64/ld-linux-x86-64.so.2"); + } + + if (link_libc) + { + add_argument(&builder, scrt1_object_path); + add_argument(&builder, "-lc"); + } + + >linker_arguments = flush_arguments(&builder); + + >result = lld_elf_link(linker_arguments.pointer, linker_arguments.length, 1, 0); + if (!result.success) + { + #trap(); + } +} + emit = fn (module: &Module) void { assert(!module.current_function); @@ -2689,6 +4713,36 @@ emit = fn (module: &Module) void >m = llvm_context_create_module(context, module.name); >builder = LLVMCreateBuilderInContext(context); + >di_builder: &LLVMDIBuilder = zero; + >di_file: &LLVMMetadata = zero; + + if (module.has_debug_info) + { + di_builder = LLVMCreateDIBuilder(m); + >last_slash = string_last_character(module.path, '/'); + if (last_slash == string_no_match) + { + report_error(); + } + + >directory = module.path[..last_slash]; + >file_name = module.path[last_slash + 1..]; + + di_file = LLVMDIBuilderCreateFile(di_builder, file_name.pointer, file_name.length, directory.pointer, directory.length); + + >language: LLVMDwarfSourceLanguage = .C17; + >producer_name = "bloat buster"; + >is_optimized = build_mode_is_optimized(module.build_mode); + >flags = ""; + >emission_kind: LLVMDwarfEmissionKind = .full; + >runtime_version: u32 = 0; + >split_name = ""; + >sysroot = ""; + >sdk = ""; + + module.scope.llvm = LLVMDIBuilderCreateCompileUnit(di_builder, language, di_file, producer_name.pointer, producer_name.length, #extend(is_optimized), flags.pointer, flags.length, runtime_version, split_name.pointer, split_name.length, emission_kind, 0, 0, #extend(is_optimized), sysroot.pointer, sysroot.length, sdk.pointer, sdk.length); + } + >target_triple: []u8 = undefined; >cpu_model: []u8 = undefined; >cpu_features: []u8 = undefined; @@ -2729,11 +4783,519 @@ emit = fn (module: &Module) void assert(!error_message); - LLVMCreateTargetMachineWithOptions(target, target_triple.pointer, target_machine_options); + >target_machine = LLVMCreateTargetMachineWithOptions(target, target_triple.pointer, target_machine_options); + + >target_data_layout = LLVMCreateTargetDataLayout(target_machine); + LLVMSetModuleDataLayout(m, target_data_layout); + LLVMSetTarget(m, target_triple.pointer); + + module.llvm = { + .context = context, + .module = m, + .builder = builder, + .di_builder = di_builder, + .file = di_file, + .target_machine = target_machine, + .target_data_layout = target_data_layout, + .pointer_type = LLVMPointerTypeInContext(context, default_address_space), + .void_type = LLVMVoidTypeInContext(context), + zero, + }; + + for (i: 0..module.llvm.intrinsic_table.length) + { + >e: LLVMIntrinsicIndex = #enum_from_int(i); + >name = #enum_name(e); + module.llvm.intrinsic_table[i] = LLVMLookupIntrinsicID(name.pointer, name.length); + } + + for (i: 0..module.llvm.attribute_table.length) + { + >e: LLVMAttributeIndex = #enum_from_int(i); + >name = #enum_name(e); + >attribute_id = LLVMGetEnumAttributeKindForName(name.pointer, name.length); + assert(attribute_id != 0); + module.llvm.attribute_table[i] = attribute_id; + } + + >global = module.first_global; + + while (global) + { + assert(!module.current_function); + assert(!module.current_macro_instantiation); + assert(!module.current_macro_declaration); + + if (global.emitted) + { + continue; + } + + >global_pointer_type = global.variable.storage.type; + assert(global_pointer_type.id == .pointer); + >global_value_type = global_pointer_type.content.pointer.element_type; + + switch (global.variable.storage.id) + { + .function, .forward_declared_function => + { + >function_type = &global_value_type.content.function; + >semantic_argument_types = function_type.base.semantic_argument_types; + >semantic_return_type = function_type.base.semantic_return_type; + function_type.abi.argument_abis = arena_allocate_slice[AbiInformation](module.arena, semantic_argument_types.length); + >llvm_abi_argument_type_buffer: [64]&LLVMType = undefined; + + >resolved_calling_convention: ResolvedCallingConvention = undefined; + >calling_convention = function_type.base.calling_convention; + switch (calling_convention) + { + .c => + { + // TODO: switch on platform + resolved_calling_convention = .system_v; + }, + } + + >is_reg_call = resolved_calling_convention == .system_v and 0; // TODO: regcall calling convention + + switch (resolved_calling_convention) + { + .system_v => + { + >abi_argument_type_buffer: [64]&Type = undefined; + >abi_argument_type_count: u16 = 0; + + function_type.abi.available_registers = { + .system_v = { + .gpr = #select(is_reg_call, 11, 6), + .sse = #select(is_reg_call, 16, 8), + }, + }; + + function_type.abi.return_abi = abi_system_v_classify_return_type(module, resolve_alias(module, semantic_return_type)); + + >return_abi_kind = function_type.abi.return_abi.flags.kind; + + >abi_return_type: &Type = undefined; + + switch (return_abi_kind) + { + .direct, .extend => + { + abi_return_type = function_type.abi.return_abi.coerce_to_type; + }, + else => + { + unreachable; + }, + } + + assert(abi_return_type != zero); + function_type.abi.abi_return_type = abi_return_type; + resolve_type_in_place(module, abi_return_type); + + if (return_abi_kind == .indirect) + { + #trap(); + } + + if (semantic_argument_types.length != 0) + { + for (i: 0..semantic_argument_types.length) + { + #trap(); + } + } + }, + .win64 => + { + #trap(); + }, + } + + >llvm_function_type = LLVMFunctionType(function_type.abi.abi_return_type.llvm.abi, &llvm_abi_argument_type_buffer[0], #truncate(function_type.abi.abi_argument_types.length), #extend(function_type.base.is_variable_argument)); + + >subroutine_type: &LLVMMetadata = zero; + + if (module.has_debug_info) + { + >debug_argument_type_buffer: [64]&LLVMMetadata = undefined; + >debug_argument_types = debug_argument_type_buffer[..function_type.abi.argument_abis.length + 1 + #extend(function_type.base.is_variable_argument)]; + debug_argument_types[0] = function_type.abi.return_abi.semantic_type.llvm.debug; + assert(debug_argument_types[0] != zero); + + // TODO: support double slicing: sliceable_value[1..][0..length] -- 2 slicing expressions + >debug_argument_type_slice = debug_argument_types[1..]; + debug_argument_type_slice = debug_argument_type_slice[0..function_type.abi.argument_abis.length]; + + for (i: 0..function_type.abi.argument_abis.length) + { + >abi = &function_type.abi.argument_abis[i]; + >debug_argument_type = &debug_argument_type_slice[i]; + debug_argument_type.& = abi.semantic_type.llvm.debug; + assert(debug_argument_type.& != zero); + } + + if (function_type.base.is_variable_argument) + { + >void_ty = void_type(module); + assert(void_ty.llvm.debug != zero); + debug_argument_types[function_type.abi.argument_abis.length + 1] = void_ty.llvm.debug; + #trap(); + } + + >flags: LLVMDIFlags = zero; + subroutine_type = LLVMDIBuilderCreateSubroutineType(module.llvm.di_builder, module.llvm.file, debug_argument_types.pointer, #truncate(debug_argument_types.length), flags); + } + + assert(global.variable.storage.type.id == .pointer); + >value_type = global.variable.storage.type.content.pointer.element_type; + value_type.llvm.abi = llvm_function_type; + value_type.llvm.debug = subroutine_type; + + >llvm_linkage: LLVMLinkage = undefined; + + switch (global.linkage) + { + .internal => + { + llvm_linkage = .internal; + }, + .external => + { + llvm_linkage = .external; + }, + } + + >llvm_function = llvm_module_create_function(module.llvm.module, llvm_function_type, llvm_linkage, default_address_space, global.variable.name); + global.variable.storage.llvm = llvm_function; + >llvm_calling_convention: LLVMCallingConvention = undefined; + switch (calling_convention) + { + .c => + { + llvm_calling_convention = .c; + }, + } + + LLVMSetFunctionCallConv(llvm_function, llvm_calling_convention); + + emit_attributes(module, llvm_function, &LLVMAddAttributeAtIndex, { + .return_abi = function_type.abi.return_abi, + .argument_abis = function_type.abi.argument_abis, + .abi_argument_types = function_type.abi.abi_argument_types, + .abi_return_type = function_type.abi.abi_return_type, + .attributes = global.variable.storage.content.function.attributes, + }); + + >subprogram: &LLVMMetadata = zero; + + if (module.has_debug_info) + { + >name = global.variable.name; + >linkage_name = name; + >line = global.variable.line; + >is_local_to_unit = global.linkage == .internal; + >is_definition = global.variable.storage.id == .function; + >scope_line = line + 1; + >flags: LLVMDIFlags = zero; + >is_optimized = build_mode_is_optimized(module.build_mode); + subprogram = LLVMDIBuilderCreateFunction(module.llvm.di_builder, module.scope.llvm, name.pointer, name.length, linkage_name.pointer, linkage_name.length, module.llvm.file, line, subroutine_type, #extend(is_local_to_unit), #extend(is_definition), scope_line, flags, #extend(is_optimized)); + LLVMSetSubprogram(llvm_function, subprogram); + } + + switch (global.variable.storage.id) + { + .function => + { + global.variable.storage.content.function.scope.llvm = subprogram; + }, + .forward_declared_function => + { + assert(global.linkage == .external); + if (module.has_debug_info) + { + LLVMDIBuilderFinalizeSubprogram(module.llvm.di_builder, subprogram); + } + }, + else => { unreachable; }, + } + }, + .global => + { + #trap(); + }, + else => + { + report_error(); + }, + } + + global = global.next; + } + + global = module.first_global; + + while (global) + { + assert(!module.current_function); + assert(!module.current_macro_instantiation); + assert(!module.current_macro_declaration); + + if (global.variable.storage.id == .function) + { + module.current_function = global; + + >function = global.variable.storage; + assert(function.id == .function); + >pointer_type = function.type; + assert(pointer_type.id == .pointer); + >value_type = pointer_type.content.pointer.element_type; + assert(value_type == global.variable.type); + assert(value_type.id == .function); + >function_type = &value_type.content.function; + >semantic_argument_types = function_type.base.semantic_argument_types; + >llvm_function = function.llvm; + assert(llvm_function != zero); + + >llvm_abi_argument_buffer: [64]&LLVMValue = undefined; + >llvm_abi_arguments = llvm_abi_argument_buffer[..function_type.abi.abi_argument_types.length]; + LLVMGetParams(llvm_function, &llvm_abi_argument_buffer[0]); + + >entry_block = llvm_context_create_basic_block(module.llvm.context, "entry", llvm_function); + >return_block = llvm_context_create_basic_block(module.llvm.context, "return_block", llvm_function); + function.content.function.llvm.return_block = return_block; + + LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); + LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); + + >return_abi = &function_type.abi.return_abi; + switch (return_abi.flags.kind) + { + .ignore => {}, + .indirect => + { + #trap(); + }, + .in_alloca => + { + #trap(); + }, + else => + { + >alloca = create_alloca(module, { + .type = return_abi.semantic_type, + .name = "return_value", + zero, + }); + function.content.function.llvm.return_alloca = alloca; + }, + } + + >arguments = function.content.function.arguments; + >argument_abis = function_type.abi.argument_abis; + assert(arguments.length == argument_abis.length); + + for (i: 0..semantic_argument_types.length) + { + >argument = &arguments[i]; + >argument_abi = &argument_abis[i]; + // TODO: double slice + >argument_abi_arguments = llvm_abi_arguments[#extend(argument_abi.abi_start)..]; + argument_abi_arguments = argument_abi_arguments[..#extend(argument_abi.abi_count)]; + + >semantic_argument_storage: &LLVMValue = zero; + + switch (argument_abi.flags.kind) + { + .direct, .extend => + { + #trap(); + }, + .indirect => + { + #trap(); + }, + else => + { + unreachable; + }, + } + } + + analyze_block(module, function.content.function.block); + + >current_basic_block = LLVMGetInsertBlock(module.llvm.builder); + + if (current_basic_block) + { + assert(!LLVMGetBasicBlockTerminator(current_basic_block)); + #trap(); + } + else + { + >has_single_jump_to_return_block: u1 = 0; + + >first_use = LLVMGetFirstUse(#pointer_cast(return_block)); + >user: &LLVMValue = zero; + + if (first_use) + { + >second_use = LLVMGetNextUse(first_use); + >has_one_use = first_use != zero and first_use == zero; + + if (has_one_use) + { + user = LLVMGetUser(first_use); + has_single_jump_to_return_block = LLVMIsABranchInst(user) != zero and? LLVMIsConditional(user) != 0 and? LLVMGetSuccessor(user, 0) == return_block; + } + } + + if (has_single_jump_to_return_block) + { + #trap(); + } + else + { + emit_block(module, return_block); + } + } + + if (module.has_debug_info) + { + LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); + >subprogram = LLVMGetSubprogram(llvm_function); + LLVMDIBuilderFinalizeSubprogram(module.llvm.di_builder, subprogram); + } + + >semantic_return_type = return_abi.semantic_type; + if (semantic_return_type == noreturn_type(module) or function.content.function.attributes.naked) + { + #trap(); + } + else if (semantic_return_type == void_type(module)) + { + LLVMBuildRetVoid(module.llvm.builder); + } + else + { + >return_value: &LLVMValue = zero; + + switch (return_abi.flags.kind) + { + .direct, .extend => + { + >return_alloca = function.content.function.llvm.return_alloca; + >coerce_to_type = abi_get_coerce_to_type(return_abi); + if (type_is_abi_equal(module, coerce_to_type, semantic_return_type) and? return_abi.attributes.direct.offset == 0) + { + >store = llvm_find_return_value_dominating_store(module.llvm.builder, return_alloca, semantic_return_type.llvm.abi); + if (store) + { + return_value = LLVMGetOperand(store, 0); + >alloca = LLVMGetOperand(store, 1); + assert(alloca == return_alloca); + LLVMInstructionEraseFromParent(store); + LLVMInstructionEraseFromParent(alloca); + } + else + { + return_value = create_load(module, { + .type = semantic_return_type, + .pointer = return_alloca, + zero, + }); + } + } + else + { + #trap(); + } + }, + .indirect => + { + #trap(); + }, + } + + LLVMBuildRet(module.llvm.builder, return_value); + } + + // END OF SCOPE + module.current_function = zero; + } + + global = global.next; + } + + if (module.has_debug_info) + { + LLVMDIBuilderFinalize(module.llvm.di_builder); + } + + >verification_error_message: []u8 = zero; + if (!llvm_module_verify(module.llvm.module, &verification_error_message)) + { + print(llvm_module_to_string(module.llvm.module)); + print("\n==========================\nLLVM VERIFICATION ERROR\n==========================\n"); + print(verification_error_message); + print("\n"); + report_error(); + } + + if (!module.silent) + { + print(llvm_module_to_string(module.llvm.module)); + } + + >optimization_level: LLVMOptimizationLevel = undefined; + + switch (module.build_mode) + { + .debug_none, .debug => + { + optimization_level = .O0; + }, + .soft_optimize => + { + optimization_level = .O1; + }, + .optimize_for_speed => + { + optimization_level = .O2; + }, + .optimize_for_size => + { + optimization_level = .Os; + }, + .aggressively_optimize_for_speed => + { + optimization_level = .O3; + }, + .aggressively_optimize_for_size => + { + optimization_level = .Oz; + }, + } + + >object_generation_result = generate_object(module.llvm.module, module.llvm.target_machine, { + .path = module.objects[0], + .optimization_level = optimization_level, + .run_optimization_passes = module.build_mode != .debug_none, + .has_debug_info = module.has_debug_info, + }); + + if (object_generation_result != .success) + { + report_error(); + } + + link(module); } compile = fn (arena: &Arena, options: CompileOptions) void { + >module: Module = undefined; >signs: [2]u1 = [0, 1]; >base_type_allocation = arena_allocate[Type](arena, @@ -2760,12 +5322,13 @@ compile = fn (arena: &Arena, options: CompileOptions) void type_it.& = { .content = { .integer = { - .bit_count = #truncate(bit_count), + .bit_count = bit_count, .signed = sign, }, }, .id = .integer, .name = name, + .scope = &module.scope, zero, }; @@ -2792,6 +5355,7 @@ compile = fn (arena: &Arena, options: CompileOptions) void }, .id = .integer, .name = name, + .scope = &module.scope, zero, }; @@ -2812,12 +5376,14 @@ compile = fn (arena: &Arena, options: CompileOptions) void .id = .void, .name = "void", .next = noreturn_type, + .scope = &module.scope, zero, }; noreturn_type.& = { .id = .noreturn, .name = "noreturn", + .scope = &module.scope, zero, }; @@ -2828,7 +5394,7 @@ compile = fn (arena: &Arena, options: CompileOptions) void zero, }; - >module: Module = { + module = { .arena = arena, .content = options.content, .void_value = void_value, @@ -2836,7 +5402,11 @@ compile = fn (arena: &Arena, options: CompileOptions) void .path = options.path, .executable = options.executable, .objects = options.objects, - .libraries = options.libraries, + .library_directories = options.library_directories, + .library_names = options.library_names, + .library_paths = options.library_paths, + .link_libc = options.link_libc, + .link_libcpp = options.link_libcpp, .target = options.target, .build_mode = options.build_mode, .has_debug_info = options.has_debug_info, @@ -2907,10 +5477,23 @@ compile_file = fn (arena: &Arena, compile_options: CompileFile) void >objects: [][]u8 = undefined; objects = [ output_object_path ][..]; + >library_directories: [][]u8 = zero; + >library_names: [][]u8 = zero; + >library_paths: [][]u8 = zero; + + if (is_compiler) + { + #trap(); + } + >options: CompileOptions = { .executable = output_executable_path, .objects = objects, - .libraries = zero, + .library_directories = library_directories, + .library_names = library_names, + .library_paths = library_paths, + .link_libc = 1, + .link_libcpp = 0, .name = base_name, .build_mode = compile_options.build_mode, .content = file_content, diff --git a/src/compiler.cpp b/src/compiler.cpp index 0df0659..0544078 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -181,12 +181,12 @@ fn String compile_file(Arena* arena, Compile options) String c_abi_library = string_literal("build/libc_abi.a"); String llvm_bindings_library = string_literal("build/libllvm_bindings.a"); - Slice library_names = {}; - Slice library_paths = {}; String library_buffer[256]; + String library_directory = {}; Slice library_directories = {}; - String library_directory = {}; + Slice library_names = {}; + Slice library_paths = {}; if (is_compiler) { @@ -435,6 +435,10 @@ global_variable String names[] = string_literal("opaque"), string_literal("basic_struct_passing"), string_literal("enum_arbitrary_abi"), + string_literal("enum_debug_info"), + string_literal("return_array"), + string_literal("bool_pair"), + string_literal("min_max"), }; void entry_point(Slice arguments, Slice envp) diff --git a/src/compiler.hpp b/src/compiler.hpp index b85a8ab..6c6fe86 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -351,13 +351,16 @@ union AbiRegisterCount AbiRegisterCountSystemV system_v; }; -struct TypeFunction +struct TypeFunctionBase { Type* semantic_return_type; Slice semantic_argument_types; CallingConvention calling_convention; bool is_variable_arguments; - // ABI +}; + +struct TypeFunctionAbi +{ Slice abi_argument_types; Type* abi_return_type; AbiRegisterCount available_registers; @@ -365,6 +368,13 @@ struct TypeFunction AbiInformation return_abi; }; +struct TypeFunction +{ + TypeFunctionBase base; + TypeFunctionAbi abi; + Type* next; +}; + struct TypePointer { Type* element_type; @@ -597,6 +607,7 @@ fn u32 get_byte_alignment(Type* type) return result; } break; case TypeId::pointer: + case TypeId::opaque: { return 8; } break; @@ -617,6 +628,10 @@ fn u32 get_byte_alignment(Type* type) { return get_byte_alignment(type->enum_array.element_type); } break; + case TypeId::function: + { + return 1; + } break; default: trap(); } } @@ -628,6 +643,11 @@ fn u64 get_bit_size(Type* type) case TypeId::integer: return type->integer.bit_count; case TypeId::enumerator: return get_bit_size(type->enumerator.backing_type); case TypeId::alias: return get_bit_size(type->alias.type); + case TypeId::array: return get_byte_size(type->array.element_type) * type->array.element_count * 8; + case TypeId::pointer: return 64; + case TypeId::structure: return type->structure.byte_size * 8; + case TypeId::union_type: return type->union_type.byte_size * 8; + case TypeId::enum_array: return get_byte_size(type->enum_array.element_type) * type->enum_array.enum_type->enumerator.fields.length * 8; default: trap(); } } @@ -792,7 +812,7 @@ struct Block enum class ValueId { infer_or_ignore, - external_function, + forward_declared_function, function, constant_integer, unary, @@ -860,6 +880,7 @@ enum class UnaryId bitwise_not, dereference, pointer_from_int, + enum_from_int, }; struct ValueUnary @@ -903,6 +924,8 @@ enum class BinaryId logical_or, logical_and_shortcircuit, logical_or_shortcircuit, + max, + min, }; struct ValueBinary @@ -1076,6 +1099,7 @@ struct Value case ValueId::array_expression: case ValueId::call: case ValueId::select: + case ValueId::slice_expression: return false; case ValueId::variable_reference: { @@ -1162,7 +1186,11 @@ struct LLVMIntrinsicId enum class IntrinsicIndex { + smax, + smin, trap, + umax, + umin, va_start, va_end, va_copy, @@ -1170,7 +1198,11 @@ enum class IntrinsicIndex }; global_variable String intrinsic_names[] = { + string_literal("llvm.smax"), + string_literal("llvm.smin"), string_literal("llvm.trap"), + string_literal("llvm.umax"), + string_literal("llvm.umin"), string_literal("llvm.va_start"), string_literal("llvm.va_end"), string_literal("llvm.va_copy"), @@ -1178,6 +1210,52 @@ global_variable String intrinsic_names[] = { static_assert(array_length(intrinsic_names) == (u64)IntrinsicIndex::count); +struct LLVMAttributeId +{ + u32 n; +}; + +enum class AttributeIndex +{ + align, + alwaysinline, + byval, + dead_on_unwind, + inlinehint, + inreg, + naked, + noalias, + noinline, + noreturn, + nounwind, + signext, + sret, + writable, + zeroext, + + count, +}; + +global_variable String attribute_names[] = { + string_literal("align"), + string_literal("alwaysinline"), + string_literal("byval"), + string_literal("dead_on_unwind"), + string_literal("inlinehint"), + string_literal("inreg"), + string_literal("naked"), + string_literal("noalias"), + string_literal("noinline"), + string_literal("noreturn"), + string_literal("nounwind"), + string_literal("signext"), + string_literal("sret"), + string_literal("writable"), + string_literal("zeroext"), +}; + +static_assert(array_length(attribute_names) == (u64)AttributeIndex::count); + struct ModuleLLVM { LLVMContextRef context; @@ -1191,6 +1269,7 @@ struct ModuleLLVM LLVMTypeRef pointer_type; LLVMTypeRef void_type; LLVMIntrinsicId intrinsic_table[(u64)IntrinsicIndex::count]; + LLVMAttributeId attribute_table[(u64)AttributeIndex::count]; LLVMValueRef memcmp; LLVMMetadataRef inlined_at; LLVMBasicBlockRef continue_block; @@ -1211,6 +1290,7 @@ struct Module Type* first_pair_struct_type; Type* first_array_type; Type* first_enum_array_type; + Type* first_function_type; Type* va_list_type; @@ -1864,6 +1944,99 @@ fn Type* get_enum_array_type(Module* module, Type* enum_type, Type* element_type return enum_array_type; } +fn Type* resolve_alias(Module* module, Type* type) +{ + Type* result_type = 0; + switch (type->id) + { + case TypeId::void_type: + case TypeId::noreturn: + case TypeId::integer: + case TypeId::enumerator: + case TypeId::function: + case TypeId::bits: + case TypeId::union_type: + case TypeId::opaque: + case TypeId::forward_declaration: + { + result_type = type; + } break; + case TypeId::pointer: + { + auto* element_type = type->pointer.element_type; + auto* resolved_element_type = resolve_alias(module, element_type); + if (element_type == resolved_element_type) + { + result_type = type; + } + else + { + 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); + if (element_type == resolved_element_type) + { + result_type = type; + } + else + { + result_type = get_array_type(module, resolved_element_type, element_count); + } + } break; + case TypeId::structure: + { + if (type->structure.is_slice) + { + auto old_element_type = type->structure.fields[0].type->pointer.element_type; + auto element_type = resolve_alias(module, old_element_type); + if (old_element_type == element_type) + { + result_type = type; + } + else + { + 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; + case TypeId::enum_array: + { + auto old_enum_type = type->enum_array.enum_type; + auto old_element_type = type->enum_array.element_type; + auto enum_type = resolve_alias(module, old_enum_type); + auto element_type = resolve_alias(module, old_element_type); + + if (old_enum_type == enum_type && old_element_type == element_type) + { + result_type = type; + } + else + { + result_type = get_enum_array_type(module, enum_type, element_type); + } + } break; + default: unreachable(); + } + + assert(result_type); + return result_type; +} + + struct ArgBuilder { char* args[128]; diff --git a/src/emitter.cpp b/src/emitter.cpp index 656b7ef..4061b6e 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -31,15 +31,7 @@ fn void emit_block(Module* module, LLVMBasicBlockRef 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); - - } + assert(LLVMGetBasicBlockParent(basic_block)); LLVMPositionBuilderAtEnd(module->llvm.builder, basic_block); } @@ -153,6 +145,7 @@ fn bool is_integral_or_enumeration_type(Type* type) case TypeId::bits: case TypeId::enumerator: return true; + case TypeId::array: case TypeId::structure: return false; default: unreachable(); @@ -176,6 +169,7 @@ fn bool is_promotable_integer_type_for_abi(Type* type) 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); case TypeId::enumerator: return is_promotable_integer_type_for_abi(type->enumerator.backing_type); + case TypeId::pointer: return false; default: unreachable(); } } @@ -251,97 +245,6 @@ fn LLVMCallConv llvm_calling_convention(CallingConvention calling_convention) return cc; } -fn Type* resolve_alias(Module* module, Type* type) -{ - Type* result_type = 0; - switch (type->id) - { - case TypeId::void_type: - case TypeId::noreturn: - case TypeId::integer: - case TypeId::enumerator: - case TypeId::function: - case TypeId::bits: - case TypeId::union_type: - case TypeId::opaque: - { - result_type = type; - } break; - case TypeId::pointer: - { - auto* element_type = type->pointer.element_type; - auto* resolved_element_type = resolve_alias(module, element_type); - if (element_type == resolved_element_type) - { - result_type = type; - } - else - { - 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); - if (element_type == resolved_element_type) - { - result_type = type; - } - else - { - result_type = get_array_type(module, resolved_element_type, element_count); - } - } break; - case TypeId::structure: - { - if (type->structure.is_slice) - { - auto old_element_type = type->structure.fields[0].type->pointer.element_type; - auto element_type = resolve_alias(module, old_element_type); - if (old_element_type == element_type) - { - result_type = type; - } - else - { - 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; - case TypeId::enum_array: - { - auto old_enum_type = type->enum_array.enum_type; - auto old_element_type = type->enum_array.element_type; - auto enum_type = resolve_alias(module, old_enum_type); - auto element_type = resolve_alias(module, old_element_type); - - if (old_enum_type == enum_type && old_element_type == element_type) - { - result_type = type; - } - else - { - result_type = get_enum_array_type(module, enum_type, element_type); - } - } break; - default: unreachable(); - } - - assert(result_type); - return result_type; -} - fn void llvm_initialize(Module* module) { llvm_initialize_all(); @@ -452,6 +355,14 @@ fn void llvm_initialize(Module* module) String name = intrinsic_names[i]; module->llvm.intrinsic_table[i].n = LLVMLookupIntrinsicID((char*)name.pointer, name.length); } + + for (u64 i = 0; i < (u64)AttributeIndex::count; i += 1) + { + String name = attribute_names[i]; + auto attribute_id = LLVMGetEnumAttributeKindForName((char*)name.pointer, name.length); + assert(attribute_id != 0); + module->llvm.attribute_table[i].n = attribute_id; + } } enum class AbiSystemVClass @@ -666,31 +577,25 @@ fn Type* abi_system_v_get_integer_type_at_offset(Module* module, Type* type, u32 { case TypeId::integer: { - auto bit_count = type->integer.bit_count; - switch (bit_count) + if (offset == 0) { - 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; + auto bit_count = type->integer.bit_count; + 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; + bool type_contains_no_user_data = contains_no_user_data(source_type, start, end); + switch (bit_count) + { + case 64: return type; + case 32: case 16: case 8: + { + if (type_contains_no_user_data) + { + return type; + } + } break; + default: break; + } } } break; case TypeId::pointer: @@ -737,10 +642,18 @@ fn Type* abi_system_v_get_integer_type_at_offset(Module* module, Type* type, u32 auto backing_type = type->enumerator.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; + case TypeId::array: + { + auto element_type = type->array.element_type; + auto element_size = get_byte_size(element_type); + auto element_offset = (offset / element_size) * element_size; + return abi_system_v_get_integer_type_at_offset(module, element_type, offset - element_offset, source_type, source_offset); + } break; default: unreachable(); } auto source_size = get_byte_size(source_type); + assert(source_size != source_offset); 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 }); @@ -927,6 +840,7 @@ fn void resolve_type_in_place_abi(Module* module, Type* type) result = LLVMIntTypeInContext(module->llvm.context, type->integer.bit_count); break; case TypeId::pointer: + case TypeId::opaque: result = module->llvm.pointer_type; break; case TypeId::array: @@ -1015,6 +929,7 @@ fn void resolve_type_in_place_memory(Module* module, Type* type) case TypeId::void_type: case TypeId::noreturn: case TypeId::pointer: + case TypeId::opaque: case TypeId::array: case TypeId::structure: case TypeId::enum_array: @@ -1100,7 +1015,7 @@ fn void resolve_type_in_place_debug(Module* module, Type* type) 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); + auto array_type = LLVMDIBuilderCreateArrayType(module->llvm.di_builder, get_bit_size(type), bit_alignment, array_element_type->llvm.debug, 0, 0); result = array_type; } break; case TypeId::enumerator: @@ -1116,7 +1031,10 @@ fn void resolve_type_in_place_debug(Module* module, Type* 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); + auto debug_aligned_type = align_integer_type(module, backing_type); + resolve_type_in_place_debug(module, debug_aligned_type); + + 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, debug_aligned_type->llvm.debug); } break; case TypeId::structure: { @@ -1133,7 +1051,7 @@ fn void resolve_type_in_place_debug(Module* module, Type* type) 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); + auto member_type = LLVMDIBuilderCreateMemberType(module->llvm.di_builder, module->scope.llvm, (char*)field.name.pointer, field.name.length, module->llvm.file, field.line, get_bit_size(field_type), get_byte_alignment(field_type) * 8, field.offset * 8, flags, field_type->llvm.debug); llvm_type_buffer[i] = member_type; } @@ -1209,6 +1127,39 @@ fn void resolve_type_in_place_debug(Module* module, Type* type) // TODO: ? return; } break; + case TypeId::function: + { + auto function_type = &type->function; + LLVMMetadataRef debug_argument_type_buffer[64]; + Slice debug_argument_types = { .pointer = debug_argument_type_buffer, .length = function_type->abi.argument_abis.length + 1 + function_type->base.is_variable_arguments }; + auto semantic_return_type = function_type->base.semantic_return_type; + resolve_type_in_place_debug(module, semantic_return_type); + debug_argument_types[0] = semantic_return_type->llvm.debug; + assert(debug_argument_types[0]); + + auto semantic_argument_types = function_type->base.semantic_argument_types; + auto debug_argument_type_slice = debug_argument_types(1)(0, semantic_argument_types.length); + + for (u64 i = 0; i < semantic_argument_types.length; i += 1) + { + auto* debug_argument_type = &debug_argument_type_slice[i]; + auto semantic_type = semantic_argument_types[i]; + resolve_type_in_place_debug(module, semantic_type); + *debug_argument_type = semantic_type->llvm.debug; + assert(*debug_argument_type); + } + + if (function_type->base.is_variable_arguments) + { + auto void_ty = void_type(module); + assert(void_ty->llvm.debug); + debug_argument_types[function_type->base.semantic_argument_types.length + 1] = void_ty->llvm.debug; + } + + LLVMDIFlags flags = {}; + auto subroutine_type = LLVMDIBuilderCreateSubroutineType(module->llvm.di_builder, module->llvm.file, debug_argument_types.pointer, (u32)debug_argument_types.length, flags); + result = subroutine_type; + } break; default: unreachable(); } @@ -1759,7 +1710,7 @@ fn AbiInformation abi_system_v_get_indirect_return_result(Type* type) } } -fn AbiInformation abi_system_classify_return_type(Module* module, Type* semantic_return_type) +fn AbiInformation abi_system_v_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]; @@ -1918,6 +1869,16 @@ fn void create_store(Module* module, StoreOptions options) LLVMSetAlignment(store, alignment); } +fn LLVMValueRef memory_to_abi(Module* module, LLVMValueRef value, Type* type) +{ + LLVMValueRef result = value; + if (type->llvm.memory != type->llvm.abi) + { + result = LLVMBuildIntCast2(module->llvm.builder, result, type->llvm.abi, type_is_signed(type), ""); + } + return result; +} + struct LoadOptions { Type* type; @@ -1947,14 +1908,7 @@ fn LLVMValueRef create_load(Module* module, LoadOptions options) { 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), ""); - } + result = memory_to_abi(module, result, options.type); } break; case TypeKind::memory: break; } @@ -1978,197 +1932,214 @@ fn LLVMValueRef create_gep(Module* module, GEPOptions options) return gep; } -fn BBLLVMAttributeList build_attribute_list(Module* module, AttributeBuildOptions options) +using EnumCallback = void (LLVMValueRef, LLVMAttributeIndex, LLVMAttributeRef); + +fn LLVMAttributeRef create_enum_attribute(Module* module, AttributeIndex attribute_index, u64 value) +{ + return LLVMCreateEnumAttribute(module->llvm.context, module->llvm.attribute_table[(u64)attribute_index].n, value); +} + +fn LLVMAttributeRef create_type_attribute(Module* module, AttributeIndex attribute_index, LLVMTypeRef type) +{ + return LLVMCreateTypeAttribute(module->llvm.context, module->llvm.attribute_table[(u64)attribute_index].n, type); +} + +fn LLVMAttributeRef create_string_attribute(Module* module, String key, String value) +{ + return LLVMCreateStringAttribute(module->llvm.context, (char*)key.pointer, key.length, (char*)value.pointer, value.length); +} + +fn void create_and_add_enum_attribute(Module* module, AttributeIndex attribute_name, u64 attribute_value, EnumCallback* add_callback, LLVMValueRef value, u32 attribute_index) +{ + auto attribute = create_enum_attribute(module, attribute_name, attribute_value); + add_callback(value, attribute_index, attribute); +} + +fn void create_and_add_type_attribute(Module* module, AttributeIndex attribute_name, LLVMTypeRef type, EnumCallback* add_callback, LLVMValueRef value, u32 attribute_index) +{ + auto attribute = create_type_attribute(module, attribute_name, type); + add_callback(value, attribute_index, attribute); +} + +fn void create_and_add_string_attribute(Module* module, String attribute_key, String attribute_value, EnumCallback* add_callback, LLVMValueRef value, u32 attribute_index) +{ + auto attribute = create_string_attribute(module, attribute_key, attribute_value); + add_callback(value, attribute_index, attribute); +} + +struct ValueAttribute +{ + u32 alignment; + u32 sign_extend:1; + u32 zero_extend:1; + u32 no_alias:1; + u32 in_reg:1; + u32 sret:1; + u32 writable:1; + u32 dead_on_unwind:1; + u32 by_value:1; +}; + +fn void add_value_attribute(Module* module, LLVMValueRef value, u32 index, EnumCallback* add_callback, LLVMTypeRef semantic_type, LLVMTypeRef abi_type, ValueAttribute attributes) +{ + assert(value); + assert(semantic_type); + assert(abi_type); + + if (attributes.alignment) + { + create_and_add_enum_attribute(module, AttributeIndex::align, attributes.alignment, add_callback, value, index); + } + + if (attributes.sign_extend) + { + create_and_add_enum_attribute(module, AttributeIndex::signext, 0, add_callback, value, index); + } + + if (attributes.zero_extend) + { + create_and_add_enum_attribute(module, AttributeIndex::zeroext, 0, add_callback, value, index); + } + + if (attributes.no_alias) + { + create_and_add_enum_attribute(module, AttributeIndex::noalias, 0, add_callback, value, index); + } + + if (attributes.in_reg) + { + create_and_add_enum_attribute(module, AttributeIndex::inreg, 0, add_callback, value, index); + } + + if (attributes.sret) + { + create_and_add_type_attribute(module, AttributeIndex::sret, semantic_type, add_callback, value, index); + } + + if (attributes.writable) + { + create_and_add_enum_attribute(module, AttributeIndex::writable, 0, add_callback, value, index); + } + + if (attributes.dead_on_unwind) + { + create_and_add_enum_attribute(module, AttributeIndex::dead_on_unwind, 0, add_callback, value, index); + } + + if (attributes.by_value) + { + create_and_add_type_attribute(module, AttributeIndex::byval, semantic_type, add_callback, value, index); + } +} + +fn void emit_attributes(Module* module, LLVMValueRef value, EnumCallback* add_callback, 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, + add_value_attribute(module, value, 0, add_callback, options.return_abi.semantic_type->llvm.memory, options.abi_return_type->llvm.abi, { .alignment = 0, + .sign_extend = options.return_abi.flags.kind == AbiKind::extend && options.return_abi.flags.sign_extension, + .zero_extend = options.return_abi.flags.kind == AbiKind::extend && !options.return_abi.flags.sign_extension, .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, + .sret = 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, + const auto& abi = options.return_abi; + auto abi_index = abi.flags.sret_after_this; + + auto abi_type = options.abi_argument_types[abi_index]; + resolve_type_in_place(module, abi_type); + add_value_attribute(module, value, abi_index + 1, add_callback, abi.semantic_type->llvm.memory, abi_type->llvm.abi, { + .alignment = get_byte_alignment(abi.semantic_type), .sign_extend = false, .zero_extend = false, - .in_reg = options.return_abi.flags.in_reg, - .no_fp_class = {}, - .struct_return = true, + .no_alias = true, + .in_reg = abi.flags.in_reg, + .sret = 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) { + resolve_type_in_place(module, abi.semantic_type); + 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), + add_value_attribute(module, value, abi_index + 1, add_callback, abi.semantic_type->llvm.memory, abi_type->llvm.abi, { + .alignment = u32(abi.flags.kind == AbiKind::indirect ? 8 : 0), + .sign_extend = abi.flags.kind == AbiKind::extend && abi.flags.sign_extension, + .zero_extend = abi.flags.kind == AbiKind::extend && !abi.flags.sign_extension, .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, + .sret = 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 + auto index = ~(u32)0; + { + auto is_noreturn = options.return_abi.semantic_type == noreturn_type(module); + if (is_noreturn) + { + create_and_add_enum_attribute(module, AttributeIndex::noreturn, 0, add_callback, value, index); + } - // CALL-SITE ATTRIBUTES - .call_no_builtins = false, + auto nounwind = true; + if (nounwind) + { + create_and_add_enum_attribute(module, AttributeIndex::nounwind, 0, add_callback, value, index); + } - // 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 no_inline = options.attributes.inline_behavior == InlineBehavior::no_inline; + if (no_inline) + { + create_and_add_enum_attribute(module, AttributeIndex::noinline, 0, add_callback, value, index); + } - auto attribute_list = llvm_attribute_list_build(module->llvm.context, &attributes, options.call_site); - return attribute_list; + auto always_inline = options.attributes.inline_behavior == InlineBehavior::always_inline; + if (always_inline) + { + create_and_add_enum_attribute(module, AttributeIndex::alwaysinline, 0, add_callback, value, index); + } + + if (module->has_debug_info) + { + create_and_add_string_attribute(module, string_literal("frame-pointer"), string_literal("all"), add_callback, value, index); + } + + if (!options.call_site) + { + if (options.attributes.naked) + { + create_and_add_enum_attribute(module, AttributeIndex::naked, 0, add_callback, value, index); + } + + if (options.attributes.inline_behavior == InlineBehavior::inline_hint) + { + create_and_add_enum_attribute(module, AttributeIndex::inlinehint, 0, add_callback, value, index); + } + } + } } fn void check_types(Module* module, Type* expected, Type* source) @@ -2220,6 +2191,7 @@ fn bool unary_is_boolean(UnaryId id) case UnaryId::bitwise_not: case UnaryId::dereference: case UnaryId::pointer_from_int: + case UnaryId::enum_from_int: return false; } } @@ -2238,6 +2210,8 @@ fn bool binary_is_boolean(BinaryId id) case BinaryId::bitwise_xor: case BinaryId::shift_left: case BinaryId::shift_right: + case BinaryId::max: + case BinaryId::min: return false; case BinaryId::compare_equal: case BinaryId::compare_not_equal: @@ -2265,7 +2239,20 @@ fn bool binary_is_shortcircuiting(BinaryId id) } } -fn void analyze_type(Module* module, Value* value, Type* expected_type, bool must_be_constant); +enum class IndexType +{ + none, + array, + enum_array, +}; + +struct TypeAnalysis +{ + Type* indexing_type; + bool must_be_constant; +}; + +fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnalysis analysis); fn void analyze_binary_type(Module* module, Value* left, Value* right, bool is_boolean, Type* expected_type, bool must_be_constant) { @@ -2296,19 +2283,19 @@ fn void analyze_binary_type(Module* module, Value* left, Value* right, bool is_b { if (left_constant) { - analyze_type(module, right, 0, must_be_constant); - analyze_type(module, left, right->type, must_be_constant); + analyze_type(module, right, 0, { .must_be_constant = must_be_constant }); + analyze_type(module, left, right->type, { .must_be_constant = must_be_constant }); } else { - analyze_type(module, left, 0, must_be_constant); - analyze_type(module, right, left->type, must_be_constant); + analyze_type(module, left, 0, { .must_be_constant = must_be_constant }); + analyze_type(module, right, left->type, { .must_be_constant = must_be_constant }); } } else if (!is_boolean && expected_type) { - analyze_type(module, left, expected_type, must_be_constant); - analyze_type(module, right, expected_type, must_be_constant); + analyze_type(module, left, expected_type, { .must_be_constant = must_be_constant }); + analyze_type(module, right, expected_type, { .must_be_constant = must_be_constant }); } else { @@ -2622,9 +2609,8 @@ fn void copy_block(Module* module, Scope* parent_scope, BlockCopy copy) } } -fn void analyze_type(Module* module, Value* value, Type* expected_type, bool must_be_constant) +fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnalysis analysis) { - unused(must_be_constant); assert(!value->type); assert(!value->llvm); @@ -2674,6 +2660,14 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus { case ValueId::constant_integer: { + if (!expected_type) + { + if (analysis.indexing_type) + { + expected_type = uint64(module); + } + } + if (!expected_type) { report_error(); @@ -2726,7 +2720,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus } auto extended_value = unary_value; - analyze_type(module, extended_value, 0, must_be_constant); + analyze_type(module, extended_value, 0, { .must_be_constant = analysis.must_be_constant }); auto source = extended_value->type; assert(source); @@ -2750,7 +2744,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus report_error(); } - analyze_type(module, unary_value, 0, must_be_constant); + analyze_type(module, unary_value, 0, { .must_be_constant = analysis.must_be_constant }); auto expected_bit_size = get_bit_size(expected_type); auto source_bit_size = get_bit_size(unary_value->type); @@ -2763,7 +2757,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus } break; case UnaryId::dereference: { - analyze_type(module, unary_value, 0, must_be_constant); + analyze_type(module, unary_value, 0, { .must_be_constant = analysis.must_be_constant }); if (value->kind == ValueKind::left) { report_error(); @@ -2777,7 +2771,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus } break; case UnaryId::int_from_enum: { - analyze_type(module, unary_value, 0, must_be_constant); + analyze_type(module, unary_value, 0, { .must_be_constant = analysis.must_be_constant }); auto value_enum_type = unary_value->type; if (value_enum_type->id != TypeId::enumerator) @@ -2792,7 +2786,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus } break; case UnaryId::int_from_pointer: { - analyze_type(module, unary_value, 0, must_be_constant); + analyze_type(module, unary_value, 0, { .must_be_constant = analysis.must_be_constant }); auto value_enum_type = unary_value->type; if (value_enum_type->id != TypeId::pointer) @@ -2815,7 +2809,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus report_error(); } - analyze_type(module, unary_value, 0, must_be_constant); + analyze_type(module, unary_value, 0, { .must_be_constant = analysis.must_be_constant }); auto value_pointer_type = unary_value->type; if (value_pointer_type == expected_type) { @@ -2833,7 +2827,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus { auto string_type = get_slice_type(module, uint8(module)); typecheck(module, expected_type, string_type); - analyze_type(module, unary_value, 0, must_be_constant); + analyze_type(module, unary_value, 0, { .must_be_constant = analysis.must_be_constant }); auto enum_type = unary_value->type; if (enum_type->id != TypeId::enumerator) { @@ -2948,7 +2942,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus report_error(); } - analyze_type(module, unary_value, 0, must_be_constant); + analyze_type(module, unary_value, 0, { .must_be_constant = analysis.must_be_constant }); auto unary_value_type = unary_value->type; if (unary_value_type->id != TypeId::integer) { @@ -2961,6 +2955,32 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus report_error(); } + value_type = expected_type; + } break; + case UnaryId::enum_from_int: + { + if (!expected_type) + { + report_error(); + } + + if (expected_type->id != TypeId::enumerator) + { + report_error(); + } + + analyze_type(module, unary_value, expected_type->enumerator.backing_type, { .must_be_constant = analysis.must_be_constant }); + auto unary_value_type = unary_value->type; + if (unary_value_type->id != TypeId::integer) + { + report_error(); + } + + if (get_bit_size(unary_value_type) != get_bit_size(expected_type)) + { + report_error(); + } + value_type = expected_type; } break; default: @@ -2968,12 +2988,12 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus auto is_boolean = unary_is_boolean(unary_id); if (is_boolean) { - analyze_type(module, unary_value, 0, must_be_constant); + analyze_type(module, unary_value, 0, { .must_be_constant = analysis.must_be_constant }); value_type = uint1(module); } else { - analyze_type(module, unary_value, expected_type, must_be_constant); + analyze_type(module, unary_value, expected_type, { .must_be_constant = analysis.must_be_constant }); value_type = unary_value->type; } @@ -3031,7 +3051,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus 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, must_be_constant); + analyze_binary_type(module, value->binary.left, value->binary.right, is_boolean, expected_type, analysis.must_be_constant); check_types(module, value->binary.left->type, value->binary.right->type); value_type = is_boolean ? uint1(module) : value->binary.left->type; @@ -3050,7 +3070,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus { auto call = &value->call; auto callable = call->callable; - analyze_type(module, callable, 0, must_be_constant); + analyze_type(module, callable, 0, { .must_be_constant = analysis.must_be_constant }); Type* function_type = 0; switch (callable->id) { @@ -3063,7 +3083,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus function_type = variable_type; break; case TypeId::pointer: { - auto* element_type = variable_type->pointer.element_type; + auto* element_type = resolve_alias(module, variable_type->pointer.element_type); switch (element_type->id) { case TypeId::function: function_type = element_type; break; @@ -3080,9 +3100,9 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus assert(function_type); call->function_type = function_type; - auto semantic_argument_types = function_type->function.semantic_argument_types; + auto semantic_argument_types = function_type->function.base.semantic_argument_types; auto call_arguments = call->arguments; - if (function_type->function.is_variable_arguments) + if (function_type->function.base.is_variable_arguments) { if (call_arguments.length < semantic_argument_types.length) { @@ -3101,17 +3121,17 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus { auto* argument_type = semantic_argument_types[i]; auto* call_argument = call_arguments[i]; - analyze_type(module, call_argument, argument_type, must_be_constant); + analyze_type(module, call_argument, argument_type, { .must_be_constant = analysis.must_be_constant }); 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, must_be_constant); + analyze_type(module, call_argument, 0, { .must_be_constant = analysis.must_be_constant }); } - auto semantic_return_type = function_type->function.semantic_return_type; + auto semantic_return_type = function_type->function.base.semantic_return_type; typecheck(module, expected_type, semantic_return_type); value_type = semantic_return_type; } break; @@ -3144,7 +3164,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus auto* element_type = expected_type->array.element_type; for (auto value : values) { - analyze_type(module, value, element_type, must_be_constant); + analyze_type(module, value, element_type, { .must_be_constant = analysis.must_be_constant }); is_constant = is_constant && value->is_constant(); } @@ -3169,7 +3189,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus for (auto value : values) { - analyze_type(module, value, expected_type, must_be_constant); + analyze_type(module, value, expected_type, { .must_be_constant = analysis.must_be_constant }); is_constant = is_constant && value->is_constant(); @@ -3209,7 +3229,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus { auto array_like = value->array_expression.array_like; array_like->kind = ValueKind::left; - analyze_type(module, array_like, 0, must_be_constant); + analyze_type(module, array_like, 0, { .must_be_constant = analysis.must_be_constant }); assert(array_like->kind == ValueKind::left); auto array_like_type = array_like->type; if (array_like_type->id != TypeId::pointer) @@ -3218,7 +3238,9 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus } auto pointer_element_type = array_like_type->pointer.element_type; - analyze_type(module, value->array_expression.index, pointer_element_type->id == TypeId::enum_array ? pointer_element_type->enum_array.enum_type : uint64(module), must_be_constant); + auto indexing_type = pointer_element_type->id == TypeId::enum_array ? pointer_element_type->enum_array.enum_type : uint64(module); + + analyze_type(module, value->array_expression.index, 0, { .indexing_type = indexing_type, .must_be_constant = analysis.must_be_constant }); Type* element_type = 0; switch (pointer_element_type->id) @@ -3261,6 +3283,11 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus } break; case ValueId::enum_literal: { + if (!expected_type) + { + expected_type = analysis.indexing_type; + } + if (!expected_type) { report_error(); @@ -3281,7 +3308,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus { auto aggregate = value->field_access.aggregate; auto field_name = value->field_access.field_name; - analyze_type(module, aggregate, 0, must_be_constant); + analyze_type(module, aggregate, 0, { .must_be_constant = analysis.must_be_constant }); if (aggregate->kind == ValueKind::right) { @@ -3373,6 +3400,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus auto field = fields[i]; value_type = field.type; } break; + case TypeId::enum_array: case TypeId::array: { if (!field_name.equal(string_literal("length"))) @@ -3391,7 +3419,16 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus } else { - trap(); + if (resolved_aggregate_type->id == TypeId::enum_array) + { + auto enum_type = resolved_aggregate_type->enum_array.enum_type; + auto backing_type = enum_type->enumerator.backing_type; + value_type = backing_type; + } + else + { + report_error(); + } } } break; case TypeId::pointer: report_error(); // Double indirection is not allowed @@ -3413,7 +3450,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus report_error(); } - analyze_type(module, array_like, 0, must_be_constant); + analyze_type(module, array_like, 0, { .must_be_constant = analysis.must_be_constant }); auto pointer_type = array_like->type; if (pointer_type->id != TypeId::pointer) @@ -3462,7 +3499,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus { if (index) { - analyze_type(module, index, index_type, must_be_constant); + analyze_type(module, index, index_type, { .must_be_constant = analysis.must_be_constant }); if (index->type->id != TypeId::integer) { @@ -3475,9 +3512,23 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus } break; case ValueId::string_literal: { - auto slice_type = get_slice_type(module, uint8(module)); - typecheck(module, expected_type, slice_type); - value_type = slice_type; + auto u8_type = uint8(module); + auto pointer_type = get_pointer_type(module, u8_type); + auto slice_type = get_slice_type(module, u8_type); + + if (pointer_type == expected_type) + { + value_type = expected_type; + } + else if (slice_type == expected_type) + { + value_type = expected_type; + } + else + { + typecheck(module, expected_type, slice_type); + value_type = slice_type; + } } break; case ValueId::va_start: { @@ -3487,7 +3538,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus } break; case ValueId::va_arg: { - analyze_type(module, value->va_arg.va_list, get_pointer_type(module, get_va_list_type(module)), must_be_constant); + analyze_type(module, value->va_arg.va_list, get_pointer_type(module, get_va_list_type(module)), { .must_be_constant = analysis.must_be_constant }); value_type = value->va_arg.type; typecheck(module, expected_type, value_type); } break; @@ -3512,12 +3563,31 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus // Right now 0, 1, 2, 3 => constant values, rest zeroed is constant because `declaration_index == initialization_index` // With constant initialization values 2, 3, 4 and rest zeroed, the aggregate initialization because `declaration_index != initialization_index`, that is, the first initialization index (0) does not match the declaration index (2). The same case can be applied for cases (1, 3) and (2, 4) - switch (resolved_type->id) + Type* aggregate_type = 0; + switch (value->kind) + { + case ValueKind::left: + { + if (resolved_type->id != TypeId::pointer) + { + report_error(); + } + + aggregate_type = resolved_type->pointer.element_type; + } break; + case ValueKind::right: + { + aggregate_type = resolved_type; + } break; + default: + } + + switch (aggregate_type->id) { case TypeId::structure: { bool is_ordered = true; - auto fields = resolved_type->structure.fields; + auto fields = aggregate_type->structure.fields; assert(fields.length <= 64); auto same_values_as_field = fields.length == elements.length; @@ -3556,7 +3626,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus report_error(); } - auto mask = 1 << declaration_index; + auto mask = (u64)1 << (u64)declaration_index; auto current_mask = field_mask; if (current_mask & mask) { @@ -3569,7 +3639,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus auto field = fields[declaration_index]; auto declaration_type = field.type; - analyze_type(module, value, declaration_type, must_be_constant); + analyze_type(module, value, declaration_type, { .must_be_constant = analysis.must_be_constant }); is_constant = is_constant && value->is_constant(); } @@ -3577,7 +3647,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus } break; case TypeId::bits: { - auto fields = resolved_type->bits.fields; + auto fields = aggregate_type->bits.fields; assert(fields.length <= 64); auto same_values_as_field = fields.length == elements.length; @@ -3627,7 +3697,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus auto field = fields[declaration_index]; auto declaration_type = field.type; - analyze_type(module, value, declaration_type, must_be_constant); + analyze_type(module, value, declaration_type, { .must_be_constant = analysis.must_be_constant }); is_constant = is_constant && value->is_constant(); } @@ -3644,7 +3714,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus auto initialization_name = elements[0].name; u64 i; - auto fields = resolved_type->union_type.fields; + auto fields = aggregate_type->union_type.fields; for (i = 0; i < fields.length; i += 1) { auto& field = fields[i]; @@ -3660,13 +3730,13 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus } auto field = &fields[i]; - analyze_type(module, initialization_value, field->type, must_be_constant); + analyze_type(module, initialization_value, field->type, { .must_be_constant = analysis.must_be_constant }); } break; case TypeId::enum_array: { bool is_ordered = true; - auto enum_type = resolved_type->enum_array.enum_type; - auto element_type = resolved_type->enum_array.element_type; + auto enum_type = aggregate_type->enum_array.enum_type; + auto element_type = aggregate_type->enum_array.element_type; assert(enum_type->id == TypeId::enumerator); auto fields = enum_type->enumerator.fields; @@ -3719,7 +3789,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus is_ordered = is_ordered && declaration_index == initialization_index; - analyze_type(module, value, element_type, must_be_constant); + analyze_type(module, value, element_type, { .must_be_constant = analysis.must_be_constant }); is_constant = is_constant && value->is_constant(); } @@ -3747,9 +3817,9 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus auto condition = value->select.condition; auto true_value = value->select.true_value; auto false_value = value->select.false_value; - analyze_type(module, condition, 0, must_be_constant); + analyze_type(module, condition, 0, { .must_be_constant = analysis.must_be_constant }); auto is_boolean = false; - analyze_binary_type(module, true_value, false_value, is_boolean, expected_type, must_be_constant); + analyze_binary_type(module, true_value, false_value, is_boolean, expected_type, analysis.must_be_constant); auto left_type = true_value->type; auto right_type = false_value->type; @@ -4081,7 +4151,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus auto string_type = get_slice_type(module, uint8(module)); - analyze_type(module, enum_string_value, string_type, must_be_constant); + analyze_type(module, enum_string_value, string_type, { .must_be_constant = analysis.must_be_constant }); value_type = struct_type; } break; case ValueId::undefined: @@ -4219,7 +4289,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus auto argument_type = declaration_argument.variable.type; assert(argument_type); - analyze_type(module, instantiation_argument, argument_type, must_be_constant); + analyze_type(module, instantiation_argument, argument_type, { .must_be_constant = analysis.must_be_constant }); } LLVMMetadataRef type_buffer[64]; @@ -4263,7 +4333,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, bool mus auto argument_type = declaration_argument.variable.type; assert(argument_type); - analyze_type(module, instantiation_argument, argument_type, must_be_constant); + analyze_type(module, instantiation_argument, argument_type, { .must_be_constant = analysis.must_be_constant }); } } @@ -4319,10 +4389,20 @@ fn ValueTypePair enter_struct_pointer_for_coerced_access(Module* module, LLVMVal if (!(first_field_size < destination_size && first_field_size < source_size)) { - trap(); + auto gep = LLVMBuildStructGEP2(module->llvm.builder, source_type->llvm.abi, source_value, 0, "coerce.dive"); + if (first_field_type->id == TypeId::structure) + { + trap(); + } + else + { + return { gep, first_field_type }; + } + } + else + { + return { source_value, source_type }; } - - return { source_value, source_type }; } fn LLVMValueRef coerce_integer_or_pointer_to_integer_or_pointer(Module* module, LLVMValueRef source, Type* source_type, Type* destination_type) @@ -4358,7 +4438,12 @@ fn LLVMValueRef create_coerced_load(Module* module, LLVMValueRef source, Type* s if (type_is_integer_backing(source_type) && type_is_integer_backing(destination_type)) { - trap(); + auto load = create_load(module, { + .type = source_type, + .pointer = source, + }); + auto result = coerce_integer_or_pointer_to_integer_or_pointer(module, load, source_type, destination_type); + return result; } else { @@ -4645,7 +4730,6 @@ fn SliceEmitResult emit_slice_expression(Module* module, Value* value) } } - fn SliceEmitResult emit_string_literal(Module* module, Value* value) { auto resolved_value_type = resolve_alias(module, value->type); @@ -4668,27 +4752,48 @@ fn SliceEmitResult emit_string_literal(Module* module, Value* value) 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, LLVMConstInt(slice_type->structure.fields[1].type->llvm.abi, length, false) }; + return { global, LLVMConstInt(uint64(module)->llvm.abi, length, false) }; } break; default: unreachable(); } } +fn void invalidate_analysis(Module* module, Value* value) +{ + switch (value->id) + { + case ValueId::variable_reference: + case ValueId::constant_integer: + break; + case ValueId::aggregate_initialization: + { + auto elements = value->aggregate_initialization.elements; + for (auto& element : elements) + { + invalidate_analysis(module, element.value); + } + } break; + case ValueId::field_access: + { + invalidate_analysis(module, value->field_access.aggregate); + } break; + default: trap(); + } + + value->type = 0; +} + +fn void reanalyze_type_as_left_value(Module* module, Value* value) +{ + assert(value->type); + assert(value->kind == ValueKind::right); + auto original_type = value->type; + invalidate_analysis(module, value); + value->kind = ValueKind::left; + auto expected_type = value->id == ValueId::aggregate_initialization ? get_pointer_type(module, original_type) : 0; + analyze_type(module, value, expected_type, {}); +} + fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type) { switch (value->id) @@ -4709,24 +4814,26 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, { auto variable = callable->variable_reference; auto variable_type = variable->type; + auto llvm_value = variable->storage->llvm; + switch (variable_type->id) { case TypeId::pointer: { - auto element_type = variable_type->pointer.element_type; + auto element_type = resolve_alias(module, 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, + .pointer = llvm_value, }); } break; default: report_error(); } } break; - case TypeId::function: llvm_callable = variable->storage->llvm; break; + case TypeId::function: llvm_callable = llvm_value; break; default: report_error(); } } break; @@ -4750,7 +4857,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, LLVMValueRef llvm_indirect_return_value = 0; - auto& return_abi = raw_function_type->function.return_abi; + auto& return_abi = raw_function_type->function.abi.return_abi; auto return_abi_kind = return_abi.flags.kind; switch (return_abi_kind) { @@ -4796,9 +4903,9 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, default: break; } - auto available_registers = raw_function_type->function.available_registers; + auto available_registers = raw_function_type->function.abi.available_registers; - auto declaration_semantic_argument_count = raw_function_type->function.semantic_argument_types.length; + auto declaration_semantic_argument_count = raw_function_type->function.base.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; @@ -4811,7 +4918,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, if (is_named_argument) { - argument_abi = raw_function_type->function.argument_abis[call_argument_index]; + argument_abi = raw_function_type->function.abi.argument_abis[call_argument_index]; semantic_argument_type = argument_abi.semantic_type; } else @@ -4835,7 +4942,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, { 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); + auto source_abi_argument_types = raw_function_type->function.abi.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; @@ -4912,17 +5019,27 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, } else { - if (src->kind == ValueKind::right) + if (src->kind == ValueKind::right && !src->is_constant()) { - if (src->id == ValueId::variable_reference) + if (!type_is_slice(src->type)) { - src->type = 0; - src->kind = ValueKind::left; - analyze_type(module, src, 0, false); + switch (src->id) + { + case ValueId::aggregate_initialization: + case ValueId::variable_reference: + case ValueId::field_access: + { + reanalyze_type_as_left_value(module, src); + } break; + default: + { + trap(); + } break; + } } } - emit_value(module, semantic_call_argument_value, TypeKind::memory, false); + emit_value(module, src, TypeKind::memory, false); 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); @@ -5023,6 +5140,16 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, trap(); } } break; + case ValueId::zero: + { + for (u32 i = 0; i < coerce_fields.length; i += 1) + { + auto& field = coerce_fields[i]; + auto field_type = field.type; + llvm_abi_argument_value_buffer[abi_argument_count] = LLVMConstNull(field_type->llvm.abi); + abi_argument_count += 1; + } + } break; default: trap(); } } @@ -5064,9 +5191,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, assert(src->type->id == TypeId::structure); assert(src->id == ValueId::variable_reference); assert(src->kind == ValueKind::right); - src->type = 0; - src->kind = ValueKind::left; - analyze_type(module, src, pointer_type, false); + reanalyze_type_as_left_value(module, src); } } else @@ -5120,7 +5245,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, 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); + assert(argument_abi.abi_start >= raw_function_type->function.abi.abi_argument_types.length || same_address_space); // TODO: handmade code, may contain bugs assert(argument_abi.abi_count == 1); @@ -5162,9 +5287,8 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, { case ValueId::variable_reference: { - semantic_call_argument_value->type = 0; - semantic_call_argument_value->kind = ValueKind::left; - analyze_value(module, semantic_call_argument_value, pointer_type, TypeKind::memory, false); + reanalyze_type_as_left_value(module, semantic_call_argument_value); + emit_value(module, semantic_call_argument_value, TypeKind::memory, false); llvm_abi_argument_value_buffer[abi_argument_count] = semantic_call_argument_value->llvm; abi_argument_count += 1; } break; @@ -5204,9 +5328,9 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, 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; + auto declaration_abi_argument_count = raw_function_type->function.abi.abi_argument_types.length; - if (raw_function_type->function.is_variable_arguments) + if (raw_function_type->function.base.is_variable_arguments) { assert(abi_argument_count >= declaration_abi_argument_count); } @@ -5218,16 +5342,18 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, 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, { + + LLVMSetInstructionCallConv(llvm_call, llvm_calling_convention(raw_function_type->function.base.calling_convention)); + + emit_attributes(module, llvm_call, &LLVMAddCallSiteAttribute, { .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, + .abi_return_type = raw_function_type->function.abi.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: @@ -5282,19 +5408,42 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, 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.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; + + switch (return_abi.semantic_type->id) { - 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(); + case TypeId::structure: + { + if (return_abi.semantic_type->structure.fields.length > 0) + { + create_coerced_store(module, source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile); + } + else + { + trap(); + } + } break; + case TypeId::array: + { + if (get_byte_size(return_abi.semantic_type) <= 8) + { + create_store(module, { + .source = source_value, + .destination = destination_pointer, + .type = source_type, + }); + } + else + { + trap(); + } + } break; + default: unreachable(); } assert(coerce_alloca); @@ -5358,7 +5507,7 @@ fn LLVMValueRef emit_va_arg_from_memory(Module* module, LLVMValueRef va_list_poi } -fn LLVMValueRef emit_va_arg(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type) +fn LLVMValueRef emit_va_arg(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type, LLVMValueRef llvm_function) { switch (value->id) { @@ -5460,9 +5609,9 @@ fn LLVMValueRef emit_va_arg(Module* module, Value* value, LLVMValueRef left_llvm 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); + auto* in_reg_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.in_reg"), llvm_function); + auto* in_mem_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.in_mem"), llvm_function); + auto* end_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.end"), llvm_function); LLVMBuildCondBr(module->llvm.builder, in_regs, in_reg_block, in_mem_block); emit_block(module, in_reg_block); @@ -5771,11 +5920,31 @@ fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef lef return trunc; } break; + case TypeId::enum_array: 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_aggregate_type->array.element_count, false); + u64 array_element_count = 0; + + switch (resolved_aggregate_type->id) + { + case TypeId::enum_array: + { + auto enum_type = resolved_aggregate_type->enum_array.enum_type; + assert(enum_type->id == TypeId::enumerator); + array_element_count = enum_type->enumerator.fields.length; + } break; + case TypeId::array: + { + array_element_count = resolved_aggregate_type->array.element_count; + } break; + default: unreachable(); + } + + assert(array_element_count); + + auto result = LLVMConstInt(array_length_type, array_element_count, false); return result; } break; default: unreachable(); @@ -5802,6 +5971,23 @@ fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef lef fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, Value* right) { + 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); + assert(!right->llvm); auto pointer_type = left_type; auto value_type = right->type; @@ -6040,7 +6226,7 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, } break; case ValueId::va_arg: { - auto result = emit_va_arg(module, right, left_llvm, left_type); + auto result = emit_va_arg(module, right, left_llvm, left_type, llvm_function); if (result != left_llvm) { trap(); @@ -6149,6 +6335,35 @@ fn LLVMValueRef emit_binary(Module* module, LLVMValueRef left, Type* left_type, { switch (id) { + case BinaryId::max: + case BinaryId::min: + { + IntrinsicIndex intrinsic; + switch (resolved_value_type->id) + { + case TypeId::integer: + { + auto is_signed = resolved_value_type->integer.is_signed; + switch (id) + { + case BinaryId::max: + { + intrinsic = is_signed ? IntrinsicIndex::smax : IntrinsicIndex::umax; + } break; + case BinaryId::min: + { + intrinsic = is_signed ? IntrinsicIndex::smin : IntrinsicIndex::umin; + } break; + default: unreachable(); + } + } break; + default: report_error(); + } + LLVMTypeRef argument_types[] = { resolved_value_type->llvm.abi }; + LLVMValueRef argument_values[] = { left, right }; + auto llvm_value = emit_intrinsic_call(module, intrinsic, array_to_slice(argument_types), array_to_slice(argument_values)); + return llvm_value; + } break; case BinaryId::shift_right: if (resolved_value_type->integer.is_signed) { @@ -6468,12 +6683,33 @@ fn LLVMValueRef emit_constant_array(Module* module, Slice elements, Type fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect_constant) { + auto must_be_constant = expect_constant || (!module->current_function && !module->current_macro_instantiation); + Global* parent_function_global = 0; + 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 + { + assert(must_be_constant); + } + + LLVMValueRef llvm_function = 0; + if (parent_function_global) + { + llvm_function = parent_function_global->variable.storage->llvm; + assert(llvm_function); + } + 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 = expect_constant || (!module->current_function && !module->current_macro_instantiation); LLVMValueRef llvm_value = 0; switch (value->id) @@ -6619,6 +6855,10 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect { llvm_value = LLVMBuildIntToPtr(module->llvm.builder, llvm_unary_value, resolved_value_type->llvm.abi, ""); } break; + case UnaryId::enum_from_int: + { + llvm_value = llvm_unary_value; + } break; } } break; case ValueId::unary_type: @@ -6785,7 +7025,8 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect case TypeKind::abi: break; case TypeKind::memory: - trap(); + llvm_value = memory_to_abi(module, llvm_value, boolean_type); + break; } } else @@ -7114,7 +7355,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect } break; case ValueId::va_arg: { - llvm_value = emit_va_arg(module, value, 0, 0); + llvm_value = emit_va_arg(module, value, 0, 0, llvm_function); } break; case ValueId::aggregate_initialization: { @@ -7122,156 +7363,184 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect auto is_constant = value->aggregate_initialization.is_constant; auto zero = value->aggregate_initialization.zero; - switch (resolved_value_type->id) + switch (value->kind) { - case TypeId::structure: + case ValueKind::left: { - auto fields = resolved_value_type->structure.fields; - - if (is_constant) + if (resolved_value_type->id != TypeId::pointer) { - LLVMValueRef constant_buffer[64]; - u32 constant_count = (u32)elements.length; - - for (u64 i = 0; i < elements.length; i += 1) - { - auto* value = elements[i].value; - emit_value(module, value, TypeKind::memory, must_be_constant); - auto llvm_value = value->llvm; - assert(llvm_value); - assert(LLVMIsAConstant(llvm_value)); - constant_buffer[i] = llvm_value; - } - - if (zero) - { - if (elements.length == fields.length) - { - unreachable(); - } - - for (u64 i = elements.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(); + report_error(); } + + auto aggregate_type = resolved_value_type->pointer.element_type; + + auto alloca = create_alloca(module, { + .type = aggregate_type, + }); + auto resolved_pointer_type = resolved_value_type; + auto old_type = value->type; + // Overwrite type so asserts are not triggered + value->type = aggregate_type; + emit_assignment(module, alloca, resolved_pointer_type, value); + value->type = old_type; + llvm_value = alloca; } break; - case TypeId::union_type: + case ValueKind::right: { - trap(); - } break; - case TypeId::bits: - { - auto fields = resolved_value_type->bits.fields; - Type* backing_type = resolved_value_type->bits.backing_type; - resolve_type_in_place(module, backing_type); - auto abi_type = get_llvm_type(backing_type, type_kind); - - if (is_constant) + switch (resolved_value_type->id) { - u64 bits_value = 0; - - for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) - { - auto value = elements[initialization_index].value; - auto name = elements[initialization_index].name; - - u32 declaration_index; - for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + case TypeId::structure: { - auto& field = fields[declaration_index]; + auto fields = resolved_value_type->structure.fields; - if (name.equal(field.name)) + if (is_constant) { - break; - } - } + LLVMValueRef constant_buffer[64]; + u32 constant_count = (u32)elements.length; - if (declaration_index == fields.length) - { - unreachable(); - } - - const auto& field = fields[declaration_index]; - u64 field_value; - switch (value->id) - { - case ValueId::constant_integer: + for (u64 i = 0; i < elements.length; i += 1) { - field_value = value->constant_integer.value; - } break; - default: unreachable(); - } + auto* value = elements[i].value; + emit_value(module, value, TypeKind::memory, must_be_constant); + auto llvm_value = value->llvm; + assert(llvm_value); + assert(LLVMIsAConstant(llvm_value)); + constant_buffer[i] = llvm_value; + } - bits_value |= field_value << field.offset; - } + if (zero) + { + if (elements.length == fields.length) + { + unreachable(); + } - llvm_value = LLVMConstInt(abi_type, bits_value, false); - } - else - { - llvm_value = LLVMConstNull(abi_type); + for (u64 i = elements.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; + } + } - for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) - { - auto value = elements[initialization_index].value; - auto name = elements[initialization_index].name; + assert(constant_count == fields.length); - 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; + llvm_value = LLVMConstNamedStruct(get_llvm_type(resolved_value_type, type_kind), constant_buffer, constant_count); } - } - - if (declaration_index == fields.length) + else + { + // TODO: shouldn't this be a left value? + unreachable(); + } + } break; + case TypeId::union_type: { - unreachable(); - } + trap(); + } break; + case TypeId::bits: + { + auto fields = resolved_value_type->bits.fields; + Type* backing_type = resolved_value_type->bits.backing_type; + resolve_type_in_place(module, backing_type); + auto abi_type = get_llvm_type(backing_type, type_kind); - const auto& field = fields[declaration_index]; + if (is_constant) + { + u64 bits_value = 0; - emit_value(module, value, TypeKind::memory, must_be_constant); + for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) + { + auto value = elements[initialization_index].value; + auto name = elements[initialization_index].name; - auto extended = LLVMBuildZExt(module->llvm.builder, value->llvm, abi_type, ""); - auto shl = LLVMBuildShl(module->llvm.builder, extended, LLVMConstInt(abi_type, field.offset, false), ""); - auto or_value = LLVMBuildOr(module->llvm.builder, llvm_value, shl, ""); - llvm_value = or_value; - } + 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(); + } + + const 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; + } + + llvm_value = LLVMConstInt(abi_type, bits_value, false); + } + else + { + llvm_value = LLVMConstNull(abi_type); + + for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) + { + auto value = elements[initialization_index].value; + auto name = elements[initialization_index].name; + + 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(); + } + + const auto& field = fields[declaration_index]; + + emit_value(module, value, TypeKind::memory, must_be_constant); + + auto extended = LLVMBuildZExt(module->llvm.builder, value->llvm, abi_type, ""); + auto shl = LLVMBuildShl(module->llvm.builder, extended, LLVMConstInt(abi_type, field.offset, false), ""); + auto or_value = LLVMBuildOr(module->llvm.builder, llvm_value, shl, ""); + llvm_value = or_value; + } + } + } break; + case TypeId::enum_array: + { + assert(is_constant); + assert(elements.length <= 64); + Value* value_buffer[64]; + for (u64 i = 0; i < elements.length; i += 1) + { + value_buffer[i] = elements[i].value; + } + Slice values = { value_buffer, elements.length }; + auto element_type = resolved_value_type->enum_array.element_type; + llvm_value = emit_constant_array(module, values, element_type); + } break; + default: unreachable(); } } break; - case TypeId::enum_array: - { - assert(is_constant); - assert(elements.length <= 64); - Value* value_buffer[64]; - for (u64 i = 0; i < elements.length; i += 1) - { - value_buffer[i] = elements[i].value; - } - Slice values = { value_buffer, elements.length }; - auto element_type = resolved_value_type->enum_array.element_type; - llvm_value = emit_constant_array(module, values, element_type); - } break; - default: unreachable(); } + } break; case ValueId::zero: { @@ -7332,9 +7601,20 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect } 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); + switch (resolved_value_type->id) + { + case TypeId::structure: + { + llvm_value = emit_slice_result(module, string_literal, resolved_value_type->llvm.abi); + } break; + case TypeId::pointer: + { + llvm_value = string_literal.values[0]; + } break; + default: + report_error(); + } } break; case ValueId::macro_instantiation: { @@ -7375,7 +7655,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect fn void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind, bool must_be_constant) { - analyze_type(module, value, expected_type, must_be_constant); + analyze_type(module, value, expected_type, { .must_be_constant = must_be_constant }); emit_value(module, value, type_kind, must_be_constant); } @@ -7420,7 +7700,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 { assert(!module->current_macro_instantiation); auto& function_type = parent_function_global->variable.storage->type->pointer.element_type->function; - auto& return_abi = function_type.return_abi; + auto& return_abi = function_type.abi.return_abi; switch (return_abi.semantic_type->id) { @@ -7453,13 +7733,13 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 report_error(); } - analyze_type(module, return_value, return_abi.semantic_type, false); + 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; + auto return_block = parent_function_global->variable.storage->function.llvm.return_block; LLVMBuildBr(module->llvm.builder, return_block); LLVMClearInsertionPosition(module->llvm.builder); } @@ -7468,7 +7748,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto macro_instantiation = module->current_macro_instantiation; auto return_type = macro_instantiation->return_type; assert(return_type); - analyze_type(module, return_value, return_type, false); + analyze_type(module, return_value, return_type, {}); emit_assignment(module, macro_instantiation->return_alloca, get_pointer_type(module, return_type), return_value); LLVMBuildBr(module->llvm.builder, macro_instantiation->return_block); LLVMClearInsertionPosition(module->llvm.builder); @@ -7483,7 +7763,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto local = statement->local; auto expected_type = local->variable.type; assert(!local->variable.storage); - analyze_type(module, local->variable.initial_value, expected_type, false); + 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) @@ -7624,7 +7904,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 { case StatementAssignmentId::assign: { - analyze_type(module, right, element_type, false); + analyze_type(module, right, element_type, {}); emit_assignment(module, left_llvm, left_type, right); } break; case StatementAssignmentId::assign_add: @@ -7875,7 +8155,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto kind = left_values[i]; auto right = right_values[i]; - analyze_type(module, right, 0, false); + analyze_type(module, right, 0, {}); Type* aggregate_type = 0; @@ -8100,7 +8380,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 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)) + if (evaluation_kind == EvaluationKind::scalar || (aggregate_type->id == TypeId::structure && aggregate_type->structure.is_slice) || (local_type->id == TypeId::structure && local_type->structure.is_slice)) { auto load = create_load(module, { .type = local_type, @@ -8163,30 +8443,39 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 Type* local_type = 0; - switch (start->id) + if (start->is_constant()) { - case ValueId::constant_integer: - { - switch (end->id) + switch (start->id) + { + case ValueId::constant_integer: { - case ValueId::constant_integer: - { - auto start_signed = start->constant_integer.is_signed; - auto end_signed = end->constant_integer.is_signed; - auto is_signed = !(!start_signed && !end_signed); - local_type = integer_type(module, { .bit_count = 64, .is_signed = is_signed }); - } break; - default: - { - analyze_type(module, end, 0, false); - auto end_type = end->type; - assert(end_type); - start->type = end_type; - local_type = end_type; - } break; - } - } break; - default: trap(); + switch (end->id) + { + case ValueId::constant_integer: + { + auto start_signed = start->constant_integer.is_signed; + auto end_signed = end->constant_integer.is_signed; + auto is_signed = !(!start_signed && !end_signed); + local_type = integer_type(module, { .bit_count = 64, .is_signed = is_signed }); + } 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(); + } + } + else + { + analyze_binary_type(module, start, end, false, 0, false); + assert(start->type == end->type); + local_type = start->type; } assert(local_type); @@ -8195,7 +8484,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 { if (!right->type) { - analyze_type(module, right, local_type, false); + analyze_type(module, right, local_type, {}); } } @@ -8332,7 +8621,7 @@ fn BBLLVMCodeGenerationPipelineResult generate_object(LLVMModuleRef module, LLVM BBLLVMCodeGenerationPipelineOptions code_generation_options = { .output_file_path = options.path, - .code_generation_file_type = (u64)BBLLVMCodeGenerationFileType::object_file, + .file_type = BBLLVMCodeGenerationFileType::object_file, .optimize_when_possible = options.optimization_level > BBLLVMOptimizationLevel::O0, .verify_module = true, }; @@ -8481,12 +8770,17 @@ void emit(Module* module) switch (global->variable.storage->id) { case ValueId::function: - case ValueId::external_function: + case ValueId::forward_declared_function: { + if (global->variable.storage->id == ValueId::forward_declared_function && global->linkage != Linkage::external) + { + report_error(); + } + 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 semantic_argument_count = function_type->base.semantic_argument_types.length; + function_type->abi.argument_abis = arena_allocate(module->arena, semantic_argument_count); + auto resolved_calling_convention = resolve_calling_convention(function_type->base.calling_convention); auto is_reg_call = resolved_calling_convention == ResolvedCallingConvention::system_v && false; // TODO: regcall calling convention LLVMTypeRef llvm_abi_argument_type_buffer[64]; @@ -8495,15 +8789,15 @@ void emit(Module* module) { case ResolvedCallingConvention::system_v: { - function_type->available_registers = { + function_type->abi.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; + auto semantic_return_type = function_type->base.semantic_return_type; + function_type->abi.return_abi = abi_system_v_classify_return_type(module, resolve_alias(module, semantic_return_type)); + auto return_abi_kind = function_type->abi.return_abi.flags.kind; Type* abi_argument_type_buffer[64]; u16 abi_argument_type_count = 0; @@ -8514,7 +8808,7 @@ void emit(Module* module) case AbiKind::direct: case AbiKind::extend: { - abi_return_type = function_type->return_abi.coerce_to_type; + abi_return_type = function_type->abi.return_abi.coerce_to_type; } break; case AbiKind::ignore: case AbiKind::indirect: @@ -8524,14 +8818,14 @@ void emit(Module* module) default: unreachable(); // TODO } assert(abi_return_type); - function_type->abi_return_type = abi_return_type; + function_type->abi.abi_return_type = abi_return_type; resolve_type_in_place(module, abi_return_type); - if (function_type->return_abi.flags.kind == AbiKind::indirect) + if (function_type->abi.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); + assert(!function_type->abi.return_abi.flags.sret_after_this); + function_type->abi.available_registers.system_v.gpr -= 1; + auto indirect_type = get_pointer_type(module, function_type->abi.return_abi.semantic_type); resolve_type_in_place(module, indirect_type); auto abi_index = abi_argument_type_count; @@ -8542,12 +8836,12 @@ void emit(Module* module) for (u64 i = 0; i < semantic_argument_count; i += 1) { - auto& abi = function_type->argument_abis[i]; - auto semantic_argument_type = resolve_alias(module, function_type->semantic_argument_types[i]); + auto& abi = function_type->abi.argument_abis[i]; + auto semantic_argument_type = resolve_alias(module, function_type->base.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), { + abi = abi_system_v_classify_argument(module, &function_type->abi.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, @@ -8558,7 +8852,7 @@ void emit(Module* module) 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; + function_type->abi.abi_argument_types = abi_argument_types; } break; case ResolvedCallingConvention::win64: { @@ -8567,31 +8861,31 @@ void emit(Module* module) 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); + auto llvm_function_type = LLVMFunctionType(function_type->abi.abi_return_type->llvm.abi, llvm_abi_argument_type_buffer, (u32)function_type->abi.abi_argument_types.length, function_type->base.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; + Slice debug_argument_types = { .pointer = debug_argument_type_buffer, .length = function_type->abi.argument_abis.length + 1 + function_type->base.is_variable_arguments }; + debug_argument_types[0] = function_type->abi.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); + auto debug_argument_type_slice = debug_argument_types(1)(0, function_type->abi.argument_abis.length); - for (u64 i = 0; i < function_type->argument_abis.length; i += 1) + for (u64 i = 0; i < function_type->abi.argument_abis.length; i += 1) { - auto& argument_abi = function_type->argument_abis[i]; + auto& argument_abi = function_type->abi.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) + if (function_type->base.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; + debug_argument_types[function_type->abi.argument_abis.length + 1] = void_ty->llvm.debug; } LLVMDIFlags flags = {}; @@ -8612,22 +8906,22 @@ void emit(Module* module) global->variable.storage->llvm = llvm_function; LLVMCallConv cc; - switch (function_type->calling_convention) + switch (function_type->base.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, + emit_attributes(module, llvm_function, &LLVMAddAttributeAtIndex, { + .return_abi = function_type->abi.return_abi, + .argument_abis = function_type->abi.argument_abis, + .abi_argument_types = function_type->abi.abi_argument_types, + .abi_return_type = function_type->abi.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; @@ -8643,398 +8937,21 @@ void emit(Module* module) LLVMSetSubprogram(llvm_function, subprogram); } - if (is_definition) + switch (global->variable.storage->id) { - 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: + case ValueId::function: + { + global->variable.storage->function.scope.llvm = subprogram; + } break; + case ValueId::forward_declared_function: + { + assert(global->linkage == Linkage::external); + if (module->has_debug_info) { - 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, .name = argument->variable.name }); - 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) - { - auto u64_type = uint64(module); - LLVMBuildMemCpy(module->llvm.builder, pointer, address_alignment, address, address_alignment, LLVMConstInt(u64_type->llvm.abi, destination_size, false)); - } - } - } - else - { - assert(argument_abi.abi_count == 1); - auto abi_argument_type = function_type->abi_argument_types[argument_abi.abi_start]; - auto destination_size = get_byte_size(pointer_type) - argument_abi.attributes.direct.offset; - auto is_volatile = false; - create_coerced_store(module, argument_abi_arguments[0], abi_argument_type, pointer, pointer_type, destination_size, is_volatile); - } - - 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); + LLVMDIBuilderFinalizeSubprogram(module->llvm.di_builder, subprogram); } - } - - 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; + default: unreachable(); } } break; case ValueId::global: @@ -9090,6 +9007,423 @@ void emit(Module* module) } } + for (auto* global = module->first_global; global; global = global->next) + { + assert(!module->current_function); + assert(!module->current_macro_instantiation); + assert(!module->current_macro_declaration); + + if (global->variable.storage->id == ValueId::function) + { + module->current_function = global; + + auto function_type = &global->variable.storage->type->pointer.element_type->function; + auto semantic_argument_types = function_type->base.semantic_argument_types; + auto llvm_function = global->variable.storage->llvm; + assert(llvm_function); + + LLVMValueRef llvm_abi_argument_buffer[64]; + Slice llvm_abi_arguments = { .pointer = llvm_abi_argument_buffer, .length = function_type->abi.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"), llvm_function); + 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->abi.return_abi.flags.kind; + switch (return_abi_kind) + { + case AbiKind::indirect: + { + auto indirect_argument_index = function_type->abi.return_abi.flags.sret_after_this; + if (function_type->abi.return_abi.flags.sret_after_this) + { + trap(); + } + + global->variable.storage->function.llvm.return_alloca = llvm_abi_arguments[indirect_argument_index]; + + if (!function_type->abi.return_abi.flags.indirect_by_value) + { + trap(); + } + } break; + case AbiKind::in_alloca: + { + trap(); + } break; + default: + { + auto alloca = create_alloca(module, { + .type = function_type->abi.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->abi.argument_abis; + assert(arguments.length == argument_abis.length); + for (u64 i = 0; i < semantic_argument_types.length; 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, .name = argument->variable.name }); + 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) + { + auto u64_type = uint64(module); + LLVMBuildMemCpy(module->llvm.builder, pointer, address_alignment, address, address_alignment, LLVMConstInt(u64_type->llvm.abi, destination_size, false)); + } + } + } + else + { + assert(argument_abi.abi_count == 1); + auto abi_argument_type = function_type->abi.abi_argument_types[argument_abi.abi_start]; + auto destination_size = get_byte_size(pointer_type) - argument_abi.attributes.direct.offset; + auto is_volatile = false; + create_coerced_store(module, argument_abi_arguments[0], abi_argument_type, pointer, pointer_type, destination_size, is_volatile); + } + + 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); + LLVMDeleteBasicBlock(return_block); + } + else + { + emit_block(module, return_block); + } + } + else + { + bool has_single_jump_to_return_block = false; + + auto first_use = LLVMGetFirstUse((LLVMValueRef)return_block); + LLVMValueRef user = 0; + if (first_use) + { + auto second_use = LLVMGetNextUse(first_use); + auto has_one_use = first_use && !second_use; + if (has_one_use) + { + user = LLVMGetUser(first_use); + has_single_jump_to_return_block = LLVMIsABranchInst(user) && !LLVMIsConditional(user) && LLVMGetSuccessor(user, 0) == return_block; + } + } + + if (has_single_jump_to_return_block) + { + assert(LLVMGetBasicBlockParent(return_block)); + auto new_return_block = LLVMGetInstructionParent(user); + // Remove unconditional branch instruction to the return block + LLVMInstructionEraseFromParent(user); + assert(!LLVMGetFirstUse((LLVMValueRef)return_block)); + assert(!LLVMGetBasicBlockTerminator(return_block)); + assert(LLVMGetBasicBlockParent(return_block)); + LLVMPositionBuilderAtEnd(module->llvm.builder, new_return_block); + LLVMDeleteBasicBlock(return_block); + } + else + { + 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->abi.return_abi.semantic_type == noreturn_type(module) || global->variable.storage->function.attributes.naked) + { + LLVMBuildUnreachable(module->llvm.builder); + } + else if (function_type->abi.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->abi.return_abi.get_coerce_to_type(); + auto return_semantic_type = function_type->abi.return_abi.semantic_type; + if (type_is_abi_equal(module, coerce_to_type, return_semantic_type) && function_type->abi.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->abi.return_abi.attributes.direct.offset == 0) + { + source = return_alloca; + } + else + { + trap(); + } + assert(source); + + auto source_type = function_type->abi.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->abi.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; + } + } + if (module->has_debug_info) { LLVMDIBuilderFinalize(module->llvm.di_builder); diff --git a/src/llvm.cpp b/src/llvm.cpp index fd6b78d..8520149 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -37,12 +37,6 @@ EXPORT LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String n return wrap(module); } -EXPORT unsigned llvm_integer_type_get_bit_count(const llvm::IntegerType& integer_type) -{ - auto result = integer_type.getBitWidth(); - return result; -} - 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) { llvm::GlobalValue::LinkageTypes linkage; @@ -63,16 +57,6 @@ EXPORT LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLV return wrap(global); } -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(llvm::GlobalVariable* global) -{ - delete global; -} - EXPORT void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type) { auto sp = llvm::unwrap(subprogram); @@ -119,30 +103,6 @@ EXPORT LLVMBasicBlockRef llvm_context_create_basic_block(LLVMContextRef context, return wrap(basic_block); } -EXPORT bool llvm_value_has_one_use(LLVMValueRef value) -{ - auto v = llvm::unwrap(value); - auto result = v->hasOneUse(); - return result; -} - -EXPORT LLVMValueRef llvm_basic_block_user_begin(LLVMBasicBlockRef basic_block) -{ - llvm::Value* value = *llvm::unwrap(basic_block)->user_begin(); - return wrap(value); -} - -EXPORT void llvm_basic_block_delete(LLVMBasicBlockRef basic_block) -{ - delete llvm::unwrap(basic_block); -} - -EXPORT llvm::BranchInst* llvm_value_to_branch(llvm::Value* value) -{ - auto* result = dyn_cast(value); - return result; -} - // If there are multiple uses of the return-value slot, just check // for something immediately preceding the IP. Sometimes this can // happen with how we generate implicit-returns; it can also happen @@ -226,588 +186,6 @@ EXPORT LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef b, LLVMTypeRef typ return wrap(builder.Insert(new llvm::AllocaInst(llvm::unwrap(type), address_space, 0, llvm_alignment), string_ref(name))); } -enum class BBLLVMAttributeFramePointerKind : u8 -{ - None = 0, - Reserved = 1, - NonLeaf = 2, - All = 3, -}; - -const unsigned BB_LLVM_ONLY_USED = 1U << 1; -const unsigned BB_LLVM_ONLY_GPR = 1U << 2; -const unsigned BB_LLVM_ONLY_ARG = 1U << 3; - -enum class BBLLVMZeroCallUsedRegsKind : unsigned int -{ - // Don't zero any call-used regs. - Skip = 1U << 0, - // Only zeros call-used GPRs used in the fn and pass args. - UsedGPRArg = BB_LLVM_ONLY_USED | BB_LLVM_ONLY_GPR | BB_LLVM_ONLY_ARG, - // Only zeros call-used GPRs used in the fn. - UsedGPR = BB_LLVM_ONLY_USED | BB_LLVM_ONLY_GPR, - // Only zeros call-used regs used in the fn and pass args. - UsedArg = BB_LLVM_ONLY_USED | BB_LLVM_ONLY_ARG, - // Only zeros call-used regs used in the fn. - Used = BB_LLVM_ONLY_USED, - // Zeros all call-used GPRs that pass args. - AllGPRArg = BB_LLVM_ONLY_GPR | BB_LLVM_ONLY_ARG, - // Zeros all call-used GPRs. - AllGPR = BB_LLVM_ONLY_GPR, - // Zeros all call-used regs that pass args. - AllArg = BB_LLVM_ONLY_ARG, - // Zeros all call-used regs. - All = 0, -}; - -enum class BBLLVMFPClassTest : unsigned -{ - None = 0, - - SNan = 0x0001, - QNan = 0x0002, - NegInf = 0x0004, - NegNormal = 0x0008, - NegSubnormal = 0x0010, - NegZero = 0x0020, - PosZero = 0x0040, - PosSubnormal = 0x0080, - PosNormal = 0x0100, - PosInf = 0x0200, - - Nan = SNan | QNan, - Inf = PosInf | NegInf, - Normal = PosNormal | NegNormal, - Subnormal = PosSubnormal | NegSubnormal, - Zero = PosZero | NegZero, - PosFinite = PosNormal | PosSubnormal | PosZero, - NegFinite = NegNormal | NegSubnormal | NegZero, - Finite = PosFinite | NegFinite, - Positive = PosFinite | PosInf, - Negative = NegFinite | NegInf, - - AllFlags = Nan | Inf | Finite, -}; - -fn llvm::AttributeSet build_argument_attributes(LLVMContextRef context, BBLLVMArgumentAttributes* attributes) -{ - llvm::AttrBuilder builder(*llvm::unwrap(context)); - - if (attributes->alignment) - { - builder.addAlignmentAttr(attributes->alignment); - } - - if (attributes->no_alias) - { - builder.addAttribute(llvm::Attribute::NoAlias); - } - - if (attributes->non_null) - { - builder.addAttribute(llvm::Attribute::NonNull); - } - - if (attributes->no_undef) - { - builder.addAttribute(llvm::Attribute::NoUndef); - } - - if (attributes->sign_extend) - { - builder.addAttribute(llvm::Attribute::SExt); - } - - if (attributes->zero_extend) - { - builder.addAttribute(llvm::Attribute::ZExt); - } - - if (attributes->in_reg) - { - builder.addAttribute(llvm::Attribute::InReg); - } - - if (attributes->no_fp_class) - { - __builtin_trap(); // TODO - } - - if (attributes->struct_return) - { - builder.addStructRetAttr(llvm::unwrap(attributes->semantic_type)); - } - - if (attributes->writable) - { - builder.addAttribute(llvm::Attribute::Writable); - } - - if (attributes->dead_on_unwind) - { - builder.addAttribute(llvm::Attribute::DeadOnUnwind); - } - - if (attributes->in_alloca) - { - __builtin_trap(); // TODO - } - - if (attributes->dereferenceable) - { - builder.addDereferenceableAttr(attributes->dereferenceable_bytes); - } - - if (attributes->dereferenceable_or_null) - { - builder.addDereferenceableOrNullAttr(attributes->dereferenceable_bytes); - } - - if (attributes->nest) - { - builder.addAttribute(llvm::Attribute::Nest); - } - - if (attributes->by_value) - { - builder.addByValAttr(llvm::unwrap(attributes->semantic_type)); - } - - if (attributes->by_reference) - { - builder.addByRefAttr(llvm::unwrap(attributes->semantic_type)); - } - - if (attributes->no_capture) - { - builder.addAttribute(llvm::Attribute::NoCapture); - } - - auto attribute_set = llvm::AttributeSet::get(*llvm::unwrap(context), builder); - return attribute_set; -} - -inline fn BBLLVMAttributeList llvm_attribute_list_to_abi(llvm::AttributeList list) -{ - static_assert(sizeof(llvm::AttributeList) == sizeof(uintptr_t)); - static_assert(sizeof(BBLLVMAttributeList) == sizeof(uintptr_t)); - - return list.getRawPointer(); -} - -inline fn llvm::AttributeList abi_attribute_list_to_llvm(BBLLVMAttributeList list) -{ - static_assert(sizeof(llvm::AttributeList) == sizeof(uintptr_t)); - static_assert(sizeof(BBLLVMAttributeList) == sizeof(uintptr_t)); - auto attribute_list = *(llvm::AttributeList*)&list; - return attribute_list; -} - -EXPORT BBLLVMAttributeList llvm_attribute_list_build(LLVMContextRef context, BBLLVMAttributeListOptions* attributes, bool call_site) -{ - llvm::AttrBuilder function_attribute_builder(*llvm::unwrap(context)); - - if (attributes->function.prefer_vector_width.length) - { - function_attribute_builder.addAttribute("prefer-vector-width", string_ref(attributes->function.prefer_vector_width)); - } - - if (attributes->function.stack_protector_buffer_size.length) - { - function_attribute_builder.addAttribute("stack-protector-buffer-size", string_ref(attributes->function.stack_protector_buffer_size)); - } - - if (attributes->function.flags0.noreturn) - { - function_attribute_builder.addAttribute(llvm::Attribute::NoReturn); - } - - if (attributes->function.flags0.cmse_ns_call) - { - function_attribute_builder.addAttribute("cmse_nonsecure_call"); - } - - if (attributes->function.flags0.nounwind) - { - function_attribute_builder.addAttribute(llvm::Attribute::NoUnwind); - } - - if (attributes->function.flags0.returns_twice) - { - function_attribute_builder.addAttribute(llvm::Attribute::ReturnsTwice); - } - - if (attributes->function.flags0.cold) - { - function_attribute_builder.addAttribute(llvm::Attribute::Cold); - } - - if (attributes->function.flags0.hot) - { - function_attribute_builder.addAttribute(llvm::Attribute::Hot); - } - - if (attributes->function.flags0.no_duplicate) - { - function_attribute_builder.addAttribute(llvm::Attribute::NoDuplicate); - } - - if (attributes->function.flags0.convergent) - { - function_attribute_builder.addAttribute(llvm::Attribute::Convergent); - } - - if (attributes->function.flags0.no_merge) - { - function_attribute_builder.addAttribute(llvm::Attribute::NoMerge); - } - - if (attributes->function.flags0.will_return) - { - function_attribute_builder.addAttribute(llvm::Attribute::WillReturn); - } - - if (attributes->function.flags0.no_caller_saved_registers) - { - function_attribute_builder.addAttribute("no-caller-saved-registers"); - } - - if (attributes->function.flags0.no_cf_check) - { - function_attribute_builder.addAttribute(llvm::Attribute::NoCfCheck); - } - - if (attributes->function.flags0.no_callback) - { - function_attribute_builder.addAttribute(llvm::Attribute::NoCallback); - } - - if (attributes->function.flags0.alloc_size) - { - __builtin_trap(); // TODO - } - - if (attributes->function.flags0.uniform_work_group_size) - { - __builtin_trap(); // TODO - } - - if (attributes->function.flags0.aarch64_pstate_sm_body) - { - function_attribute_builder.addAttribute("aarch64_pstate_sm_body"); - } - - if (attributes->function.flags0.aarch64_pstate_sm_enabled) - { - function_attribute_builder.addAttribute("aarch64_pstate_sm_enabled"); - } - - if (attributes->function.flags0.aarch64_pstate_sm_compatible) - { - function_attribute_builder.addAttribute("aarch64_pstate_sm_compatible"); - } - - if (attributes->function.flags0.aarch64_preserves_za) - { - function_attribute_builder.addAttribute("aarch64_preserves_za"); - } - - if (attributes->function.flags0.aarch64_in_za) - { - function_attribute_builder.addAttribute("aarch64_in_za"); - } - - if (attributes->function.flags0.aarch64_out_za) - { - function_attribute_builder.addAttribute("aarch64_out_za"); - } - - if (attributes->function.flags0.aarch64_inout_za) - { - function_attribute_builder.addAttribute("aarch64_inout_za"); - } - - if (attributes->function.flags0.aarch64_preserves_zt0) - { - function_attribute_builder.addAttribute("aarch64_preserves_zt0"); - } - - if (attributes->function.flags0.aarch64_in_zt0) - { - function_attribute_builder.addAttribute("aarch64_in_zt0"); - } - - if (attributes->function.flags0.aarch64_out_zt0) - { - function_attribute_builder.addAttribute("aarch64_out_zt0"); - } - - if (attributes->function.flags0.aarch64_inout_zt0) - { - function_attribute_builder.addAttribute("aarch64_inout_zt0"); - } - - if (attributes->function.flags0.optimize_for_size) - { - function_attribute_builder.addAttribute(llvm::Attribute::OptimizeForSize); - } - - if (attributes->function.flags0.min_size) - { - function_attribute_builder.addAttribute(llvm::Attribute::MinSize); - } - - if (attributes->function.flags0.no_red_zone) - { - function_attribute_builder.addAttribute(llvm::Attribute::NoRedZone); - } - - if (attributes->function.flags0.indirect_tls_seg_refs) - { - function_attribute_builder.addAttribute("indirect-tls-seg-refs"); - } - - if (attributes->function.flags0.no_implicit_floats) - { - function_attribute_builder.addAttribute(llvm::Attribute::NoImplicitFloat); - } - - 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) - { - function_attribute_builder.addMemoryAttr(llvm::MemoryEffects::none()); - } - - if (attributes->function.flags0.memory_readonly) - { - function_attribute_builder.addMemoryAttr(llvm::MemoryEffects::readOnly()); - } - - 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) - { - llvm::Attribute attribute = function_attribute_builder.getAttribute(llvm::Attribute::Memory); - function_attribute_builder.addMemoryAttr(attribute.getMemoryEffects() | llvm::MemoryEffects::argMemOnly()); - } - - // TODO: branch protection function attributes - - // TODO: cpu features - - if (call_site) - { - if (attributes->function.flags0.call_no_builtins) - { - function_attribute_builder.addAttribute(llvm::Attribute::NoBuiltin); - } - } - else - { - if (attributes->function.definition_probe_stack.length) - { - function_attribute_builder.addAttribute("probe-stack", string_ref(attributes->function.definition_probe_stack)); - } - - if (attributes->function.definition_stack_probe_size.length) - { - function_attribute_builder.addAttribute("stack-probe-size", string_ref(attributes->function.definition_stack_probe_size)); - } - - 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; - case BBLLVMAttributeFramePointerKind::NonLeaf: frame_pointer_kind_name = "non-leaf"; break; - case BBLLVMAttributeFramePointerKind::All: frame_pointer_kind_name = "all"; break; - } - function_attribute_builder.addAttribute("frame-pointer", frame_pointer_kind_name); - - 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) - { - function_attribute_builder.addAttribute(llvm::Attribute::NullPointerIsValid); - } - - 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) - { - function_attribute_builder.addAttribute("no-infs-fp-math", "true"); - } - - 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) - { - function_attribute_builder.addAttribute("approx-func-fp-math", "true"); - } - - if (attributes->function.flags0.definition_unsafe_fp_math) - { - function_attribute_builder.addAttribute("unsafe-fp-math", "true"); - } - - 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) - { - function_attribute_builder.addAttribute("no-signed-zeros-fp-math", "true"); - } - - if (attributes->function.flags0.definition_stack_realignment) - { - function_attribute_builder.addAttribute("stackrealign"); - } - - if (attributes->function.flags0.definition_backchain) - { - function_attribute_builder.addAttribute("backchain"); - } - - if (attributes->function.flags0.definition_split_stack) - { - function_attribute_builder.addAttribute("split-stack"); - } - - if (attributes->function.flags0.definition_speculative_load_hardening) - { - function_attribute_builder.addAttribute("split-stack"); - } - - 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) - { - function_attribute_builder.addAttribute(llvm::Attribute::NonLazyBind); - } - - if (attributes->function.flags0.definition_cmse_nonsecure_entry) - { - function_attribute_builder.addAttribute("cmse_nonsecure_entry"); - } - - llvm::UWTableKind unwind_table_kind; - switch ((BBLLVMUWTableKind)attributes->function.flags0.definition_unwind_table_kind) - { - 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) - { - function_attribute_builder.addAttribute("disable-tail-calls", "true"); - } - - if (attributes->function.flags1.definition_stack_protect_strong) - { - function_attribute_builder.addAttribute(llvm::Attribute::StackProtectStrong); - } - - if (attributes->function.flags1.definition_stack_protect) - { - function_attribute_builder.addAttribute(llvm::Attribute::StackProtect); - } - - if (attributes->function.flags1.definition_stack_protect_req) - { - function_attribute_builder.addAttribute(llvm::Attribute::StackProtectReq); - } - - if (attributes->function.flags1.definition_aarch64_new_za) - { - function_attribute_builder.addAttribute("aarch64_new_za"); - } - - if (attributes->function.flags1.definition_aarch64_new_zt0) - { - function_attribute_builder.addAttribute("aarch64_new_zt0"); - } - - if (attributes->function.flags1.definition_optimize_none) - { - function_attribute_builder.addAttribute(llvm::Attribute::OptimizeNone); - } - - if (attributes->function.flags1.definition_naked) - { - function_attribute_builder.addAttribute(llvm::Attribute::Naked); - } - - if (attributes->function.flags1.definition_inline_hint) - { - function_attribute_builder.addAttribute(llvm::Attribute::InlineHint); - } - } - - auto function_attributes = llvm::AttributeSet::get(*llvm::unwrap(context), function_attribute_builder); - - auto return_attributes = build_argument_attributes(context, &attributes->return_); - - 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) - { - auto attribute_set = build_argument_attributes(context, &attributes->argument_pointer[i]); - argument_attribute_buffer[i] = attribute_set; - } - - llvm::ArrayRef argument_attributes = llvm::ArrayRef(argument_attribute_buffer, attributes->argument_count); - - auto attribute_list = llvm::AttributeList::get(*llvm::unwrap(context), function_attributes, return_attributes, argument_attributes); - - return llvm_attribute_list_to_abi(attribute_list); -} - -EXPORT bool llvm_instruction_is_call_base(llvm::Instruction* instruction) -{ - return isa(instruction); -} - -EXPORT void llvm_function_set_attributes(LLVMValueRef f, BBLLVMAttributeList attribute_list_handle) -{ - 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(LLVMValueRef call_value, BBLLVMAttributeList attribute_list_handle) -{ - 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(llvm::raw_string_ostream& stream) { // No need to call stream.flush(); because it's string-based @@ -819,8 +197,9 @@ fn String stream_to_string(llvm::raw_string_ostream& stream) u8* result = 0; if (length) { - result = new u8[length]; + result = new u8[length + 1]; memcpy(result, string.c_str(), length); + result[length] = 0; } return String{ result, length }; @@ -918,7 +297,7 @@ EXPORT String llvm_host_cpu_features() u8* result = 0; if (length) { - result = new u8[length]; + result = new u8[length + 1]; memcpy(result, feature_string.c_str(), length + 1); result[length] = 0; } @@ -926,293 +305,6 @@ EXPORT String llvm_host_cpu_features() return { result, length }; } -EXPORT LLVMTargetMachineRef llvm_create_target_machine(const BBLLVMTargetMachineCreate* create, String* error_message) -{ - std::string error_message_string; - const llvm::Target* target = llvm::TargetRegistry::lookupTarget(string_ref(create->target_triple), error_message_string); - - if (target) - { - std::optional code_model; - switch (create->code_model) - { - case BBLLVMCodeModel::none: code_model = std::nullopt; 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; - - switch (create->relocation_model) - { - case BBLLVMRelocationModel::default_relocation: relocation_model = std::nullopt; 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; - } - - llvm::CodeGenOptLevel optimization_level; - switch (create->optimization_level) - { - 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 - llvm::TargetOptions target_options; - - target_options.UnsafeFPMath = create->target_options.unsafe_fp_math; - target_options.NoInfsFPMath = create->target_options.no_infs_fp_math; - target_options.NoNaNsFPMath = create->target_options.no_nans_fp_math; - target_options.NoTrappingFPMath = create->target_options.no_trapping_fp_math; - target_options.NoSignedZerosFPMath = create->target_options.no_signed_zeroes_fp_math; - target_options.ApproxFuncFPMath = create->target_options.approx_func_fp_math; - target_options.EnableAIXExtendedAltivecABI = create->target_options.enable_aix_extended_altivec_abi; - target_options.HonorSignDependentRoundingFPMathOption = create->target_options.honor_sign_dependent_rounding_fp_math; - target_options.NoZerosInBSS = create->target_options.no_zeroes_in_bss; - target_options.GuaranteedTailCallOpt = create->target_options.guaranteed_tail_call_optimization; - target_options.StackSymbolOrdering = create->target_options.stack_symbol_ordering; - target_options.EnableFastISel = create->target_options.enable_fast_isel; - target_options.EnableGlobalISel = create->target_options.enable_global_isel; - - auto global_isel_abort_mode = (BBLLVMGlobalISelAbortMode)create->target_options.global_isel_abort_mode; - switch (global_isel_abort_mode) - { - 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 = 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; - target_options.DisableIntegratedAS = create->target_options.disable_integrated_assembler; - target_options.FunctionSections = create->target_options.function_sections; - target_options.DataSections = create->target_options.data_sections; - target_options.IgnoreXCOFFVisibility = create->target_options.ignore_xcoff_visibility; - target_options.XCOFFTracebackTable = create->target_options.xcoff_traceback_table; - target_options.UniqueSectionNames = create->target_options.unique_section_names; - target_options.UniqueBasicBlockSectionNames = create->target_options.unique_basic_block_section_names; -#if LLVM_VERSION_MAJOR >= 19 - target_options.SeparateNamedSections = create->target_options.separate_named_sections; -#endif - target_options.TrapUnreachable = create->target_options.trap_unreachable; - target_options.NoTrapAfterNoreturn = create->target_options.no_trap_after_noreturn; - target_options.TLSSize = create->target_options.tls_size; - target_options.EmulatedTLS = create->target_options.emulated_tls; - target_options.EnableTLSDESC = create->target_options.enable_tls_descriptors; - target_options.EnableIPRA = create->target_options.enable_ipra; - target_options.EmitStackSizeSection = create->target_options.emit_stack_size_section; - target_options.EnableMachineOutliner = create->target_options.enable_machine_outliner; - target_options.EnableMachineFunctionSplitter = create->target_options.enable_machine_function_splitter; - target_options.SupportsDefaultOutlining = create->target_options.supports_default_outlining; - target_options.EmitAddrsig = create->target_options.emit_address_significance_table; -#if LLVM_VERSION_MAJOR >= 19 - target_options.BBAddrMap = create->target_options.bb_address_map; -#endif - - auto bb_sections = (BBLLVMBasicBlockSection) create->target_options.bb_sections; - switch (bb_sections) - { - 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; - target_options.SupportsDebugEntryValues = create->target_options.supports_debug_entry_values; - target_options.EnableDebugEntryValues = create->target_options.enable_debug_entry_values; - target_options.ValueTrackingVariableLocations = create->target_options.value_tracking_variable_locations; - target_options.ForceDwarfFrameSection = create->target_options.force_dwarf_frame_section; - target_options.XRayFunctionIndex = create->target_options.xray_function_index; - target_options.DebugStrictDwarf = create->target_options.debug_strict_dwarf; - target_options.Hotpatch = create->target_options.hotpatch; - target_options.PPCGenScalarMASSEntries = create->target_options.ppc_gen_scalar_mass_entries; - target_options.JMCInstrument = create->target_options.jmc_instrument; - target_options.EnableCFIFixup = create->target_options.enable_cfi_fixup; - target_options.MisExpect = create->target_options.mis_expect; - target_options.XCOFFReadOnlyPointers = create->target_options.xcoff_read_only_pointers; - - auto float_abi = (BBLLVMFloatAbi) create->target_options.float_abi; - switch (float_abi) - { - 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 = 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 = 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 = 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 = 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 = 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; - target_options.BinutilsVersion = { create->target_options.binutils_version[0], create->target_options.binutils_version[1] }; - - if (create->target_options.mc.abi_name.length) - { - target_options.MCOptions.ABIName = { (char*)create->target_options.mc.abi_name.pointer, create->target_options.mc.abi_name.length }; - } - - if (create->target_options.mc.assembly_language.length) - { - target_options.MCOptions.AssemblyLanguage = { (char*)create->target_options.mc.assembly_language.pointer, create->target_options.mc.assembly_language.length }; - } - - if (create->target_options.mc.split_dwarf_file.length) - { - target_options.MCOptions.SplitDwarfFile = { (char*)create->target_options.mc.split_dwarf_file.pointer, create->target_options.mc.split_dwarf_file.length }; - } - - if (create->target_options.mc.as_secure_log_file.length) - { - target_options.MCOptions.AsSecureLogFile = { (char*)create->target_options.mc.as_secure_log_file.pointer, create->target_options.mc.as_secure_log_file.length }; - } - - if (create->target_options.mc.argv_count) - { - target_options.MCOptions.Argv0 = create->target_options.mc.argv0; - - // TODO: - __builtin_trap(); - } - - if (create->target_options.mc.integrated_assembler_search_path_count) - { - // TODO: - __builtin_trap(); - } - - target_options.MCOptions.MCRelaxAll = create->target_options.mc.relax_all; - target_options.MCOptions.MCNoExecStack = create->target_options.mc.no_exec_stack; - target_options.MCOptions.MCFatalWarnings = create->target_options.mc.fatal_warnings; - target_options.MCOptions.MCNoWarn = create->target_options.mc.no_warn; - target_options.MCOptions.MCNoDeprecatedWarn = create->target_options.mc.no_deprecated_warn; - target_options.MCOptions.MCNoTypeCheck = create->target_options.mc.no_type_check; - target_options.MCOptions.MCSaveTempLabels = create->target_options.mc.save_temp_labels; - target_options.MCOptions.MCIncrementalLinkerCompatible = create->target_options.mc.incremental_linker_compatible; -#if LLVM_VERSION_MAJOR >= 19 - target_options.MCOptions.FDPIC = create->target_options.mc.fdpic; -#endif - target_options.MCOptions.ShowMCEncoding = create->target_options.mc.show_mc_encoding; - target_options.MCOptions.ShowMCInst = create->target_options.mc.show_mc_inst; - target_options.MCOptions.AsmVerbose = create->target_options.mc.asm_verbose; - target_options.MCOptions.PreserveAsmComments = create->target_options.mc.preserve_asm_comments; - target_options.MCOptions.Dwarf64 = create->target_options.mc.dwarf64; -#if LLVM_VERSION_MAJOR >= 19 - target_options.MCOptions.Crel = create->target_options.mc.crel; - target_options.MCOptions.X86RelaxRelocations = create->target_options.mc.x86_relax_relocations; - target_options.MCOptions.X86Sse2Avx = create->target_options.mc.x86_sse2_avx; -#endif - - auto emit_dwarf_unwind = (BBLLVMEmitDwarfUnwindType) create->target_options.mc.emit_dwarf_unwind; - switch (emit_dwarf_unwind) - { - 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 = 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 = 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 - - target_options.MCOptions.EmitCompactUnwindNonCanonical = create->target_options.mc.emit_compact_unwind_non_canonical; - target_options.MCOptions.PPCUseFullRegisterNames = create->target_options.mc.ppc_use_full_register_names; - - return reinterpret_cast(const_cast(target->createTargetMachine(string_ref(create->target_triple), string_ref(create->cpu_model), string_ref(create->cpu_features), target_options, relocation_model, code_model, optimization_level, create->jit))); - } - else - { - auto length = error_message_string.length(); - auto* result = new u8[length]; - memcpy(result, error_message_string.c_str(), length); - - *error_message = { result, length }; - return 0; - } -} - -EXPORT void llvm_module_set_target(LLVMModuleRef m, LLVMTargetMachineRef tm) -{ - auto module = llvm::unwrap(m); - auto target_machine = (llvm::TargetMachine*)tm; - module->setDataLayout(target_machine->createDataLayout()); - auto& triple_string = target_machine->getTargetTriple().getTriple(); - module->setTargetTriple(llvm::StringRef(triple_string)); -} - EXPORT void llvm_module_run_optimization_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, BBLLVMOptimizationPipelineOptions options) { auto module = llvm::unwrap(m); @@ -1344,7 +436,7 @@ EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeli } llvm::CodeGenFileType file_type; - switch ((BBLLVMCodeGenerationFileType)options->code_generation_file_type) + switch (options->file_type) { case BBLLVMCodeGenerationFileType::assembly_file: file_type = llvm::CodeGenFileType::AssemblyFile; break; case BBLLVMCodeGenerationFileType::object_file: file_type = llvm::CodeGenFileType::ObjectFile; break; @@ -1397,17 +489,19 @@ fn LLDResult lld_api_generic(lld_api_args(), LinkerFunction linker_function) auto stdout_length = stdout_string.length(); if (stdout_length) { - auto* stdout_pointer = new u8[stdout_length]; + auto* stdout_pointer = new u8[stdout_length + 1]; memcpy(stdout_pointer, stdout_string.data(), stdout_length); result.stdout_string = { stdout_pointer, stdout_length }; + stdout_pointer[stdout_length] = 0; } auto stderr_length = stderr_string.length(); if (stderr_length) { - auto* stderr_pointer = new u8[stderr_length]; + auto* stderr_pointer = new u8[stderr_length + 1]; memcpy(stderr_pointer, stderr_string.data(), stderr_length); result.stderr_string = { stderr_pointer, stderr_length }; + stderr_pointer[stderr_length] = 0; } // TODO: should we only call it on success? diff --git a/src/llvm.hpp b/src/llvm.hpp index 806ac1e..f2faa1e 100644 --- a/src/llvm.hpp +++ b/src/llvm.hpp @@ -29,20 +29,16 @@ enum class BBLLVMCodeGenerationFileType : u8 null = 2, }; -#define BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (60) - struct BBLLVMCodeGenerationPipelineOptions { String output_dwarf_file_path; String output_file_path; - u64 code_generation_file_type:2; - u64 optimize_when_possible:1; - u64 verify_module:1; - u64 reserved: BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; + BBLLVMCodeGenerationFileType file_type; + bool optimize_when_possible; + bool verify_module; }; static_assert(sizeof(BBLLVMCodeGenerationPipelineOptions) == 5 * sizeof(u64)); -static_assert(BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 60); enum class BBLLVMOptimizationLevel : u8 { @@ -74,186 +70,6 @@ struct BBLLVMOptimizationPipelineOptions static_assert(sizeof(BBLLVMOptimizationPipelineOptions) == sizeof(u64)); static_assert(BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 51); -enum class BBLLVMUWTableKind : u64 -{ - 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, @@ -301,306 +117,6 @@ enum class DwarfType 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)); - -enum class BBLLVMEmitDwarfUnwindType : u8 -{ - always = 0, - no_compact_unwind = 1, - normal = 2, -}; - -enum class BBLLVMDwarfDirectory : u8 -{ - disable = 0, - enable = 1, - normal = 2, -}; - -enum class BBLLVMDebugCompressionType : u8 -{ - none = 0, - zlib = 1, - zstd = 2, -}; - -#define BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT (7) - -struct BBLLVMMCTargetOptions -{ - String abi_name; - String assembly_language; - String split_dwarf_file; - String as_secure_log_file; - const char* argv0; - String* argv_pointer; - u64 argv_count; - String* integrated_assembler_search_path_pointer; - u64 integrated_assembler_search_path_count; - u32 relax_all:1; - u32 no_exec_stack:1; - u32 fatal_warnings:1; - u32 no_warn:1; - u32 no_deprecated_warn:1; - u32 no_type_check:1; - u32 save_temp_labels:1; - u32 incremental_linker_compatible:1; - u32 fdpic:1; - u32 show_mc_encoding:1; - u32 show_mc_inst:1; - u32 asm_verbose:1; - u32 preserve_asm_comments:1 = true; - u32 dwarf64:1; - u32 crel:1; - u32 x86_relax_relocations:1; - u32 x86_sse2_avx:1; - u32 emit_dwarf_unwind:2 = 2; - u32 use_dwarf_directory:2 = 2; - u32 debug_compression_type:2 = 0; - u32 emit_compact_unwind_non_canonical:1; - u32 ppc_use_full_register_names:1; - u32 reserved:BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT; -}; - -static_assert(sizeof(BBLLVMMCTargetOptions) == 112); -static_assert(BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT == 7); - -enum class BBLLVMCodeModel : u8 -{ - none = 0, - tiny = 1, - small = 2, - kernel = 3, - medium = 4, - large = 5, -}; - -enum class BBLLVMRelocationModel : u8 -{ - default_relocation = 0, - static_relocation = 1, - pic = 2, - dynamic_no_pic = 3, - ropi = 4, - rwpi = 5, - ropi_rwpi = 6, -}; - -enum class BBLLVMCodeGenerationOptimizationLevel : u8 -{ - none = 0, // -O0 - less = 1, // -O1 - normal = 2, // -O2, -Os - aggressive = 3 // -O3 -}; - -enum class BBLLVMGlobalISelAbortMode : u8 -{ - disable = 0, - enable = 1, - disable_with_diag = 2, -}; - -enum class BBLLVMSwiftAsyncFramePointerMode : u8 -{ - deployment_based = 0, - always = 1, - never = 2, -}; - -enum class BBLLVMBasicBlockSection : u8 -{ - all = 0, - list = 1, - preset = 2, - none = 3, -}; - -enum class BBLLVMFloatAbi : u8 -{ - normal = 0, - soft = 1, - hard = 2, -}; - -enum class BBLLVMFPOpFusion : u8 -{ - fast = 0, - standard = 1, - strict = 2, -}; - -enum class BBLLVMThreadModel : u8 -{ - posix = 0, - single = 1, -}; - -enum class BBLLVMEAbi : u8 -{ - unknown = 0, - normal = 1, - eabi4 = 2, - eabi5 = 3, - gnu = 4, -}; - -enum class BBLLVMDebuggerKind : u8 -{ - normal = 0, - gdb = 1, - lldb = 2, - sce = 3, - dbx = 4, -}; - -enum class BBLLVMExceptionHandling : u8 -{ - none = 0, - dwarf_cfi = 1, - setjmp_longjmp = 2, - arm = 3, - win_eh = 4, - wasm = 5, - aix = 6, - zos = 7, -}; - -#define BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT (21) - -struct BBLLVMTargetOptions -{ - u64 unsafe_fp_math:1; - u64 no_infs_fp_math:1; - u64 no_nans_fp_math:1; - u64 no_trapping_fp_math:1 = true; - u64 no_signed_zeroes_fp_math:1; - u64 approx_func_fp_math:1; - u64 enable_aix_extended_altivec_abi:1; - u64 honor_sign_dependent_rounding_fp_math:1; - u64 no_zeroes_in_bss:1; - u64 guaranteed_tail_call_optimization:1; - u64 stack_symbol_ordering:1 = true; - u64 enable_fast_isel:1; - u64 enable_global_isel:1 = 1; - u64 global_isel_abort_mode:2; - u64 swift_async_frame_pointer:2 = 1; - u64 use_init_array:1; - u64 disable_integrated_assembler:1; - u64 function_sections:1; - u64 data_sections:1; - u64 ignore_xcoff_visibility:1; - u64 xcoff_traceback_table:1 = true; - u64 unique_section_names:1 = true; - u64 unique_basic_block_section_names:1; - u64 separate_named_sections:1; - u64 trap_unreachable:1; - u64 no_trap_after_noreturn:1; - u64 tls_size:8; - u64 emulated_tls:1; - u64 enable_tls_descriptors:1; - u64 enable_ipra:1; - u64 emit_stack_size_section:1; - u64 enable_machine_outliner:1; - u64 enable_machine_function_splitter:1; - u64 supports_default_outlining:1; - u64 emit_address_significance_table:1; - u64 bb_address_map:1; - u64 bb_sections:3 = 3; - u64 emit_call_site_information:1; - u64 supports_debug_entry_values:1; - u64 enable_debug_entry_values:1; - u64 value_tracking_variable_locations:1; - u64 force_dwarf_frame_section:1; - u64 xray_function_index:1 = true; - u64 debug_strict_dwarf:1; - u64 hotpatch:1; - u64 ppc_gen_scalar_mass_entries:1; - u64 jmc_instrument:1; - u64 enable_cfi_fixup:1; - u64 mis_expect:1; - u64 xcoff_read_only_pointers:1; - u64 float_abi:2 = 0; - u64 thread_model:1 = 0; - u32 fp_op_fusion_mode:2 = 1; - u32 eabi_version:3 = 1; - u32 debugger_kind:3 = 0; - u32 exception_handling:3 = 0; - u32 reserved:BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT; - unsigned loop_alignment = 0; - int binutils_version[2]; - - BBLLVMMCTargetOptions mc; -}; - -static_assert(sizeof(BBLLVMTargetOptions) == 136); -static_assert(BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT == 21); - -#define BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT (4) - -struct BBLLVMTargetMachineCreate -{ - BBLLVMTargetOptions target_options; - String target_triple; - String cpu_model; - String cpu_features; - BBLLVMRelocationModel relocation_model; - BBLLVMCodeModel code_model; - BBLLVMCodeGenerationOptimizationLevel optimization_level; - bool jit; - u8 reserved[BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT]; -}; - -static_assert(sizeof(BBLLVMTargetMachineCreate) == 192); -static_assert(BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT == 4); fn bool llvm_initialized = false; @@ -611,21 +127,14 @@ extern "C" String llvm_host_cpu_features(); extern "C" LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String name); 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); 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); @@ -635,8 +144,6 @@ extern "C" void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMet extern "C" String llvm_module_to_string(LLVMModuleRef module); -extern "C" LLVMTargetMachineRef llvm_create_target_machine(const BBLLVMTargetMachineCreate* create, String* error_message); -extern "C" void llvm_module_set_target(LLVMModuleRef m, LLVMTargetMachineRef tm); extern "C" void llvm_module_run_optimization_pipeline(LLVMModuleRef module, LLVMTargetMachineRef target_machine, BBLLVMOptimizationPipelineOptions options); extern "C" BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, const BBLLVMCodeGenerationPipelineOptions* options); diff --git a/src/parser.cpp b/src/parser.cpp index 577a28a..9f97707 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4,11 +4,14 @@ enum class ValueIntrinsic { align_of, byte_size, + enum_from_int, enum_name, extend, integer_max, int_from_enum, int_from_pointer, + max, + min, pointer_cast, pointer_from_int, select, @@ -450,6 +453,280 @@ fn u64 parse_integer_decimal_assume_valid(String string) fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder); +struct FunctionHeaderArgument +{ + String name; + u32 line; +}; + +struct FunctionHeaderParsing +{ + Type* type; + Slice arguments; + FunctionAttributes attributes; +}; + +fn bool type_function_base_compare(Module* module, TypeFunctionBase& a, TypeFunctionBase& b) +{ + auto same_return_type = resolve_alias(module, a.semantic_return_type) == b.semantic_return_type; + auto same_calling_convention = a.calling_convention == b.calling_convention; + auto same_is_variable_arguments = a.is_variable_arguments == b.is_variable_arguments; + + auto same_argument_length = a.semantic_argument_types.length == b.semantic_argument_types.length; + auto same_argument_types = same_argument_length; + + if (same_argument_length) + { + for (u64 i = 0; i < a.semantic_argument_types.length; i += 1) + { + auto a_type = resolve_alias(module, a.semantic_argument_types[i]); + auto b_type = resolve_alias(module, b.semantic_argument_types[i]); + + auto is_same_argument_type = a_type == b_type; + + same_argument_types = same_argument_types && is_same_argument_type; + } + } + + return same_return_type && same_calling_convention && same_is_variable_arguments && same_argument_types; +} + +fn Type* get_function_type(Module* module, TypeFunctionBase base) +{ + base.semantic_return_type = resolve_alias(module, base.semantic_return_type); + for (u64 i = 0; i < base.semantic_argument_types.length; i += 1) + { + base.semantic_argument_types[i] = resolve_alias(module, base.semantic_argument_types[i]); + } + + Type* last_function_type = module->first_function_type; + + while (last_function_type) + { + assert(last_function_type->id == TypeId::function); + if (type_function_base_compare(module, base, last_function_type->function.base)) + { + return last_function_type; + } + + auto next = last_function_type->function.next; + if (!next) + { + break; + } + + last_function_type = next; + } + + auto result = type_allocate_init(module, Type{ + .function = { + .base = base, + }, + .id = TypeId::function, + .name = string_literal(""), + .scope = &module->scope, + }); + + if (last_function_type) + { + assert(module->first_function_type); + last_function_type->function.next = result; + } + else + { + assert(!module->first_function_type); + module->first_function_type = result; + } + + return result; +} + + +fn Type* parse_type(Module* module, Scope* scope); +fn FunctionHeaderParsing parse_function_header(Module* module, Scope* scope, bool mandate_argument_names) +{ + auto calling_convention = CallingConvention::c; + auto function_attributes = FunctionAttributes{}; + bool is_variable_arguments = false; + + if (consume_character_if_match(module, left_bracket)) + { + while (module->offset < module->content.length) + { + auto function_identifier = parse_identifier(module); + + enum class FunctionKeyword + { + cc, + count, + }; + + String function_keywords[] = { + string_literal("cc"), + }; + static_assert(array_length(function_keywords) == (u64)FunctionKeyword::count); + + backing_type(FunctionKeyword) i; + for (i = 0; i < (backing_type(FunctionKeyword))(FunctionKeyword::count); i += 1) + { + auto function_keyword = function_keywords[i]; + if (function_keyword.equal(function_identifier)) + { + break; + } + } + + auto function_keyword = (FunctionKeyword)i; + skip_space(module); + + switch (function_keyword) + { + case FunctionKeyword::cc: + { + expect_character(module, left_parenthesis); + skip_space(module); + auto calling_convention_string = parse_identifier(module); + String calling_conventions[] = { + string_literal("c"), + }; + static_assert(array_length(calling_conventions) == (u64)CallingConvention::count); + + backing_type(CallingConvention) i; + for (i = 0; i < (backing_type(CallingConvention))CallingConvention::count; i += 1) + { + auto calling_convention = calling_conventions[i]; + if (calling_convention.equal(calling_convention_string)) + { + break; + } + } + + auto candidate_calling_convention = (CallingConvention)i; + if (candidate_calling_convention == CallingConvention::count) + { + report_error(); + } + + calling_convention = candidate_calling_convention; + + skip_space(module); + expect_character(module, right_parenthesis); + } break; + case FunctionKeyword::count: + { + report_error(); + } break; + } + + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + else + { + report_error(); + } + } + } + + skip_space(module); + + expect_character(module, left_parenthesis); + + Type* semantic_argument_type_buffer[64]; + String semantic_argument_name_buffer[64]; + u32 argument_line_buffer[64]; + u32 semantic_argument_count = 0; + + while (module->offset < module->content.length) + { + skip_space(module); + + if (consume_character_if_match(module, '.')) + { + expect_character(module, '.'); + expect_character(module, '.'); + skip_space(module); + expect_character(module, right_parenthesis); + is_variable_arguments = true; + break; + } + + if (consume_character_if_match(module, right_parenthesis)) + { + break; + } + + auto line = get_line(module); + argument_line_buffer[semantic_argument_count] = line; + + String argument_name = {}; + + if (mandate_argument_names) + { + argument_name = parse_identifier(module); + + skip_space(module); + + expect_character(module, ':'); + + skip_space(module); + } + + semantic_argument_name_buffer[semantic_argument_count] = argument_name; + + auto argument_type = parse_type(module, scope); + semantic_argument_type_buffer[semantic_argument_count] = argument_type; + + skip_space(module); + + unused(consume_character_if_match(module, ',')); + + semantic_argument_count += 1; + } + + skip_space(module); + + auto return_type = parse_type(module, scope); + + skip_space(module); + + Slice argument_types = {}; + if (semantic_argument_count != 0) + { + argument_types = new_type_array(module, semantic_argument_count); + memcpy(argument_types.pointer, semantic_argument_type_buffer, semantic_argument_count * sizeof(Type*)); + } + + auto function_type = get_function_type(module, { + .semantic_return_type = return_type, + .semantic_argument_types = argument_types, + .calling_convention = calling_convention, + .is_variable_arguments = is_variable_arguments, + }); + + Slice arguments = {}; + if (mandate_argument_names) + { + arguments = arena_allocate(module->arena, semantic_argument_count); + for (u64 i = 0; i < semantic_argument_count; i += 1) + { + arguments[i] = { + .name = semantic_argument_name_buffer[i], + .line = argument_line_buffer[i], + }; + } + } + + return { + .type = function_type, + .arguments = arguments, + .attributes = function_attributes, + }; +} + fn Type* parse_type(Module* module, Scope* scope) { auto start_character = module->content[module->offset]; @@ -478,6 +755,14 @@ fn Type* parse_type(Module* module, Scope* scope) auto enum_array_type = get_enum_array_type(module, enum_type, element_type); return enum_array_type; } + else if (identifier.equal(string_literal("fn"))) + { + skip_space(module); + auto mandate_argument_names = false; + auto function_header = parse_function_header(module, scope, mandate_argument_names); + auto result = function_header.type; + return result; + } else { auto is_int_type = identifier.length > 1 && (identifier[0] == 's' || identifier[0] == 'u'); @@ -667,7 +952,7 @@ fn Type* parse_type(Module* module, Scope* scope) { case TypeIntrinsic::return_type: { - auto return_type = module->current_function->variable.type->function.semantic_return_type; + auto return_type = module->current_function->variable.type->function.base.semantic_return_type; return return_type; } break; case TypeIntrinsic::count: report_error(); @@ -767,7 +1052,7 @@ fn u8 escape_character(u8 ch) case 't': return '\t'; case 'r': return '\r'; case '\'': return '\''; - default: trap(); + default: report_error(); } } @@ -776,6 +1061,7 @@ fn String parse_string_literal(Module* module) expect_character(module, '"'); auto start = module->offset; + u64 escape_character_count = 0; while (1) { @@ -784,19 +1070,32 @@ fn String parse_string_literal(Module* module) { break; } - else if (ch == '\\') - { - trap(); - } - else - { - module->offset += 1; - } + escape_character_count += ch == '\\'; + module->offset += 1; } auto end = module->offset; - module->offset += 1; - auto string_literal = module->content(start, end); + auto length = end - start - escape_character_count; + auto pointer = (u8*)arena_allocate_bytes(module->arena, length + 1, 1); + auto string_literal = String{ .pointer = pointer, .length = length }; + + for (u64 source_i = start, i = 0; source_i < end; source_i += 1, i += 1) + { + auto ch = module->content[source_i]; + if (ch == '\\') + { + source_i += 1; + ch = module->content[source_i]; + string_literal[i] = escape_character(ch); + } + else + { + string_literal[i] = ch; + } + } + + expect_character(module, '"'); + return string_literal; } @@ -855,11 +1154,14 @@ fn Token tokenize(Module* module) String value_intrinsics[] = { string_literal("align_of"), string_literal("byte_size"), + string_literal("enum_from_int"), string_literal("enum_name"), string_literal("extend"), string_literal("integer_max"), string_literal("int_from_enum"), string_literal("int_from_pointer"), + string_literal("max"), + string_literal("min"), string_literal("pointer_cast"), string_literal("pointer_from_int"), string_literal("select"), @@ -1239,6 +1541,7 @@ fn Value* parse_aggregate_initialization(Module* module, Scope* scope, ValueBuil } auto field_index = field_count; + auto checkpoint = get_checkpoint(module); if (consume_character_if_match(module, '.')) { auto name = parse_identifier(module); @@ -1363,6 +1666,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) switch (intrinsic) { + case ValueIntrinsic::enum_from_int: case ValueIntrinsic::enum_name: case ValueIntrinsic::extend: case ValueIntrinsic::int_from_enum: @@ -1375,6 +1679,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) UnaryId id; switch (intrinsic) { + case ValueIntrinsic::enum_from_int: id = UnaryId::enum_from_int; break; case ValueIntrinsic::enum_name: id = UnaryId::enum_name; break; case ValueIntrinsic::extend: id = UnaryId::extend; break; case ValueIntrinsic::int_from_enum: id = UnaryId::int_from_enum; break; @@ -1526,6 +1831,37 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) { trap(); } break; + case ValueIntrinsic::min: + case ValueIntrinsic::max: + { + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + auto left = parse_value(module, scope, {}); + skip_space(module); + expect_character(module, ','); + skip_space(module); + auto right = parse_value(module, scope, {}); + skip_space(module); + expect_character(module, right_parenthesis); + + BinaryId binary_id; + switch (intrinsic) + { + case ValueIntrinsic::max: binary_id = BinaryId::max; break; + case ValueIntrinsic::min: binary_id = BinaryId::min; break; + default: unreachable(); + } + + *result = { + .binary = { + .left = left, + .right = right, + .id = binary_id, + }, + .id = ValueId::binary, + }; + } break; case ValueIntrinsic::count: unreachable(); } } break; @@ -1536,7 +1872,26 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) skip_space(module); - if (module->content[module->offset] == '.') + auto checkpoint = get_checkpoint(module); + bool is_aggregate_initialization = false; + if (consume_character_if_match(module, '.')) + { + auto identifier = parse_identifier(module); + + skip_space(module); + is_aggregate_initialization = consume_character_if_match(module, '='); + if (!is_aggregate_initialization) + { + if (!consume_character_if_match(module, ',')) + { + report_error(); + } + } + } + + set_checkpoint(module, checkpoint); + + if (is_aggregate_initialization) { result = parse_aggregate_initialization(module, scope, builder, right_bracket); } @@ -2819,16 +3174,23 @@ void parse(Module* module) auto global_name = parse_identifier(module); + Global* global_forward_declaration = 0; Global* last_global = module->first_global; while (last_global) { if (global_name.equal(last_global->variable.name)) { - report_error(); - } + global_forward_declaration = last_global; + if (last_global->variable.storage->id != ValueId::forward_declared_function) + { + report_error(); + } + + if (last_global->linkage == Linkage::external) + { + report_error(); + } - if (!last_global->next) - { break; } @@ -2836,14 +3198,14 @@ void parse(Module* module) } Type* type_it = module->scope.types.first; - Type* forward_declaration = 0; + Type* type_forward_declaration = 0; while (type_it) { if (global_name.equal(type_it->name)) { if (type_it->id == TypeId::forward_declaration) { - forward_declaration = type_it; + type_forward_declaration = type_it; break; } else @@ -2920,6 +3282,12 @@ void parse(Module* module) } auto global_keyword = (GlobalKeyword)i; + + if (global_forward_declaration && global_keyword != GlobalKeyword::function) + { + report_error(); + } + switch (global_keyword) { case GlobalKeyword::bits: @@ -3137,206 +3505,73 @@ void parse(Module* module) } break; case GlobalKeyword::function: { - auto calling_convention = CallingConvention::c; - auto function_attributes = FunctionAttributes{}; - bool is_variable_arguments = false; + auto mandate_argument_names = true; + auto function_header = parse_function_header(module, scope, mandate_argument_names); - if (consume_character_if_match(module, left_bracket)) + auto function_type = function_header.type; + auto function_attributes = function_header.attributes; + + auto semantic_argument_types = function_type->function.base.semantic_argument_types; + + auto pointer_to_function_type = get_pointer_type(module, function_type); + + Global* global = 0; + if (global_forward_declaration) { - while (module->offset < module->content.length) + global = global_forward_declaration; + if (global_forward_declaration->variable.type != function_type) { - auto function_identifier = parse_identifier(module); - - enum class FunctionKeyword - { - cc, - count, - }; - - String function_keywords[] = { - string_literal("cc"), - }; - static_assert(array_length(function_keywords) == (u64)FunctionKeyword::count); - - backing_type(FunctionKeyword) i; - for (i = 0; i < (backing_type(FunctionKeyword))(FunctionKeyword::count); i += 1) - { - auto function_keyword = function_keywords[i]; - if (function_keyword.equal(function_identifier)) - { - break; - } - } - - auto function_keyword = (FunctionKeyword)i; - skip_space(module); - - switch (function_keyword) - { - case FunctionKeyword::cc: - { - expect_character(module, left_parenthesis); - skip_space(module); - auto calling_convention_string = parse_identifier(module); - String calling_conventions[] = { - string_literal("c"), - }; - static_assert(array_length(calling_conventions) == (u64)CallingConvention::count); - - backing_type(CallingConvention) i; - for (i = 0; i < (backing_type(CallingConvention))CallingConvention::count; i += 1) - { - auto calling_convention = calling_conventions[i]; - if (calling_convention.equal(calling_convention_string)) - { - break; - } - } - - auto candidate_calling_convention = (CallingConvention)i; - if (candidate_calling_convention == CallingConvention::count) - { - report_error(); - } - - calling_convention = candidate_calling_convention; - - skip_space(module); - expect_character(module, right_parenthesis); - } break; - case FunctionKeyword::count: - { - report_error(); - } break; - } - - skip_space(module); - - if (consume_character_if_match(module, right_bracket)) - { - break; - } - else - { - report_error(); - } - } - } - - skip_space(module); - - expect_character(module, left_parenthesis); - - Type* semantic_argument_type_buffer[64]; - String semantic_argument_name_buffer[64]; - u32 argument_line_buffer[64]; - u32 semantic_argument_count = 0; - - while (module->offset < module->content.length) - { - skip_space(module); - - if (consume_character_if_match(module, '.')) - { - expect_character(module, '.'); - expect_character(module, '.'); - skip_space(module); - expect_character(module, right_parenthesis); - is_variable_arguments = true; - break; + report_error(); } - if (consume_character_if_match(module, right_parenthesis)) - { - break; - } + assert(global_forward_declaration->variable.storage->type == pointer_to_function_type); - auto line = get_line(module); - argument_line_buffer[semantic_argument_count] = line; - - auto argument_name = parse_identifier(module); - semantic_argument_name_buffer[semantic_argument_count] = argument_name; - - skip_space(module); - - expect_character(module, ':'); - - skip_space(module); - - auto argument_type = parse_type(module, scope); - semantic_argument_type_buffer[semantic_argument_count] = argument_type; - - skip_space(module); - - unused(consume_character_if_match(module, ',')); - - semantic_argument_count += 1; + global->variable.name = global_name; + global->variable.line = global_line; + global->variable.column = global_column; } - - skip_space(module); - - auto return_type = parse_type(module, scope); - - skip_space(module); - - Slice argument_types = {}; - if (semantic_argument_count != 0) + else { - argument_types = new_type_array(module, semantic_argument_count); - memcpy(argument_types.pointer, semantic_argument_type_buffer, semantic_argument_count * sizeof(Type*)); + auto storage = new_value(module); + *storage = { + .type = pointer_to_function_type, + .id = ValueId::forward_declared_function, + // TODO? .kind = ValueKind::left, + }; + + global = new_global(module); + *global = { + .variable = { + .storage = storage, + .initial_value = 0, + .type = function_type, + .scope = scope, + .name = global_name, + .line = global_line, + .column = global_column, + }, + .linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal, + }; } - auto is_declaration = consume_character_if_match(module, ';'); - - auto function_type = type_allocate_init(module, { - .function = { - .semantic_return_type = return_type, - .semantic_argument_types = argument_types, - .calling_convention = calling_convention, - .is_variable_arguments = is_variable_arguments, - }, - .id = TypeId::function, - .name = string_literal(""), - .scope = &module->scope, - }); - - auto storage = new_value(module); - *storage = { - .type = get_pointer_type(module, function_type), - .id = ValueId::external_function, - // TODO? .kind = ValueKind::left, - }; - auto global = new_global(module); - *global = { - .variable = { - .storage = storage, - .initial_value = 0, - .type = function_type, - .scope = scope, - .name = global_name, - .line = global_line, - .column = global_column, - }, - .linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal, - }; - - if (!is_declaration) + if (!consume_character_if_match(module, ';')) { module->current_function = global; - Slice arguments = arena_allocate(module->arena, semantic_argument_count); - for (u32 i = 0; i < semantic_argument_count; i += 1) + Slice arguments = arena_allocate(module->arena, semantic_argument_types.length); + for (u32 i = 0; i < semantic_argument_types.length; i += 1) { Argument* argument = &arguments[i]; - auto name = semantic_argument_name_buffer[i]; - auto* type = semantic_argument_type_buffer[i]; - auto line = argument_line_buffer[i]; + auto header_argument = function_header.arguments[i]; + auto name = header_argument.name; + auto* type = semantic_argument_types[i]; + auto line = header_argument.line; *argument = { .variable = { .storage = 0, .initial_value = 0, .type = type, - .scope = &storage->function.scope, + .scope = &global->variable.storage->function.scope, .name = name, .line = line, .column = 0, @@ -3345,7 +3580,7 @@ void parse(Module* module) }; } - storage->function = { + global->variable.storage->function = { .arguments = arguments, .scope = { .parent = scope, @@ -3356,9 +3591,9 @@ void parse(Module* module) .block = 0, .attributes = function_attributes, }; - storage->id = ValueId::function; + global->variable.storage->id = ValueId::function; - storage->function.block = parse_block(module, &storage->function.scope); + global->variable.storage->function.block = parse_block(module, &global->variable.storage->function.scope); module->current_function = 0; } } break; @@ -3528,9 +3763,9 @@ void parse(Module* module) skip_space(module); Type* struct_type; - if (forward_declaration) + if (type_forward_declaration) { - struct_type = forward_declaration; + struct_type = type_forward_declaration; } else { @@ -3642,9 +3877,9 @@ void parse(Module* module) expect_character(module, left_brace); Type* union_type; - if (forward_declaration) + if (type_forward_declaration) { - union_type = forward_declaration; + union_type = type_forward_declaration; } else { diff --git a/tests/bool_pair.bbb b/tests/bool_pair.bbb new file mode 100644 index 0000000..13eed46 --- /dev/null +++ b/tests/bool_pair.bbb @@ -0,0 +1,18 @@ +BoolPair = struct +{ + a: u1, + b: u1, +} + +bool_pair = fn () BoolPair +{ + return { .a = 0, .b = 1 }; +} + +[export] main = fn [cc(c)] () s32 +{ + >result = bool_pair(); + if (result.a) #trap(); + if (!result.b) #trap(); + return 0; +} diff --git a/tests/enum_debug_info.bbb b/tests/enum_debug_info.bbb new file mode 100644 index 0000000..9fb82ec --- /dev/null +++ b/tests/enum_debug_info.bbb @@ -0,0 +1,38 @@ +TypeId = enum +{ + void, + noreturn, + forward_declaration, + integer, + function, + pointer, + array, + enum, + struct, + bits, + alias, + union, + unresolved, + vector, + floating_point, + enum_array, + opaque, +} + +Type = struct +{ + arr: [5]u32, + id: TypeId, + a: [2]u64, + b: u64, +} + +[export] main = fn [cc(c)] () s32 +{ + >t: Type = { + .id = .integer, + zero, + }; + t.arr[0] = 1; + return 0; +} diff --git a/tests/min_max.bbb b/tests/min_max.bbb new file mode 100644 index 0000000..14b3c12 --- /dev/null +++ b/tests/min_max.bbb @@ -0,0 +1,10 @@ +[export] main = fn [cc(c)] () s32 +{ + >a: u32 = 1; + >b: u32 = 2; + >min = #min(a, b); + >max = #max(a, b); + if (min != a) #trap(); + if (max != b) #trap(); + return 0; +} diff --git a/tests/return_array.bbb b/tests/return_array.bbb new file mode 100644 index 0000000..a9674db --- /dev/null +++ b/tests/return_array.bbb @@ -0,0 +1,22 @@ +SomeEnum = enum +{ + a, + b, + c, + d, + e, + f, +} + +foo = fn () [2]SomeEnum +{ + return [ .f, .e ]; +} + +[export] main = fn [cc(c)] () s32 +{ + >result = foo(); + if (result[0] != .f) #trap(); + if (result[1] != .e) #trap(); + return 0; +}