diff --git a/src/compiler.bbb b/src/compiler.bbb index 97c841d..e2ac186 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 @@ -539,8 +552,12 @@ LLVMBuilder = opaque; LLVMValue = opaque; LLVMType = opaque; LLVMBasicBlock = opaque; + LLVMIntrinsicId = typealias u32; +LLVMAttributeId = typealias u32; +LLVMAttribute = opaque; + LLVMMetadata = opaque; LLVMDIBuilder = opaque; @@ -551,10 +568,99 @@ LLVMTargetMachineOptions = opaque; LLVMIntrinsicIndex = enum u32 { - trap, - va_start, - va_end, - va_copy, + "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 @@ -658,6 +764,7 @@ Type = struct; Value = struct; Local = struct; Block = struct; +Module = struct; ScopeKind = enum { @@ -746,7 +853,7 @@ TypeId = enum TypeInteger = struct { - bit_count: u32, + bit_count: u64, signed: u1, } @@ -767,18 +874,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, @@ -786,6 +1056,13 @@ TypeFunction = struct return_abi: AbiInformation, } +TypeFunction = struct +{ + base: TypeFunctionBase, + abi: TypeFunctionAbi, + next: &Type, +} + TypeContent = union { integer: TypeInteger, @@ -793,6 +1070,13 @@ TypeContent = union pointer: TypePointer, } +TypeLLVM = struct +{ + abi: &LLVMType, + memory: &LLVMType, + debug: &LLVMMetadata, +} + Type = struct { content: TypeContent, @@ -800,20 +1084,160 @@ Type = struct name: []u8, next: &Type, scope: &Scope, + llvm: TypeLLVM, +} + +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 @@ -821,6 +1245,7 @@ ValueFunction = struct arguments: []Argument, scope: Scope, block: &Block, + llvm: ValueFunctionLLVM, attributes: FunctionAttributes, } @@ -842,6 +1267,7 @@ Value = struct type: &Type, id: ValueId, kind: ValueKind, + llvm: &LLVMValue, } i128_offset: u64 = 64 * 2; @@ -980,19 +1406,144 @@ LLVMDwarfEmissionKind = enum u32 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, +} + [extern] LLVMContextCreate = fn [cc(c)] () &LLVMContext; [extern] llvm_context_create_module = fn (context: &LLVMContext, name: []u8) &LLVMModule; [extern] LLVMCreateBuilderInContext = fn (context: &LLVMContext) &LLVMBuilder; [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] 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] 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] 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] 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] LLVMBuildBr = fn [cc(c)] (builder: &LLVMBuilder, target_block: &LLVMBasicBlock) &LLVMValue; + [extern] LLVMCreateTargetMachineOptions = fn () &LLVMTargetMachineOptions; [extern] LLVMTargetMachineOptionsSetCPU = fn (target_machine_options: &LLVMTargetMachineOptions, cpu: &u8) void; [extern] LLVMTargetMachineOptionsSetFeatures = fn (target_machine_options: &LLVMTargetMachineOptions, features: &u8) void; @@ -1003,6 +1554,9 @@ LLVMDwarfEmissionKind = enum u32 [extern] LLVMSetModuleDataLayout = fn (module: &LLVMModule, target_data_layout: &LLVMTargetDataLayout) void; [extern] LLVMSetTarget = fn (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; + default_address_space: u32 = 0; ModuleLLVM = struct @@ -1017,6 +1571,7 @@ ModuleLLVM = struct 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, @@ -1223,7 +1778,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); @@ -1231,6 +1786,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; @@ -1685,7 +2245,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 @@ -2086,7 +2646,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, @@ -2566,7 +3126,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)) { @@ -2682,10 +3242,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, }, }, @@ -2697,7 +3259,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, @@ -2784,6 +3346,881 @@ parse = fn (module: &Module) void } } +resolve_alias = fn (module: &Module, type: &Type) &Type +{ + >result: &Type = zero; + + switch (type.id) + { + .void, + .integer, + => + { + result = 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)); + }, + 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, + => + { + 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); + }, + 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; +} + +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; +} + +TypeKind = enum +{ + abi, + memory, +} + +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 => + { + #trap(); + }, + .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); + } +} + emit = fn (module: &Module) void { assert(!module.current_function); @@ -2889,10 +4326,338 @@ emit = fn (module: &Module) void >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); + + module.current_function = zero; + } + + global = global.next; + } + + if (module.has_debug_info) + { + LLVMDIBuilderFinalize(module.llvm.di_builder); + } } compile = fn (arena: &Arena, options: CompileOptions) void { + >module: Module = undefined; >signs: [2]u1 = [0, 1]; >base_type_allocation = arena_allocate[Type](arena, @@ -2919,12 +4684,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, }; @@ -2951,6 +4717,7 @@ compile = fn (arena: &Arena, options: CompileOptions) void }, .id = .integer, .name = name, + .scope = &module.scope, zero, }; @@ -2971,12 +4738,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, }; @@ -2987,7 +4756,7 @@ compile = fn (arena: &Arena, options: CompileOptions) void zero, }; - >module: Module = { + module = { .arena = arena, .content = options.content, .void_value = void_value, diff --git a/src/compiler.cpp b/src/compiler.cpp index 0df0659..23dd87c 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -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 ff9ae29..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; @@ -618,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(); } } @@ -629,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(); } } @@ -793,7 +812,7 @@ struct Block enum class ValueId { infer_or_ignore, - external_function, + forward_declared_function, function, constant_integer, unary, @@ -905,6 +924,8 @@ enum class BinaryId logical_or, logical_and_shortcircuit, logical_or_shortcircuit, + max, + min, }; struct ValueBinary @@ -1078,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: { @@ -1164,7 +1186,11 @@ struct LLVMIntrinsicId enum class IntrinsicIndex { + smax, + smin, trap, + umax, + umin, va_start, va_end, va_copy, @@ -1172,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"), @@ -1180,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; @@ -1193,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; @@ -1213,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; @@ -1866,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 464114c..e5a1318 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -153,6 +153,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(); @@ -252,97 +253,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(); @@ -453,6 +363,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 @@ -667,31 +585,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: @@ -738,10 +650,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 }); @@ -1103,7 +1023,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: @@ -1119,7 +1039,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: { @@ -1136,7 +1059,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; } @@ -1212,6 +1135,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(); } @@ -1762,7 +1718,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]; @@ -1981,197 +1937,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) @@ -2242,6 +2215,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: @@ -2278,8 +2253,8 @@ enum class IndexType struct TypeAnalysis { + Type* indexing_type; bool must_be_constant; - bool is_index; }; fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnalysis analysis); @@ -2692,7 +2667,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal { if (!expected_type) { - if (analysis.is_index) + if (analysis.indexing_type) { expected_type = uint64(module); } @@ -3113,7 +3088,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal 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; @@ -3130,9 +3105,9 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal 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) { @@ -3161,7 +3136,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal 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; @@ -3268,7 +3243,9 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal } auto pointer_element_type = array_like_type->pointer.element_type; - analyze_type(module, value->array_expression.index, 0, { .must_be_constant = analysis.must_be_constant, .is_index = true }); + 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) @@ -3311,6 +3288,11 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal } break; case ValueId::enum_literal: { + if (!expected_type) + { + expected_type = analysis.indexing_type; + } + if (!expected_type) { report_error(); @@ -3572,12 +3554,31 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal // 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; @@ -3637,7 +3638,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal } 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; @@ -3704,7 +3705,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal 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]; @@ -3725,8 +3726,8 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal 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; @@ -4379,10 +4380,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) @@ -4418,7 +4429,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 { @@ -4749,6 +4765,42 @@ fn SliceEmitResult emit_string_literal(Module* module, Value* value) } } +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) @@ -4769,24 +4821,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; @@ -4810,7 +4864,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) { @@ -4856,9 +4910,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; @@ -4871,7 +4925,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 @@ -4895,7 +4949,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; @@ -4972,17 +5026,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, {}); + 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); @@ -5083,6 +5147,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(); } } @@ -5124,9 +5198,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, {}); + reanalyze_type_as_left_value(module, src); } } else @@ -5180,7 +5252,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); @@ -5222,9 +5294,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; @@ -5264,9 +5335,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); } @@ -5278,16 +5349,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: @@ -5342,19 +5415,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); @@ -6229,6 +6325,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) { @@ -7206,156 +7331,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: { @@ -7504,7 +7657,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) { @@ -7543,7 +7696,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 } 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); } @@ -8247,30 +8400,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, {}); - 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); @@ -8565,12 +8727,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]; @@ -8579,15 +8746,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; @@ -8598,7 +8765,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: @@ -8608,14 +8775,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; @@ -8626,12 +8793,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, @@ -8642,7 +8809,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: { @@ -8651,31 +8818,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 = {}; @@ -8696,22 +8863,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; @@ -8727,398 +8894,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: @@ -9174,6 +8964,410 @@ 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"), 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->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); + llvm_basic_block_delete(return_block); + } + else + { + emit_block(module, return_block); + } + } + else + { + bool is_reachable = false; + + if (llvm_value_has_one_use((LLVMValueRef)return_block)) + { + auto user = llvm_basic_block_user_begin(return_block); + is_reachable = LLVMIsABranchInst(user) && !LLVMIsConditional(user) && LLVMGetSuccessor(user, 0) == return_block; + if (is_reachable) + { + LLVMPositionBuilderAtEnd(module->llvm.builder, LLVMGetInstructionParent(user)); + LLVMInstructionEraseFromParent(user); + llvm_basic_block_delete(return_block); + } + } + + if (!is_reachable) + { + emit_block(module, return_block); + } + } + + if (module->has_debug_info) + { + LLVMSetCurrentDebugLocation2(module->llvm.builder, 0); + auto subprogram = LLVMGetSubprogram(llvm_function); + LLVMDIBuilderFinalizeSubprogram(module->llvm.di_builder, subprogram); + } + + if (function_type->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/parser.cpp b/src/parser.cpp index 73606fb..19a736e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -10,6 +10,8 @@ enum class ValueIntrinsic integer_max, int_from_enum, int_from_pointer, + max, + min, pointer_cast, pointer_from_int, select, @@ -451,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]; @@ -479,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'); @@ -668,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(); @@ -862,6 +1146,8 @@ fn Token tokenize(Module* module) 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"), @@ -1241,6 +1527,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); @@ -1530,6 +1817,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; @@ -1540,7 +1858,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); } @@ -2823,16 +3160,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; } @@ -2840,14 +3184,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 @@ -2924,6 +3268,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: @@ -3141,206 +3491,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, @@ -3349,7 +3566,7 @@ void parse(Module* module) }; } - storage->function = { + global->variable.storage->function = { .arguments = arguments, .scope = { .parent = scope, @@ -3360,9 +3577,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; @@ -3532,9 +3749,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 { @@ -3646,9 +3863,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; +}