From 0a506b2e01b6172cb1c0790b052d44f7ce2c214f Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Sun, 8 Jun 2025 09:07:52 -0600 Subject: [PATCH] Pass some basic tests --- CMakeLists.txt | 2 +- src/compiler.bbb | 5225 +++++++++++++++++++++++++++-- src/compiler.cpp | 4 +- src/compiler.hpp | 14 +- src/emitter.cpp | 1014 +++--- src/llvm.cpp | 209 +- src/llvm.hpp | 21 - src/parser.cpp | 164 +- tests/leading_trailing_zeroes.bbb | 10 + tests/pointer_sub.bbb | 9 + 10 files changed, 5628 insertions(+), 1044 deletions(-) create mode 100644 tests/leading_trailing_zeroes.bbb create mode 100644 tests/pointer_sub.bbb diff --git a/CMakeLists.txt b/CMakeLists.txt index 708b224..ae44d6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,6 @@ target_link_libraries(llvm_bindings PUBLIC target_link_libraries(bb PUBLIC llvm_bindings) -add_compile_options(-Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -Wno-missing-designated-field-initializers -funsigned-char -fwrapv -fno-strict-aliasing) +add_compile_options(-Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -Wno-missing-designated-field-initializers -fno-signed-char -fwrapv -fno-strict-aliasing) add_compile_definitions(CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}") add_compile_definitions(BB_CI=${BB_CI}) diff --git a/src/compiler.bbb b/src/compiler.bbb index b7e6b00..f333dee 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -502,6 +502,7 @@ arena_duplicate_string = fn (arena: &Arena, string: []u8) []u8 { >result = arena_allocate_bytes(arena, string.length + 1, 1); memcpy(result, string.pointer, string.length); + result[string.length] = 0; return result[..string.length]; } @@ -677,7 +678,10 @@ os_execute = fn (arena: &Arena, arguments: []&u8, environment: &&u8, options: Ex >is_pipe1 = options.policies[1] == .pipe; if (is_pipe0 or is_pipe1) { - allocation = arena_allocate_slice[u8](arena, allocation_size * (#extend(is_pipe0) + #extend(is_pipe1))); + >element_count: u64 = 0; + element_count += #extend(is_pipe0); + element_count += #extend(is_pipe1); + allocation = arena_allocate_slice[u8](arena, allocation_size * element_count); } >offset: u64 = 0; @@ -847,7 +851,8 @@ LLVMAttributeIndex = enum u32 LLVMLinkage = enum u32 { - external, // Externally visible function available_externally, + 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 @@ -866,7 +871,23 @@ LLVMLinkage = enum u32 linker_private_weak // Like LinkerPrivate, but is weak. */ } -LLVMCallingConvention = enum +LLVMThreadLocalMode = enum u32 +{ + none = 0, + general_dynamic = 1, + local_dynamic = 2, + initial_exec = 3, + local_exec = 4, +} + +LLVMUnnamedAddress = enum u32 +{ + none = 0, + local = 1, + global = 2, +} + +LLVMCallingConvention = enum u32 { c = 0, fast = 8, @@ -911,6 +932,13 @@ LLVMCallingConvention = enum amdgpu_es = 96 } +LLVMVerifyFailureAction = enum u32 +{ + abort, + print, + return, +} + CompilerCommand = enum { compile, @@ -1315,11 +1343,64 @@ TypeFunction = struct next: &Type, } +TypeArray = struct +{ + element_type: &Type, + element_count: u64, + next: &Type, +} + +TypeEnumArray = struct +{ + enum_type: &Type, + element_type: &Type, + next: &Type, +} + +EnumField = struct +{ + name: []u8, + value: u64, +} + +TypeEnum = struct +{ + fields: []EnumField, + backing_type: &Type, + enum_to_string_function: &LLVMValue, + string_to_enum_function: &LLVMValue, + string_to_enum_struct_type: &Type, + name_array: &Global, + line: u32, +} + +Field = struct +{ + name: []u8, + type: &Type, + offset: u64, // Could either be bytes or bits, depending on the aggregate type + line: u32, +} + +TypeStruct = struct +{ + fields: []Field, + byte_size: u64, + byte_alignment: u32, + line: u32, + is_slice: u1, + next: &Type, +} + TypeContent = union { integer: TypeInteger, function: TypeFunction, pointer: TypePointer, + array: TypeArray, + enum_array: TypeEnumArray, + enum: TypeEnum, + struct: TypeStruct, } TypeLLVM = struct @@ -1347,6 +1428,10 @@ type_is_signed = fn (type: &Type) u1 { return type.content.integer.signed; }, + .enum => + { + return type_is_signed(type.content.enum.backing_type); + }, else => { #trap(); @@ -1388,6 +1473,55 @@ get_evaluation_kind = fn (type: &Type) EvaluationKind } } +type_is_aggregate_type_for_abi = fn (type: &Type) u1 +{ + >evaluation_kind = get_evaluation_kind(type); + >is_member_function_pointer_type: u1 = 0; // TODO + return evaluation_kind != .scalar or is_member_function_pointer_type; +} + +is_illegal_vector_type = fn (type: &Type) u1 +{ + switch (type.id) + { + .vector => + { + #trap(); + } + else => { return 0; }, + } +} + +is_arbitrary_bit_integer = fn (type: &Type) u1 +{ + switch (type.id) + { + .integer => + { + >bit_count = type.content.integer.bit_count; + + switch (bit_count) + { + 8, 16, 32, 64, 128 => { return 0; }, + else => { return 1; }, + } + }, + .unresolved => { unreachable; }, + .bits => + { + #trap(); + }, + .enum => + { + #trap(); + }, + else => + { + return 0; + }, + } +} + integer_max_value = fn (bit_count: u64, signed: u1) u64 { >value: u64 = #select(bit_count == 64, ~0, (1 << (bit_count - #extend(signed))) - 1); @@ -1417,20 +1551,27 @@ get_byte_size = fn (type: &Type) u64 assert(byte_count == 1 or byte_count == 2 or byte_count == 4 or byte_count == 8 or byte_count == 16); return byte_count; }, - else => + .pointer => { - #trap(); + return 8; }, - } -} - -get_bit_size = fn (type: &Type) u64 -{ - switch (type.id) - { - .integer => + .array => { - return type.content.integer.bit_count; + >element_type = type.content.array.element_type; + >element_count = type.content.array.element_count; + assert(element_count != 0); + >element_size = get_byte_size(element_type); + >result = element_size * element_count; + return result; + }, + .enum => + { + >byte_size = get_byte_size(type.content.enum.backing_type); + return byte_size; + }, + .struct => + { + return type.content.struct.byte_size; }, else => { @@ -1449,6 +1590,22 @@ get_byte_alignment = fn (type: &Type) u32 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; }, + .pointer => { return 8; }, + .array => + { + >element_type = type.content.array.element_type; + return get_byte_alignment(element_type); + }, + .enum => + { + >backing_type = type.content.enum.backing_type; + return get_byte_alignment(backing_type); + }, + .struct => + { + >alignment = type.content.struct.byte_alignment; + return alignment; + }, else => { #trap(); @@ -1456,6 +1613,46 @@ get_byte_alignment = fn (type: &Type) u32 } } +get_bit_size = fn (type: &Type) u64 +{ + switch (type.id) + { + .integer => + { + return type.content.integer.bit_count; + }, + .array => + { + return get_bit_size(type.content.array.element_type); + }, + .enum => + { + return get_bit_size(type.content.enum.backing_type); + }, + .pointer => + { + return 64; + }, + .struct => + { + >byte_size = get_byte_size(type); + return byte_size * 8; + }, + else => + { + #trap(); + }, + } +} + +get_byte_allocation_size = fn (type: &Type) u64 +{ + >size = get_byte_size(type); + >alignment = get_byte_alignment(type); + >result = align_forward(size, #extend(alignment)); + return result; +} + is_integral_or_enumeration_type = fn (type: &Type) u1 { switch (type.id) @@ -1514,6 +1711,20 @@ ValueId = enum variable, local, unary_type, + macro_reference, + call, + array_initialization, + array_expression, + slice_expression, + enum_literal, + trap, + va_start, + has_debug_info, + field_access, + argument, + aggregate_initialization, + zero, + va_arg, } ValueConstantInteger = struct @@ -1673,6 +1884,44 @@ ValueBinary = struct id: BinaryId, } +ValueCall = struct +{ + callable: &Value, + arguments: []&Value, + function_type: &Type, +} + +ValueArrayInitialization = struct +{ + values: []&Value, + is_constant: u1, +} + +ValueArrayExpression = struct +{ + array_like: &Value, + index: &Value, +} + +ValueSliceExpression = struct +{ + array_like: &Value, + start: &Value, + end: &Value, +} + +ValueFieldAccess = struct +{ + aggregate: &Value, + field_name: []u8, +} + +ValueVaArg = struct +{ + va_list: &Value, + type: &Type, +} + ValueContent = union { constant_integer: ValueConstantInteger, @@ -1681,6 +1930,14 @@ ValueContent = union binary: ValueBinary, variable: &Variable, unary_type: ValueUnaryType, + call: ValueCall, + array_initialization: ValueArrayInitialization, + array_expression: ValueArrayExpression, + slice_expression: ValueSliceExpression, + enum_literal: []u8, + field_access: ValueFieldAccess, + string_literal: []u8, + va_arg: ValueVaArg, } ValueKind = enum @@ -1707,6 +1964,15 @@ value_is_constant = fn (value: &Value) u1 return 1; }, .unary => + { + return value_is_constant(value.content.unary.value); + }, + .binary => + { + return value_is_constant(value.content.binary.left) and value_is_constant(value.content.binary.right); + }, + .field_access, + => { return 0; }, @@ -1715,6 +1981,14 @@ value_is_constant = fn (value: &Value) u1 >variable = value.content.variable; return value.kind == .left and variable.scope.kind == .global; }, + .array_expression => + { + return 0; + }, + .slice_expression => + { + return 0; + }, else => { #trap(); @@ -1742,13 +2016,13 @@ MacroInstantiation = struct [extern] LLVMInitializeX86AsmParser = fn [cc(c)] () void; [extern] LLVMInitializeX86Disassembler = fn [cc(c)] () void; -[extern] llvm_default_target_triple = fn [cc(c)] () []u8; -[extern] llvm_host_cpu_name = fn [cc(c)] () []u8; -[extern] llvm_host_cpu_features = fn [cc(c)] () []u8; +[extern] LLVMGetDefaultTargetTriple = fn [cc(c)] () &u8; +[extern] LLVMGetHostCPUName = fn [cc(c)] () &u8; +[extern] LLVMGetHostCPUFeatures = fn [cc(c)] () &u8; -host_target_triple: []u8 = zero; -host_cpu_model: []u8 = zero; -host_cpu_features: []u8 = zero; +host_target_triple: &u8 = zero; +host_cpu_model: &u8 = zero; +host_cpu_features: &u8 = zero; llvm_targets_initialized: u1 = 0; @@ -1763,9 +2037,9 @@ llvm_initialize_targets = fn () void LLVMInitializeX86AsmParser(); LLVMInitializeX86Disassembler(); - host_target_triple = llvm_default_target_triple(); - host_cpu_model = llvm_host_cpu_name(); - host_cpu_features = llvm_host_cpu_features(); + host_target_triple = LLVMGetDefaultTargetTriple(); + host_cpu_model = LLVMGetHostCPUName(); + host_cpu_features = LLVMGetHostCPUFeatures(); llvm_targets_initialized = 1; } @@ -2017,13 +2291,19 @@ LLVMICmpPredicate = enum u32 } [extern] LLVMContextCreate = fn [cc(c)] () &LLVMContext; -[extern] llvm_context_create_module = fn (context: &LLVMContext, name: []u8) &LLVMModule; +[extern] LLVMModuleCreateWithNameInContext = fn (name: &u8, context: &LLVMContext) &LLVMModule; [extern] LLVMCreateBuilderInContext = fn (context: &LLVMContext) &LLVMBuilder; +[extern] LLVMTypeOf = fn (value: &LLVMValue) &LLVMType; + [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] LLVMArrayType2 = fn [cc(c)] (element_type: &LLVMType, element_count: u64) &LLVMType; +[extern] LLVMStructTypeInContext = fn [cc(c)] (context: &LLVMContext, type_pointer: &&LLVMType, type_count: u32, is_packed: s32) &LLVMType; + +[extern] LLVMStoreSizeOfType = fn [cc(c)] (target_data_layout: &LLVMTargetDataLayout, type: &LLVMType) u64; [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; @@ -2032,6 +2312,12 @@ LLVMICmpPredicate = enum u32 [extern] LLVMDIBuilderCreateBasicType = fn [cc(c)] (di_builder: &LLVMDIBuilder, name_pointer: &u8, name_length: u64, bit_size: u64, dwarf_encoding: LLVMDwarfTypeEncoding, flags: LLVMDIFlags) &LLVMMetadata; [extern] LLVMDIBuilderCreatePointerType = fn [cc(c)] (di_builder: &LLVMDIBuilder, element_type: &LLVMMetadata, bit_size: u64, bit_alignment: u32, address_space: u32, name_pointer: &u8, name_length: u64) &LLVMMetadata; [extern] LLVMDIBuilderCreateSubroutineType = fn [cc(c)] (di_builder: &LLVMDIBuilder, file: &LLVMMetadata, argument_type_pointer: &&LLVMMetadata, argument_type_count: u32, flags: LLVMDIFlags) &LLVMMetadata; +[extern] LLVMDIBuilderCreateArrayType = fn [cc(c)] (di_builder: &LLVMDIBuilder, bit_size: u64, bit_alignment: u32, element_type: &LLVMMetadata, subscript_pointer: &LLVMMetadata, subscript_count: u32) &LLVMMetadata; +[extern] LLVMDIBuilderCreateEnumerator = fn [cc(c)] (di_builder: &LLVMDIBuilder, name_pointer: &u8, name_length: u64, value: u64, sign_extend: s32) &LLVMMetadata; +[extern] LLVMDIBuilderCreateEnumerationType = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, bit_size: u64, bit_alignment: u32, field_pointer: &&LLVMMetadata, field_count: u64, backing_type: &LLVMMetadata) &LLVMMetadata; +[extern] LLVMDIBuilderCreateReplaceableCompositeType = fn [cc(c)] (di_builder: &LLVMDIBuilder, tag: u32, name_pointer: &u8, name_length: u64, scope: &LLVMMetadata, file: &LLVMMetadata, line: u32, runtime_language: u32, bit_size: u64, bit_alignment: u32, flags: LLVMDIFlags, unique_identifier_pointer: &u8, unique_identifier_length: u64) &LLVMMetadata; +[extern] LLVMDIBuilderCreateMemberType = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, bit_size: u64, bit_alignment: u32, bit_offset: u64, flags: LLVMDIFlags, type: &LLVMMetadata) &LLVMMetadata; +[extern] LLVMDIBuilderCreateStructType = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, bit_size: u64, bit_alignment: u32, flags: LLVMDIFlags, derived_from: &LLVMMetadata, element_pointer: &&LLVMMetadata, element_count: u32, runtime_language: u32, vtable_holder: &LLVMMetadata, unique_identifier_pointer: &u8, unique_identifier_length: u64) &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; @@ -2039,6 +2325,7 @@ LLVMICmpPredicate = enum u32 [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] LLVMDIBuilderCreateAutoVariable = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, type: &LLVMMetadata, always_preserve: s32, flags: LLVMDIFlags, bit_alignment: u32) &LLVMMetadata; +[extern] LLVMDIBuilderCreateParameterVariable = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, argument_index: u32, file: &LLVMMetadata, line: u32, type: &LLVMMetadata, always_preserve: s32, flags: LLVMDIFlags) &LLVMMetadata; [extern] LLVMDIBuilderInsertDeclareRecordAtEnd = fn [cc(c)] (di_builder: &LLVMDIBuilder, storage: &LLVMValue, local_variable: &LLVMMetadata, expression: &LLVMMetadata, debug_location: &LLVMMetadata, basic_block: &LLVMBasicBlock) &LLVMDebugRecord; [extern] LLVMDIBuilderCreateExpression = fn [cc(c)] (di_builder: &LLVMDIBuilder, address: &u64, length: u64) &LLVMMetadata; @@ -2048,6 +2335,8 @@ LLVMICmpPredicate = enum u32 [extern] LLVMGetSubprogram = fn [cc(c)] (function: &LLVMValue) &LLVMMetadata; [extern] LLVMLookupIntrinsicID = fn [cc(c)] (name_pointer: &u8, name_length: u64) LLVMIntrinsicId; +[extern] LLVMGetIntrinsicDeclaration = fn [cc(c)] (module: &LLVMModule, intrinsic_id: LLVMIntrinsicId, argument_type_pointer: &&LLVMType, argument_type_count: u64) &LLVMValue; +[extern] LLVMIntrinsicGetType = fn [cc(c)] (context: &LLVMContext, intrinsic_id: LLVMIntrinsicId, argument_type_pointer: &&LLVMType, argument_type_count: u64) &LLVMType; [extern] LLVMGetEnumAttributeKindForName = fn [cc(c)] (name_pointer: &u8, name_length: u64) LLVMAttributeId; @@ -2055,28 +2344,80 @@ LLVMICmpPredicate = enum u32 [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] LLVMAddCallSiteAttribute = 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] LLVMAppendBasicBlockInContext = fn [cc(c)] (context: &LLVMContext, function: &LLVMValue, name: &u8) &LLVMBasicBlock; + [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] llvm_builder_create_alloca = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, address_space: u32, alignment: u32, name: []u8) &LLVMValue; +[extern] LLVMSetAlignment = fn [cc(c)] (value: &LLVMValue, alignment: u32) void; +[extern] LLVMBuildAlloca = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, name: &u8) &LLVMValue; + +llvm_create_alloca = fn (builder: &LLVMBuilder, type: &LLVMType, alignment: u32, name: []u8) &LLVMValue +{ + assert(name.pointer[name.length] == 0); + >alloca = LLVMBuildAlloca(builder, type, name.pointer); + LLVMSetAlignment(alloca, alignment); + return alloca; +} + +[extern] LLVMAddGlobal = fn [cc(c)] (module: &LLVMModule, type: &LLVMType, name: &u8) &LLVMValue; +[extern] LLVMSetGlobalConstant = fn [cc(c)] (global: &LLVMValue, is_constant: s32) void; +[extern] LLVMSetLinkage = fn [cc(c)] (value: &LLVMValue, linkage_type: LLVMLinkage) void; +[extern] LLVMSetInitializer = fn [cc(c)] (global: &LLVMValue, value: &LLVMValue) void; +[extern] LLVMSetThreadLocalMode = fn [cc(c)] (global: &LLVMValue, thread_local_mode: LLVMThreadLocalMode) void; +[extern] LLVMSetExternallyInitialized = fn [cc(c)] (global: &LLVMValue, externally_initialized: s32) void; +[extern] LLVMSetUnnamedAddress = fn [cc(c)] (global: &LLVMValue, unnamed_address: LLVMUnnamedAddress) void; + +llvm_create_global_variable = fn (module: &LLVMModule, type: &LLVMType, is_constant: u1, linkage: LLVMLinkage, initial_value: &LLVMValue, name: []u8, thread_local_mode: LLVMThreadLocalMode, externally_initialized: u1, alignment: u32, unnamed_address: LLVMUnnamedAddress) &LLVMValue +{ + assert(name.pointer[name.length] == 0); + >global = LLVMAddGlobal(module, type, name.pointer); + + LLVMSetGlobalConstant(global, #extend(is_constant)); + LLVMSetLinkage(global, linkage); + LLVMSetInitializer(global, initial_value); + LLVMSetThreadLocalMode(global, thread_local_mode); + LLVMSetExternallyInitialized(global, #extend(externally_initialized)); + LLVMSetUnnamedAddress(global, unnamed_address); + LLVMSetAlignment(global, alignment); + + return global; +} + [extern] LLVMBuildStore = fn [cc(c)] (builder: &LLVMBuilder, source: &LLVMValue, destination: &LLVMValue) &LLVMValue; [extern] LLVMBuildLoad2 = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, pointer: &LLVMValue, name: &u8) &LLVMValue; +[extern] LLVMBuildGEP2 = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, pointer: &LLVMValue, index_pointer: &&LLVMValue, index_length: u32, name: &u8) &LLVMValue; +[extern] LLVMBuildInBoundsGEP2 = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, pointer: &LLVMValue, index_pointer: &&LLVMValue, index_length: u32, name: &u8) &LLVMValue; +[extern] LLVMBuildStructGEP2 = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, pointer: &LLVMValue, index: u32, name: &u8) &LLVMValue; + +[extern] LLVMBuildInsertValue = fn [cc(c)] (builder: &LLVMBuilder, aggregate: &LLVMValue, element: &LLVMValue, index: u32, name: &u8) &LLVMValue; +[extern] LLVMBuildExtractValue = fn [cc(c)] (builder: &LLVMBuilder, aggregate: &LLVMValue, index: u32, name: &u8) &LLVMValue; + [extern] LLVMBuildRet = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue) &LLVMValue; [extern] LLVMBuildRetVoid = fn [cc(c)] (builder: &LLVMBuilder) &LLVMValue; -[extern] LLVMBuildBr = fn [cc(c)] (builder: &LLVMBuilder, target_block: &LLVMBasicBlock) &LLVMValue; +[extern] LLVMBuildUnreachable = fn [cc(c)] (builder: &LLVMBuilder) &LLVMValue; +[extern] LLVMBuildBr = fn [cc(c)] (builder: &LLVMBuilder, target_block: &LLVMBasicBlock) &LLVMValue; +[extern] LLVMBuildCondBr = fn [cc(c)] (builder: &LLVMBuilder, condition: &LLVMValue, taken_block: &LLVMBasicBlock, not_taken_block: &LLVMBasicBlock) &LLVMValue; + +[extern] LLVMBuildPhi = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, name: &u8) &LLVMValue; +[extern] LLVMAddIncoming = fn [cc(c)] (phi: &LLVMValue, values: &&LLVMValue, blocks: &&LLVMBasicBlock, count: u32) void; + +[extern] LLVMBuildIntToPtr = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, name: &u8) &LLVMValue; +[extern] LLVMBuildPtrToInt = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, name: &u8) &LLVMValue; [extern] LLVMBuildIntCast2 = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, signed: s32, name: &u8) &LLVMValue; [extern] LLVMBuildNeg = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, name: &u8) &LLVMValue; [extern] LLVMBuildSExt = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, name: &u8) &LLVMValue; [extern] LLVMBuildZExt = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, name: &u8) &LLVMValue; [extern] LLVMBuildTrunc = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, name: &u8) &LLVMValue; +[extern] LLVMBuildNot = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, name: &u8) &LLVMValue; [extern] LLVMBuildICmp = fn [cc(c)] (builder: &LLVMBuilder, predicate: LLVMICmpPredicate, left: &LLVMValue, right: &LLVMValue, name: &u8) &LLVMValue; @@ -2094,28 +2435,48 @@ LLVMICmpPredicate = enum u32 [extern] LLVMBuildSRem = fn [cc(c)] (builder: &LLVMBuilder, left: &LLVMValue, right: &LLVMValue, name: &u8) &LLVMValue; [extern] LLVMBuildURem = fn [cc(c)] (builder: &LLVMBuilder, left: &LLVMValue, right: &LLVMValue, name: &u8) &LLVMValue; +[extern] LLVMBuildCall2 = fn [cc(c)] (builder: &LLVMBuilder, function_type: &LLVMType, function_value: &LLVMValue, argument_pointer: &&LLVMValue, argument_count: u32, name: &u8) &LLVMValue; + +[extern] LLVMBuildMemCpy = fn [cc(c)] (builder: &LLVMBuilder, destination: &LLVMValue, destination_alignment: u32, source: &LLVMValue, source_alignment: u32, size: &LLVMValue) &LLVMValue; + [extern] LLVMGetInsertBlock = fn [cc(c)] (builder: &LLVMBuilder) &LLVMBasicBlock; [extern] LLVMGetBasicBlockTerminator = fn [cc(c)] (basic_block: &LLVMBasicBlock) &LLVMValue; [extern] LLVMGetBasicBlockParent = fn [cc(c)] (basic_block: &LLVMBasicBlock) &LLVMValue; +[extern] LLVMGetPoison = fn [cc(c)] (type: &LLVMType) &LLVMValue; + +[extern] LLVMConstNull = fn [cc(c)] (type: &LLVMType) &LLVMValue; [extern] LLVMConstInt = fn [cc(c)] (type: &LLVMType, value: u64, is_signed: s32) &LLVMValue; [extern] LLVMConstNeg = fn [cc(c)] (value: &LLVMValue) &LLVMValue; +[extern] LLVMConstArray2 = fn [cc(c)] (element_type: &LLVMType, value_pointer: &&LLVMValue, value_count: u64) &LLVMValue; +[extern] LLVMConstStringInContext2 = fn [cc(c)] (context: &LLVMContext, pointer: &u8, length: u64, dont_null_terminate: s32) &LLVMValue; [extern] LLVMInstructionEraseFromParent = fn [cc(c)] (value: &LLVMValue) void; [extern] LLVMGetOperand = fn [cc(c)] (value: &LLVMValue, index: u32) &LLVMValue; [extern] LLVMGetFirstUse = fn [cc(c)] (value: &LLVMValue) &LLVMUse; [extern] LLVMGetNextUse = fn [cc(c)] (use: &LLVMUse) &LLVMUse; [extern] LLVMGetUser = fn [cc(c)] (use: &LLVMUse) &LLVMValue; +[extern] LLVMGetFirstInstruction = fn [cc(c)] (basic_block: &LLVMBasicBlock) &LLVMValue; + +[extern] LLVMReplaceAllUsesWith = fn [cc(c)] (old_value: &LLVMValue, new_value: &LLVMValue) void; +[extern] LLVMMetadataReplaceAllUsesWith = fn [cc(c)] (old: &LLVMMetadata, new: &LLVMMetadata) void; + +[extern] LLVMDeleteBasicBlock = fn [cc(c)] (basic_block: &LLVMBasicBlock) void; [extern] LLVMIsABranchInst = fn [cc(c)] (value: &LLVMValue) &LLVMValue; [extern] LLVMIsConditional = fn [cc(c)] (value: &LLVMValue) s32; [extern] LLVMGetSuccessor = fn [cc(c)] (value: &LLVMValue, index: u32) &LLVMBasicBlock; -[extern] LLVMSetAlignment = fn [cc(c)] (value: &LLVMValue, alignment: u32) void; - [extern] llvm_find_return_value_dominating_store = fn [cc(c)] (builder: &LLVMBuilder, return_alloca: &LLVMValue, element_type: &LLVMType) &LLVMValue; -[extern] llvm_module_verify = fn [cc(c)] (module: &LLVMModule, error_message: &[]u8) u1; -[extern] llvm_module_to_string = fn [cc(c)] (module: &LLVMModule) []u8; + +[extern] LLVMVerifyModule = fn [cc(c)] (module: &LLVMModule, action: LLVMVerifyFailureAction, message_pointer: &&u8) s32; +[extern] LLVMPrintModuleToString = fn [cc(c)] (module: &LLVMModule) &u8; + +llvm_module_to_string = fn (module: &LLVMModule) []u8 +{ + >module_string = c_string_to_slice(LLVMPrintModuleToString(module)); + return module_string; +} [extern] LLVMCreateTargetMachineOptions = fn [cc(c)] () &LLVMTargetMachineOptions; [extern] LLVMTargetMachineOptionsSetCPU = fn [cc(c)] (target_machine_options: &LLVMTargetMachineOptions, cpu: &u8) void; @@ -2127,8 +2488,18 @@ LLVMICmpPredicate = enum u32 [extern] LLVMSetModuleDataLayout = fn [cc(c)] (module: &LLVMModule, target_data_layout: &LLVMTargetDataLayout) void; [extern] LLVMSetTarget = fn [cc(c)] (module: &LLVMModule, target_triple: &u8) void; -[extern] llvm_module_create_function = fn [cc(c)] (module: &LLVMModule, function_type: &LLVMType, linkage_type: LLVMLinkage, address_space: u32, name: []u8) &LLVMValue; +[extern] LLVMAddFunction = fn [cc(c)] (module: &LLVMModule, name: &u8, function_type: &LLVMType) &LLVMValue; + +llvm_module_create_function = fn (module: &LLVMModule, function_type: &LLVMType, linkage_type: LLVMLinkage, name: []u8) &LLVMValue +{ + assert(name.pointer[name.length] == 0); + >function = LLVMAddFunction(module, name.pointer, function_type); + LLVMSetLinkage(function, linkage_type); + return function; +} + [extern] LLVMSetFunctionCallConv = fn [cc(c)] (function: &LLVMValue, calling_convention: LLVMCallingConvention) void; +[extern] LLVMSetInstructionCallConv = fn [cc(c)] (call: &LLVMValue, calling_convention: LLVMCallingConvention) void; [extern] llvm_module_run_optimization_pipeline = fn [cc(c)] (module: &LLVMModule, target_machine: &LLVMTargetMachine, options: &LLVMOptimizationOptions) void; [extern] llvm_module_run_code_generation_pipeline = fn [cc(c)] (module: &LLVMModule, target_machine: &LLVMTargetMachine, options: &LLVMCodeGenerationOptions) LLVMCodeGenerationResult; @@ -2175,6 +2546,8 @@ Module = struct first_slice_type: &Type, first_pair_struct_type: &Type, first_array_type: &Type, + first_enum_array_type: &Type, + first_function_type: &Type, va_list_type: &Type, @@ -2321,8 +2694,19 @@ Statement = struct scope_to_block = fn (scope: &Scope) &Block { assert(scope.kind == .local); - >result: &Block = #field_parent_pointer(scope, "scope"); - return result; + return #field_parent_pointer(scope, "scope"); +} + +scope_to_function = fn (scope: &Scope) &ValueFunction +{ + assert(scope.kind == .function); + return #field_parent_pointer(scope, "scope"); +} + +scope_to_module = fn (scope: &Scope) &Module +{ + assert(scope.kind == .global); + return #field_parent_pointer(scope, "scope"); } new_local = fn (module: &Module, scope: &Scope) &Local @@ -2420,6 +2804,16 @@ uint1 = fn (module: &Module) &Type return integer_type(module, { .bit_count = 1, .signed = 0 }); } +uint8 = fn (module: &Module) &Type +{ + return integer_type(module, { .bit_count = 8, .signed = 0 }); +} + +uint32 = fn (module: &Module) &Type +{ + return integer_type(module, { .bit_count = 32, .signed = 0 }); +} + uint64 = fn (module: &Module) &Type { return integer_type(module, { .bit_count = 64, .signed = 0 }); @@ -2482,6 +2876,111 @@ get_pointer_type = fn (module: &Module, element_type: &Type) &Type return result; } +left_bracket: u8 = '['; +right_bracket: u8 = ']'; +left_parenthesis: u8 = '('; +right_parenthesis: u8 = ')'; +left_brace: u8 = '{'; +right_brace: u8 = '}'; + +format_integer_decimal = fn (buffer: []u8, v: u64) u64 +{ + >byte_count: u64 = 0; + >value = v; + + if (value != 0) + { + >reverse_buffer: [64]u8 = undefined; + >reverse_index: u64 = 0; + + while (value != 0) + { + >digit_value: u8 = #truncate(value % 10); + >ascii_character = digit_value + '0'; + value /= 10; + reverse_buffer[reverse_index] = ascii_character; + reverse_index += 1; + } + + while (reverse_index != 0) + { + reverse_index -= 1; + buffer[byte_count] = reverse_buffer[reverse_index]; + byte_count += 1; + } + } + else + { + buffer[0] = '0'; + byte_count = 1; + } + + return byte_count; +} + +array_name = fn (module: &Module, element_type: &Type, element_count: u64) []u8 +{ + >buffer: [512]u8 = undefined; + >buffer_slice = buffer[..]; + + >i: u64 = 0; + + buffer[i] = left_bracket; + i += 1; + + i += format_integer_decimal(buffer_slice[i..], element_count); + + buffer[i] = right_bracket; + i += 1; + + >element_name = element_type.name; + memcpy(&buffer[i], element_name.pointer, element_name.length); + i += element_name.length; + + >name = arena_duplicate_string(module.arena, buffer_slice[..i]); + return name; +} + +get_array_type = fn (module: &Module, element_type: &Type, element_count: u64) &Type +{ + assert(element_type != zero); + assert(element_count != 0); + + >array_type = module.first_array_type; + + if (array_type != zero) + { + #trap(); + } + + >last_array_type = array_type; + + >result = new_type(module, { + .content = { + .array = { + .element_type = element_type, + .element_count = element_count, + zero, + }, + }, + .id = .array, + .name = array_name(module, element_type, element_count), + .scope = element_type.scope, + zero, + }); + + if (last_array_type != zero) + { + last_array_type.content.array.next = result; + } + else + { + module.first_array_type = result; + } + + return result; +} + is_space = fn (ch: u8) u1 { return ch == ' ' or ch == '\n' or ch == '\t' or ch == '\r'; @@ -2662,6 +3161,86 @@ parse_identifier = fn (module: &Module) []u8 return result; } +escape_character = fn (ch: u8) u8 +{ + switch (ch) + { + 'n' => { return '\n'; }, + 't' => { return '\t'; }, + 'r' => { return '\r'; }, + '\'' => { return '\''; }, + '\\' => { return '\\'; }, + else => { report_error(); }, + } +} + +parse_string_literal = fn (module: &Module) []u8 +{ + expect_character(module, '"'); + + >start = module.offset; + >escape_character_count: u64 = 0; + + while (1) + { + >ch = module.content[module.offset]; + + if (ch == '"') + { + break; + } + + escape_character_count += #extend(ch == '\\'); + module.offset += 1; + } + + >end = module.offset; + >length = end - start - escape_character_count; + >pointer = arena_allocate_bytes(module.arena, length + 1, 1); + >string_literal = pointer[..length]; + + >source_i = start; + >i: u64 = 0; + + while (source_i < end) + { + >ch = module.content[source_i]; + if (ch == '\\') + { + source_i += 1; + ch = module.content[source_i]; + string_literal[i] = escape_character(ch); + } + else + { + string_literal[i] = ch; + } + + source_i += 1; + i += 1; + } + + expect_character(module, '"'); + + return string_literal; +} + +parse_name = fn (module: &Module) []u8 +{ + >result: []u8 = undefined; + + if (module.content[module.offset] == '"') + { + result = parse_string_literal(module); + } + else + { + result = parse_identifier(module); + } + + return result; +} + new_value = fn (module: &Module) &Value { >value = arena_allocate[Value](module.arena, 1); @@ -2691,13 +3270,6 @@ FunctionTypeAttribute = enum cc, } -left_bracket: u8 = '['; -right_bracket: u8 = ']'; -left_parenthesis: u8 = '('; -right_parenthesis: u8 = ')'; -left_brace: u8 = '{'; -right_brace: u8 = '}'; - accumulate_decimal = fn(accumulator: u64, ch: u8) u64 { assert(is_decimal(ch)); @@ -2798,6 +3370,515 @@ TypeKeyword = enum enum_array, } +type_is_slice = fn (type: &Type) u1 +{ + return type.id == .struct and type.content.struct.is_slice; +} + +get_slice_type = fn (module: &Module, element_type: &Type) &Type +{ + >slice_type = module.first_slice_type; + + while (slice_type) + { + assert(type_is_slice(slice_type)); + assert(slice_type.content.struct.fields.length == 2); + + >pointer_type = slice_type.content.struct.fields[0].type; + assert(pointer_type.id == .pointer); + + >candidate_element_type = pointer_type.content.pointer.element_type; + if (candidate_element_type == element_type) + { + return slice_type; + } + + >next = slice_type.content.struct.next; + if (!next) + { + break; + } + + slice_type = next; + } + + >last_slice_type = slice_type; + >fields = arena_allocate_slice[Field](module.arena, 2); + + fields[0] = { + .name = "pointer", + .type = get_pointer_type(module, element_type), + .offset = 0, + .line = 0, + }; + + fields[1] = { + .name = "length", + .type = uint64(module), + .offset = 8, + .line = 0, + }; + + >slice_name = arena_join_string(module.arena, [ "[]", element_type.name ][..]); + + >result = new_type(module, { + .content = { + .struct = { + .fields = fields, + .byte_size = 16, + .byte_alignment = 8, + .line = 0, + .is_slice = 1, + zero, + }, + }, + .id = .struct, + .name = slice_name, + .scope = element_type.scope, + zero, + }); + + if (last_slice_type) + { + last_slice_type.content.struct.next = result; + } + else + { + module.first_slice_type = result; + } + + return result; +} + +get_anonymous_struct_pair = fn (module: &Module, low: &Type, high: &Type) &Type +{ + >pair = module.first_pair_struct_type; + + while (pair) + { + assert(pair.id == .struct); + assert(pair.content.struct.fields.length == 2); + + if (pair.content.struct.fields[0].type == low and pair.content.struct.fields[1].type == high) + { + return pair; + } + + >next = pair.content.struct.next; + + if (!next) + { + break; + } + + pair = next; + } + + >high_alignment = get_byte_alignment(high); + >alignment = #max(get_byte_alignment(low), high_alignment); + >high_offset = align_forward(get_byte_size(low), #extend(alignment)); + >byte_size = align_forward(high_offset + get_byte_size(high), #extend(alignment)); + + assert(low.scope != zero); + assert(high.scope != zero); + + >scope = #select(low.scope.kind == .global, high.scope, low.scope); + + >fields = arena_allocate_slice[Field](module.arena, 2); + + fields[0] = { + .name = "low", + .type = low, + .offset = 0, + .line = 0, + }; + + fields[1] = { + .name = "high", + .type = high, + .offset = high_offset, + .line = 0, + }; + + >struct_type = new_type(module, { + .content = { + .struct = { + .fields = fields, + .byte_size = byte_size, + .byte_alignment = alignment, + zero, + }, + }, + .id = .struct, + .name = "", + .scope = scope, + zero, + }); + + if (pair) + { + assert(module.first_pair_struct_type != zero); + pair.content.struct.next = struct_type; + } + else + { + assert(!module.first_pair_struct_type); + module.first_pair_struct_type = struct_type; + } + + return struct_type; +} + +get_by_value_argument_pair = fn (module: &Module, low: &Type, high: &Type) &Type +{ + >low_size = get_byte_allocation_size(low); + >high_alignment = get_byte_alignment(high); + >high_start = align_forward(low_size, #extend(high_alignment)); + assert(high_start != 0 and high_start <= 8); + + if (high_start != 8) + { + #trap(); + } + + >result = get_anonymous_struct_pair(module, low, high); + return result; +} + +parse_type = fn (module: &Module, scope: &Scope) &Type; + +FunctionHeaderArgument = struct +{ + name: []u8, + line: u32, +} + +FunctionHeaderParsing = struct +{ + type: &Type, + arguments: []FunctionHeaderArgument, + attributes: FunctionAttributes, +} + +FunctionKeyword = enum +{ + cc, +} + +resolve_alias = fn (module: &Module, type: &Type) &Type +{ + >result: &Type = zero; + + switch (type.id) + { + .void, + .noreturn, + .integer, + .enum, + .function, + .bits, + .union, + .opaque, + .forward_declaration, + => + { + result = type; + }, + .pointer => + { + >element_type = type.content.pointer.element_type; + >resolved_element_type = resolve_alias(module, element_type); + if (element_type == resolved_element_type) + { + result = type; + } + else + { + result = get_pointer_type(module, resolved_element_type); + } + }, + .array => + { + >element_type = type.content.array.element_type; + >resolved_element_type = resolve_alias(module, element_type); + if (element_type == resolved_element_type) + { + result = type; + } + else + { + result = get_array_type(module, resolved_element_type, type.content.array.element_count); + } + }, + .struct => + { + >is_slice = type.content.struct.is_slice; + + if (is_slice) + { + >old_element_type = type.content.struct.fields[0].type.content.pointer.element_type; + >element_type = resolve_alias(module, old_element_type); + + if (old_element_type == element_type) + { + result = type; + } + else + { + result = get_slice_type(module, element_type); + } + } + else + { + result = type; + } + }, + else => + { + #trap(); + }, + } + + assert(result != zero); + + return result; +} + +type_function_base_compare = fn (module: &Module, a: TypeFunctionBase, b: TypeFunctionBase) u1 +{ + >same_return_type = a.semantic_return_type == b.semantic_return_type; + >same_calling_convention = a.calling_convention == b.calling_convention; + >same_is_variable_argument = a.is_variable_argument == b.is_variable_argument; + + >same_argument_length = a.semantic_argument_types.length == b.semantic_argument_types.length; + >same_argument_types = same_argument_length; + + if (same_argument_length) + { + for (i: 0..a.semantic_argument_types.length) + { + >a_type = a.semantic_argument_types[i]; + >b_type = b.semantic_argument_types[i]; + + >is_same_argument_type = a_type == b_type; + + same_argument_types = same_argument_types and is_same_argument_type; + } + } + + >result = same_return_type and same_calling_convention and same_is_variable_argument and same_argument_types; + return result; +} + +get_function_type = fn (module: &Module, base: TypeFunctionBase) &Type +{ + base.semantic_return_type = resolve_alias(module, base.semantic_return_type); + + for (i: 0..base.semantic_argument_types.length) + { + base.semantic_argument_types[i] = resolve_alias(module, base.semantic_argument_types[i]); + } + + >last_function_type = module.first_function_type; + + while (last_function_type) + { + assert(last_function_type.id == .function); + + if (type_function_base_compare(module, base, last_function_type.content.function.base)) + { + return last_function_type; + } + + >next = last_function_type.content.function.next; + + if (!next) + { + break; + } + + last_function_type = next; + } + + >result = new_type(module, { + .content = { + .function = { + .base = base, + zero, + }, + }, + .id = .function, + .name = "", + .scope = &module.scope, + zero, + }); + + if (last_function_type) + { + assert(module.first_function_type != zero); + last_function_type.content.function.next = result; + } + else + { + assert(!module.first_function_type); + module.first_function_type = result; + } + + return result; +} + +parse_function_header = fn (module: &Module, scope: &Scope, mandate_argument_names: u1) FunctionHeaderParsing +{ + >calling_convention: CallingConvention = .c; + >function_attributes: FunctionAttributes = zero; + >is_variable_argument: u1 = 0; + + if (consume_character_if_match(module, left_bracket)) + { + while (module.offset < module.content.length) + { + >function_identifier = parse_identifier(module); + >function_keyword_s2e = #string_to_enum(FunctionKeyword, function_identifier); + + if (!function_keyword_s2e.is_valid) + { + report_error(); + } + + skip_space(module); + + >function_keyword = function_keyword_s2e.enum_value; + switch (function_keyword) + { + .cc => + { + expect_character(module, left_parenthesis); + skip_space(module); + >calling_convention_string = parse_identifier(module); + >calling_convention_s2e = #string_to_enum(CallingConvention, calling_convention_string); + + if (!calling_convention_s2e.is_valid) + { + report_error(); + } + + >candidate_calling_convention = calling_convention_s2e.enum_value; + + calling_convention = candidate_calling_convention; + + skip_space(module); + expect_character(module, right_parenthesis); + }, + } + + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + else + { + report_error(); + } + } + } + + skip_space(module); + + expect_character(module, left_parenthesis); + + >semantic_argument_type_buffer: [64]&Type = undefined; + >semantic_argument_name_buffer: [64][]u8 = undefined; + >argument_line_buffer: [64]u32 = undefined; + >semantic_argument_count: u64 = 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_argument = 1; + break; + } + + if (consume_character_if_match(module, right_parenthesis)) + { + break; + } + + >line = get_line(module); + argument_line_buffer[semantic_argument_count] = line; + + >argument_name: []u8 = zero; + + if (mandate_argument_names) + { + argument_name = arena_duplicate_string(module.arena, parse_identifier(module)); + + skip_space(module); + expect_character(module, ':'); + skip_space(module); + } + + semantic_argument_name_buffer[semantic_argument_count] = argument_name; + + >argument_type = parse_type(module, scope); + semantic_argument_type_buffer[semantic_argument_count] = argument_type; + + skip_space(module); + + consume_character_if_match(module, ','); + + semantic_argument_count += 1; + } + + skip_space(module); + + >return_type = parse_type(module, scope); + + skip_space(module); + + >argument_types: []&Type = zero; + if (semantic_argument_count != 0) + { + argument_types = arena_allocate_slice[&Type](module.arena, semantic_argument_count); + memcpy(#pointer_cast(argument_types.pointer), #pointer_cast(&semantic_argument_type_buffer), semantic_argument_count * #byte_size(&Type)); + } + + >function_type = get_function_type(module, { + .semantic_return_type = return_type, + .semantic_argument_types = argument_types, + .calling_convention = calling_convention, + .is_variable_argument = is_variable_argument, + }); + + >arguments: []FunctionHeaderArgument = zero; + if (mandate_argument_names) + { + arguments = arena_allocate_slice[FunctionHeaderArgument](module.arena, semantic_argument_count); + + for (i: 0..semantic_argument_count) + { + arguments[i] = { + .name = semantic_argument_name_buffer[i], + .line = argument_line_buffer[i], + }; + } + } + + return { + .type = function_type, + .arguments = arguments, + .attributes = function_attributes, + }; +} + parse_type = fn (module: &Module, scope: &Scope) &Type { >start_character = module.content[module.offset]; @@ -2816,11 +3897,11 @@ parse_type = fn (module: &Module, scope: &Scope) &Type { .void => { - #trap(); + return void_type(module); }, .noreturn => { - #trap(); + return noreturn_type(module); }, .enum_array => { @@ -2879,17 +3960,24 @@ parse_type = fn (module: &Module, scope: &Scope) &Type } else { - //>type = module.first_type; + >it_scope = scope; - //while (type) - //{ - // if (string_equal(identifier, type.name)) - // { - // return type; - // } + while (it_scope) + { + >type = it_scope.types.first; - // type = type.next; - //} + while (type) + { + if (string_equal(identifier, type.name)) + { + return type; + } + + type = type.next; + } + + it_scope = it_scope.parent; + } report_error(); } @@ -2897,10 +3985,77 @@ parse_type = fn (module: &Module, scope: &Scope) &Type } else if (start_character == '&') { - #trap(); + module.offset += 1; + skip_space(module); + >element_type = parse_type(module, scope); + >pointer_type = get_pointer_type(module, element_type); + return pointer_type; } else if (start_character == left_bracket) { + module.offset += 1; + skip_space(module); + + >is_slice = consume_character_if_match(module, right_bracket); + if (is_slice) + { + skip_space(module); + >element_type = parse_type(module, scope); + >slice_type = get_slice_type(module, element_type); + return slice_type; + } + else + { + >checkpoint = get_checkpoint(module); + >length_inferred: u1 = 0; + if (consume_character_if_match(module, '_')) + { + skip_space(module); + length_inferred = consume_character_if_match(module, ']'); + } + + >length_value: &Value = zero; + >element_count: u64 = 0; + >resolved: u1 = 0; + + if (!length_inferred) + { + #trap(); + } + + skip_space(module); + + >element_type = parse_type(module, scope); + + if (length_inferred) + { + assert(!length_value); + >result = new_type(module, { + .content = { + .array = { + .element_type = element_type, + .element_count = 0, + zero, + }, + }, + .id = .array, + .name = "", + .scope = element_type.scope, + zero, + }); + return result; + } + else + { + if (!resolved) + { + report_error(); + } + + #trap(); + } + #trap(); + } #trap(); } else if (start_character == '#') @@ -3229,19 +4384,92 @@ tokenize = fn (module: &Module) Token }, '=' => { - #trap(); + >next_ch = module.content[start_index + 1]; + >is_compare_equal = next_ch == '='; + >id: TokenId = #select(is_compare_equal, .compare_equal, .assign); + module.offset += #extend(is_compare_equal) + 1; + token = { + .id = id, + zero, + }; }, '.' => { - #trap(); + >id: TokenId = undefined; + >next_ch = module.content[start_index + 1]; + switch (next_ch) + { + else => { id = .dot; }, + '&' => { id = .pointer_dereference; }, + '.' => + { + switch (module.content[start_index + 2]) + { + '.' => { id = .triple_dot; }, + else => { id = .double_dot; }, + } + }, + } + + >add: u64 = undefined; + + switch (id) + { + .dot => { add = 1; }, + .double_dot, .pointer_dereference => { add = 2; }, + .triple_dot => { add = 3; }, + else => { unreachable; }, + } + + module.offset += add; + + token = { + .id = id, + zero, + }; }, '"' => { - #trap(); + >string_literal = parse_string_literal(module); + + token = { + .content = { + .string_literal = string_literal, + }, + .id = .string_literal, + }; }, '\'' => { - #trap(); + module.offset += 1; + + >ch: u8 = undefined; + if (module.content[module.offset] == '\\') + { + module.offset += 1; + ch = escape_character(module.content[module.offset]); + } + else + { + ch = module.content[module.offset]; + if (ch == '\'') + { + report_error(); + } + } + + module.offset += 1; + expect_character(module, '\''); + + token = { + .content = { + .integer = { + .value = #extend(ch), + .kind = .character_literal, + }, + }, + .id = .integer, + }; }, '0'...'9' => { @@ -3437,11 +4665,41 @@ reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: [ { .global => { - #trap(); + assert(module == scope_to_module(scope)); + + >global = module.first_global; + + while (global != zero) + { + if (string_equal(identifier, global.variable.name)) + { + variable = &global.variable; + break; + } + + global = global.next; + } + + >macro_declaration = module.first_macro_declaration; + + while (macro_declaration != zero) + { + #trap(); + } }, .function => { - #trap(); + assert(scope.parent != zero); + >function = scope_to_function(scope); + + for (&argument: function.arguments) + { + if (string_equal(identifier, argument.variable.name)) + { + variable = &argument.variable; + break; + } + } }, .local => { @@ -3661,12 +4919,49 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value .has_debug_info, => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + expect_character(module, right_parenthesis); + + >id: ValueId = undefined; + switch (intrinsic) + { + .trap => { id = .trap; }, + .va_start => { id = .va_start; }, + .has_debug_info => { id = .has_debug_info; }, + else => { unreachable; }, + } + + result.& = { + .id = id, + zero, + }; }, // (value, T) .va_arg => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + >va_list = parse_value(module, scope, zero); + skip_space(module); + expect_character(module, ','); + skip_space(module); + >type = parse_type(module, scope); + skip_space(module); + expect_character(module, right_parenthesis); + + result.& = { + .content = { + .va_arg = { + .va_list = va_list, + .type = type, + }, + }, + .id = .va_arg, + zero, + }; }, // TODO .va_copy => @@ -3689,19 +4984,98 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .left_bracket => { - #trap(); + >element_count: u64 = 0; + >value_buffer: [64]&Value = undefined; + + skip_space(module); + + >checkpoint = get_checkpoint(module); + >is_aggregate_initialization: u1 = 0; + + if (consume_character_if_match(module, '.')) + { + >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) + { + #trap(); + } + else + { + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + + >value = parse_value(module, scope, zero); + value_buffer[element_count] = value; + element_count += 1; + + consume_character_if_match(module, ','); + } + + >values = arena_allocate_slice[&Value](module.arena, element_count); + memcpy(#pointer_cast(values.pointer), #pointer_cast(&value_buffer), element_count * #byte_size(&Value)); + + result = new_value(module); + + result.& = { + .content = { + .array_initialization = { + .values = values, + .is_constant = 0, // This is analyzed later + }, + }, + .id = .array_initialization, + zero, + }; + } }, .dot => { - #trap(); + >identifier = parse_identifier(module); + result = new_value(module); + result.& = { + .content = { + .enum_literal = identifier, + }, + .id = .enum_literal, + zero, + }; }, .left_parenthesis => { - #trap(); + result = parse_value(module, scope, { .kind = builder.kind, zero }); + expect_character(module, right_parenthesis); }, .string_literal => { - #trap(); + result = new_value(module); + result.& = { + .content = { + .string_literal = token.content.string_literal, + }, + .id = .string_literal, + zero, + }; }, .left_brace => { @@ -3804,6 +5178,42 @@ get_token_precedence = fn (token: Token) Precedence } } +parse_call_arguments = fn (module: &Module, scope: &Scope) []&Value +{ + >arguments: []&Value = zero; + + >argument_count: u64 = 0; + >argument_buffer: [64]&Value = undefined; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_parenthesis)) + { + break; + } + + >argument = parse_value(module, scope, zero); + >argument_index = argument_count; + argument_buffer[argument_index] = argument; + + skip_space(module); + + consume_character_if_match(module, ','); + + argument_count += 1; + } + + if (argument_count != 0) + { + arguments = arena_allocate_slice[&Value](module.arena, argument_count); + memcpy(#pointer_cast(arguments.pointer), #pointer_cast(&argument_buffer), argument_count * #byte_size(&Value)); + } + + return arguments; +} + parse_right = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value { >left = builder.left; @@ -3887,19 +5297,137 @@ parse_right = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .pointer_dereference => { - #trap(); + result = new_value(module); + result.& = { + .content = { + .unary = { + .value = left, + .id = .dereference, + }, + }, + .id = .unary, + .kind = .right, + zero, + }; }, .left_parenthesis => { - #trap(); + result = new_value(module); + + switch (left.id) + { + .macro_reference => + { + #trap(); + }, + else => + { + >arguments = parse_call_arguments(module, scope); + result.& = { + .content = { + .call = { + .callable = left, + .arguments = arguments, + zero, + }, + }, + .id = .call, + .kind = .right, + zero, + }; + }, + } }, .left_bracket => { - #trap(); + skip_space(module); + + result = new_value(module); + + if (left.id == .macro_reference) + { + #trap(); + } + else + { + left.kind = .left; + + >start_value: &Value = zero; + >start = !(module.content[module.offset] == '.' and module.content[module.offset + 1] == '.'); + if (start) + { + start_value = parse_value(module, scope, zero); + } + + >is_array = consume_character_if_match(module, right_bracket); + + if (is_array) + { + if (!start_value) + { + report_error(); + } + + >index = start_value; + result.& = { + .content = { + .array_expression = { + .array_like = left, + .index = index, + }, + }, + .id = .array_expression, + .kind = builder.kind, + zero, + }; + } + else + { + expect_character(module, '.'); + expect_character(module, '.'); + + >end_value: &Value = zero; + + if (!consume_character_if_match(module, right_bracket)) + { + end_value = parse_value(module, scope, zero); + expect_character(module, right_bracket); + } + + result.& = { + .content = { + .slice_expression = { + .array_like = left, + .start = start_value, + .end = end_value, + }, + }, + .id = .slice_expression, + zero, + }; + } + } }, .dot => { - #trap(); + left.kind = .left; + + skip_space(module); + + >identifier = parse_identifier(module); + result = new_value(module); + + result.& = { + .content = { + .field_access = { + .aggregate = left, + .field_name = identifier, + }, + }, + .id = .field_access, + .kind = builder.kind, + zero, + }; }, else => { @@ -3978,6 +5506,8 @@ StatementStartKeyword = enum continue, } +parse_block = fn (module: &Module, parent_scope: &Scope) &Block; + parse_statement = fn (module: &Module, scope: &Scope) &Statement { >require_semicolon: u1 = 1; @@ -4001,7 +5531,7 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement module.offset += 1; skip_space(module); - >local_name = parse_identifier(module); + >local_name = arena_duplicate_string(module.arena, parse_identifier(module)); skip_space(module); >local_type: &Type = zero; @@ -4038,12 +5568,19 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement }, '#' => { - statement.content.expression = parse_value(module, scope, zero); + statement.content = { + .expression = parse_value(module, scope, zero), + }; statement.id = .expression; }, left_brace => { - #trap(); + >block = parse_block(module, scope); + statement.content = { + .block = block, + }; + statement.id = .block; + require_semicolon = 0; }, else => { @@ -4073,7 +5610,51 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement }, .if => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + >condition = parse_value(module, scope, zero); + + skip_space(module); + expect_character(module, right_parenthesis); + skip_space(module); + + >if_statement = parse_statement(module, scope); + + skip_space(module); + + >is_else: u1 = 0; + >else_statement: &Statement = zero; + + if (is_identifier_start(module.content[module.offset])) + { + >checkpoint = get_checkpoint(module); + >identifier = parse_identifier(module); + + is_else = string_equal(identifier, "else"); + + if (is_else) + { + skip_space(module); + else_statement = parse_statement(module, scope); + } + else + { + set_checkpoint(module, checkpoint); + } + } + + require_semicolon = 0; + + statement.content = { + .if = { + .condition = condition, + .if = if_statement, + .else = else_statement, + }, + }; + statement.id = .if; }, .for => { @@ -4081,7 +5662,27 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement }, .while => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + >condition = parse_value(module, scope, zero); + + skip_space(module); + expect_character(module, right_parenthesis); + skip_space(module); + + >block = parse_block(module, scope); + + require_semicolon = 0; + + statement.content = { + .while = { + .condition = condition, + .block = block, + }, + }; + statement.id = .while; }, .switch => { @@ -4102,7 +5703,51 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement set_checkpoint(module, checkpoint); >left = parse_value(module, scope, { .kind = .left, zero }); - #trap(); + + skip_space(module); + + if (consume_character_if_match(module, ';')) + { + require_semicolon = 0; + statement.content = { + .expression = left, + }; + statement.id = .expression; + } + else + { + >token = tokenize(module); + + >id: StatementAssignmentId = undefined; + + switch (token.id) + { + .assign => { id = .assign; }, + .assign_plus => { id = .assign_add; }, + .assign_dash => { id = .assign_sub; }, + .assign_asterisk => { id = .assign_mul; }, + .assign_forward_slash => { id = .assign_div; }, + .assign_percentage => { id = .assign_rem; }, + .assign_shift_left => { id = .assign_shift_left; }, + .assign_shift_right => { id = .assign_shift_right; }, + .assign_ampersand => { id = .assign_and; }, + .assign_bar => { id = .assign_or; }, + .assign_caret => { id = .assign_xor; }, + else => { unreachable; }, + } + + skip_space(module); + + >right = parse_value(module, scope, zero); + statement.content = { + .assignment = { + .left = left, + .right = right, + .id = id, + }, + }; + statement.id = .assignment; + } } } else @@ -4173,6 +5818,13 @@ parse_block = fn (module: &Module, parent_scope: &Scope) &Block return block; } +enum_bit_count = fn (highest_value: u64) u64 +{ + >needed_bit_count: u64 = 64 - #leading_zeroes(highest_value); + needed_bit_count = #select(needed_bit_count != 0, needed_bit_count, 1); + return needed_bit_count; +} + parse = fn (module: &Module) void { >scope = &module.scope; @@ -4229,7 +5881,9 @@ parse = fn (module: &Module) void skip_space(module); } - >global_name = parse_identifier(module); + >global_name = arena_duplicate_string(module.arena, parse_identifier(module)); + + >global_forward_declaration: &Global = zero; >last_global = module.first_global; @@ -4237,7 +5891,20 @@ parse = fn (module: &Module) void { if (string_equal(global_name, last_global.variable.name)) { - report_error(); + global_forward_declaration = last_global; + + if (last_global.variable.storage.id != .forward_declared_function) + { + report_error(); + } + + // TODO: only forbid extern, not exports + if (last_global.linkage == .external) + { + report_error(); + } + + break; } if (!last_global.next) @@ -4307,6 +5974,11 @@ parse = fn (module: &Module) void { >global_keyword = global_keyword_s2e.enum_value; + if (global_forward_declaration != zero and global_keyword != .fn) + { + report_error(); + } + switch (global_keyword) { .bits => @@ -4315,181 +5987,205 @@ parse = fn (module: &Module) void }, .enum => { - #trap(); - }, - .fn => - { - >calling_convention: CallingConvention = .c; - >function_attributes: FunctionAttributes = zero; - >is_variable_argument: u1 = 0; - - if (consume_character_if_match(module, left_bracket)) + >is_implicit_type = module.content[module.offset] == left_brace; + >backing_type: &Type = zero; + if (!is_implicit_type) { - while (module.offset < module.content.length) - { - >function_identifier = parse_identifier(module); - - >function_type_attribute_s2e = #string_to_enum(FunctionTypeAttribute, function_identifier); - - if (!function_type_attribute_s2e.is_valid) - { - report_error(); - } - - >function_type_attribute = function_type_attribute_s2e.enum_value; - - switch (function_type_attribute) - { - .cc => - { - expect_character(module, left_parenthesis); - skip_space(module); - >calling_convention_string = parse_identifier(module); - - >calling_convention_s2e = #string_to_enum(CallingConvention, calling_convention_string); - - if (!calling_convention_s2e.is_valid) - { - report_error(); - } - - >candidate_calling_convention = calling_convention_s2e.enum_value; - calling_convention = candidate_calling_convention; - - skip_space(module); - expect_character(module, right_parenthesis); - }, - } - - skip_space(module); - - if (consume_character_if_match(module, right_bracket)) - { - break; - } - else - { - report_error(); - } - } + backing_type = parse_type(module, scope); } skip_space(module); + expect_character(module, left_brace); - expect_character(module, left_parenthesis); + >name_buffer: [64][]u8 = undefined; + >int_value_buffer: [64]u64 = undefined; + >field_count: u64 = 0; + >is_resolved: u1 = 1; - >semantic_argument_type_buffer: [64]&Type = undefined; - >semantic_argument_name_buffer: [64][]u8 = undefined; - >argument_line_buffer: [64]u32 = undefined; - >semantic_argument_count: u64 = 0; - - while (module.offset < module.content.length) + while (1) { skip_space(module); - if (consume_character_if_match(module, '.')) - { - #trap(); - } - - if (consume_character_if_match(module, right_parenthesis)) + if (consume_character_if_match(module, right_brace)) { break; } - >line = get_line(module); - argument_line_buffer[semantic_argument_count] = line; - - >argument_name = parse_identifier(module); - semantic_argument_name_buffer[semantic_argument_count] = argument_name; + >field_index = field_count; + field_count += 1; + >field_name = parse_name(module); + name_buffer[field_index] = field_name; + skip_space(module); - expect_character(module, ':'); + >field_integer_value = field_index; - skip_space(module); - - >argument_type = parse_type(module, scope); - semantic_argument_type_buffer[semantic_argument_count] = argument_type; - - skip_space(module); - - consume_character_if_match(module, '.'); - - semantic_argument_count += 1; - } - - skip_space(module); - - >return_type = parse_type(module, scope); - - skip_space(module); - - >argument_types: []&Type = zero; - - if (semantic_argument_count != 0) - { - #trap(); - } - - >is_declaration = consume_character_if_match(module, ';'); - - >function_type = new_type(module, { - .content = { - .function = { - .base = { - .semantic_return_type = return_type, - .semantic_argument_types = argument_types, - .calling_convention = calling_convention, - .is_variable_argument = is_variable_argument, - }, - zero, - }, - }, - .id = .function, - .name = "", - .scope = &module.scope, - zero, - }); - - >storage = new_value(module); - storage.& = { - .id = .forward_declared_function, - .type = get_pointer_type(module, function_type), - zero, - // TODO? kind = .left, - }; - - >global = new_global(module); - global.& = { - .variable = { - .storage = storage, - .type = function_type, - .scope = scope, - .name = global_name, - .line = global_line, - .column = global_column, - }, - .linkage = #select(is_export or is_extern, .external, .internal), - zero, - }; - - if (!is_declaration) - { - module.current_function = global; - >arguments = arena_allocate_slice[Argument](module.arena, semantic_argument_count); - - for (i: 0..semantic_argument_count) + if (consume_character_if_match(module, '=')) { - >argument = &arguments[i]; - >name = semantic_argument_name_buffer[i]; - >type = semantic_argument_type_buffer[i]; - >line = argument_line_buffer[i]; + skip_space(module); - #trap(); + >field_value = parse_value(module, scope, zero); + if (is_resolved) + { + if (value_is_constant(field_value)) + { + switch (field_value.id) + { + .constant_integer => + { + field_integer_value = field_value.content.constant_integer.value; + }, + else => { report_error(); }, + } + } + else + { + // TODO: ?? + report_error(); + } + } + else + { + // TODO: ?? + report_error(); + } + } + else + { + if (!is_resolved) + { + report_error(); + } } - storage.content.function = { + int_value_buffer[field_index] = field_integer_value; + + skip_space(module); + consume_character_if_match(module, ','); + } + + if (is_resolved) + { + >fields = arena_allocate_slice[EnumField](module.arena, field_count); + >highest_value: u64 = 0; + + for (i: 0..field_count) + { + >value = int_value_buffer[i]; + >highest_value = #max(highest_value, value); + fields[i] = { + .name = name_buffer[i], + .value = value, + }; + } + + >needed_bit_count = enum_bit_count(highest_value); + + if (!backing_type) + { + backing_type = integer_type(module, { .bit_count = needed_bit_count, .signed = 0 }); + } + + >enum_type = new_type(module, { + .content = { + .enum = { + .fields = fields, + .backing_type = backing_type, + .line = global_line, + zero, + }, + }, + .id = .enum, + .name = global_name, + .scope = &module.scope, + zero, + }); + } + else + { + report_error(); + } + }, + .fn => + { + >mandate_argument_names: u1 = 1; + >function_header = parse_function_header(module, scope, mandate_argument_names); + + >function_type = function_header.type; + >function_attributes = function_header.attributes; + >semantic_argument_types = function_type.content.function.base.semantic_argument_types; + >pointer_to_function_type = get_pointer_type(module, function_type); + + >global: &Global = zero; + if (global_forward_declaration) + { + global = global_forward_declaration; + + if (global_forward_declaration.variable.type != function_type) + { + report_error(); + } + + assert(global_forward_declaration.variable.storage.type == pointer_to_function_type); + + global.variable.name = global_name; + global.variable.line = global_line; + global.variable.column = global_column; + } + else + { + >storage = new_value(module); + storage.& = { + .type = pointer_to_function_type, + .id = .forward_declared_function, + // TODO: .kind = .left // ???? + zero, + }; + + global = new_global(module); + global.& = { + .variable = { + .storage = storage, + .type = function_type, + .scope = scope, + .name = global_name, + .line = global_line, + .column = global_column, + }, + .initial_value = zero, + .linkage = #select(is_export or is_extern, .external, .internal), + zero, + }; + } + + if (!consume_character_if_match(module, ';')) + { + module.current_function = global; + >arguments = arena_allocate_slice[Argument](module.arena, semantic_argument_types.length); + + for (i: 0..semantic_argument_types.length) + { + >argument = &arguments[i]; + >header_argument = &function_header.arguments[i]; + >name = header_argument.name; + >line = header_argument.line; + >type = semantic_argument_types[i]; + + argument.& = { + .variable = { + .storage = zero, + .type = type, + .scope = &global.variable.storage.content.function.scope, + .name = name, + .line = line, + .column = 0, + }, + .index = #truncate(i + 1), + }; + } + + global.variable.storage.content.function = { .arguments = arguments, .scope = { .parent = scope, @@ -4501,8 +6197,10 @@ parse = fn (module: &Module) void .attributes = function_attributes, zero, }; - storage.id = .function; - storage.content.function.block = parse_block(module, &storage.content.function.scope); + global.variable.storage.id = .function; + + global.variable.storage.content.function.block = parse_block(module, &global.variable.storage.content.function.scope); + module.current_function = zero; } }, @@ -4541,42 +6239,6 @@ parse = fn (module: &Module) void } } -resolve_alias = fn (module: &Module, type: &Type) &Type -{ - >result: &Type = zero; - - switch (type.id) - { - .void, - .integer, - => - { - result = type; - }, - .pointer => - { - >element_type = type.content.pointer.element_type; - >resolved_element_type = resolve_alias(module, element_type); - if (element_type == resolved_element_type) - { - result = type; - } - else - { - result = get_pointer_type(module, resolved_element_type); - } - }, - else => - { - #trap(); - }, - } - - assert(result != zero); - - return result; -} - resolve_type_in_place_abi = fn (module: &Module, type: &Type) void { if (!type.llvm.abi) @@ -4601,6 +6263,40 @@ resolve_type_in_place_abi = fn (module: &Module, type: &Type) void { result = module.llvm.pointer_type; }, + .array => + { + >element_type = type.content.array.element_type; + >element_count = type.content.array.element_count; + assert(element_count != 0); + resolve_type_in_place_memory(module, element_type); + >array_type = LLVMArrayType2(element_type.llvm.memory, element_count); + result = array_type; + }, + .enum => + { + >backing_type = type.content.enum.backing_type; + resolve_type_in_place_abi(module, backing_type); + result = backing_type.llvm.abi; + }, + .struct => + { + >llvm_type_buffer: [64]&LLVMType = undefined; + >fields = type.content.struct.fields; + + for (i: 0..fields.length) + { + >field = &fields[i]; + >field_type = field.type; + resolve_type_in_place_memory(module, field_type); + llvm_type_buffer[i] = field_type.llvm.memory; + } + + >is_packed: u1 = 0; + result = LLVMStructTypeInContext(module.llvm.context, &llvm_type_buffer[0], #truncate(fields.length), #extend(is_packed)); + >llvm_size = LLVMStoreSizeOfType(module.llvm.target_data_layout, result); + >size = get_byte_size(type); + assert(llvm_size == size); + }, else => { #trap(); @@ -4639,6 +6335,12 @@ resolve_type_in_place_memory = fn (module: &Module, type: &Type) void >bit_count = byte_size * 8; result = LLVMIntTypeInContext(module.llvm.context, #truncate(bit_count)); }, + .enum => + { + >backing_type = type.content.enum.backing_type; + resolve_type_in_place_memory(module, backing_type); + result = backing_type.llvm.memory; + }, else => { #trap(); @@ -4650,6 +6352,15 @@ resolve_type_in_place_memory = fn (module: &Module, type: &Type) void } } +align_integer_type = fn (module: &Module, type: &Type) &Type +{ + >bit_count = get_bit_size(type); + >abi_bit_count = align_bit_count(bit_count); + >is_signed = type_is_signed(type); + >result = integer_type(module, { .bit_count = abi_bit_count, .signed = is_signed }); + return result; +} + resolve_type_in_place_debug = fn (module: &Module, type: &Type) void { if (module.has_debug_info) @@ -4690,6 +6401,71 @@ resolve_type_in_place_debug = fn (module: &Module, type: &Type) void result = LLVMDIBuilderCreatePointerType(module.llvm.di_builder, element_type.llvm.debug, 64, 64, default_address_space, type.name.pointer, type.name.length); } }, + .array => + { + >element_type = type.content.array.element_type; + >element_count = type.content.array.element_count; + assert(element_count != 0); + resolve_type_in_place_debug(module, element_type); + + >bit_alignment = get_byte_alignment(type) * 8; + >array_type = LLVMDIBuilderCreateArrayType(module.llvm.di_builder, get_bit_size(type), bit_alignment, element_type.llvm.debug, zero, 0); + result = array_type; + }, + .enum => + { + >backing_type = type.content.enum.backing_type; + resolve_type_in_place_debug(module, backing_type); + + >field_buffer: [64]&LLVMMetadata = undefined; + >fields = type.content.enum.fields; + + for (i: 0..fields.length) + { + >field = &fields[i]; + >enum_field = LLVMDIBuilderCreateEnumerator(module.llvm.di_builder, field.name.pointer, field.name.length, field.value, #extend(!type_is_signed(backing_type))); + field_buffer[i] = enum_field; + } + + >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, type.name.pointer, type.name.length, module.llvm.file, type.content.enum.line, get_bit_size(type), get_byte_alignment(type) * 8, &field_buffer[0], fields.length, debug_aligned_type.llvm.debug); + }, + .struct => + { + >flags: LLVMDIFlags = zero; + >runtime_language: u32 = 0; + >byte_size = get_byte_size(type); + >alignment = get_byte_alignment(type); + >name = type.name; + >forward_declaration = LLVMDIBuilderCreateReplaceableCompositeType(module.llvm.di_builder, module.llvm.debug_tag, name.pointer, name.length, module.scope.llvm, module.llvm.file, type.content.struct.line, runtime_language, byte_size * 8, alignment * 8, flags, name.pointer, name.length); + type.llvm.debug = forward_declaration; + module.llvm.debug_tag += 1; + + >llvm_type_buffer: [64]&LLVMMetadata = undefined; + + >fields = type.content.struct.fields; + + for (i: 0..fields.length) + { + >field = &fields[i]; + >field_type = field.type; + resolve_type_in_place_debug(module, field_type); + + >member_type = LLVMDIBuilderCreateMemberType(module.llvm.di_builder, module.scope.llvm, 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; + } + + >derived_from: &LLVMMetadata = zero; + >runtime_language: u32 = 0; + >vtable_holder: &LLVMMetadata = zero; + + >struct_type = LLVMDIBuilderCreateStructType(module.llvm.di_builder, module.scope.llvm, name.pointer, name.length, module.llvm.file, type.content.struct.line, byte_size * 8, alignment * 8, flags, derived_from, &llvm_type_buffer[0], #truncate(fields.length), runtime_language, vtable_holder, name.pointer, name.length); + LLVMMetadataReplaceAllUsesWith(forward_declaration, struct_type); + result = struct_type; + }, else => { #trap(); @@ -4714,11 +6490,89 @@ AbiSystemVClass = enum memory, } +// AMD64-ABI 3.2.3p2: Rule 4. Each field of an object is +// classified recursively so that always two fields are +// considered. The resulting class is calculated according to +// the classes of the fields in the eightbyte: +// +// (a) If both classes are equal, this is the resulting class. +// +// (b) If one of the classes is NO_CLASS, the resulting class is +// the other class. +// +// (c) If one of the classes is MEMORY, the result is the MEMORY +// class. +// +// (d) If one of the classes is INTEGER, the result is the +// INTEGER. +// +// (e) If one of the classes is X87, X87UP, COMPLEX_X87 class, +// MEMORY is used as class. +// +// (f) Otherwise class SSE is used. + +// Accum should never be memory (we should have returned) or +// ComplexX87 (because this cannot be passed in a structure). + +abi_system_v_merge_class = fn (accumulator: AbiSystemVClass, field: AbiSystemVClass) AbiSystemVClass +{ + assert(accumulator != .memory and accumulator != .complex_x87); + + if (accumulator == field or field == .none) + { + return accumulator; + } + + if (field == .memory) + { + return .memory; + } + + if (accumulator == .integer or field == .integer) + { + return .integer; + } + + if (field == .x87 or field == .x87_up or field == .complex_x87 or accumulator == .x87 or accumulator == .x87_up) + { + return .memory; + } + + return .sse; +} + +abi_system_v_classify_post_merge = fn (aggregate_size: u64, classes: [2]AbiSystemVClass) [2]AbiSystemVClass +{ + >result = classes; + + if (result[1] == .memory) + { + result[0] = .memory; + } + + if (result[1] == .x87_up) + { + #trap(); + } + + if (aggregate_size > 16 and (result[0] != .sse or result[1] != .sse_up)) + { + result[0] = .memory; + } + + if (result[1] == .sse_up and result[0] == .sse) + { + result[0] = .sse; + } + + return result; +} + AbiSystemVClassifyArgument = struct { base_offset: u64, is_variable_argument: u1, - is_register_call: u1, + register_call: u1, } abi_system_v_classify_type = fn (type: &Type, options: AbiSystemVClassifyArgument) [2]AbiSystemVClass @@ -4737,6 +6591,10 @@ abi_system_v_classify_type = fn (type: &Type, options: AbiSystemVClassifyArgumen { result[current_index] = .none; }, + .pointer => + { + result[current_index] = .integer; + }, .integer => { >bit_count = type.content.integer.bit_count; @@ -4753,6 +6611,71 @@ abi_system_v_classify_type = fn (type: &Type, options: AbiSystemVClassifyArgumen report_error(); } }, + .struct, + .union, + => + { + >byte_size = get_byte_size(type); + + if (byte_size <= 64) + { + >has_variable_array: u1 = 0; // TODO + if (!has_variable_array) + { + result[current_index] = .none; + + switch (type.id) + { + .struct => + { + >fields = type.content.struct.fields; + + for (&field: fields) + { + >offset = options.base_offset + field.offset; + >member_type = field.type; + >member_size = get_byte_size(member_type); + >member_alignment: u64 = #extend(get_byte_alignment(member_type)); + + >native_vector_size: u64 = 16; + + >gt_16 = byte_size > 16 and ((type.id != .union and byte_size != member_size) or byte_size > native_vector_size); + >padding = offset % member_alignment != 0; + + if (gt_16 or padding) + { + result[0] = .memory; + #trap(); + } + + >member_classes = abi_system_v_classify_type(member_type, { + .base_offset = offset, + .is_variable_argument = options.is_variable_argument, + .register_call = options.register_call, + }); + + for (i: 0..member_classes.length) + { + result[i] = abi_system_v_merge_class(result[i], member_classes[i]); + } + + if (result[0] == .memory or result[1] == .memory) + { + break; + } + } + + result = abi_system_v_classify_post_merge(byte_size, result); + }, + .union => + { + #trap(); + } + else => { unreachable; }, + } + } + } + }, else => { #trap(); @@ -4788,6 +6711,36 @@ contains_no_user_data = fn (type: &Type, start: u64, end: u64) u1 return result; } +get_member_at_offset = fn (struct_type: &Type, offset: u64) &Field +{ + assert(struct_type.id == .struct); + + >result: &Field = zero; + + >byte_size = get_byte_size(struct_type); + if (byte_size > offset) + { + >offset_it: u64 = 0; + >fields = struct_type.content.struct.fields; + + for (&field: fields) + { + if (offset_it > offset) + { + break; + } + + result = field; + assert(offset_it == field.offset); + offset_it = align_forward(offset_it + get_byte_size(field.type), #extend(get_byte_alignment(field.type))); + } + + assert(result != zero); + } + + 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) @@ -4814,6 +6767,55 @@ abi_system_v_get_integer_type_at_offset = fn (module: &Module, type: &Type, offs } } }, + .pointer => + { + if (offset == 0) + { + return type; + } + else + { + #trap(); + } + }, + .struct => + { + >field = get_member_at_offset(type, offset); + if (field) + { + >field_type = field.type; + + switch (field_type.id) + { + .integer, + .enum, + => + { + field_type = align_integer_type(module, field_type); + }, + else => {}, + } + + return abi_system_v_get_integer_type_at_offset(module, field_type, offset - field.offset, source_type, source_offset); + } + else + { + unreachable; + } + }, + .bits => + { + #trap(); + }, + .enum => + { + #trap(); + }, + .array => + { + #trap(); + }, + else => { #trap(); }, } >source_size = get_byte_size(source_type); @@ -4862,6 +6864,124 @@ abi_system_v_get_direct = fn (module: &Module, direct: AbiSystemVDirect) AbiInfo return result; } +abi_system_v_get_ignore = fn (module: &Module, semantic_type: &Type) AbiInformation +{ + resolve_type_in_place(module, semantic_type); + + return { + .semantic_type = semantic_type, + .flags = { + .kind = .ignore, + zero, + }, + zero, + }; +} + +AbiSystemVExtendOptions = struct +{ + semantic_type: &Type, + type: &Type, + sign: u1, +} + +abi_system_v_get_extend = fn (options: AbiSystemVExtendOptions) AbiInformation +{ + assert(is_integral_or_enumeration_type(options.semantic_type)); + >result: AbiInformation = { + .semantic_type = options.semantic_type, + .flags = { + .kind = .extend, + zero, + }, + zero, + }; + + abi_set_coerce_to_type(&result, #select(options.type != zero, options.type, options.semantic_type)); + abi_set_padding_type(&result, zero); + abi_set_direct_offset(&result, 0); + abi_set_direct_alignment(&result, 0); + result.flags.sign_extension = options.sign; + + return result; +} + +AbiSystemVIndirectOptions = struct +{ + semantic_type: &Type, + padding_type: &Type, + alignment: u32, + not_by_value: u1, // This is the inverse of `by_value` because we want `by_value` to `true` by default + realign: u1, +} + +abi_system_v_get_indirect = fn (indirect: AbiSystemVIndirectOptions) AbiInformation +{ + >result: AbiInformation = { + .semantic_type = indirect.semantic_type, + .attributes = { + .indirect = { + .alignment = 0, + .address_space = 0, + }, + }, + .flags = { + .kind = .indirect, + zero, + }, + zero, + }; + + abi_set_indirect_align(&result, indirect.alignment); + abi_set_indirect_by_value(&result, !indirect.not_by_value); + abi_set_indirect_realign(&result, indirect.realign); + abi_set_sret_after_this(&result, 0); + abi_set_padding_type(&result, indirect.padding_type); + + return result; +} + +abi_system_v_get_indirect_result = fn (module: &Module, type: &Type, free_gpr: u32) AbiInformation +{ + if (!type_is_aggregate_type_for_abi(type) and !is_illegal_vector_type(type) and !is_arbitrary_bit_integer(type)) + { + if (is_promotable_integer_type_for_abi(type)) + { + #trap(); + } + else + { + return abi_system_v_get_direct(module, { + .semantic_type = type, + .type = type, + zero, + }); + } + } + else + { + >alignment = #max(get_byte_alignment(type), 8); + >size = get_byte_size(type); + + if (free_gpr == 0 and alignment != 8 and size <= 8) + { + return abi_system_v_get_direct(module, { + .semantic_type = type, + .type = integer_type(module, { .bit_count = size * 8, .signed = 0 }), + zero, + }); + } + else + { + return abi_system_v_get_indirect({ + .semantic_type = type, + .alignment = alignment, + zero, + }); + } + } +} + abi_system_v_classify_return_type = fn (module: &Module, semantic_return_type: &Type) AbiInformation { >classes = abi_system_v_classify_type(semantic_return_type, zero); @@ -4876,7 +6996,7 @@ abi_system_v_classify_return_type = fn (module: &Module, semantic_return_type: & { if (classes[1] == .none) { - #trap(); + return abi_system_v_get_ignore(module, semantic_return_type); } else { @@ -4936,6 +7056,199 @@ abi_system_v_classify_return_type = fn (module: &Module, semantic_return_type: & return result; } +AbiSystemVClassifyArgumentTypeOptions = struct +{ + available_gpr: u32, + is_named_argument: u1, + register_call: u1, +} + +AbiSystemVClassifyArgumentTypeResult = struct +{ + abi: AbiInformation, + needed_registers: AbiRegisterCountSystemV, +} + +abi_system_v_classify_argument_type = fn (module: &Module, semantic_argument_type: &Type, options: AbiSystemVClassifyArgumentTypeOptions) AbiSystemVClassifyArgumentTypeResult +{ + >classes = abi_system_v_classify_type(semantic_argument_type, { + .base_offset = 0, + .is_variable_argument = !options.is_named_argument, + .register_call = options.register_call, + }); + + >needed_registers: AbiRegisterCountSystemV = zero; + + >low_type: &Type = zero; + + switch (classes[0]) + { + .none => { unreachable; }, + .integer => + { + needed_registers.gpr += 1; + low_type = abi_system_v_get_integer_type_at_offset(module, semantic_argument_type, 0, semantic_argument_type, 0); + + if (classes[1] == .none and low_type.id == .integer) + { + // TODO: if enumerator? + + if (is_integral_or_enumeration_type(semantic_argument_type) and? is_promotable_integer_type_for_abi(semantic_argument_type)) + { + return { + .abi = abi_system_v_get_extend({ + .semantic_type = semantic_argument_type, + .sign = type_is_signed(semantic_argument_type), + zero, + }), + .needed_registers = needed_registers, + }; + } + } + }, + .memory => + { + return { + .abi = abi_system_v_get_indirect_result(module, semantic_argument_type, options.available_gpr), + .needed_registers = needed_registers, + }; + }, + else => + { + unreachable; + }, + } + + >high_type: &Type = zero; + + switch (classes[1]) + { + .none => {}, + .integer => + { + needed_registers.gpr += 1; + high_type = abi_system_v_get_integer_type_at_offset(module, semantic_argument_type, 8, semantic_argument_type, 8); + + if (classes[0] == .none) + { + #trap(); + } + }, + else => { unreachable; }, + } + + >result_type = low_type; + if (high_type) + { + result_type = get_by_value_argument_pair(module, low_type, high_type); + } + + return { + .abi = abi_system_v_get_direct(module, { + .semantic_type = semantic_argument_type, + .type = result_type, + zero, + }), + .needed_registers = needed_registers, + }; +} + +AbiSystemVClassifyArgumentOptions = struct +{ + type: &Type, + abi_start: u16, + is_named_argument: u1, + register_call: u1, +} + +abi_system_v_classify_argument = fn (module: &Module, available_registers: &AbiRegisterCountSystemV, llvm_abi_argument_type_buffer: []&LLVMType, abi_argument_type_buffer: []&Type, options: AbiSystemVClassifyArgumentOptions) AbiInformation +{ + >semantic_argument_type = options.type; + + if (options.register_call) + { + #trap(); + } + + >result = abi_system_v_classify_argument_type(module, semantic_argument_type, { + .available_gpr = available_registers.gpr, + .is_named_argument = options.is_named_argument, + .register_call = options.register_call, + }); + + >abi = result.abi; + >needed_registers = result.needed_registers; + + >argument_abi: AbiInformation = undefined; + + if (available_registers.gpr >= needed_registers.gpr and available_registers.sse >= needed_registers.sse) + { + available_registers.gpr -= needed_registers.gpr; + available_registers.sse -= needed_registers.sse; + + argument_abi = abi; + } + else + { + argument_abi = abi_system_v_get_indirect_result(module, semantic_argument_type, available_registers.gpr); + } + + if (abi_get_padding_type(&argument_abi)) + { + #trap(); + } + + argument_abi.abi_start = options.abi_start; + + >count: u16 = 0; + + >abi_start: u64 = #extend(argument_abi.abi_start); + + switch (argument_abi.flags.kind) + { + .direct, + .extend, + => + { + >coerce_to_type = abi_get_coerce_to_type(&argument_abi); + resolve_type_in_place(module, coerce_to_type); + + >is_flattened_struct = argument_abi.flags.kind == .direct and abi_get_can_be_flattened(&argument_abi) and coerce_to_type.id == .struct; + + if (is_flattened_struct) + { + >fields = coerce_to_type.content.struct.fields; + for (i: 0..fields.length) + { + >field = &fields[i]; + >field_type = field.type; + + llvm_abi_argument_type_buffer[abi_start + i] = field_type.llvm.abi; + abi_argument_type_buffer[abi_start + i] = field_type; + } + + count = #truncate(coerce_to_type.content.struct.fields.length); + } + else + { + llvm_abi_argument_type_buffer[abi_start] = coerce_to_type.llvm.abi; + abi_argument_type_buffer[abi_start] = coerce_to_type; + count = 1; + } + } + .indirect => + { + #trap(); + } + else => { unreachable; }, + } + + assert(count != 0); + argument_abi.abi_count = count; + + return argument_abi; +} + 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 @@ -5023,7 +7336,7 @@ add_value_attribute = fn (module: &Module, value: &LLVMValue, index: u32, add_ca AttributeBuildOptions = struct { - return_abi: AbiInformation, + return_abi: &AbiInformation, argument_abis: []AbiInformation, abi_argument_types: []&Type, abi_return_type: &Type, @@ -5032,7 +7345,7 @@ AttributeBuildOptions = struct emit_attributes = fn (module: &Module, value: &LLVMValue, add_callback: &LLVMAttributeCallback, options: AttributeBuildOptions) void { - >return_abi = &options.return_abi; + >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; @@ -5172,7 +7485,7 @@ create_alloca = fn (module: &Module, options: AllocaOptions) &LLVMValue alignment = get_byte_alignment(abi_type); } - >alloca = llvm_builder_create_alloca(module.llvm.builder, abi_type.llvm.memory, default_address_space, alignment, options.name); + >alloca = llvm_create_alloca(module.llvm.builder, abi_type.llvm.memory, alignment, options.name); return alloca; } @@ -5259,6 +7572,22 @@ create_load = fn (module: &Module, options: LoadOptions) &LLVMValue return result; } +GEPOptions = struct +{ + type: &LLVMType, + pointer: &LLVMValue, + indices: []&LLVMValue, + not_inbounds: u1, +} + +create_gep = fn (module: &Module, options: GEPOptions) &LLVMValue +{ + assert(options.indices.length == 1 or options.indices.length == 2); + >gep_function = #select(options.not_inbounds, &LLVMBuildGEP2, &LLVMBuildInBoundsGEP2); + >gep = gep_function(module.llvm.builder, options.type, options.pointer, options.indices.pointer, #truncate(options.indices.length), ""); + return gep; +} + check_types = fn (module: &Module, expected: &Type, source: &Type) void { assert(expected != zero); @@ -5294,7 +7623,15 @@ TypeAnalysis = struct indexing_type: &Type, must_be_constant: u1, } + analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysis: TypeAnalysis) void; +emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_constant: u1) void; + +analyze_value = fn (module: &Module, value: &Value, expected_type: &Type, type_kind: TypeKind, must_be_constant: u1) void +{ + analyze_type(module, value, expected_type, { .must_be_constant = must_be_constant, zero }); + emit_value(module, value, type_kind, must_be_constant); +} analyze_binary_type = fn (module: &Module, left: &Value, right: &Value, is_boolean: u1, expected_type: &Type, must_be_constant: u1) void { @@ -5348,6 +7685,43 @@ analyze_binary_type = fn (module: &Module, left: &Value, right: &Value, is_boole assert(right.type != zero); } +get_va_list_type = fn (module: &Module) &Type +{ + >va_list_type = module.va_list_type; + if (!va_list_type) + { + >u32_type = uint32(module); + // TODO: this is fake + >void_pointer = get_pointer_type(module, uint8(module)); + >fields = arena_allocate_slice[Field](module.arena, 4); + + fields[0] = { .name = "gp_offset", .type = u32_type, .offset = 0, zero }; + fields[1] = { .name = "fp_offset", .type = u32_type, .offset = 4, zero }; + fields[2] = { .name = "overflow_arg_area", .type = void_pointer, .offset = 8, zero }; + fields[3] = { .name = "reg_save_area", .type = void_pointer, .offset = 16, zero }; + + >va_list_struct = new_type(module, { + .content = { + .struct = { + .fields = fields, + .byte_size = 24, + .byte_alignment = 16, + zero, + }, + }, + .id = .struct, + .name = "va_list", + .scope = &module.scope, + zero, + }); + + va_list_type = get_array_type(module, va_list_struct, 1); + module.va_list_type = va_list_type; + } + + return va_list_type; +} + analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysis: TypeAnalysis) void { assert(!value.type); @@ -5488,15 +7862,46 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi }, .dereference => { - #trap(); + analyze_type(module, unary_value, zero, { .must_be_constant = analysis.must_be_constant, zero }); + if (value.kind == .left) + { + report_error(); + } + >pointer_type = unary_value.type; + assert(pointer_type.id == .pointer); + >dereference_type = pointer_type.content.pointer.element_type; + + typecheck(module, expected_type, dereference_type); + value_type = dereference_type; }, .int_from_enum => { - #trap(); + analyze_type(module, unary_value, zero, { .must_be_constant = analysis.must_be_constant, zero }); + + >value_enum_type = unary_value.type; + if (value_enum_type.id != .enum) + { + report_error(); + } + + >backing_type = value_enum_type.content.enum.backing_type; + typecheck(module, expected_type, backing_type); + + value_type = backing_type; }, .int_from_pointer => { - #trap(); + analyze_type(module, unary_value, zero, { .must_be_constant = analysis.must_be_constant, zero }); + + >value_enum_type = unary_value.type; + + if (value_enum_type.id != .pointer) + { + report_error(); + } + + value_type = uint64(module); + typecheck(module, expected_type, value_type); }, .pointer_cast => { @@ -5533,6 +7938,25 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi typecheck(module, expected_type, value_type); }, + // TODO: this is the default case + .ampersand, + .exclamation, + => + { + >is_boolean = unary_is_boolean(unary_id); + if (is_boolean) + { + analyze_type(module, unary_value, zero, { .must_be_constant = analysis.must_be_constant, zero }); + value_type = uint1(module); + } + else + { + analyze_type(module, unary_value, expected_type, { .must_be_constant = analysis.must_be_constant, zero }); + value_type = unary_value.type; + } + + typecheck(module, expected_type, value_type); + }, else => { // TODO @@ -5607,6 +8031,407 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi typecheck(module, expected_type, value_type); }, + .call => + { + >call = &value.content.call; + >callable = call.callable; + analyze_type(module, callable, zero, { .must_be_constant = analysis.must_be_constant, zero }); + + >function_type: &Type = zero; + + switch (callable.id) + { + .variable => + { + >variable = callable.content.variable; + >variable_type = variable.type; + + switch (variable_type.id) + { + .function => + { + function_type = variable_type; + }, + .pointer => + { + >element_type = resolve_alias(module, variable_type.content.pointer.element_type); + + switch (element_type.id) + { + .function => + { + function_type = element_type; + }, + else => { report_error(); }, + } + }, + else => + { + report_error(); + }, + } + }, + else => + { + report_error(); + }, + } + + assert(function_type != zero); + assert(function_type.id == .function); + call.function_type = function_type; + + >semantic_argument_types = function_type.content.function.base.semantic_argument_types; + >call_arguments = call.arguments; + + if (function_type.content.function.base.is_variable_argument) + { + if (call_arguments.length < semantic_argument_types.length) + { + report_error(); + } + } + else + { + if (call_arguments.length != semantic_argument_types.length) + { + report_error(); + } + } + + for (i: 0..semantic_argument_types.length) + { + >argument_type = semantic_argument_types[i]; + >call_argument = call_arguments[i]; + analyze_type(module, call_argument, argument_type, { .must_be_constant = analysis.must_be_constant, zero }); + check_types(module, argument_type, call_argument.type); + } + + for (i: semantic_argument_types.length..call_arguments.length) + { + >call_argument = call_arguments[i]; + analyze_type(module, call_argument, zero, { .must_be_constant = analysis.must_be_constant, zero }); + } + + >semantic_return_type = function_type.content.function.base.semantic_return_type; + + typecheck(module, expected_type, semantic_return_type); + value_type = semantic_return_type; + }, + .array_initialization => + { + >values = value.content.array_initialization.values; + + if (expected_type != zero) + { + if (expected_type.id != .array) + { + report_error(); + } + + >element_type = expected_type.content.array.element_type; + + if (expected_type.content.array.element_count == 0) + { + // TODO: use existing types? + >element_count = values.length; + expected_type.content.array.element_count = element_count; + assert(string_equal(expected_type.name, "")); + expected_type.name = array_name(module, element_type, element_count); + } + else + { + if (expected_type.content.array.element_count != values.length) + { + report_error(); + } + } + + >is_constant: u1 = 1; + + for (value: values) + { + analyze_type(module, value, element_type, { .must_be_constant = analysis.must_be_constant, zero }); + is_constant = is_constant and value_is_constant(value); + } + + value.content.array_initialization.is_constant = is_constant; + + if (value.kind == .left) // TODO: possible? + { + report_error(); + } + + value_type = expected_type; + } + else + { + #trap(); + } + }, + .array_expression => + { + >array_like = value.content.array_expression.array_like; + // Override the value kind in order for `array_like` to be a l-value + array_like.kind = .left; + analyze_type(module, array_like, zero, { .must_be_constant = analysis.must_be_constant, zero }); + + assert(array_like.kind == .left); + + >array_like_type = array_like.type; + if (array_like_type.id != .pointer) + { + report_error(); + } + + >pointer_element_type = array_like_type.content.pointer.element_type; + >indexing_type = #select(pointer_element_type.id == .enum_array, pointer_element_type.content.enum_array.enum_type, uint64(module)); + + analyze_type(module, value.content.array_expression.index, zero, { .indexing_type = indexing_type, .must_be_constant = analysis.must_be_constant }); + + >element_type: &Type = zero; + + switch (pointer_element_type.id) + { + .array => + { + element_type = pointer_element_type.content.array.element_type; + }, + .struct => + { + >slice_type = pointer_element_type; + + if (!slice_type.content.struct.is_slice) + { + report_error(); + } + + >slice_pointer_type = slice_type.content.struct.fields[0].type; + assert(slice_pointer_type.id == .pointer); + element_type = slice_pointer_type.content.pointer.element_type; + }, + .pointer => + { + element_type = pointer_element_type.content.pointer.element_type; + }, + .enum_array => + { + #trap(); + }, + else => { unreachable; }, + } + + assert(element_type != zero); + + value_type = element_type; + if (value.kind == .left) + { + value_type = get_pointer_type(module, element_type); + } + + typecheck(module, expected_type, value_type); + }, + .enum_literal => + { + if (!expected_type) + { + expected_type = analysis.indexing_type; + } + + if (!expected_type) + { + report_error(); + } + + if (expected_type.id != .enum) + { + report_error(); + } + + value_type = expected_type; + }, + .trap => + { + value_type = noreturn_type(module); + }, + .field_access => + { + >aggregate = value.content.field_access.aggregate; + >field_name = value.content.field_access.field_name; + analyze_type(module, aggregate, zero, { .must_be_constant = analysis.must_be_constant, zero }); + + if (aggregate.kind == .right) + { + report_error(); + } + + >aggregate_type = aggregate.type; + if (aggregate_type.id != .pointer) + { + report_error(); + } + + >aggregate_element_type = aggregate_type.content.pointer.element_type; + >real_aggregate_type = #select(aggregate_element_type.id == .pointer, aggregate_element_type.content.pointer.element_type, aggregate_element_type); + >resolved_aggregate_type = resolve_alias(module, real_aggregate_type); + + switch (resolved_aggregate_type.id) + { + .struct => + { + >result_field: &Field = zero; + >fields = resolved_aggregate_type.content.struct.fields; + + for (i: 0..fields.length) + { + >field = &fields[i]; + + if (string_equal(field_name, field.name)) + { + result_field = field; + break; + } + } + + if (!result_field) + { + report_error(); + } + + >field_type = result_field.type; + + value_type = #select(value.kind == .left, get_pointer_type(module, field_type), field_type); + }, + .union => + { + #trap(); + }, + .bits => + { + #trap(); + }, + .enum_array, .array => + { + #trap(); + }, + .pointer => + { + report_error(); + }, + else => + { + report_error(); + }, + } + + assert(value_type != zero); + + typecheck(module, expected_type, value_type); + }, + .slice_expression => + { + >array_like = value.content.slice_expression.array_like; + >start = value.content.slice_expression.start; + >end = value.content.slice_expression.end; + + if (array_like.kind != .left) + { + report_error(); + } + + analyze_type(module, array_like, zero, { .must_be_constant = analysis.must_be_constant, zero }); + + >pointer_type = array_like.type; + if (pointer_type.id != .pointer) + { + report_error(); + } + + >sliceable_type = resolve_alias(module, pointer_type.content.pointer.element_type); + + >element_type: &Type = zero; + + switch (sliceable_type.id) + { + .pointer => + { + element_type = sliceable_type.content.pointer.element_type; + }, + .struct => + { + if (!sliceable_type.content.struct.is_slice) + { + report_error(); + } + + >slice_pointer_type = sliceable_type.content.struct.fields[0].type; + assert(slice_pointer_type.id == .pointer); + >slice_element_type = slice_pointer_type.content.pointer.element_type; + element_type = slice_element_type; + }, + .array => + { + element_type = sliceable_type.content.array.element_type; + }, + else => { report_error(); }, + } + + assert(element_type != zero); + + >slice_type = get_slice_type(module, element_type); + typecheck(module, expected_type, slice_type); + + >index_type = uint64(module); + + >indices: [_]&Value = [ start, end ]; + + for (index: indices) + { + if (index) + { + analyze_type(module, index, index_type, { .must_be_constant = analysis.must_be_constant, zero }); + + if (index.type.id != .integer) + { + report_error(); + } + } + } + + value_type = slice_type; + }, + .string_literal => + { + >u8_type = uint8(module); + >pointer_type = get_pointer_type(module, u8_type); + >slice_type = get_slice_type(module, u8_type); + + if (pointer_type == expected_type) + { + value_type = expected_type; + } + else if (slice_type == expected_type) + { + value_type = expected_type; + } + else + { + typecheck(module, expected_type, slice_type); + value_type = slice_type; + } + }, + .va_start => + { + >va_list_type = get_va_list_type(module); + typecheck(module, expected_type, va_list_type); + value_type = va_list_type; + }, + .va_arg => + { + analyze_type(module, value.content.va_arg.va_list, get_pointer_type(module, get_va_list_type(module)), { .must_be_constant = analysis.must_be_constant, zero }); + value_type = value.content.va_arg.type; + typecheck(module, expected_type, value_type); + }, else => { #trap(); @@ -5719,7 +8544,26 @@ emit_binary = fn (module: &Module, left: &LLVMValue, left_type: &Type, right: &L }, .pointer => { - #trap(); + >element_type = resolved_value_type.content.pointer.element_type; + resolve_type_in_place(module, element_type); + + if (id != .add and id != .sub) + { + report_error(); + } + + >index = right; + if (id == .sub) + { + index = LLVMBuildNeg(module.llvm.builder, index, ""); + } + + return create_gep(module, { + .type = element_type.llvm.abi, + .pointer = left, + .indices = [ index ][..], + zero, + }); } else => { @@ -5728,16 +8572,939 @@ emit_binary = fn (module: &Module, left: &LLVMValue, left_type: &Type, right: &L } } +type_is_abi_equal = fn (module: &Module, a: &Type, b: &Type) u1 +{ + resolve_type_in_place(module, a); + resolve_type_in_place(module, b); + + >result = a == b; + if (!result) + { + result = a.llvm.abi == b.llvm.abi; + } + + return result; +} + +emit_call = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type: &Type) &LLVMValue +{ + assert(value.id == .call); + + >call = &value.content.call; + + >raw_function_type = call.function_type; + >callable = call.callable; + >call_arguments = call.arguments; + + >llvm_callable: &LLVMValue = zero; + + switch (callable.id) + { + .variable => + { + >variable = callable.content.variable; + >variable_type = variable.type; + >llvm_value = variable.storage.llvm; + + switch (variable_type.id) + { + .pointer => + { + >element_type = resolve_alias(module, variable_type.content.pointer.element_type); + + switch (element_type.id) + { + .function => + { + llvm_callable = create_load(module, { + .type = get_pointer_type(module, raw_function_type), + .pointer = llvm_value, + zero, + }); + }, + else => { report_error(); }, + } + }, + .function => { llvm_callable = llvm_value; }, + } + }, + else => { report_error(); }, + } + + assert(llvm_callable != zero); + + >llvm_abi_argument_value_buffer: [64]&LLVMValue = undefined; + >llvm_abi_argument_type_buffer: [64]&LLVMType = undefined; + >abi_argument_type_buffer: [64]&Type = undefined; + >argument_abi_buffer: [64]AbiInformation = undefined; + >llvm_abi_argument_type_buffer_slice = llvm_abi_argument_type_buffer[..]; + >abi_argument_type_buffer_slice = abi_argument_type_buffer[..]; + + >abi_argument_count: u16 = 0; + + >uses_in_alloca: u1 = 0; + if (uses_in_alloca) + { + #trap(); + } + + >llvm_indirect_return_value: &LLVMValue = zero; + + >return_abi = &raw_function_type.content.function.abi.return_abi; + >return_abi_kind = return_abi.flags.kind; + + switch (return_abi_kind) + { + .indirect, + .in_alloca, + .coerce_and_expand, + => + { + #trap(); + }, + else => {}, + } + + >available_registers = raw_function_type.content.function.abi.available_registers; + >declaration_semantic_argument_types = raw_function_type.content.function.base.semantic_argument_types; + + for (call_argument_index: 0..call_arguments.length) + { + >is_named_argument = call_argument_index < declaration_semantic_argument_types.length; + >semantic_call_argument_value = call_arguments[call_argument_index]; + + >semantic_argument_type: &Type = undefined; + >argument_abi: AbiInformation = undefined; + + if (is_named_argument) + { + argument_abi = raw_function_type.content.function.abi.argument_abis[call_argument_index]; + semantic_argument_type = argument_abi.semantic_type; + } + else + { + semantic_argument_type = semantic_call_argument_value.type; + argument_abi = abi_system_v_classify_argument(module, &available_registers.system_v, llvm_abi_argument_type_buffer_slice, abi_argument_type_buffer_slice, { + .type = resolve_alias(module, semantic_argument_type), + .abi_start = abi_argument_count, + .is_named_argument = 0, + zero, + }); + } + + assert(semantic_argument_type != zero); + + resolve_type_in_place(module, semantic_argument_type); + + if (is_named_argument) + { + // TODO: better slice single statement + >llvm_abi_argument_types = llvm_abi_argument_type_buffer_slice[#extend(argument_abi.abi_start)..#extend(argument_abi.abi_start + argument_abi.abi_count)]; + >destination_abi_argument_types = abi_argument_type_buffer_slice[#extend(argument_abi.abi_start)..#extend(argument_abi.abi_start + argument_abi.abi_count)]; + >source_abi_argument_types = raw_function_type.content.function.abi.abi_argument_types[#extend(argument_abi.abi_start)..#extend(argument_abi.abi_start + argument_abi.abi_count)]; + + for (i: 0..argument_abi.abi_count) + { + llvm_abi_argument_types[i] = source_abi_argument_types[i].llvm.abi; + destination_abi_argument_types[i] = source_abi_argument_types[i]; + } + } + + argument_abi_buffer[call_argument_index] = argument_abi; + + if (argument_abi.padding.type != zero) + { + #trap(); + } + + assert(abi_argument_count == argument_abi.abi_start); + + switch (argument_abi.flags.kind) + { + .direct, + .extend, + => + { + >coerce_to_type = abi_get_coerce_to_type(&argument_abi); + resolve_type_in_place(module, coerce_to_type); + + if (coerce_to_type.id != .struct and argument_abi.attributes.direct.offset == 0 and type_is_abi_equal(module, semantic_argument_type, coerce_to_type)) + { + emit_value(module, semantic_call_argument_value, .abi, 0); + + >evaluation_kind = get_evaluation_kind(argument_abi.semantic_type); + >v: &Value = undefined; + + switch (evaluation_kind) + { + .scalar => { v = semantic_call_argument_value; }, + .aggregate => { #trap(); }, + .complex => { #trap(); }, + } + + if (!type_is_abi_equal(module, coerce_to_type, v.type)) + { + #trap(); + } + + llvm_abi_argument_value_buffer[abi_argument_count] = v.llvm; + abi_argument_count += 1; + } + else + { + if (coerce_to_type.id == .struct and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) + { + #trap(); + } + + // TODO: fix this hack and collapse it into the generic path + if (coerce_to_type == uint8(module) and semantic_argument_type == uint1(module)) + { + #trap(); + } + else + { + >evaluation_kind = get_evaluation_kind(semantic_argument_type); + >src: &Value = zero; + + switch (evaluation_kind) + { + .scalar => { #trap(); }, + .aggregate => { src = semantic_call_argument_value; }, + .complex => { #trap(); }, + } + + assert(src != zero); + + if (argument_abi.attributes.direct.offset != 0) + { + report_error(); + } + + if (coerce_to_type.id == .struct and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) + { + >source_type_is_scalable: u1 = 0; + if (source_type_is_scalable) + { + #trap(); + } + else + { + if (src.kind == .right and !value_is_constant(src)) + { + if (!type_is_slice(src.type)) + { + switch (src.id) + { + .aggregate_initialization, + .variable, + .field_access, + => + { + #trap(); + }, + else => + { + #trap(); + } + } + } + } + + emit_value(module, src, .memory, 0); + + >destination_size = get_byte_size(coerce_to_type); + >source_size = get_byte_size(argument_abi.semantic_type); + >alignment = get_byte_alignment(argument_abi.semantic_type); + + >source = src.llvm; + + if (source_size < destination_size) + { + #trap(); + } + + assert(coerce_to_type.id == .struct); + >coerce_fields = coerce_to_type.content.struct.fields; + + // TODO: + assert(argument_abi.attributes.direct.offset == 0); + + switch (semantic_call_argument_value.kind) + { + .left => + { + #trap(); + }, + .right => + { + if (type_is_abi_equal(module, coerce_to_type, semantic_argument_type)) + { + for (i: 0..coerce_fields.length) + { + llvm_abi_argument_value_buffer[abi_argument_count] = LLVMBuildExtractValue(module.llvm.builder, source, #truncate(i), ""); + abi_argument_count += 1; + } + } + else + { + switch (semantic_call_argument_value.id) + { + .aggregate_initialization => + { + #trap(); + } + .zero => + { + #trap(); + }, + else => + { + #trap(); + }, + } + } + }, + } + } + } + else + { + #trap(); + } + } + } + }, + .indirect, + .indirect_aliased, + => + { + #trap(); + }, + .ignore => { unreachable; }, + else => { #trap(); }, // TODO + } + + // TODO: uncomment this + assert(abi_argument_count == argument_abi.abi_start + argument_abi.abi_count); + } + + >declaration_abi_argument_types = raw_function_type.content.function.abi.abi_argument_types; + + if (raw_function_type.content.function.base.is_variable_argument) + { + assert(declaration_abi_argument_types.length <= #extend(abi_argument_count)); + } + else + { + assert(declaration_abi_argument_types.length == #extend(abi_argument_count)); + } + + assert(raw_function_type.llvm.abi != zero); + + >llvm_call = LLVMBuildCall2(module.llvm.builder, raw_function_type.llvm.abi, llvm_callable, &llvm_abi_argument_value_buffer[0], #extend(abi_argument_count), ""); + >llvm_calling_convention: LLVMCallingConvention = undefined; + switch (raw_function_type.content.function.base.calling_convention) + { + .c => { llvm_calling_convention = .c; }, + } + + LLVMSetInstructionCallConv(llvm_call, llvm_calling_convention); + + >argument_abis = argument_abi_buffer[..call_arguments.length]; + + emit_attributes(module, llvm_call, &LLVMAddCallSiteAttribute, { + .return_abi = return_abi, + .argument_abis = argument_abis, + .abi_argument_types = abi_argument_type_buffer[..#extend(abi_argument_count)], + .abi_return_type = raw_function_type.content.function.abi.abi_return_type, + .attributes = zero, + }); + + switch (return_abi_kind) + { + .ignore => + { + assert(return_abi.semantic_type == noreturn_type(module) or return_abi.semantic_type == void_type(module)); + return llvm_call; + }, + .direct, + .extend, + => + { + >coerce_to_type = abi_get_coerce_to_type(return_abi); + + if (return_abi.attributes.direct.offset == 0 and type_is_abi_equal(module, return_abi.semantic_type, coerce_to_type)) + { + >evaluation_kind = get_evaluation_kind(coerce_to_type); + + switch (evaluation_kind) + { + .scalar => { return llvm_call; }, + .aggregate => {}, + .complex => { unreachable; }, + } + + #trap(); + } + + #trap(); + }, + .indirect => + { + assert(llvm_indirect_return_value != zero); + return llvm_indirect_return_value; + }, + else => { unreachable; }, + } +} + +emit_constant_array = fn (module: &Module, elements: []&Value, element_type: &Type) &LLVMValue +{ + >value_buffer: [64]&LLVMValue = undefined; + + resolve_type_in_place(module, element_type); + + for (i: 0..elements.length) + { + >v = elements[i]; + emit_value(module, v, .memory, 1); + value_buffer[i] = v.llvm; + } + + >constant_array = LLVMConstArray2(element_type.llvm.memory, &value_buffer[0], elements.length); + return constant_array; +} + +emit_intrinsic_call = fn (module: &Module, index: LLVMIntrinsicIndex, argument_types: []&LLVMType, argument_values: []&LLVMValue) &LLVMValue +{ + >intrinsic_id = module.llvm.intrinsic_table[index]; + >intrinsic_function = LLVMGetIntrinsicDeclaration(module.llvm.module, intrinsic_id, argument_types.pointer, argument_types.length); + >intrinsic_function_type = LLVMIntrinsicGetType(module.llvm.context, intrinsic_id, argument_types.pointer, argument_types.length); + >call = LLVMBuildCall2(module.llvm.builder, intrinsic_function_type, intrinsic_function, argument_values.pointer, #truncate(argument_values.length), ""); + return call; +} + +StructLikeFieldAccess = struct +{ + type: &Type, + struct_type: &LLVMType, + field_index: u32, +} + +emit_field_access = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type: &Type, type_kind: TypeKind) &LLVMValue +{ + assert(value.id == .field_access); + >aggregate = value.content.field_access.aggregate; + >field_name = value.content.field_access.field_name; + + emit_value(module, aggregate, .memory, 0); + + assert(aggregate.kind == .left); + >aggregate_type = aggregate.type; + assert(aggregate_type.id == .pointer); + >aggregate_element_type = aggregate_type.content.pointer.element_type; + + >real_aggregate_type = #select(aggregate_element_type.id == .pointer, aggregate_element_type.content.pointer.element_type, aggregate_element_type); + >resolved_aggregate_type = resolve_alias(module, real_aggregate_type); + resolve_type_in_place(module, resolved_aggregate_type); + + >v: &LLVMValue = undefined; + + if (real_aggregate_type != aggregate_element_type) + { + #trap(); + } + else + { + v = aggregate.llvm; + } + + switch (resolved_aggregate_type.id) + { + .struct, .union => + { + >field_access: StructLikeFieldAccess = undefined; + + switch (resolved_aggregate_type.id) + { + .struct => + { + >result_field: &Field = zero; + >fields = resolved_aggregate_type.content.struct.fields; + + for (i: 0..fields.length) + { + >field = &fields[i]; + if (string_equal(field_name, field.name)) + { + result_field = field; + break; + } + } + + assert(result_field != zero); + >field_index: u32 = #truncate(result_field - fields.pointer); + + field_access = { + .type = resolved_aggregate_type.content.struct.fields[field_index].type, + .field_index = field_index, + .struct_type = resolved_aggregate_type.llvm.memory, + }; + }, + .union => + { + #trap(); + }, + else => { unreachable; }, + } + + >gep = LLVMBuildStructGEP2(module.llvm.builder, field_access.struct_type, v, field_access.field_index, ""); + + if (left_llvm) + { + #trap(); + } + else + { + switch (value.kind) + { + .left => { return gep; }, + .right => + { + >load = create_load(module, { + .type = field_access.type, + .pointer = gep, + .kind = type_kind, + zero, + }); + return load; + }, + } + } + }, + .bits => + { + #trap(); + }, + .enum_array, .array => + { + #trap(); + }, + else => { unreachable; }, + } +} + +emit_slice_expresion = fn (module: &Module, value: &Value) [2]&LLVMValue +{ + assert(value.id == .slice_expression); + + >value_type = value.type; + assert(value_type != zero); + assert(type_is_slice(value_type)); + >slice_pointer_type = value_type.content.struct.fields[0].type; + assert(slice_pointer_type.id == .pointer); + >slice_element_type = slice_pointer_type.content.pointer.element_type; + + >index_type = uint64(module); + resolve_type_in_place(module, index_type); + + >llvm_index_type = index_type.llvm.abi; + >index_zero = LLVMConstInt(llvm_index_type, 0, 0); + + >array_like = value.content.slice_expression.array_like; + >start = value.content.slice_expression.start; + >end = value.content.slice_expression.end; + + assert(array_like.kind == .left); + emit_value(module, array_like, .memory, 0); + + >pointer_type = array_like.type; + assert(pointer_type.id == .pointer); + >sliceable_type = pointer_type.content.pointer.element_type; + + >has_start = start != zero; + + if (start != zero and? start.id == .constant_integer and? start.content.constant_integer.value == 0) + { + has_start = 0; + } + + if (has_start) + { + emit_value(module, start, .memory, 0); + } + + if (end) + { + emit_value(module, end, .memory, 0); + } + + switch (sliceable_type.id) + { + .pointer => + { + #trap(); + }, + .struct => + { + #trap(); + }, + .array => + { + assert(sliceable_type.content.array.element_type == slice_element_type); + + >slice_pointer = array_like.llvm; + + if (has_start) + { + slice_pointer = create_gep(module, { + .type = sliceable_type.llvm.memory, + .pointer = slice_pointer, + .indices = [ index_zero, start.llvm ][..], + zero, + }); + } + + >slice_length: &LLVMValue = zero; + if (has_start) + { + #trap(); + } + else if (end != zero) + { + slice_length = end.llvm; + } + else + { + >element_count = sliceable_type.content.array.element_count; + slice_length = LLVMConstInt(llvm_index_type, element_count, 0); + } + + assert(slice_length != zero); + + return [ slice_pointer, slice_length ]; + }, + } +} + +emit_slice_result = fn (module: &Module, slice_values: [2]&LLVMValue, slice_type: &LLVMType) &LLVMValue +{ + >result = LLVMGetPoison(slice_type); + result = LLVMBuildInsertValue(module.llvm.builder, result, slice_values[0], 0, ""); + result = LLVMBuildInsertValue(module.llvm.builder, result, slice_values[1], 1, ""); + return result; +} + +emit_va_arg_from_memory = fn (module: &Module, va_list_pointer: &LLVMValue, va_list_struct: &Type, argument_type: &Type) &LLVMValue +{ + assert(va_list_struct.id == .struct); + + >overflow_arg_area_pointer = LLVMBuildStructGEP2(module.llvm.builder, va_list_struct.llvm.abi, va_list_pointer, 2, ""); + >overflow_arg_area_type = va_list_struct.content.struct.fields[2].type; + >overflow_arg_area = create_load(module, { + .type = overflow_arg_area_type, + .pointer = overflow_arg_area_pointer, + zero, + }); + + if (get_byte_alignment(argument_type) > 8) + { + #trap(); + } + + >argument_type_size = get_byte_size(argument_type); + + >raw_offset = align_forward(argument_type_size, 8); + >u32_type = uint32(module).llvm.abi; + >offset = LLVMConstInt(u32_type, raw_offset, 0); + + >new_overflow_arg_area = create_gep(module, { + .type = u32_type, + .pointer = overflow_arg_area, + .indices = [ offset ][..], + .not_inbounds = 1, + }); + + create_store(module, { + .source = new_overflow_arg_area, + .destination = overflow_arg_area_pointer, + .type = overflow_arg_area_type, + zero, + }); + + return overflow_arg_area; +} + +emit_block = fn (module: &Module, basic_block: &LLVMBasicBlock) void +{ + >current_basic_block = LLVMGetInsertBlock(module.llvm.builder); + if (current_basic_block) + { + if (!LLVMGetBasicBlockTerminator(current_basic_block)) + { + LLVMBuildBr(module.llvm.builder, basic_block); + } + } + + assert(LLVMGetBasicBlockParent(basic_block) != zero); + LLVMPositionBuilderAtEnd(module.llvm.builder, basic_block); +} + +emit_va_arg = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type: &Type, llvm_function: &LLVMValue) &LLVMValue +{ + assert(value.id == .va_arg); + + >raw_va_list_type = get_va_list_type(module); + >va_list_value = value.content.va_arg.va_list; + emit_value(module, va_list_value, .memory, 0); + + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + >index_zero = LLVMConstNull(u64_type.llvm.memory); + >va_list_value_llvm = create_gep(module, { + .type = raw_va_list_type.llvm.memory, + .pointer = va_list_value.llvm, + .indices = [ index_zero, index_zero ][..], + zero, + }); + + >va_arg_type = value.content.va_arg.type; + >result = abi_system_v_classify_argument_type(module, va_arg_type, zero); + >abi = result.abi; + >needed_registers = result.needed_registers; + assert(abi.flags.kind != .ignore); + + assert(raw_va_list_type.id == .array); + >va_list_struct = raw_va_list_type.content.array.element_type; + + >address: &LLVMValue = zero; + + if (needed_registers.gpr == 0 and needed_registers.sse == 0) + { + address = emit_va_arg_from_memory(module, va_list_value_llvm, va_list_struct, va_arg_type); + } + else + { + >va_list_struct_llvm = va_list_struct.llvm.memory; + + >gpr_offset_pointer: &LLVMValue = zero; + >gpr_offset: &LLVMValue = zero; + + if (needed_registers.gpr != 0) + { + gpr_offset_pointer = LLVMBuildStructGEP2(module.llvm.builder, va_list_struct_llvm, va_list_value_llvm, 0, ""); + gpr_offset = create_load(module, { + .type = va_list_struct.content.struct.fields[0].type, + .pointer = gpr_offset_pointer, + .alignment = 16, + zero, + }); + } + else + { + #trap(); + } + + >raw_in_regs = 48 - needed_registers.gpr * 8; + >u32_type = uint32(module); + resolve_type_in_place(module, u32_type); + >u32_llvm = u32_type.llvm.memory; + + >in_regs: &LLVMValue = zero; + if (needed_registers.gpr != 0) + { + in_regs = LLVMConstInt(u32_llvm, #extend(raw_in_regs), 0); + } + else + { + #trap(); + } + + if (needed_registers.gpr != 0) + { + in_regs = LLVMBuildICmp(module.llvm.builder, .ule, gpr_offset, in_regs, ""); + } + else + { + #trap(); + } + + assert(in_regs != zero); + + >fp_offset_pointer: &LLVMValue = zero; + if (needed_registers.sse != 0) + { + #trap(); + } + + >fp_offset: &LLVMValue = zero; + if (needed_registers.sse != 0) + { + #trap(); + } + + >raw_fits_in_fp = 176 - needed_registers.sse * 16; + >fits_in_fp: &LLVMValue = zero; + if (needed_registers.sse != 0) + { + #trap(); + } + + if (needed_registers.sse != 0 and needed_registers.gpr != 0) + { + #trap(); + } + + >in_reg_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "va_arg.in_reg"); + >in_mem_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "va_arg.in_mem"); + >end_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "va_arg.end"); + + LLVMBuildCondBr(module.llvm.builder, in_regs, in_reg_block, in_mem_block); + + emit_block(module, in_reg_block); + + >reg_save_area_type = va_list_struct.content.struct.fields[3].type; + >reg_save_area = create_load(module, { + .type = reg_save_area_type, + .pointer = LLVMBuildStructGEP2(module.llvm.builder, va_list_struct_llvm, va_list_value_llvm, 3, ""), + .alignment = 16, + zero, + }); + + >register_address: &LLVMValue = zero; + + if (needed_registers.gpr != 0 and needed_registers.sse != 0) + { + #trap(); + } + else if (needed_registers.gpr != 0) + { + >t = reg_save_area_type.content.pointer.element_type; + resolve_type_in_place(module, t); + + register_address = create_gep(module, { + .type = t.llvm.abi, + .pointer = reg_save_area, + .indices = [ gpr_offset ][..], + .not_inbounds = 1, + }); + } + else if (needed_registers.sse == 1) + { + #trap(); + } + else if (needed_registers.sse == 2) + { + #trap(); + } + else + { + unreachable; + } + + if (needed_registers.gpr != 0) + { + >raw_offset = needed_registers.gpr * 8; + >new_offset = LLVMBuildAdd(module.llvm.builder, gpr_offset, LLVMConstInt(u32_llvm, #extend(raw_offset), 0), ""); + create_store(module, { + .source = new_offset, + .destination = gpr_offset_pointer, + .type = u32_type, + zero, + }); + } + + if (needed_registers.sse != 0) + { + #trap(); + } + + LLVMBuildBr(module.llvm.builder, end_block); + + emit_block(module, in_mem_block); + + >memory_address = emit_va_arg_from_memory(module, va_list_value_llvm, va_list_struct, va_arg_type); + + emit_block(module, end_block); + + >phi = LLVMBuildPhi(module.llvm.builder, module.llvm.pointer_type, ""); + + >values: [_]&LLVMValue = [ register_address, memory_address ]; + >blocks: [_]&LLVMBasicBlock = [ in_reg_block, in_mem_block ]; + LLVMAddIncoming(phi, &values[0], &blocks[0], 2); + + address = phi; + } + + assert(address != zero); + + >evaluation_kind = get_evaluation_kind(va_arg_type); + + switch (evaluation_kind) + { + .scalar => + { + assert(!left_llvm); + assert(!left_type); + return create_load(module, { + .type = va_arg_type, + .pointer = address, + zero, + }); + }, + .aggregate => + { + if (left_llvm) + { + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + + >memcpy_size = get_byte_size(va_arg_type); + >alignment = get_byte_alignment(va_arg_type); + LLVMBuildMemCpy(module.llvm.builder, left_llvm, alignment, address, alignment, LLVMConstInt(u64_type.llvm.abi, memcpy_size, 0)); + return left_llvm; + } + else + { + #trap(); + } + }, + .complex => { #trap(); }, + } +} + emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_constant: u1) void { + >must_be_constant = expect_constant or (!module.current_function and !module.current_macro_instantiation); + + >parent_function_global: &Global = zero; + if (module.current_function) + { + parent_function_global = module.current_function; + } + else if (module.current_macro_instantiation) + { + #trap(); + } + else + { + assert(must_be_constant); + } + + >llvm_function: &LLVMValue = zero; + if (parent_function_global) + { + llvm_function = parent_function_global.variable.storage.llvm; + assert(llvm_function != zero); + } + assert(value.type != zero); assert(!value.llvm); >resolved_value_type = resolve_alias(module, value.type); resolve_type_in_place(module, resolved_value_type); - >must_be_constant = expect_constant or (!module.current_function and !module.current_macro_instantiation); - >llvm_value: &LLVMValue = zero; switch (value.id) @@ -5826,6 +9593,64 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con llvm_value = LLVMBuildTrunc(module.llvm.builder, llvm_unary_value, destination_type, ""); }, + .ampersand => + { + assert(resolved_value_type == resolved_unary_type); + llvm_value = llvm_unary_value; + }, + .dereference => + { + switch (value.kind) + { + .right => + { + >pointer_type = unary_value.type; + assert(pointer_type.id == .pointer); + >child_type = resolve_alias(module, pointer_type.content.pointer.element_type); + assert(child_type == resolved_value_type); + >load = create_load(module, { + .type = child_type, + .pointer = unary_value.llvm, + .kind = type_kind, + zero, + }); + llvm_value = load; + }, + .left => + { + #trap(); + } + } + }, + .int_from_enum => + { + llvm_value = llvm_unary_value; + }, + .exclamation => + { + if (resolved_value_type == resolved_unary_type) + { + llvm_value = LLVMBuildNot(module.llvm.builder, llvm_unary_value, ""); + } + else + { + switch (resolved_unary_type.id) + { + .pointer => + { + llvm_value = LLVMBuildICmp(module.llvm.builder, .eq, llvm_unary_value, LLVMConstNull(resolved_unary_type.llvm.abi), ""); + }, + else => + { + report_error(); + }, + } + } + }, + .int_from_pointer => + { + llvm_value = LLVMBuildPtrToInt(module.llvm.builder, llvm_unary_value, resolved_value_type.llvm.abi, ""); + }, else => { #trap(); }, } }, @@ -5921,6 +9746,212 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con }, } }, + .call => + { + llvm_value = emit_call(module, value, zero, zero); + }, + .array_initialization => + { + >values = value.content.array_initialization.values; + + if (value.content.array_initialization.is_constant) + { + assert(value.kind == .right); + assert(resolved_value_type.id == .array); + >element_type = resolved_value_type.content.array.element_type; + llvm_value = emit_constant_array(module, values, element_type); + } + else + { + #trap(); + } + }, + .array_expression => + { + >array_like = value.content.array_expression.array_like; + >index = value.content.array_expression.index; + + if (array_like.kind == .right) + { + report_error(); + } + + emit_value(module, array_like, .memory, must_be_constant); + emit_value(module, index, .memory, must_be_constant); + + >array_like_type = array_like.type; + assert(array_like_type.id == .pointer); + + >pointer_element_type = array_like_type.content.pointer.element_type; + + switch (pointer_element_type.id) + { + .enum_array, + .array, + => + { + >array_type = pointer_element_type; + + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + >u64_llvm = u64_type.llvm.abi; + >zero_index = LLVMConstNull(u64_llvm); + + >element_type: &Type = zero; + >llvm_index = index.llvm; + + switch (array_type.id) + { + .array => + { + element_type = array_type.content.array.element_type; + }, + .enum_array => + { + #trap(); + }, + else => { unreachable; }, + } + + assert(element_type != zero); + assert(llvm_index != zero); + + >indices: [2]&LLVMValue = [ zero_index, llvm_index ]; + + >gep = create_gep(module, { + .type = array_type.llvm.memory, + .pointer = array_like.llvm, + .indices = indices[..], + zero, + }); + + switch (value.kind) + { + .left => + { + llvm_value = gep; + }, + .right => + { + llvm_value = create_load(module, { + .type = element_type, + .pointer = gep, + zero, + }); + }, + } + }, + .struct => + { + >slice_type = pointer_element_type; + assert(slice_type.content.struct.is_slice); + >slice_pointer_type = slice_type.content.struct.fields[0].type; + assert(slice_pointer_type.id == .pointer); + >slice_element_type = slice_pointer_type.content.pointer.element_type; + resolve_type_in_place(module, slice_element_type); + + >pointer_load = create_load(module, { + .type = slice_pointer_type, + .pointer = array_like.llvm, + zero, + }); + + >gep = create_gep(module, { + .type = slice_element_type.llvm.memory, + .pointer = pointer_load, + .indices = [ index.llvm ][..], + zero, + }); + + llvm_value = gep; + + if (value.kind == .right) + { + llvm_value = create_load(module, { + .type = slice_element_type, + .pointer = gep, + zero, + }); + } + }, + .pointer => + { + >element_type = pointer_element_type.content.pointer.element_type; + // TODO: consider not emitting the and doing straight GEP? + >pointer_load = create_load(module, { + .type = pointer_element_type, + .pointer = array_like.llvm, + zero, + }); + >gep = create_gep(module, { + .type = element_type.llvm.memory, + .pointer = pointer_load, + .indices = [ index.llvm ][..], + zero, + }); + + llvm_value = gep; + + if (value.kind == .right) + { + llvm_value = create_load(module, { + .type = element_type, + .pointer = gep, + zero, + }); + } + }, + else => + { + #trap(); + }, + } + }, + .enum_literal => + { + assert(resolved_value_type.id == .enum); + >enum_name = value.content.enum_literal; + >result_field: &EnumField = zero; + >fields = resolved_value_type.content.enum.fields; + for (i: 0..fields.length) + { + >field = &fields[i]; + + if (string_equal(enum_name, field.name)) + { + result_field = field; + break; + } + } + + if (!result_field) + { + report_error(); + } + + >llvm_type = get_llvm_type(resolved_value_type, type_kind); + llvm_value = LLVMConstInt(llvm_type, result_field.value, #extend(type_is_signed(resolved_value_type))); + }, + .trap => + { + >call = emit_intrinsic_call(module, ."llvm.trap", zero, zero); + LLVMBuildUnreachable(module.llvm.builder); + LLVMClearInsertionPosition(module.llvm.builder); + llvm_value = call; + }, + .field_access => + { + llvm_value = emit_field_access(module, value, zero, zero, type_kind); + }, + .slice_expression => + { + >slice_values = emit_slice_expresion(module, value); + llvm_value = emit_slice_result(module, slice_values, resolved_value_type.llvm.abi); + }, + .va_arg => + { + llvm_value = emit_va_arg(module, value, zero, zero, llvm_function); + }, else => { #trap(); @@ -5931,6 +9962,29 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con value.llvm = llvm_value; } +emit_string_literal = fn (module: &Module, value: &Value) [2]&LLVMValue +{ + assert(value.id == .string_literal); + >resolved_value_type = resolve_alias(module, value.type); + + >null_terminate: u1 = 1; + >pointer = value.content.string_literal.pointer; + >length = value.content.string_literal.length; + + >constant_string = LLVMConstStringInContext2(module.llvm.context, pointer, length, #extend(!null_terminate)); + + >u8_type = uint8(module); + resolve_type_in_place(module, u8_type); + + >string_type = LLVMArrayType2(u8_type.llvm.abi, length + #extend(null_terminate)); + >is_constant: u1 = 1; + >thread_local_mode: LLVMThreadLocalMode = .none; + >externally_initialized: u1 = 0; + >alignment: u32 = 1; + >global = llvm_create_global_variable(module.llvm.module, string_type, is_constant, .internal, constant_string, "conststring", thread_local_mode, externally_initialized, alignment, .global); + return [ global, LLVMConstInt(uint64(module).llvm.abi, length, 0) ]; +} + emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, right: &Value) void { assert(right.llvm == zero); @@ -5966,7 +10020,71 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, }, .aggregate => { - #trap(); + switch (right.id) + { + .array_initialization => + { + >values = right.content.array_initialization.values; + assert(resolved_value_type.id == .array); + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + >element_type = resolved_value_type.content.array.element_type; + >element_count = resolved_value_type.content.array.element_count; + + if (right.content.array_initialization.is_constant) + { + emit_value(module, right, .memory, 0); + + >is_constant: u1 = 1; + >linkage: LLVMLinkage = .internal; + >initial_value = right.llvm; + >thread_local_mode: LLVMThreadLocalMode = .none; + >externally_initialized: u1 = 0; + >alignment = get_byte_alignment(resolved_value_type); + >unnamed_address: LLVMUnnamedAddress = .global; + + >global = llvm_create_global_variable(module.llvm.module, resolved_value_type.llvm.memory, is_constant, linkage, initial_value, "array.init", thread_local_mode, externally_initialized, alignment, unnamed_address); + + >memcpy_size = get_byte_size(resolved_value_type); + LLVMBuildMemCpy(module.llvm.builder, left_llvm, alignment, global, alignment, LLVMConstInt(u64_type.llvm.abi, memcpy_size, 0)); + } + else + { + #trap(); + } + }, + .string_literal => + { + >string_literal = emit_string_literal(module, right); + >slice_type = get_slice_type(module, uint8(module)); + + for (i: 0..string_literal.length) + { + >member_pointer = LLVMBuildStructGEP2(module.llvm.builder, slice_type.llvm.abi, left_llvm, #truncate(i), ""); + >slice_member_type = slice_type.content.struct.fields[i].type; + create_store(module, { + .source = string_literal[i], + .destination = member_pointer, + .type = slice_member_type, + zero, + }); + } + }, + .va_start => + { + assert(resolved_value_type == get_va_list_type(module)); + assert(pointer_type.content.pointer.element_type == get_va_list_type(module)); + + >argument_types: [_]&LLVMType = [ module.llvm.pointer_type ]; + >argument_values: [_]&LLVMValue = [ left_llvm ]; + + emit_intrinsic_call(module, ."llvm.va_start", argument_types[..], argument_values[..]); + }, + else => + { + #trap(); + }, + } }, .complex => { @@ -6037,6 +10155,27 @@ emit_local = fn (module: &Module, local: &Local) void } } +emit_condition = fn (module: &Module, condition: &Value) &LLVMValue +{ + >llvm_condition = condition.llvm; + >condition_type = condition.type; + assert(llvm_condition != zero); + assert(condition_type != zero); + + assert(condition_type.id == .integer or condition_type.id == .pointer); + + if (!(condition_type.id == .integer and condition_type.content.integer.bit_count == 1)) + { + llvm_condition = LLVMBuildICmp(module.llvm.builder, .ne, llvm_condition, LLVMConstNull(condition_type.llvm.abi), ""); + } + + assert(llvm_condition != zero); + + return llvm_condition; +} + +analyze_block = fn (module: &Module, block: &Block) void; + analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) void { >parent_function_global: &Global = undefined; @@ -6139,6 +10278,189 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v emit_local(module, local); emit_assignment(module, local.variable.storage.llvm, local.variable.storage.type, local.initial_value); }, + .if => + { + >condition = statement.content.if.condition; + >taken_statement = statement.content.if.if; + >not_taken_statement = statement.content.if.else; + + >taken_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "if.taken"); + >not_taken_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "if.not_taken"); + >exit_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "if.exit"); + + analyze_value(module, condition, zero, .abi, 0); + >llvm_condition = emit_condition(module, condition); + + LLVMBuildCondBr(module.llvm.builder, llvm_condition, taken_block, not_taken_block); + LLVMPositionBuilderAtEnd(module.llvm.builder, taken_block); + + analyze_statement(module, scope, taken_statement); + + if (LLVMGetInsertBlock(module.llvm.builder) != zero) + { + LLVMBuildBr(module.llvm.builder, exit_block); + } + + LLVMPositionBuilderAtEnd(module.llvm.builder, not_taken_block); + if (not_taken_statement != zero) + { + analyze_statement(module, scope, not_taken_statement); + } + + if (LLVMGetInsertBlock(module.llvm.builder) != zero) + { + LLVMBuildBr(module.llvm.builder, exit_block); + } + + LLVMPositionBuilderAtEnd(module.llvm.builder, exit_block); + }, + .block => + { + analyze_block(module, statement.content.block); + }, + .expression => + { + analyze_value(module, statement.content.expression, zero, .memory, 0); + }, + .while => + { + >entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "while.entry"); + LLVMBuildBr(module.llvm.builder, entry_block); + LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); + + >body_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "while.body"); + >continue_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "while.continue"); + >exit_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "while.exit"); + + >previous_continue_block = module.llvm.continue_block; + >previous_exit_block = module.llvm.exit_block; + module.llvm.continue_block = continue_block; + module.llvm.exit_block = exit_block; + + >condition = statement.content.while.condition; + >block = statement.content.while.block; + + if (value_is_constant(condition)) + { + switch (condition.id) + { + .constant_integer => + { + >value = condition.content.constant_integer.value; + if (value == 0) + { + report_error(); + } + }, + else => { report_error(); }, + } + + LLVMBuildBr(module.llvm.builder, body_block); + } + else + { + analyze_value(module, condition, zero, .abi, 0); + + >llvm_condition = emit_condition(module, condition); + LLVMBuildCondBr(module.llvm.builder, llvm_condition, body_block, exit_block); + } + + LLVMPositionBuilderAtEnd(module.llvm.builder, body_block); + + analyze_block(module, block); + + if (LLVMGetInsertBlock(module.llvm.builder)) + { + LLVMBuildBr(module.llvm.builder, continue_block); + } + + LLVMPositionBuilderAtEnd(module.llvm.builder, continue_block); + + LLVMBuildBr(module.llvm.builder, entry_block); + + if (!LLVMGetFirstUse(#pointer_cast(body_block))) + { + #trap(); + } + + if (!LLVMGetFirstUse(#pointer_cast(exit_block))) + { + #trap(); + } + + LLVMPositionBuilderAtEnd(module.llvm.builder, exit_block); + + module.llvm.continue_block = previous_continue_block; + module.llvm.exit_block = previous_exit_block; + }, + .assignment => + { + >left = statement.content.assignment.left; + >right = statement.content.assignment.right; + >id = statement.content.assignment.id; + + analyze_value(module, left, zero, .memory, 0); + + >left_type = left.type; + if (left_type.id != .pointer) + { + report_error(); + } + + >element_type = left_type.content.pointer.element_type; + >left_llvm = left.llvm; + + switch (id) + { + .assign => + { + analyze_type(module, right, element_type, zero); + emit_assignment(module, left_llvm, left_type, right); + }, + else => + { + >evaluation_kind = get_evaluation_kind(element_type); + assert(evaluation_kind == .scalar); + + >load = create_load(module, { + .type = element_type, + .pointer = left_llvm, + .kind = .abi, + zero, + }); + + analyze_value(module, right, element_type, .abi, 0); + + >a = load; + >b = right.llvm; + + >binary_id: BinaryId = undefined; + switch (id) + { + .assign_add => { binary_id = .add; }, + .assign_sub => { binary_id = .sub; }, + .assign_mul => { binary_id = .mul; }, + .assign_div => { binary_id = .div; }, + .assign_rem => { binary_id = .rem; }, + .assign_shift_left => { binary_id = .shift_left; }, + .assign_shift_right => { binary_id = .shift_right; }, + .assign_and => { binary_id = .bitwise_and; }, + .assign_or => { binary_id = .bitwise_or; }, + .assign_xor => { binary_id = .bitwise_xor; }, + .assign => { unreachable; }, + } + + >result = emit_binary(module, a, element_type, b, right.type, binary_id, element_type); + + create_store(module, { + .source = result, + .destination = left_llvm, + .type = element_type, + zero, + }); + }, + } + }, else => { #trap(); @@ -6164,35 +10486,6 @@ analyze_block = fn (module: &Module, block: &Block) void } } -emit_block = fn (module: &Module, basic_block: &LLVMBasicBlock) void -{ - >current_basic_block = LLVMGetInsertBlock(module.llvm.builder); - if (current_basic_block) - { - if (!LLVMGetBasicBlockTerminator(current_basic_block)) - { - LLVMBuildBr(module.llvm.builder, basic_block); - } - } - - assert(LLVMGetBasicBlockParent(basic_block) != zero); - LLVMPositionBuilderAtEnd(module.llvm.builder, basic_block); -} - -type_is_abi_equal = fn (module: &Module, a: &Type, b: &Type) u1 -{ - resolve_type_in_place(module, a); - resolve_type_in_place(module, b); - - >result = a == b; - if (!result) - { - result = a.llvm.abi == b.llvm.abi; - } - - return result; -} - LLVMObjectGenerate = struct { path: []u8, @@ -6346,6 +10639,28 @@ link = fn (module: &Module) void } } +emit_debug_argument = fn (module: &Module, argument: &Argument, basic_block: &LLVMBasicBlock) void +{ + assert(module.has_debug_info); + + resolve_type_in_place(module, argument.variable.type); + + >always_preserve: u1 = 1; + >flags: LLVMDIFlags = zero; + >scope = argument.variable.scope.llvm; + + >parameter_variable = LLVMDIBuilderCreateParameterVariable(module.llvm.di_builder, scope, argument.variable.name.pointer, argument.variable.name.length, argument.index, module.llvm.file, argument.variable.line, argument.variable.type.llvm.debug, #extend(always_preserve), flags); + + >inlined_at = module.llvm.inlined_at; + >debug_location = LLVMDIBuilderCreateDebugLocation(module.llvm.context, argument.variable.line, argument.variable.column, scope, inlined_at); + LLVMDIBuilderInsertDeclareRecordAtEnd(module.llvm.di_builder, argument.variable.storage.llvm, parameter_variable, null_expression(module), debug_location, basic_block); +} + +create_coerced_store = fn (module: &Module, source_value: &LLVMValue, source_type: &Type, destination_value: &LLVMValue, destination_type: &Type, destination_size: u64, destination_volatile: u1) void +{ + #trap(); +} + emit = fn (module: &Module) void { assert(!module.current_function); @@ -6353,7 +10668,7 @@ emit = fn (module: &Module) void assert(!module.current_macro_declaration); llvm_initialize_targets(); >context = LLVMContextCreate(); - >m = llvm_context_create_module(context, module.name); + >m = LLVMModuleCreateWithNameInContext(module.name.pointer, context); >builder = LLVMCreateBuilderInContext(context); >di_builder: &LLVMDIBuilder = zero; @@ -6386,9 +10701,9 @@ emit = fn (module: &Module) void module.scope.llvm = LLVMDIBuilderCreateCompileUnit(di_builder, language, di_file, producer_name.pointer, producer_name.length, #extend(is_optimized), flags.pointer, flags.length, runtime_version, split_name.pointer, split_name.length, emission_kind, 0, 0, #extend(is_optimized), sysroot.pointer, sysroot.length, sdk.pointer, sdk.length); } - >target_triple: []u8 = undefined; - >cpu_model: []u8 = undefined; - >cpu_features: []u8 = undefined; + >target_triple: &u8 = zero; + >cpu_model: &u8 = zero; + >cpu_features: &u8 = zero; if (target_compare(module.target, target_get_native())) { @@ -6402,8 +10717,8 @@ emit = fn (module: &Module) void } >target_machine_options = LLVMCreateTargetMachineOptions(); - LLVMTargetMachineOptionsSetCPU(target_machine_options, cpu_model.pointer); - LLVMTargetMachineOptionsSetFeatures(target_machine_options, cpu_features.pointer); + LLVMTargetMachineOptionsSetCPU(target_machine_options, cpu_model); + LLVMTargetMachineOptionsSetFeatures(target_machine_options, cpu_features); >code_generation_optimization_level: LLVMCodeGenerationOptimizationLevel = undefined; switch (module.build_mode) @@ -6418,7 +10733,7 @@ emit = fn (module: &Module) void >target: &LLVMTarget = zero; >error_message: &u8 = zero; - >result = LLVMGetTargetFromTriple(target_triple.pointer, &target, &error_message); + >result = LLVMGetTargetFromTriple(target_triple, &target, &error_message); if (result != 0) { report_error(); @@ -6426,11 +10741,11 @@ emit = fn (module: &Module) void assert(!error_message); - >target_machine = LLVMCreateTargetMachineWithOptions(target, target_triple.pointer, target_machine_options); + >target_machine = LLVMCreateTargetMachineWithOptions(target, target_triple, target_machine_options); >target_data_layout = LLVMCreateTargetDataLayout(target_machine); LLVMSetModuleDataLayout(m, target_data_layout); - LLVMSetTarget(m, target_triple.pointer); + LLVMSetTarget(m, target_triple); module.llvm = { .context = context, @@ -6499,7 +10814,7 @@ emit = fn (module: &Module) void }, } - >is_reg_call = resolved_calling_convention == .system_v and 0; // TODO: regcall calling convention + >register_call = resolved_calling_convention == .system_v and 0; // TODO: regcall calling convention switch (resolved_calling_convention) { @@ -6510,8 +10825,8 @@ emit = fn (module: &Module) void function_type.abi.available_registers = { .system_v = { - .gpr = #select(is_reg_call, 11, 6), - .sse = #select(is_reg_call, 16, 8), + .gpr = #select(register_call, 11, 6), + .sse = #select(register_call, 16, 8), }, }; @@ -6523,13 +10838,21 @@ emit = fn (module: &Module) void switch (return_abi_kind) { - .direct, .extend => + .direct, + .extend, + => { abi_return_type = function_type.abi.return_abi.coerce_to_type; }, + .ignore, + .indirect, + => + { + abi_return_type = void_type(module); + }, else => { - unreachable; + unreachable; // TODO }, } @@ -6546,8 +10869,24 @@ emit = fn (module: &Module) void { for (i: 0..semantic_argument_types.length) { - #trap(); + >abi = &function_type.abi.argument_abis[i]; + >semantic_argument_type = function_type.base.semantic_argument_types[i]; + >is_named_argument = i < semantic_argument_types.length; + assert(is_named_argument); + + abi.& = abi_system_v_classify_argument(module, &function_type.abi.available_registers.system_v, llvm_abi_argument_type_buffer[..], abi_argument_type_buffer[..], { + .type = semantic_argument_type, + .abi_start = abi_argument_type_count, + .is_named_argument = is_named_argument, + zero, + }); + + abi_argument_type_count += abi.abi_count; } + + >abi_argument_types = arena_allocate_slice[&Type](module.arena, #extend(abi_argument_type_count)); + memcpy(#pointer_cast(abi_argument_types.pointer), #pointer_cast(&abi_argument_type_buffer), #extend(#byte_size(&Type) * abi_argument_type_count)); + function_type.abi.abi_argument_types = abi_argument_types; } }, .win64 => @@ -6584,7 +10923,6 @@ emit = fn (module: &Module) void >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; @@ -6610,7 +10948,7 @@ emit = fn (module: &Module) void }, } - >llvm_function = llvm_module_create_function(module.llvm.module, llvm_function_type, llvm_linkage, default_address_space, global.variable.name); + >llvm_function = llvm_module_create_function(module.llvm.module, llvm_function_type, llvm_linkage, global.variable.name); global.variable.storage.llvm = llvm_function; >llvm_calling_convention: LLVMCallingConvention = undefined; switch (calling_convention) @@ -6624,7 +10962,7 @@ emit = fn (module: &Module) void LLVMSetFunctionCallConv(llvm_function, llvm_calling_convention); emit_attributes(module, llvm_function, &LLVMAddAttributeAtIndex, { - .return_abi = function_type.abi.return_abi, + .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, @@ -6705,8 +11043,8 @@ emit = fn (module: &Module) void >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); + >entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "entry"); + >return_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "return_block"); function.content.function.llvm.return_block = return_block; LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); @@ -6753,7 +11091,189 @@ emit = fn (module: &Module) void { .direct, .extend => { - #trap(); + >first_argument = argument_abi_arguments[0]; + >coerce_to_type = abi_get_coerce_to_type(argument_abi); + + if (coerce_to_type.id != .struct and argument_abi.attributes.direct.offset == 0 and type_is_abi_equal(module, coerce_to_type, argument_abi.semantic_type)) + { + assert(argument_abi.abi_count == 1); + >is_promoted: u1 = 0; + >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)) + { + >bit_count = get_bit_size(argument_abi.semantic_type); + >abi_bit_count = align_bit_count(bit_count); + >is_signed = type_is_signed(argument_abi.semantic_type); + >destination_type = integer_type(module, { .bit_count = abi_bit_count, .signed = is_signed }); + >alloca = create_alloca(module, { + .type = destination_type, + .name = argument.variable.name, + zero, + }); + + >result: &LLVMValue = undefined; + + if (bit_count < abi_bit_count) + { + >llvm_type = destination_type.llvm.memory; + if (is_signed) + { + result = LLVMBuildSExt(module.llvm.builder, first_argument, llvm_type, ""); + } + else + { + result = LLVMBuildZExt(module.llvm.builder, first_argument, llvm_type, ""); + } + } + else + { + #trap(); + } + + create_store(module, { + .source = result, + .destination = alloca, + .type = destination_type, + zero, + }); + + semantic_argument_storage = alloca; + } + else + { + >alloca = create_alloca(module, { + .type = argument_abi.semantic_type, + .name = argument.variable.name, + zero, + }); + + create_store(module, { + .source = first_argument, + .destination = alloca, + .type = argument_abi.semantic_type, + zero, + }); + + semantic_argument_storage = alloca; + } + } + else + { + >is_fixed_vector_type: u1 = 0; + if (is_fixed_vector_type) + { + #trap(); + } + + if (coerce_to_type.id == .struct and coerce_to_type.content.struct.fields.length > 1 and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) + { + >contains_homogeneous_scalable_vector_types: u1 = 0; + if (contains_homogeneous_scalable_vector_types) + { + #trap(); + } + } + + >alloca = create_alloca(module, { + .type = argument_abi.semantic_type, + .name = argument.variable.name, + zero, + }); + + >pointer: &LLVMValue = undefined; + >pointer_type: &Type = undefined; + + if (argument_abi.attributes.direct.offset > 0) + { + #trap(); + } + else + { + pointer = alloca; + pointer_type = argument_abi.semantic_type; + } + + if (coerce_to_type.id == .struct and coerce_to_type.content.struct.fields.length > 1 and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) + { + >struct_size = get_byte_size(coerce_to_type); + >pointer_element_size = get_byte_size(pointer_type); + >is_scalable: u1 = 0; + + if (is_scalable) + { + #trap(); + } + else + { + >source_size = struct_size; + >destination_size = pointer_element_size; + >address_alignment = get_byte_alignment(argument_abi.semantic_type); + + >address: &LLVMValue = undefined; + + if (source_size <= destination_size) + { + address = alloca; + } + else + { + address = create_alloca(module, { + .type = coerce_to_type, + .name = "coerce", + .alignment = address_alignment, + }); + } + + >fields = coerce_to_type.content.struct.fields; + assert(fields.length == #extend(argument_abi.abi_count)); + + resolve_type_in_place(module, coerce_to_type); + + for (i: 0..fields.length) + { + >field = &fields[i]; + >gep = LLVMBuildStructGEP2(module.llvm.builder, coerce_to_type.llvm.abi, address, #truncate(i), ""); + create_store(module, { + .source = argument_abi_arguments[i], + .destination = gep, + .type = fields[i].type, + zero, + }); + } + + if (source_size > destination_size) + { + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + + >memcpy_size = LLVMConstInt(u64_type.llvm.abi, destination_size, 0); + LLVMBuildMemCpy(module.llvm.builder, pointer, address_alignment, address, address_alignment, memcpy_size); + } + } + } + else + { + assert(argument_abi.abi_count == 1); + >abi_argument_type = function_type.abi.abi_argument_types[argument_abi.abi_start]; + >destination_size: u64 = get_byte_size(pointer_type) - #extend(argument_abi.attributes.direct.offset); + >is_volatile: u1 = 0; + create_coerced_store(module, argument_abi_arguments[0], abi_argument_type, pointer, pointer_type, destination_size, is_volatile); + } + + semantic_argument_storage = alloca; + } }, .indirect => { @@ -6764,6 +11284,23 @@ emit = fn (module: &Module) void unreachable; }, } + + assert(semantic_argument_storage != zero); + + >storage = new_value(module); + >value_type = argument.variable.type; + storage.& = { + .type = get_pointer_type(module, value_type), + .id = .argument, + .llvm = semantic_argument_storage, + zero, + }; + argument.variable.storage = storage; + + if (module.has_debug_info) + { + emit_debug_argument(module, argument, entry_block); + } } analyze_block(module, function.content.function.block); @@ -6773,7 +11310,15 @@ emit = fn (module: &Module) void if (current_basic_block) { assert(!LLVMGetBasicBlockTerminator(current_basic_block)); - #trap(); + if (!LLVMGetFirstInstruction(current_basic_block) or !LLVMGetFirstUse(#pointer_cast(current_basic_block))) + { + LLVMReplaceAllUsesWith(#pointer_cast(return_block), #pointer_cast(current_basic_block)); + LLVMDeleteBasicBlock(return_block); + } + else + { + #trap(); + } } else { @@ -6876,12 +11421,13 @@ emit = fn (module: &Module) void LLVMDIBuilderFinalize(module.llvm.di_builder); } - >verification_error_message: []u8 = zero; - if (!llvm_module_verify(module.llvm.module, &verification_error_message)) + >verification_error_message: &u8 = zero; + >verify_result = LLVMVerifyModule(module.llvm.module, .return, &verification_error_message) == 0; + if (!verify_result) { print(llvm_module_to_string(module.llvm.module)); print("\n==========================\nLLVM VERIFICATION ERROR\n==========================\n"); - print(verification_error_message); + print(c_string_to_slice(verification_error_message)); print("\n"); report_error(); } @@ -7184,6 +11730,15 @@ names: [_][]u8 = [ "extend", "integer_max", "integer_hex", + "basic_pointer", + "basic_call", + "basic_branch", + "basic_array", + "basic_enum", + "basic_slice", + "basic_string", + "basic_varargs", + "basic_while", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/compiler.cpp b/src/compiler.cpp index 543d771..8ba2702 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -440,6 +440,8 @@ global_variable String names[] = string_literal("bool_pair"), string_literal("min_max"), string_literal("field_parent_pointer"), + string_literal("leading_trailing_zeroes"), + string_literal("pointer_sub"), }; void entry_point(Slice arguments, Slice envp) @@ -612,7 +614,7 @@ void entry_point(Slice arguments, Slice envp) auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0; if (!success) { - print(string_literal("Self-hosted tests failed to compile: ")); + print(string_literal("Self-hosted tests failed: ")); print(build_mode_to_string(compiler_build_mode)); print(compiler_has_debug_info ? string_literal(" with debug info\n") : string_literal(" with no debug info\n")); bb_fail(); diff --git a/src/compiler.hpp b/src/compiler.hpp index 21049dd..ba16c7c 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -872,7 +872,6 @@ enum class UnaryId plus, ampersand, exclamation, - tilde, enum_name, extend, truncate, @@ -884,6 +883,8 @@ enum class UnaryId dereference, pointer_from_int, enum_from_int, + leading_zeroes, + trailing_zeroes, }; struct ValueUnary @@ -1105,7 +1106,9 @@ struct Value case ValueId::zero: return true; case ValueId::unary: + return unary.value->is_constant(); case ValueId::binary: + return binary.left->is_constant() && binary.right->is_constant(); case ValueId::field_access: case ValueId::array_expression: case ValueId::call: @@ -1197,6 +1200,8 @@ struct LLVMIntrinsicId enum class IntrinsicIndex { + clz, + ctz, smax, smin, trap, @@ -1209,6 +1214,8 @@ enum class IntrinsicIndex }; global_variable String intrinsic_names[] = { + string_literal("llvm.ctlz"), + string_literal("llvm.cttz"), string_literal("llvm.smax"), string_literal("llvm.smin"), string_literal("llvm.trap"), @@ -1389,6 +1396,11 @@ fn Type* sint32(Module* module) return integer_type(module, { .bit_count = 32, .is_signed = true }); } +fn Type* sint64(Module* module) +{ + return integer_type(module, { .bit_count = 64, .is_signed = true }); +} + struct Options { String content; diff --git a/src/emitter.cpp b/src/emitter.cpp index 2aba2c9..128a6d6 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -1,6 +1,36 @@ #include #include +fn LLVMValueRef llvm_module_create_function(Arena* arena, LLVMModuleRef module, LLVMTypeRef function_type, LLVMLinkage linkage_type, String name) +{ + assert(name.pointer[name.length] == 0); + auto function = LLVMAddFunction(module, (char*)name.pointer, function_type); + LLVMSetLinkage(function, linkage_type); + return function; +} + +fn LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMThreadLocalMode thread_local_mode, bool externally_initialized, u32 alignment, LLVMUnnamedAddr unnamed_address) +{ + assert(name.pointer[name.length] == 0); + auto global = LLVMAddGlobal(module, type, (char*)name.pointer); + LLVMSetGlobalConstant(global, is_constant); + LLVMSetLinkage(global, linkage_type); + LLVMSetInitializer(global, initial_value); + LLVMSetThreadLocalMode(global, thread_local_mode); + LLVMSetExternallyInitialized(global, externally_initialized); + LLVMSetAlignment(global, alignment); + LLVMSetUnnamedAddress(global, unnamed_address); + return global; +} + +fn LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef b, LLVMTypeRef type, u32 alignment, String name) +{ + assert(name.pointer[name.length] == 0); + auto alloca = LLVMBuildAlloca(b, type, (char*)name.pointer); + LLVMSetAlignment(alloca, alignment); + return alloca; +} + enum class EvaluationKind { scalar, @@ -106,9 +136,9 @@ fn u64 get_byte_allocation_size(Type* type) struct LLVMGlobal { - String host_triple; - String host_cpu_model; - String host_cpu_features; + char* host_triple; + char* host_cpu_model; + char* host_cpu_features; }; global_variable LLVMGlobal llvm_global; @@ -174,6 +204,110 @@ fn bool is_promotable_integer_type_for_abi(Type* type) } } +fn bool receives_type(Value* value) +{ + switch (value->id) + { + case ValueId::constant_integer: + case ValueId::enum_literal: + case ValueId::string_literal: + case ValueId::zero: + return true; + case ValueId::array_expression: + case ValueId::call: + return false; + case ValueId::variable_reference: + { + return value->kind == ValueKind::left && value->id == ValueId::global; + } break; + case ValueId::field_access: + { + auto aggregate = value->field_access.aggregate; + auto field_name = value->field_access.field_name; + + if (field_name.equal(string_literal("length")) && aggregate->id == ValueId::variable_reference && aggregate->kind == ValueKind::left && aggregate->variable_reference->type->id == TypeId::array) + { + return true; + } + else + { + return false; + } + } break; + case ValueId::unary: + { + auto unary_id = value->unary.id; + auto unary_value = value->unary.value; + + switch (unary_id) + { + case UnaryId::extend: + case UnaryId::truncate: + case UnaryId::pointer_cast: + case UnaryId::pointer_from_int: + return true; + case UnaryId::int_from_pointer: + case UnaryId::ampersand: + case UnaryId::dereference: + case UnaryId::va_end: + case UnaryId::leading_zeroes: + case UnaryId::trailing_zeroes: + case UnaryId::exclamation: + case UnaryId::int_from_enum: + case UnaryId::enum_from_int: + case UnaryId::enum_name: + return false; + case UnaryId::minus: + case UnaryId::plus: + case UnaryId::bitwise_not: + return receives_type(unary_value); + } + } break; + case ValueId::binary: + { + auto left = value->binary.left; + auto right = value->binary.right; + auto binary_id = value->binary.id; + + return receives_type(left) && receives_type(right); + } break; + case ValueId::unary_type: + { + auto unary_type_id = value->unary_type.id; + switch (unary_type_id) + { + case UnaryTypeId::align_of: + case UnaryTypeId::byte_size: + case UnaryTypeId::integer_max: + return true; + default: trap(); + } + } break; + case ValueId::infer_or_ignore: + case ValueId::forward_declared_function: + case ValueId::function: + case ValueId::macro_reference: + case ValueId::macro_instantiation: + case ValueId::global: + case ValueId::array_initialization: + case ValueId::slice_expression: + case ValueId::trap: + case ValueId::va_start: + case ValueId::va_arg: + case ValueId::aggregate_initialization: + case ValueId::undefined: + case ValueId::unreachable: + case ValueId::select: + case ValueId::string_to_enum: + case ValueId::local: + case ValueId::argument: + case ValueId::build_mode: + case ValueId::has_debug_info: + case ValueId::field_parent_pointer: + trap(); + } +} + fn void llvm_initialize_all_raw() { assert(!llvm_initialized); @@ -186,9 +320,9 @@ fn void llvm_initialize_all_raw() LLVMInitializeX86Disassembler(); llvm_global = { - .host_triple = llvm_default_target_triple(), - .host_cpu_model = llvm_host_cpu_name(), - .host_cpu_features = llvm_host_cpu_features(), + .host_triple = LLVMGetDefaultTargetTriple(), + .host_cpu_model = LLVMGetHostCPUName(), + .host_cpu_features = LLVMGetHostCPUFeatures(), }; } @@ -230,7 +364,8 @@ fn u64 integer_max_value(u32 bit_count, bool is_signed) fn void dump_module(Module* module) { - print(llvm_module_to_string(module->llvm.module)); + auto module_str = LLVMPrintModuleToString(module->llvm.module); + print(c_string_to_slice(module_str)); } fn LLVMCallConv llvm_calling_convention(CallingConvention calling_convention) @@ -250,7 +385,7 @@ fn void llvm_initialize(Module* module) llvm_initialize_all(); auto context = LLVMContextCreate(); - auto m = llvm_context_create_module(context, module->name); + auto m = LLVMModuleCreateWithNameInContext((char*)module->name.pointer, context); auto builder = LLVMCreateBuilderInContext(context); LLVMDIBuilderRef di_builder = 0; @@ -279,9 +414,9 @@ fn void llvm_initialize(Module* module) module->scope.llvm = di_compile_unit; } - String target_triple = {}; - String cpu_model = {}; - String cpu_features = {}; + char* target_triple = {}; + char* cpu_model = {}; + char* cpu_features = {}; if (target_compare(module->target, target_get_native())) { @@ -296,8 +431,8 @@ fn void llvm_initialize(Module* module) } auto target_machine_options = LLVMCreateTargetMachineOptions(); - LLVMTargetMachineOptionsSetCPU(target_machine_options, (char*)cpu_model.pointer); - LLVMTargetMachineOptionsSetFeatures(target_machine_options, (char*)cpu_features.pointer); + LLVMTargetMachineOptionsSetCPU(target_machine_options, cpu_model); + LLVMTargetMachineOptionsSetFeatures(target_machine_options, cpu_features); LLVMCodeGenOptLevel code_generation_optimization_level; switch (module->build_mode) @@ -324,18 +459,18 @@ fn void llvm_initialize(Module* module) LLVMTargetRef target = 0; char* error_message = 0; - auto result = LLVMGetTargetFromTriple((char*)target_triple.pointer, &target, &error_message); + auto result = LLVMGetTargetFromTriple(target_triple, &target, &error_message); if (result != 0) { report_error(); } assert(!error_message); - auto target_machine = LLVMCreateTargetMachineWithOptions(target, (char*)target_triple.pointer, target_machine_options); + auto target_machine = LLVMCreateTargetMachineWithOptions(target, target_triple, target_machine_options); auto target_data = LLVMCreateTargetDataLayout(target_machine); LLVMSetModuleDataLayout(m, target_data); - LLVMSetTarget(m, (char*)target_triple.pointer); + LLVMSetTarget(m, target_triple); module->llvm = { .context = context, @@ -353,7 +488,9 @@ fn void llvm_initialize(Module* module) for (u64 i = 0; i < (u64)IntrinsicIndex::count; i += 1) { String name = intrinsic_names[i]; - module->llvm.intrinsic_table[i].n = LLVMLookupIntrinsicID((char*)name.pointer, name.length); + auto intrinsic_id = LLVMLookupIntrinsicID((char*)name.pointer, name.length); + assert(intrinsic_id != 0); + module->llvm.intrinsic_table[i].n = intrinsic_id; } for (u64 i = 0; i < (u64)AttributeIndex::count; i += 1) @@ -754,7 +891,7 @@ fn AbiSystemVClassifyResult abi_system_v_classify_type(Type* type, AbiSystemVCla case TypeId::structure: case TypeId::union_type: { - auto byte_size = type->structure.byte_size; + auto byte_size = get_byte_size(type); if (byte_size <= 64) { @@ -997,7 +1134,8 @@ fn void resolve_type_in_place_debug(Module* module, Type* type) { DwarfType dwarf_type = type->integer.bit_count == 1 ? DwarfType::boolean : (type->integer.is_signed ? DwarfType::signed_type : DwarfType::unsigned_type); LLVMDIFlags flags = {}; - result = LLVMDIBuilderCreateBasicType(module->llvm.di_builder, (char*)type->name.pointer, type->name.length, type->integer.bit_count, (u32)dwarf_type, flags); + auto bit_count = dwarf_type == DwarfType::boolean ? 8 : type->integer.bit_count; + result = LLVMDIBuilderCreateBasicType(module->llvm.di_builder, (char*)type->name.pointer, type->name.length, bit_count, (u32)dwarf_type, flags); } break; case TypeId::pointer: { @@ -1005,7 +1143,8 @@ fn void resolve_type_in_place_debug(Module* module, Type* type) result = type->llvm.debug; if (!result) { - result = LLVMDIBuilderCreatePointerType(module->llvm.di_builder, type->pointer.element_type->llvm.debug, 64, 64, 0, (char*)type->name.pointer, type->name.length); + u32 address_space = 0; + result = LLVMDIBuilderCreatePointerType(module->llvm.di_builder, type->pointer.element_type->llvm.debug, get_bit_size(type), get_byte_alignment(type) * 8, address_space, (char*)type->name.pointer, type->name.length); } } break; case TypeId::array: @@ -1027,7 +1166,7 @@ fn void resolve_type_in_place_debug(Module* module, Type* type) for (u64 i = 0; i < type->enumerator.fields.length; i += 1) { auto& field = type->enumerator.fields[i]; - auto enum_field = LLVMDIBuilderCreateEnumerator(module->llvm.di_builder, (char*)field.name.pointer, field.name.length, field.value, type_is_signed(backing_type)); + auto enum_field = LLVMDIBuilderCreateEnumerator(module->llvm.di_builder, (char*)field.name.pointer, field.name.length, field.value, !type_is_signed(backing_type)); field_buffer[i] = enum_field; } @@ -1416,7 +1555,6 @@ fn Type* get_by_value_argument_pair(Module* module, Type* low, Type* high) return result; } - struct IndirectOptions { Type* semantic_type; @@ -1503,7 +1641,10 @@ fn AbiInformation abi_system_v_get_indirect_result(Module* module, Type* type, u if (free_gpr == 0 && alignment == 8 && size <= 8) { - trap(); + return abi_system_v_get_direct(module, { + .semantic_type = type, + .type = integer_type(module, { .bit_count = size * 8, .is_signed = false }), + }); } else { @@ -1823,7 +1964,7 @@ fn LLVMValueRef create_alloca(Module* module, AllocaOptions options) alignment = get_byte_alignment(abi_type); } - auto alloca = llvm_builder_create_alloca(module->llvm.builder, abi_type->llvm.memory, 0, alignment, options.name); + auto alloca = llvm_builder_create_alloca(module->llvm.builder, abi_type->llvm.memory, alignment, options.name); return alloca; } @@ -2181,7 +2322,6 @@ fn bool unary_is_boolean(UnaryId id) case UnaryId::minus: case UnaryId::plus: case UnaryId::ampersand: - case UnaryId::tilde: case UnaryId::enum_name: case UnaryId::extend: case UnaryId::truncate: @@ -2193,6 +2333,8 @@ fn bool unary_is_boolean(UnaryId id) case UnaryId::dereference: case UnaryId::pointer_from_int: case UnaryId::enum_from_int: + case UnaryId::leading_zeroes: + case UnaryId::trailing_zeroes: return false; } } @@ -2255,56 +2397,79 @@ struct TypeAnalysis fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnalysis analysis); -fn void analyze_binary_type(Module* module, Value* left, Value* right, bool is_boolean, Type* expected_type, bool must_be_constant) +fn void analyze_binary_type(Module* module, Value* left, Value* right, bool is_boolean, Type* expected_type, bool must_be_constant, bool is_sub) { auto left_constant = left->is_constant(); auto right_constant = right->is_constant(); + auto left_receives_type = receives_type(left); + auto right_receives_type = receives_type(right); - if (!expected_type) + if (!expected_type && left_receives_type && right_receives_type) { - if (left_constant && right_constant) + if (left->id == right->id) { - if (!left->type && !right->type) + switch (left->id) { - auto are_string_literal = left->id == ValueId::string_literal && right->id == ValueId::string_literal; - - if (are_string_literal) - { - expected_type = get_slice_type(module, uint8(module)); - } - else - { + case ValueId::string_literal: + { + expected_type = get_slice_type(module, uint8(module)); + } break; + default: report_error(); - } } } + else + { + report_error(); + } } - if (is_boolean || !expected_type) + if (!left_receives_type && !right_receives_type) { - if (left_constant) - { - analyze_type(module, right, 0, { .must_be_constant = must_be_constant }); - analyze_type(module, left, right->type, { .must_be_constant = must_be_constant }); - } - else - { - analyze_type(module, left, 0, { .must_be_constant = must_be_constant }); - analyze_type(module, right, left->type, { .must_be_constant = must_be_constant }); - } + analyze_type(module, left, 0, { .must_be_constant = must_be_constant }); + analyze_type(module, right, 0, { .must_be_constant = must_be_constant }); } - else if (!is_boolean && expected_type) + else if (left_receives_type && !right_receives_type) { + analyze_type(module, right, 0, { .must_be_constant = must_be_constant }); + analyze_type(module, left, right->type, { .must_be_constant = must_be_constant }); + } + else if (!left_receives_type && right_receives_type) + { + analyze_type(module, left, 0, { .must_be_constant = must_be_constant }); + analyze_type(module, right, left->type, { .must_be_constant = must_be_constant }); + } + else if (left_receives_type && right_receives_type) + { + assert(expected_type); + if (is_boolean) + { + report_error(); + } + analyze_type(module, left, expected_type, { .must_be_constant = must_be_constant }); analyze_type(module, right, expected_type, { .must_be_constant = must_be_constant }); } else { - report_error(); // TODO: this might not be an error necessarily? + unreachable(); } assert(left->type); assert(right->type); + + if (expected_type) + { + if (expected_type->id == TypeId::integer && left->type->id == TypeId::pointer && right->type->id == TypeId::pointer && is_sub) + { + check_types(module, left->type, right->type); + } + else if (!is_boolean) + { + typecheck(module, expected_type, left->type); + typecheck(module, expected_type, right->type); + } + } } fn Type* get_va_list_type(Module* module) @@ -2364,7 +2529,9 @@ fn Global* get_enum_name_array_global(Module* module, Type* enum_type) field.name, }; unsigned address_space = 0; - auto name_global = llvm_module_create_global_variable(module->llvm.module, LLVMArrayType2(u8_type->llvm.abi, field.name.length + null_terminate), is_constant, LLVMInternalLinkage, LLVMConstStringInContext2(module->llvm.context, (char*)field.name.pointer, field.name.length, false), arena_join_string(module->arena, array_to_slice(name_parts)), name_before, LLVMNotThreadLocal, address_space, false); + auto initial_value = LLVMConstStringInContext2(module->llvm.context, (char*)field.name.pointer, field.name.length, false); + u32 alignment = 1; + auto name_global = llvm_module_create_global_variable(module->llvm.module, LLVMArrayType2(u8_type->llvm.abi, field.name.length + null_terminate), is_constant, LLVMInternalLinkage, initial_value, arena_join_string(module->arena, array_to_slice(name_parts)), LLVMNotThreadLocal, false, alignment, LLVMGlobalUnnamedAddr); name_before = name_global; LLVMValueRef constants[] = { name_global, @@ -2380,9 +2547,7 @@ fn Global* get_enum_name_array_global(Module* module, Type* enum_type) auto name_array_type = LLVMArrayType2(slice_type->llvm.abi, array_element_count); auto is_constant = true; unsigned address_space = 0; - auto name_array_variable = llvm_module_create_global_variable(module->llvm.module, name_array_type, is_constant, LLVMInternalLinkage, name_array, string_literal("name.array.enum"), name_before, LLVMNotThreadLocal, address_space, false); - LLVMSetAlignment(name_array_variable, get_byte_alignment(slice_type)); - LLVMSetUnnamedAddress(name_array_variable, LLVMGlobalUnnamedAddr); + auto name_array_variable = llvm_module_create_global_variable(module->llvm.module, name_array_type, is_constant, LLVMInternalLinkage, name_array, string_literal("name.array.enum"), LLVMNotThreadLocal, false, get_byte_alignment(slice_type), LLVMGlobalUnnamedAddr); auto global_type = get_array_type(module, slice_type, array_element_count); resolve_type_in_place(module, global_type); @@ -2896,13 +3061,13 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal enum_type->name, }; auto function_name = arena_join_string(module->arena, array_to_slice(name_parts)); - auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, LLVMInternalLinkage, 0, function_name); + auto llvm_function = llvm_module_create_function(module->arena, module->llvm.module, llvm_function_type, LLVMInternalLinkage, function_name); LLVMSetFunctionCallConv(llvm_function, LLVMFastCallConv); LLVMValueRef llvm_argument; LLVMGetParams(llvm_function, &llvm_argument); - auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("entry"), llvm_function); + auto* entry_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "entry"); LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); auto alloca = create_alloca(module, { @@ -2910,8 +3075,8 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal .name = string_literal("retval"), }); - auto* return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("return_block"), llvm_function); - auto* else_block = llvm_context_create_basic_block(module->llvm.context, string_literal("else_block"), llvm_function); + auto* return_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "return_block"); + auto* else_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "else_block"); auto enum_fields = enum_type->enumerator.fields; @@ -2923,7 +3088,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal for (u64 i = 0; i < enum_fields.length; i += 1) { auto& field = enum_fields[i]; - auto* case_block = llvm_context_create_basic_block(module->llvm.context, string_literal("case_block"), llvm_function); + auto* case_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "case_block"); auto case_value = LLVMConstInt(backing_type, field.value, false); LLVMAddCase(switch_instruction, case_value, case_block); @@ -3134,11 +3299,86 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal } break; case ValueId::binary: { - auto is_boolean = binary_is_boolean(value->binary.id); - analyze_binary_type(module, value->binary.left, value->binary.right, is_boolean, expected_type, analysis.must_be_constant); - check_types(module, value->binary.left->type, value->binary.right->type); + auto id = value->binary.id; + auto is_boolean = binary_is_boolean(id); + auto left = value->binary.left; + auto right = value->binary.right; + auto is_sub = id == BinaryId::sub; + analyze_binary_type(module, left, right, is_boolean, expected_type, analysis.must_be_constant, is_sub); + check_types(module, left->type, right->type); - value_type = is_boolean ? uint1(module) : value->binary.left->type; + if (is_sub && left->type->id == TypeId::pointer && right->type->id == TypeId::pointer) + { + assert(left->type == right->type); + + auto u64_type = uint64(module); + auto s64_type = sint64(module); + auto left_int_from_pointer = new_value(module); + *left_int_from_pointer = { + .unary = { + .value = left, + .id = UnaryId::int_from_pointer, + }, + .type = u64_type, + .id = ValueId::unary, + }; + auto right_int_from_pointer = new_value(module); + *right_int_from_pointer = { + .unary = { + .value = right, + .id = UnaryId::int_from_pointer, + }, + .type = u64_type, + .id = ValueId::unary, + }; + + value->type = s64_type; + value->binary.left = left_int_from_pointer; + value->binary.right = right_int_from_pointer; + + auto sub = new_value(module); + *sub = *value; + + auto size_constant = new_value(module); + + assert(left->type->id == TypeId::pointer); + auto element_type = left->type->pointer.element_type; + *size_constant = { + .unary_type = { + .type = element_type, + .id = UnaryTypeId::byte_size, + }, + .id = ValueId::unary_type, + }; + + analyze_type(module, size_constant, s64_type, { .must_be_constant = 1 }); + + *value = { + .binary = { + .left = sub, + .right = size_constant, + .id = BinaryId::div, + }, + .id = ValueId::binary, + }; + + if (expected_type) + { + trap(); + } + else + { + value_type = s64_type; + } + } + else if (is_boolean) + { + value_type = uint1(module); + } + else + { + value_type = left->type; + } } break; case ValueId::variable_reference: { @@ -3231,6 +3471,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal if (expected_type->array.element_count == 0) { + // TODO: use existing types? expected_type->array.element_count = values.length; assert(expected_type->name.equal(string_literal(""))); expected_type->name = array_name(module, expected_type->array.element_type, expected_type->array.element_count); @@ -3907,7 +4148,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal auto false_value = value->select.false_value; analyze_type(module, condition, 0, { .must_be_constant = analysis.must_be_constant }); auto is_boolean = false; - analyze_binary_type(module, true_value, false_value, is_boolean, expected_type, analysis.must_be_constant); + analyze_binary_type(module, true_value, false_value, is_boolean, expected_type, analysis.must_be_constant, false); auto left_type = true_value->type; auto right_type = false_value->type; @@ -3988,7 +4229,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal enum_type->name, }; auto function_name = arena_join_string(module->arena, array_to_slice(name_parts)); - auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, LLVMInternalLinkage, 0, function_name); + auto llvm_function = llvm_module_create_function(module->arena, module->llvm.module, llvm_function_type, LLVMInternalLinkage, function_name); LLVMSetFunctionCallConv(llvm_function, LLVMFastCallConv); auto name_array_global = get_enum_name_array_global(module, enum_type); @@ -4006,19 +4247,16 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal auto value_array = LLVMConstArray2(enum_value_type, value_constant_buffer, array_element_count); auto value_array_variable_type = LLVMArrayType2(enum_value_type, array_element_count); auto is_constant = true; - LLVMValueRef before = 0; LLVMThreadLocalMode thread_local_mode = LLVMNotThreadLocal; unsigned address_space = 0; auto externally_initialized = false; - auto value_array_variable = llvm_module_create_global_variable(module->llvm.module, value_array_variable_type, is_constant, LLVMInternalLinkage, value_array, string_literal("value.array.enum"), before, thread_local_mode, address_space, externally_initialized); - LLVMSetAlignment(value_array_variable, enum_alignment); - LLVMSetUnnamedAddress(value_array_variable, LLVMGlobalUnnamedAddr); + auto value_array_variable = llvm_module_create_global_variable(module->llvm.module, value_array_variable_type, is_constant, LLVMInternalLinkage, value_array, string_literal("value.array.enum"), thread_local_mode, externally_initialized, enum_alignment, LLVMGlobalUnnamedAddr); - auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("entry"), llvm_function); - auto* return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("return_block"), llvm_function); - auto* loop_entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("loop.entry"), llvm_function); - auto* loop_body_block = llvm_context_create_basic_block(module->llvm.context, string_literal("loop.body"), llvm_function); - auto* loop_exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("loop.exit"), llvm_function); + auto* entry_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "entry"); + auto* return_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "return_block"); + auto* loop_entry_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "loop.entry"); + auto* loop_body_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "loop.body"); + auto* loop_exit_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "loop.exit"); LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); @@ -4082,8 +4320,8 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal auto length_comparison = LLVMBuildICmp(module->llvm.builder, LLVMIntEQ, slice_length, element_length, ""); - auto* length_match_block = llvm_context_create_basic_block(module->llvm.context, string_literal("length.match"), llvm_function); - auto* length_mismatch_block = llvm_context_create_basic_block(module->llvm.context, string_literal("length.mismatch"), llvm_function); + auto* length_match_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "length.match"); + auto* length_mismatch_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "length.mismatch"); LLVMBuildCondBr(module->llvm.builder, length_comparison, length_match_block, length_mismatch_block); LLVMPositionBuilderAtEnd(module->llvm.builder, length_match_block); @@ -4103,7 +4341,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal u64_type->llvm.abi, }; auto llvm_function_type = LLVMFunctionType(s32_type->llvm.abi, arguments, array_length(arguments), false); - auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, LLVMExternalLinkage, address_space, string_literal("memcmp")); + auto llvm_function = llvm_module_create_function(module->arena, module->llvm.module, llvm_function_type, LLVMExternalLinkage, string_literal("memcmp")); memcmp = llvm_function; } @@ -4138,7 +4376,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal }; auto memcmp_return_result = LLVMBuildCall2(module->llvm.builder, LLVMGlobalGetValueType(memcmp), memcmp, memcmp_arguments, array_length(memcmp_arguments), ""); auto content_comparison = LLVMBuildICmp(module->llvm.builder, LLVMIntEQ, memcmp_return_result, LLVMConstNull(s32_type->llvm.abi), ""); - auto* content_match_block = llvm_context_create_basic_block(module->llvm.context, string_literal("content.match"), llvm_function); + auto* content_match_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "content.match"); LLVMBuildCondBr(module->llvm.builder, content_comparison, content_match_block, length_mismatch_block); LLVMPositionBuilderAtEnd(module->llvm.builder, content_match_block); @@ -4648,7 +4886,9 @@ fn void create_coerced_store(Module* module, LLVMValueRef source_value, Type* so auto source_size = get_byte_size(source_type); - if (!type_is_abi_equal(module, source_type, destination_type)) + // TODO: this smells badly + //destination_type != uint1(module) + if (!type_is_abi_equal(module, source_type, destination_type) && destination_type->id == TypeId::structure) { auto r = enter_struct_pointer_for_coerced_access(module, destination_value, destination_type, source_size); destination_value = r.value; @@ -4893,13 +5133,10 @@ fn SliceEmitResult emit_string_literal(Module* module, Value* value) resolve_type_in_place(module, u8_type); auto string_type = LLVMArrayType2(u8_type->llvm.abi, length + null_terminate); auto is_constant = true; - LLVMValueRef before = 0; LLVMThreadLocalMode tlm = LLVMNotThreadLocal; bool externally_initialized = false; - unsigned address_space = 0; - auto global = llvm_module_create_global_variable(module->llvm.module, string_type, is_constant, LLVMInternalLinkage, constant_string, string_literal("conststring"), before, tlm, address_space, externally_initialized); - LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); - auto slice_type = get_slice_type(module, u8_type); + u32 alignment = 1; + auto global = llvm_module_create_global_variable(module->llvm.module, string_type, is_constant, LLVMInternalLinkage, constant_string, string_literal("conststring"), tlm, externally_initialized, alignment, LLVMGlobalUnnamedAddr); return { global, LLVMConstInt(uint64(module)->llvm.abi, length, false) }; } break; @@ -4927,6 +5164,15 @@ fn void invalidate_analysis(Module* module, Value* value) { invalidate_analysis(module, value->field_access.aggregate); } break; + case ValueId::binary: + { + invalidate_analysis(module, value->binary.left); + invalidate_analysis(module, value->binary.right); + } break; + case ValueId::unary: + { + invalidate_analysis(module, value->unary.value); + } break; default: trap(); } @@ -5081,11 +5327,6 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, }); } - if (get_byte_size(semantic_argument_type) > 60 && argument_abi.flags.kind != AbiKind::indirect) - { - trap(); - } - resolve_type_in_place(module, semantic_argument_type); if (is_named_argument) @@ -5145,245 +5386,249 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, trap(); } - auto evaluation_kind = get_evaluation_kind(semantic_argument_type); - Value* src = 0; - switch (evaluation_kind) + // TODO: fix this hack and collapse it into the generic path + if (coerce_to_type == uint8(module) && semantic_argument_type == uint1(module)) { - case EvaluationKind::scalar: trap(); - case EvaluationKind::aggregate: src = semantic_call_argument_value; break; - case EvaluationKind::complex: trap(); - } - assert(src); - - if (argument_abi.attributes.direct.offset != 0) - { - trap(); - } - - if (coerce_to_type->id == TypeId::structure && argument_abi.flags.kind == AbiKind::direct && argument_abi.flags.can_be_flattened) - { - auto source_type_is_scalable = false; - if (source_type_is_scalable) - { - trap(); - } - else - { - if (src->kind == ValueKind::right && !src->is_constant()) - { - if (!type_is_slice(src->type)) - { - 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, 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); - - LLVMValueRef source = src->llvm; - if (source_size < destination_size) - { - auto alloca = create_alloca(module, { - .type = argument_abi.semantic_type, - .name = string_literal("coerce"), - .alignment = alignment, - }); - auto u64_type = uint64(module); - resolve_type_in_place(module, u64_type); - LLVMBuildMemCpy(module->llvm.builder, alloca, alignment, source, alignment, LLVMConstInt(u64_type->llvm.abi, source_size, false)); - source = alloca; - } - - auto coerce_fields = coerce_to_type->structure.fields; - - // TODO: - assert(argument_abi.attributes.direct.offset == 0); - - switch (semantic_call_argument_value->kind) - { - case ValueKind::left: - { - for (u32 i = 0; i < (u32)coerce_fields.length; i += 1) - { - auto& field = coerce_fields[i]; - auto gep = LLVMBuildStructGEP2(module->llvm.builder, coerce_to_type->llvm.memory, source, i, ""); - auto maybe_undef = false; - if (maybe_undef) - { - trap(); - } - - auto load = create_load(module, { - .type = field.type, - .pointer = gep, - .alignment = alignment, - }); - llvm_abi_argument_value_buffer[abi_argument_count] = load; - abi_argument_count += 1; - } - } break; - case ValueKind::right: - { - if (type_is_abi_equal(module, coerce_to_type, semantic_argument_type)) - { - for (u32 i = 0; i < (u32)coerce_fields.length; i += 1) - { - llvm_abi_argument_value_buffer[abi_argument_count] = LLVMBuildExtractValue(module->llvm.builder, source, i, ""); - abi_argument_count += 1; - } - } - else - { - switch (semantic_call_argument_value->id) - { - case ValueId::aggregate_initialization: - { - auto is_constant = semantic_call_argument_value->aggregate_initialization.is_constant; - - if (is_constant) - { - bool is_constant = true; - LLVMLinkage linkage_type = LLVMInternalLinkage; - LLVMValueRef before = 0; - LLVMThreadLocalMode thread_local_mode = {}; - u32 address_space = 0; - bool externally_initialized = false; - - auto global = llvm_module_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("conststruct"), before, thread_local_mode, address_space, externally_initialized); - LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); - - auto alignment = get_byte_alignment(semantic_argument_type); - LLVMSetAlignment(global, alignment); - - for (u32 i = 0; i < coerce_fields.length; i += 1) - { - auto gep = LLVMBuildStructGEP2(module->llvm.builder, coerce_to_type->llvm.abi, global, i, ""); - auto& field = coerce_fields[i]; - auto maybe_undef = false; - if (maybe_undef) - { - trap(); - } - - auto load = create_load(module, { .type = field.type, .pointer = gep, .alignment = alignment }); - - llvm_abi_argument_value_buffer[abi_argument_count] = load; - abi_argument_count += 1; - } - } - else - { - trap(); - } - } break; - 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(); - } - } - } break; - } - } + 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; } else { - assert(argument_abi.abi_count == 1); - auto destination_type = coerce_to_type; - - LLVMValueRef v = 0; - switch (src->id) + auto evaluation_kind = get_evaluation_kind(semantic_argument_type); + Value* src = 0; + switch (evaluation_kind) { - case ValueId::zero: + case EvaluationKind::scalar: trap(); + case EvaluationKind::aggregate: src = semantic_call_argument_value; break; + case EvaluationKind::complex: trap(); + } + assert(src); + + if (argument_abi.attributes.direct.offset != 0) + { + trap(); + } + + if (coerce_to_type->id == TypeId::structure && argument_abi.flags.kind == AbiKind::direct && argument_abi.flags.can_be_flattened) + { + auto source_type_is_scalable = false; + if (source_type_is_scalable) + { + trap(); + } + else + { + if (src->kind == ValueKind::right && !src->is_constant()) { - trap(); - } break; - default: - { - LLVMValueRef pointer = 0; - Type* pointer_type = 0; - if (src->type->id != TypeId::pointer) + if (!type_is_slice(src->type)) { - auto type = src->type; - pointer_type = get_pointer_type(module, type); - if (src->id != ValueId::variable_reference) + switch (src->id) { - pointer = create_alloca(module, { - .type = type, - .name = string_literal("my.coerce"), + 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, 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); + + LLVMValueRef source = src->llvm; + if (source_size < destination_size) + { + auto alloca = create_alloca(module, { + .type = argument_abi.semantic_type, + .name = string_literal("coerce"), + .alignment = alignment, }); - emit_assignment(module, pointer, pointer_type, src); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + LLVMBuildMemCpy(module->llvm.builder, alloca, alignment, source, alignment, LLVMConstInt(u64_type->llvm.abi, source_size, false)); + source = alloca; + } + + auto coerce_fields = coerce_to_type->structure.fields; + + // TODO: + assert(argument_abi.attributes.direct.offset == 0); + + switch (semantic_call_argument_value->kind) + { + case ValueKind::left: + { + for (u32 i = 0; i < (u32)coerce_fields.length; i += 1) + { + auto& field = coerce_fields[i]; + auto gep = LLVMBuildStructGEP2(module->llvm.builder, coerce_to_type->llvm.memory, source, i, ""); + auto maybe_undef = false; + if (maybe_undef) + { + trap(); + } + + auto load = create_load(module, { + .type = field.type, + .pointer = gep, + .alignment = alignment, + }); + llvm_abi_argument_value_buffer[abi_argument_count] = load; + abi_argument_count += 1; + } + } break; + case ValueKind::right: + { + if (type_is_abi_equal(module, coerce_to_type, semantic_argument_type)) + { + for (u32 i = 0; i < (u32)coerce_fields.length; i += 1) + { + llvm_abi_argument_value_buffer[abi_argument_count] = LLVMBuildExtractValue(module->llvm.builder, source, i, ""); + abi_argument_count += 1; + } + } + else + { + switch (semantic_call_argument_value->id) + { + case ValueId::aggregate_initialization: + { + auto is_constant = semantic_call_argument_value->aggregate_initialization.is_constant; + + if (is_constant) + { + bool is_constant = true; + LLVMLinkage linkage_type = LLVMInternalLinkage; + LLVMThreadLocalMode thread_local_mode = {}; + u32 address_space = 0; + bool externally_initialized = false; + auto alignment = get_byte_alignment(semantic_argument_type); + + auto global = llvm_module_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("conststruct"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); + + for (u32 i = 0; i < coerce_fields.length; i += 1) + { + auto gep = LLVMBuildStructGEP2(module->llvm.builder, coerce_to_type->llvm.abi, global, i, ""); + auto& field = coerce_fields[i]; + auto maybe_undef = false; + if (maybe_undef) + { + trap(); + } + + auto load = create_load(module, { .type = field.type, .pointer = gep, .alignment = alignment }); + + llvm_abi_argument_value_buffer[abi_argument_count] = load; + abi_argument_count += 1; + } + } + else + { + trap(); + } + } break; + 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(); + } + } + } break; + } + } + } + else + { + assert(argument_abi.abi_count == 1); + auto destination_type = coerce_to_type; + + LLVMValueRef v = 0; + switch (src->id) + { + case ValueId::zero: + { + v = LLVMConstNull(coerce_to_type->llvm.abi); + } break; + default: + { + LLVMValueRef pointer = 0; + Type* pointer_type = 0; + if (src->type->id != TypeId::pointer) + { + auto type = src->type; + pointer_type = get_pointer_type(module, type); + if (src->id != ValueId::variable_reference) + { + pointer = create_alloca(module, { + .type = type, + .name = string_literal("my.coerce"), + }); + emit_assignment(module, pointer, pointer_type, src); + } + else + { + assert(src->id == ValueId::variable_reference); + assert(src->kind == ValueKind::right); + reanalyze_type_as_left_value(module, src); + } } else { - assert(src->kind == ValueKind::right); - assert(src->type->id == TypeId::structure); - assert(src->id == ValueId::variable_reference); - assert(src->kind == ValueKind::right); - reanalyze_type_as_left_value(module, src); + trap(); } - } - else - { - trap(); - } - assert(pointer_type); - assert(pointer_type->id == TypeId::pointer); - auto element_type = pointer_type->pointer.element_type; + assert(pointer_type); + assert(pointer_type->id == TypeId::pointer); + auto element_type = pointer_type->pointer.element_type; - if (!pointer) - { - assert(src->type->id == TypeId::pointer); - assert(src->type->llvm.abi == module->llvm.pointer_type); - emit_value(module, src, TypeKind::memory, false); - pointer = src->llvm; - } + if (!pointer) + { + assert(src->type->id == TypeId::pointer); + assert(src->type->llvm.abi == module->llvm.pointer_type); + emit_value(module, src, TypeKind::memory, false); + pointer = src->llvm; + } - auto source_type = element_type; - assert(source_type == argument_abi.semantic_type); - auto load = create_coerced_load(module, pointer, source_type, destination_type); + auto source_type = element_type; + assert(source_type == argument_abi.semantic_type); + auto load = create_coerced_load(module, pointer, source_type, destination_type); - auto is_cmse_ns_call = false; - if (is_cmse_ns_call) - { - trap(); - } + auto is_cmse_ns_call = false; + if (is_cmse_ns_call) + { + trap(); + } - auto maybe_undef = false; - if (maybe_undef) - { - trap(); - } + auto maybe_undef = false; + if (maybe_undef) + { + trap(); + } - v = load; - } break; + v = load; + } break; + } + assert(v); + + llvm_abi_argument_value_buffer[abi_argument_count] = v; + abi_argument_count += 1; } - assert(v); - - llvm_abi_argument_value_buffer[abi_argument_count] = v; - abi_argument_count += 1; } } } break; @@ -5415,16 +5660,12 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, bool is_constant = true; LLVMLinkage linkage_type = LLVMInternalLinkage; - LLVMValueRef before = 0; LLVMThreadLocalMode thread_local_mode = {}; u32 address_space = 0; bool externally_initialized = false; - - auto global = llvm_module_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("conststruct"), before, thread_local_mode, address_space, externally_initialized); - LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); - auto alignment = get_byte_alignment(semantic_argument_type); - LLVMSetAlignment(global, alignment); + + auto global = llvm_module_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("conststruct"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); llvm_abi_argument_value_buffer[abi_argument_count] = global; abi_argument_count += 1; @@ -5590,7 +5831,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, } else { - trap(); + create_coerced_store(module, source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile); } } break; default: unreachable(); @@ -5759,9 +6000,9 @@ fn LLVMValueRef emit_va_arg(Module* module, Value* value, LLVMValueRef left_llvm trap(); } - auto* in_reg_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.in_reg"), llvm_function); - auto* in_mem_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.in_mem"), llvm_function); - auto* end_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.end"), llvm_function); + auto* in_reg_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "va_arg.in_reg"); + auto* in_mem_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "va_arg.in_mem"); + auto* end_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "va_arg.end"); LLVMBuildCondBr(module->llvm.builder, in_regs, in_reg_block, in_mem_block); emit_block(module, in_reg_block); @@ -5900,8 +6141,6 @@ fn LLVMValueRef emit_va_arg(Module* module, Value* value, LLVMValueRef left_llvm fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type, TypeKind type_kind) { - unused(left_type); - switch (value->id) { case ValueId::field_access: @@ -5919,9 +6158,6 @@ fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef lef Type* real_aggregate_type = aggregate_element_type->id == TypeId::pointer ? aggregate_element_type->pointer.element_type : aggregate_element_type; auto resolved_aggregate_type = resolve_alias(module, real_aggregate_type); resolve_type_in_place(module, resolved_aggregate_type); - unused(left_llvm); - unused(left_type); - unused(type_kind); LLVMValueRef v; if (real_aggregate_type != aggregate_element_type) { @@ -6100,21 +6336,6 @@ fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef lef } break; default: unreachable(); } - - trap(); - - // auto resolved_element_type = resolve_alias(module, element_type); - // auto base_child_type = base_type->pointer.element_type; - // auto pointer_type = resolve_alias(module, base_child_type->id == TypeId::pointer ? base_child_type->pointer.element_type : base_type); - // assert(pointer_type->id == TypeId::pointer); - // auto element_type = pointer_type->pointer.element_type; - // resolve_type_in_place(module, element_type); - // - - // switch (resolved_element_type->id) - // { - // default: unreachable(); - // } } break; default: unreachable(); } @@ -6174,7 +6395,10 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, { auto values = right->array_initialization.values; assert(resolved_value_type->id == TypeId::array); + auto element_type = resolved_value_type->array.element_type; + auto element_count = resolved_value_type->array.element_count; auto uint64_type = uint64(module); + assert(values.length == element_count); resolve_type_in_place(module, uint64_type); if (right->array_initialization.is_constant) @@ -6183,29 +6407,20 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, bool is_constant = true; LLVMLinkage linkage_type = LLVMInternalLinkage; - LLVMValueRef before = 0; LLVMThreadLocalMode thread_local_mode = {}; u32 address_space = 0; bool externally_initialized = false; - - auto global = llvm_module_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("constarray"), before, thread_local_mode, address_space, externally_initialized); - - LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); - auto alignment = get_byte_alignment(resolved_value_type); - LLVMSetAlignment(global, alignment); - auto element_type = resolved_value_type->array.element_type; - auto element_count = resolved_value_type->array.element_count; - assert(values.length == element_count); + auto global = llvm_module_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("array.init"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); - u64 memcpy_size = get_byte_size(element_type) * element_count; + u64 memcpy_size = get_byte_size(resolved_value_type); LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, global, alignment, LLVMConstInt(uint64_type->llvm.abi, memcpy_size, false)); } else { auto u64_zero = LLVMConstNull(uint64_type->llvm.abi); - auto pointer_to_element_type = get_pointer_type(module, resolved_value_type->array.element_type); + auto pointer_to_element_type = get_pointer_type(module, element_type); for (u64 i = 0; i < values.length; i += 1) { @@ -6268,13 +6483,10 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, emit_value(module, right, TypeKind::memory, true); LLVMLinkage linkage_type = LLVMInternalLinkage; - LLVMValueRef before = 0; unsigned address_space = 0; LLVMThreadLocalMode thread_local_mode = LLVMNotThreadLocal; bool externally_initialized = false; - auto global = llvm_module_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("constarray"), before, thread_local_mode, address_space, externally_initialized); - LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); - LLVMSetAlignment(global, alignment); + auto global = llvm_module_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("constarray"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, global, alignment, byte_size_value); } else @@ -6720,7 +6932,7 @@ fn void emit_macro_instantiation(Module* module, Value* value) module->llvm.inlined_at = caller_debug_location; auto llvm_function = current_function->variable.storage->llvm; - auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("macro.entry"), llvm_function); + auto* entry_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "macro.entry"); LLVMBuildBr(module->llvm.builder, entry_block); LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); @@ -6737,7 +6949,7 @@ fn void emit_macro_instantiation(Module* module, Value* value) assert(!macro_instantiation->return_alloca); macro_instantiation->return_alloca = return_alloca; - auto* return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("macro.return_block"), llvm_function); + auto* return_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "macro.return_block"); assert(!macro_instantiation->return_block); macro_instantiation->return_block = return_block; @@ -6794,7 +7006,6 @@ fn void emit_macro_instantiation(Module* module, Value* value) } } - fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u32* last_line, u32* last_column, LLVMMetadataRef* last_debug_location); fn void analyze_block(Module* module, Block* block) @@ -6926,10 +7137,6 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect } } } break; - case UnaryId::tilde: - { - trap(); - } break; case UnaryId::enum_name: { assert(type_kind == TypeKind::abi); @@ -7010,6 +7217,17 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect { llvm_value = llvm_unary_value; } break; + case UnaryId::leading_zeroes: + case UnaryId::trailing_zeroes: + { + auto intrinsic = unary_id == UnaryId::leading_zeroes ? IntrinsicIndex::clz : IntrinsicIndex::ctz; + auto u1_type = uint1(module); + resolve_type_in_place(module, u1_type); + auto zero_is_poison = LLVMConstNull(u1_type->llvm.abi); + LLVMValueRef values[] = { llvm_unary_value, zero_is_poison }; + LLVMTypeRef types[] = { destination_type }; + llvm_value = emit_intrinsic_call(module, intrinsic, array_to_slice(types), array_to_slice(values)); + } break; } } break; case ValueId::unary_type: @@ -7070,10 +7288,8 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect auto array_type = resolved_value_type->pointer.element_type; assert(array_type->id == TypeId::array); resolve_type_in_place(module, array_type); - auto value_array_variable = llvm_module_create_global_variable(module->llvm.module, array_type->llvm.memory, is_constant, LLVMInternalLinkage, array_value, string_literal("enum.values"), 0, LLVMNotThreadLocal, 0, 0); auto alignment = get_byte_alignment(resolved_value_type); - LLVMSetAlignment(value_array_variable, alignment); - LLVMSetUnnamedAddress(value_array_variable, LLVMGlobalUnnamedAddr); + auto value_array_variable = llvm_module_create_global_variable(module->llvm.module, array_type->llvm.memory, is_constant, LLVMInternalLinkage, array_value, string_literal("enum.values"), LLVMNotThreadLocal, 0, alignment, LLVMGlobalUnnamedAddr); llvm_value = value_array_variable; } break; } @@ -7112,8 +7328,8 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect auto llvm_function = module->current_function->variable.storage->llvm; assert(llvm_function); - auto* right_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.right"), llvm_function); - auto* end_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.end"), llvm_function); + auto* right_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "shortcircuit.right"); + auto* end_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "shortcircuit.end"); LLVMBasicBlockRef true_block; LLVMBasicBlockRef false_block; @@ -7412,7 +7628,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect assert(element_type); assert(llvm_index); - LLVMValueRef indices[] = { zero_index, index->llvm }; + LLVMValueRef indices[] = { zero_index, llvm_index }; auto gep = create_gep(module, { .type = array_type->llvm.memory, .pointer = array_like->llvm, @@ -8030,13 +8246,13 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 } break; case StatementId::if_st: { - auto* taken_block = llvm_context_create_basic_block(module->llvm.context, string_literal("if.taken"), llvm_function); - auto* not_taken_block = llvm_context_create_basic_block(module->llvm.context, string_literal("if.not_taken"), llvm_function); - auto* exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("if.exit"), llvm_function); + auto* taken_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "if.taken"); + auto* not_taken_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "if.not_taken"); + auto* exit_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "if.exit"); auto condition = statement->if_st.condition; analyze_value(module, condition, 0, TypeKind::abi, false); - auto llvm_condition = emit_condition(module, statement->if_st.condition); + auto llvm_condition = emit_condition(module, condition); LLVMBuildCondBr(module->llvm.builder, llvm_condition, taken_block, not_taken_block); LLVMPositionBuilderAtEnd(module->llvm.builder, taken_block); @@ -8072,13 +8288,13 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 } break; case StatementId::while_st: { - auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.entry"), llvm_function); + auto* entry_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "while.entry"); LLVMBuildBr(module->llvm.builder, entry_block); LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); - auto body_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.body"), llvm_function); - auto continue_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.continue"), llvm_function); - auto exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.exit"), llvm_function); + auto body_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "while.body"); + auto continue_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "while.continue"); + auto exit_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "while.exit"); auto previous_continue_block = module->llvm.continue_block; auto previous_exit_block = module->llvm.exit_block; @@ -8125,12 +8341,12 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 LLVMBuildBr(module->llvm.builder, entry_block); - if (llvm_value_use_empty((LLVMValueRef)body_block)) + if (!LLVMGetFirstUse((LLVMValueRef)body_block)) { trap(); } - if (llvm_value_use_empty((LLVMValueRef)exit_block)) + if (!LLVMGetFirstUse((LLVMValueRef)exit_block)) { trap(); } @@ -8212,7 +8428,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 } break; case StatementId::switch_st: { - auto* exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("switch.exit"), llvm_function); + auto* exit_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "switch.exit"); auto discriminant = statement->switch_st.discriminant; auto clauses = statement->switch_st.clauses; @@ -8239,7 +8455,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 for (u64 i = 0; i < clauses.length; i += 1) { auto& clause = clauses[i]; - clause.basic_block = llvm_context_create_basic_block(module->llvm.context, clause.values.length == 0 ? string_literal("switch.else_case_block") : string_literal("switch.case_block"), llvm_function); + clause.basic_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, clause.values.length == 0 ? "switch.else_case_block" : "switch.case_block"); discriminant_case_count += clause.values.length; if (clause.values.length == 0) @@ -8300,7 +8516,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 } else { - else_block = llvm_context_create_basic_block(module->llvm.context, string_literal("switch.else_case_block"), llvm_function); + else_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "switch.else_case_block"); } auto switch_instruction = LLVMBuildSwitch(module->llvm.builder, discriminant->llvm, else_block, discriminant_case_count); @@ -8383,10 +8599,10 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 resolve_type_in_place(module, index_type); auto index_zero = LLVMConstNull(index_type->llvm.abi); - auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.entry"), llvm_function); - auto* body_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.body"), llvm_function); - auto* continue_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.continue"), llvm_function); - auto* exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.exit"), llvm_function); + auto* entry_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "for_each.entry"); + auto* body_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "for_each.body"); + auto* continue_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "for_each.continue"); + auto* exit_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "for_each.exit"); auto previous_continue_block = module->llvm.continue_block; auto previous_exit_block = module->llvm.exit_block; @@ -8621,8 +8837,10 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 LLVMValueRef indices[] = { body_index_load, }; + auto gep_type = aggregate_type->structure.fields[0].type->pointer.element_type; + resolve_type_in_place(module, gep_type); auto gep = create_gep(module, { - .type = aggregate_type->structure.fields[0].type->pointer.element_type->llvm.memory, + .type = gep_type->llvm.memory, .pointer = extract_pointer, .indices = array_to_slice(indices), }); @@ -8733,7 +8951,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 } else { - analyze_binary_type(module, start, end, false, 0, false); + analyze_binary_type(module, start, end, false, 0, false, false); assert(start->type == end->type); local_type = start->type; } @@ -9161,8 +9379,7 @@ void emit(Module* module) case Linkage::internal: llvm_linkage_type = LLVMInternalLinkage; break; case Linkage::external: llvm_linkage_type = LLVMExternalLinkage; break; } - unsigned address_space = 0; - auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, llvm_linkage_type, address_space, global->variable.name); + auto llvm_function = llvm_module_create_function(module->arena, module->llvm.module, llvm_function_type, llvm_linkage_type, global->variable.name); global->variable.storage->llvm = llvm_function; LLVMCallConv cc; @@ -9243,14 +9460,12 @@ void emit(Module* module) case Linkage::external: linkage = LLVMExternalLinkage; break; } - LLVMValueRef before = 0; LLVMThreadLocalMode thread_local_mode = LLVMNotThreadLocal; unsigned address_space = 0; bool externally_initialized = false; - auto global_llvm = llvm_module_create_global_variable(module->llvm.module, global_type->llvm.memory, is_constant, linkage, global->variable.initial_value->llvm, global->variable.name, before, thread_local_mode, address_space, externally_initialized); auto alignment = get_byte_alignment(global_type); - LLVMSetAlignment(global_llvm, alignment); + auto global_llvm = llvm_module_create_global_variable(module->llvm.module, global_type->llvm.memory, is_constant, linkage, global->variable.initial_value->llvm, global->variable.name, thread_local_mode, externally_initialized, alignment, LLVMNoUnnamedAddr); global->variable.storage->llvm = global_llvm; global->variable.storage->type = get_pointer_type(module, global_type); @@ -9286,8 +9501,8 @@ void emit(Module* module) Slice llvm_abi_arguments = { .pointer = llvm_abi_argument_buffer, .length = function_type->abi.abi_argument_types.length }; LLVMGetParams(llvm_function, llvm_abi_argument_buffer); - auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("entry"), llvm_function); - auto return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("return_block"), llvm_function); + auto* entry_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "entry"); + auto return_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "return_block"); global->variable.storage->function.llvm.return_block = return_block; LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); @@ -9552,7 +9767,7 @@ void emit(Module* module) { assert(!LLVMGetBasicBlockTerminator(current_basic_block)); - if (llvm_basic_block_is_empty(current_basic_block) || llvm_value_use_empty((LLVMValueRef)current_basic_block)) + if (!LLVMGetFirstInstruction(current_basic_block) || !LLVMGetFirstUse((LLVMValueRef)current_basic_block)) { LLVMReplaceAllUsesWith((LLVMValueRef)return_block, (LLVMValueRef)current_basic_block); LLVMDeleteBasicBlock(return_block); @@ -9633,7 +9848,7 @@ void emit(Module* module) auto alloca = LLVMGetOperand(store, 1); assert(alloca == return_alloca); LLVMInstructionEraseFromParent(store); - assert(llvm_value_use_empty(alloca)); + assert(!LLVMGetFirstUse(alloca)); LLVMInstructionEraseFromParent(alloca); } else @@ -9689,12 +9904,13 @@ void emit(Module* module) LLVMDIBuilderFinalize(module->llvm.di_builder); } - String verification_error_message = {}; - if (!llvm_module_verify(module->llvm.module, &verification_error_message)) + char* verification_error_message = 0; + auto result = LLVMVerifyModule(module->llvm.module, LLVMReturnStatusAction, &verification_error_message) == 0; + if (!result) { dump_module(module); print(string_literal("\n==========================\nLLVM VERIFICATION ERROR\n==========================\n")); - print(verification_error_message); + print(c_string_to_slice(verification_error_message)); bb_fail(); } diff --git a/src/llvm.cpp b/src/llvm.cpp index 8520149..141fe49 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -26,83 +26,12 @@ #include "lld/Common/CommonLinkerContext.h" -fn llvm::StringRef string_ref(String string) -{ - return llvm::StringRef((char*)string.pointer, string.length); -} - -EXPORT LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String name) -{ - auto module = new llvm::Module(string_ref(name), *llvm::unwrap(context)); - return wrap(module); -} - -EXPORT LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMValueRef before, LLVMThreadLocalMode thread_local_mode, unsigned address_space, bool externally_initialized) -{ - llvm::GlobalValue::LinkageTypes linkage; - switch (linkage_type) - { - case LLVMExternalLinkage: linkage = llvm::GlobalValue::ExternalLinkage; break; - case LLVMInternalLinkage: linkage = llvm::GlobalValue::InternalLinkage; break; - default: trap(); - } - - llvm::GlobalValue::ThreadLocalMode tlm; - switch (thread_local_mode) - { - case LLVMNotThreadLocal: tlm = llvm::GlobalValue::NotThreadLocal; break; - default: trap(); - } - auto* global = new llvm::GlobalVariable(*llvm::unwrap(module), llvm::unwrap(type), is_constant, linkage, llvm::unwrap(initial_value), string_ref(name), before ? llvm::unwrap(before) : 0, tlm, address_space, externally_initialized); - return wrap(global); -} - EXPORT void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type) { auto sp = llvm::unwrap(subprogram); sp->replaceType(llvm::unwrap(subroutine_type)); } -EXPORT LLVMValueRef llvm_module_create_function(LLVMModuleRef module, LLVMTypeRef function_type, LLVMLinkage linkage_type, unsigned address_space, String name) -{ - llvm::GlobalValue::LinkageTypes llvm_linkage_type; - switch (linkage_type) - { - case LLVMExternalLinkage: - llvm_linkage_type = llvm::GlobalValue::LinkageTypes::ExternalLinkage; break; - case LLVMAvailableExternallyLinkage: - llvm_linkage_type = llvm::GlobalValue::LinkageTypes::AvailableExternallyLinkage; break; - case LLVMLinkOnceAnyLinkage: - llvm_linkage_type = llvm::GlobalValue::LinkageTypes::LinkOnceAnyLinkage; break; - case LLVMLinkOnceODRLinkage: - llvm_linkage_type = llvm::GlobalValue::LinkageTypes::LinkOnceODRLinkage; break; - case LLVMWeakAnyLinkage: - llvm_linkage_type = llvm::GlobalValue::LinkageTypes::WeakAnyLinkage; break; - case LLVMWeakODRLinkage: - llvm_linkage_type = llvm::GlobalValue::LinkageTypes::WeakODRLinkage; break; - case LLVMAppendingLinkage: - llvm_linkage_type = llvm::GlobalValue::LinkageTypes::AppendingLinkage; break; - case LLVMInternalLinkage: - llvm_linkage_type = llvm::GlobalValue::LinkageTypes::InternalLinkage; break; - case LLVMPrivateLinkage: - llvm_linkage_type = llvm::GlobalValue::LinkageTypes::PrivateLinkage; break; - case LLVMExternalWeakLinkage: - llvm_linkage_type = llvm::GlobalValue::LinkageTypes::ExternalWeakLinkage; break; - case LLVMCommonLinkage: - llvm_linkage_type = llvm::GlobalValue::LinkageTypes::CommonLinkage; break; - default: - trap(); - } - auto* function = llvm::Function::Create(llvm::unwrap(function_type), llvm_linkage_type, address_space, string_ref(name), llvm::unwrap(module)); - return wrap(function); -} - -EXPORT LLVMBasicBlockRef llvm_context_create_basic_block(LLVMContextRef context, String name, LLVMValueRef parent_function) -{ - auto* basic_block = llvm::BasicBlock::Create(*llvm::unwrap(context), string_ref(name), parent_function ? llvm::unwrap(parent_function) : 0); - return wrap(basic_block); -} - // If there are multiple uses of the return-value slot, just check // for something immediately preceding the IP. Sometimes this can // happen with how we generate implicit-returns; it can also happen @@ -169,142 +98,6 @@ EXPORT LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LL return wrap(store); } -EXPORT bool llvm_value_use_empty(LLVMValueRef value) -{ - return llvm::unwrap(value)->use_empty(); -} - -EXPORT bool llvm_basic_block_is_empty(LLVMBasicBlockRef basic_block) -{ - return llvm::unwrap(basic_block)->empty(); -} - -EXPORT LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef b, LLVMTypeRef type, unsigned address_space, u32 alignment, String name) -{ - auto& builder = *llvm::unwrap(b); - auto llvm_alignment = llvm::Align(alignment); - return wrap(builder.Insert(new llvm::AllocaInst(llvm::unwrap(type), address_space, 0, llvm_alignment), string_ref(name))); -} - -fn String stream_to_string(llvm::raw_string_ostream& stream) -{ - // No need to call stream.flush(); because it's string-based - stream.flush(); - - auto string = stream.str(); - auto length = string.length(); - - u8* result = 0; - if (length) - { - result = new u8[length + 1]; - memcpy(result, string.c_str(), length); - result[length] = 0; - } - - return String{ result, length }; -} - -EXPORT String llvm_function_to_string(llvm::Function& function) -{ - std::string buffer; - llvm::raw_string_ostream os(buffer); - function.print(os); - os.flush(); - auto result = stream_to_string(os); - return result; -} - -EXPORT bool llvm_function_verify(LLVMValueRef function_value, String* error_message) -{ - std::string message_buffer; - llvm::raw_string_ostream message_stream(message_buffer); - - auto& function = *llvm::unwrap(function_value); - bool result = verifyFunction(function, &message_stream); - *error_message = stream_to_string(message_stream); - - // We invert the condition because LLVM conventions are just stupid - return !result; -} - -EXPORT bool llvm_module_verify(LLVMModuleRef m, String* error_message) -{ - std::string message_buffer; - llvm::raw_string_ostream message_stream(message_buffer); - - const auto& module = *llvm::unwrap(m); - bool result = llvm::verifyModule(module, &message_stream); - *error_message = stream_to_string(message_stream); - - // We invert the condition because LLVM conventions are just stupid - return !result; -} - -EXPORT String llvm_module_to_string(LLVMModuleRef module) -{ - std::string buffer; - llvm::raw_string_ostream stream(buffer); - llvm::unwrap(module)->print(stream, 0); - - return stream_to_string(stream); -} - -EXPORT String llvm_default_target_triple() -{ - auto triple = llvm::sys::getDefaultTargetTriple(); - auto length = triple.length(); - - u8* pointer = 0; - if (length) - { - pointer = new u8[length + 1]; - memcpy(pointer, triple.c_str(), length); - pointer[length] = 0; - } - - return { pointer, length }; -} - -EXPORT String llvm_host_cpu_name() -{ - auto cpu = llvm::sys::getHostCPUName(); - auto result = String { (u8*)cpu.data(), cpu.size() }; - assert(result.pointer[result.length] == 0); - return result; -} - -EXPORT String llvm_host_cpu_features() -{ - llvm::SubtargetFeatures Features; -#if LLVM_VERSION_MAJOR >= 19 - auto host_cpu_features = llvm::sys::getHostCPUFeatures(); -#else - StringMap host_cpu_features; - if (!sys::getHostCPUFeatures(host_cpu_features)) { - return {}; - } -#endif - - for (const auto &[Feature, IsEnabled] : host_cpu_features) - { - Features.AddFeature(Feature, IsEnabled); - } - - auto feature_string = Features.getString(); - auto length = feature_string.length(); - - u8* result = 0; - if (length) - { - result = new u8[length + 1]; - memcpy(result, feature_string.c_str(), length + 1); - result[length] = 0; - } - - return { result, length }; -} - EXPORT void llvm_module_run_optimization_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, BBLLVMOptimizationPipelineOptions options) { auto module = llvm::unwrap(m); @@ -423,7 +216,7 @@ EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeli { std::error_code error_code; - stream = std::make_unique(string_ref(options->output_file_path), error_code, llvm::sys::fs::OF_None); + stream = std::make_unique(llvm::StringRef((char*)options->output_file_path.pointer, options->output_file_path.length), error_code, llvm::sys::fs::OF_None); if (error_code) { diff --git a/src/llvm.hpp b/src/llvm.hpp index f2faa1e..dafd084 100644 --- a/src/llvm.hpp +++ b/src/llvm.hpp @@ -117,33 +117,12 @@ enum class DwarfType HP_VAX_complex_float_d = 0x90, // D floating complex. }; - fn bool llvm_initialized = false; -extern "C" String llvm_default_target_triple(); -extern "C" String llvm_host_cpu_name(); -extern "C" String llvm_host_cpu_features(); - -extern "C" LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String name); - -extern "C" LLVMValueRef llvm_module_create_function(LLVMModuleRef module, LLVMTypeRef function_type, LLVMLinkage linkage_type, unsigned address_space, String name); - -extern "C" LLVMBasicBlockRef llvm_context_create_basic_block(LLVMContextRef context, String name, LLVMValueRef parent_function); - -extern "C" LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMValueRef before, LLVMThreadLocalMode thread_local_mode, unsigned address_space, bool externally_initialized); - -extern "C" LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef builder, LLVMTypeRef type, unsigned address_space, u32 alignment, String name); -extern "C" bool llvm_basic_block_is_empty(LLVMBasicBlockRef basic_block); - extern "C" LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LLVMValueRef ra, LLVMTypeRef et); -extern "C" bool llvm_value_use_empty(LLVMValueRef value); -extern "C" bool llvm_function_verify(LLVMValueRef function_value, String* error_message); -extern "C" bool llvm_module_verify(LLVMModuleRef m, String* error_message); extern "C" void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type); -extern "C" String llvm_module_to_string(LLVMModuleRef module); - extern "C" void llvm_module_run_optimization_pipeline(LLVMModuleRef module, LLVMTargetMachineRef target_machine, BBLLVMOptimizationPipelineOptions options); extern "C" BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, const BBLLVMCodeGenerationPipelineOptions* options); diff --git a/src/parser.cpp b/src/parser.cpp index 3a4a7d7..13a51e3 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -14,12 +14,14 @@ enum class ValueIntrinsic integer_max, int_from_enum, int_from_pointer, + leading_zeroes, max, min, pointer_cast, pointer_from_int, select, string_to_enum, + trailing_zeroes, trap, truncate, va_start, @@ -399,6 +401,77 @@ fn String parse_identifier(Module* module) return module->content(start, end); } +fn u8 escape_character(u8 ch) +{ + switch (ch) + { + case 'n': return '\n'; + case 't': return '\t'; + case 'r': return '\r'; + case '\'': return '\''; + case '\\': return '\\'; + default: report_error(); + } +} + +fn String parse_string_literal(Module* module) +{ + expect_character(module, '"'); + + auto start = module->offset; + u64 escape_character_count = 0; + + while (1) + { + auto ch = module->content[module->offset]; + if (ch == '"') + { + break; + } + escape_character_count += ch == '\\'; + module->offset += 1; + } + + auto end = module->offset; + auto length = end - start - escape_character_count; + auto pointer = (u8*)arena_allocate_bytes(module->arena, length + 1, 1); + auto string_literal = String{ .pointer = pointer, .length = length }; + + for (u64 source_i = start, i = 0; source_i < end; source_i += 1, i += 1) + { + auto ch = module->content[source_i]; + if (ch == '\\') + { + source_i += 1; + ch = module->content[source_i]; + string_literal[i] = escape_character(ch); + } + else + { + string_literal[i] = ch; + } + } + + expect_character(module, '"'); + + return string_literal; +} + +fn String parse_name(Module* module) +{ + String result; + if (module->content[module->offset] == '"') + { + result = parse_string_literal(module); + } + else + { + result = parse_identifier(module); + } + return result; +} + + fn u64 accumulate_hexadecimal(u64 accumulator, u8 ch) { u64 value; @@ -670,7 +743,7 @@ fn FunctionHeaderParsing parse_function_header(Module* module, Scope* scope, boo if (mandate_argument_names) { - argument_name = parse_identifier(module); + argument_name = arena_duplicate_string(module->arena, parse_identifier(module)); skip_space(module); @@ -1048,61 +1121,6 @@ fn u64 parse_binary(Module* module) return value; } -fn u8 escape_character(u8 ch) -{ - switch (ch) - { - case 'n': return '\n'; - case 't': return '\t'; - case 'r': return '\r'; - case '\'': return '\''; - default: report_error(); - } -} - -fn String parse_string_literal(Module* module) -{ - expect_character(module, '"'); - - auto start = module->offset; - u64 escape_character_count = 0; - - while (1) - { - auto ch = module->content[module->offset]; - if (ch == '"') - { - break; - } - escape_character_count += ch == '\\'; - module->offset += 1; - } - - auto end = module->offset; - auto length = end - start - escape_character_count; - auto pointer = (u8*)arena_allocate_bytes(module->arena, length + 1, 1); - auto string_literal = String{ .pointer = pointer, .length = length }; - - for (u64 source_i = start, i = 0; source_i < end; source_i += 1, i += 1) - { - auto ch = module->content[source_i]; - if (ch == '\\') - { - source_i += 1; - ch = module->content[source_i]; - string_literal[i] = escape_character(ch); - } - else - { - string_literal[i] = ch; - } - } - - expect_character(module, '"'); - - return string_literal; -} - fn Token tokenize(Module* module) { skip_space(module); @@ -1168,12 +1186,14 @@ fn Token tokenize(Module* module) string_literal("integer_max"), string_literal("int_from_enum"), string_literal("int_from_pointer"), + string_literal("leading_zeroes"), string_literal("max"), string_literal("min"), string_literal("pointer_cast"), string_literal("pointer_from_int"), string_literal("select"), string_literal("string_to_enum"), + string_literal("trailing_zeroes"), string_literal("trap"), string_literal("truncate"), string_literal("va_start"), @@ -1682,9 +1702,11 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) case ValueIntrinsic::extend: case ValueIntrinsic::int_from_enum: case ValueIntrinsic::int_from_pointer: + case ValueIntrinsic::leading_zeroes: case ValueIntrinsic::truncate: case ValueIntrinsic::pointer_cast: case ValueIntrinsic::pointer_from_int: + case ValueIntrinsic::trailing_zeroes: case ValueIntrinsic::va_end: { UnaryId id; @@ -1695,9 +1717,11 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) case ValueIntrinsic::extend: id = UnaryId::extend; break; case ValueIntrinsic::int_from_enum: id = UnaryId::int_from_enum; break; case ValueIntrinsic::int_from_pointer: id = UnaryId::int_from_pointer; break; + case ValueIntrinsic::leading_zeroes: id = UnaryId::leading_zeroes; break; case ValueIntrinsic::truncate: id = UnaryId::truncate; break; case ValueIntrinsic::pointer_cast: id = UnaryId::pointer_cast; break; case ValueIntrinsic::pointer_from_int: id = UnaryId::pointer_from_int; break; + case ValueIntrinsic::trailing_zeroes: id = UnaryId::trailing_zeroes; break; case ValueIntrinsic::va_end: id = UnaryId::va_end; break; default: unreachable(); } @@ -1973,7 +1997,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) } break; case TokenId::dot: { - auto identifier = parse_identifier(module); + auto identifier = parse_name(module); result = new_value(module); *result = { @@ -2573,7 +2597,7 @@ fn Statement* parse_statement(Module* module, Scope* scope) module->offset += 1; skip_space(module); - auto local_name = parse_identifier(module); + auto local_name = arena_duplicate_string(module->arena, parse_identifier(module)); skip_space(module); Type* local_type = 0; @@ -2761,7 +2785,7 @@ fn Statement* parse_statement(Module* module, Scope* scope) if (is_identifier_start(module->content[module->offset])) { - auto local_name = parse_identifier(module); + auto local_name = arena_duplicate_string(module->arena, parse_identifier(module)); auto local = new_local(module, scope); *local = { .variable = { @@ -3128,20 +3152,6 @@ fn Block* parse_block(Module* module, Scope* parent_scope) return block; } -fn String parse_name(Module* module) -{ - String result; - if (module->content[module->offset] == '"') - { - result = parse_string_literal(module); - } - else - { - result = parse_identifier(module); - } - return result; -} - void parse(Module* module) { auto scope = &module->scope; @@ -3217,7 +3227,7 @@ void parse(Module* module) skip_space(module); } - auto global_name = parse_identifier(module); + auto global_name = arena_duplicate_string(module->arena, parse_identifier(module)); Global* global_forward_declaration = 0; Global* last_global = module->first_global; @@ -3444,8 +3454,6 @@ void parse(Module* module) u64 int_value_buffer[64]; bool is_resolved = true; - bool implicit_value = false; - unused(implicit_value); while (1) { @@ -3674,7 +3682,7 @@ void parse(Module* module) break; } - auto argument_name = parse_identifier(module); + auto argument_name = arena_duplicate_string(module->arena, parse_identifier(module)); skip_space(module); @@ -3757,7 +3765,7 @@ void parse(Module* module) auto argument_line = get_line(module); auto argument_column = get_column(module); - auto argument_name = parse_identifier(module); + auto argument_name = arena_duplicate_string(module->arena, parse_identifier(module)); skip_space(module); expect_character(module, ':'); diff --git a/tests/leading_trailing_zeroes.bbb b/tests/leading_trailing_zeroes.bbb new file mode 100644 index 0000000..bac6816 --- /dev/null +++ b/tests/leading_trailing_zeroes.bbb @@ -0,0 +1,10 @@ +[export] main = fn [cc(c)] () s32 +{ + >a: u32 = 0b111; + if (#leading_zeroes(a) != 29) #trap(); + if (#trailing_zeroes(a) != 0) #trap(); + >b: u8 = 0b11010; + if (#leading_zeroes(b) != 3) #trap(); + if (#trailing_zeroes(b) != 1) #trap(); + return 0; +} diff --git a/tests/pointer_sub.bbb b/tests/pointer_sub.bbb new file mode 100644 index 0000000..d6a04c4 --- /dev/null +++ b/tests/pointer_sub.bbb @@ -0,0 +1,9 @@ +[export] main = fn [cc(c)] () s32 +{ + >a: [_]s32 = [ 3, 1 ]; + >p0 = &a[0]; + >p1 = p0 + 1; + >sub: u32 = #truncate(p1 - p0); + if (sub != 1) #trap(); + return 0; +}