diff --git a/CMakeLists.txt b/CMakeLists.txt index e54a247..aed1abf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable(bb src/llvm.cpp ) +add_library(c_abi tests/c_abi.c) target_include_directories(bb PUBLIC src) target_compile_definitions(bb PUBLIC diff --git a/src/compiler.bbb b/src/compiler.bbb index c30f9a3..6a69b54 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -445,7 +445,7 @@ arena_allocate_bytes = fn (arena: &Arena, size: u64, alignment: u64) &u8 arena_allocate = macro [T] (arena: &Arena, count: u64) &T { - return #pointer_cast(arena_allocate_bytes(arena, #byte_size(T) * count, #alignof(T))); + return #pointer_cast(arena_allocate_bytes(arena, #byte_size(T) * count, #align_of(T))); } arena_duplicate_string = fn (arena: &Arena, string: []u8) []u8 diff --git a/src/compiler.cpp b/src/compiler.cpp index 91e7585..5d213db 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -85,6 +85,7 @@ fn void compile(Arena* arena, Options options) .path = options.path, .executable = options.executable, .objects = options.objects, + .libraries = options.libraries, .target = options.target, .build_mode = options.build_mode, .has_debug_info = options.has_debug_info, @@ -105,18 +106,18 @@ fn String compile_file(Arena* arena, Compile options) auto relative_file_path = options.relative_file_path; if (relative_file_path.length < 5) { - fail(); + bb_fail(); } auto extension_start = string_last_character(relative_file_path, '.'); if (extension_start == string_no_match) { - fail(); + bb_fail(); } if (!relative_file_path(extension_start).equal(string_literal(".bbb"))) { - fail(); + bb_fail(); } auto separator_index = string_last_character(relative_file_path, '/'); @@ -129,7 +130,7 @@ fn String compile_file(Arena* arena, Compile options) String output_path_dir_parts[] = { string_literal(base_cache_dir), - is_compiler ? string_literal("/compiler") : string_literal("/"), + is_compiler ? string_literal("/compiler/") : string_literal("/"), build_mode_to_string(options.build_mode), string_literal("_"), options.has_debug_info ? string_literal("di") : string_literal("nodi"), @@ -160,14 +161,20 @@ fn String compile_file(Arena* arena, Compile options) auto file_content = file_read(arena, relative_file_path); auto file_path = path_absolute(arena, relative_file_path); - auto c_abi_object_path = string_literal("build/c_abi.o"); String objects[] = { - c_abi_object_path, output_object_path, }; Slice object_slice = array_to_slice(objects); - object_slice = object_slice(!base_name.equal(string_literal("c_abi"))); + + String libraries[] = { + string_literal("build/libc_abi.a"), + }; + Slice library_slice = {}; + if (base_name.equal(string_literal("c_abi"))) + { + library_slice = array_to_slice(libraries); + } compile(arena, { .content = file_content, @@ -175,6 +182,7 @@ fn String compile_file(Arena* arena, Compile options) .executable = output_executable_path, .name = base_name, .objects = object_slice, + .libraries = library_slice, .target = { .cpu = CPUArchitecture::x86_64, .os = OperatingSystem::linux_, @@ -264,9 +272,10 @@ global_variable String names[] = string_literal("c_struct_with_array"), string_literal("c_function_pointer"), string_literal("basic_bool_call"), + string_literal("abi_enum_bool"), + string_literal("return_small_struct"), string_literal("c_abi"), string_literal("string_to_enum"), - string_literal("abi_enum_bool"), string_literal("empty_if"), string_literal("else_if"), string_literal("else_if_complicated"), @@ -279,32 +288,35 @@ global_variable String names[] = string_literal("slice_of_slices"), string_literal("type_alias"), string_literal("integer_formats"), - string_literal("return_small_struct"), string_literal("for_each_int"), string_literal("bool_array"), string_literal("basic_union"), string_literal("break_continue"), string_literal("constant_global_reference"), + string_literal("concat_logical_or"), + string_literal("strict_array_type"), + string_literal("pointer_struct_initialization"), + string_literal("slice_array_literal"), + string_literal("slice_only_start"), - // string_literal("self_referential_struct"), // TODO - // string_literal("forward_declared_type"), - string_literal("basic_macro"), string_literal("generic_macro"), string_literal("generic_pointer_macro"), string_literal("noreturn_macro"), string_literal("generic_pointer_array"), + + // string_literal("self_referential_struct"), // TODO + // string_literal("forward_declared_type"), }; void entry_point(Slice arguments, Slice environment) { - unused(environment); Arena* arena = arena_initialize_default(8 * mb); if (arguments.length < 2) { - fail_with_message(string_literal("error: Not enough arguments\n")); + bb_fail_with_message(string_literal("error: Not enough arguments\n")); } String command_string = c_string_to_slice(arguments[1]); @@ -332,7 +344,7 @@ void entry_point(Slice arguments, Slice environment) { if (arguments.length < 3) { - fail_with_message(string_literal("Not enough arguments for command 'compile'\n")); + bb_fail_with_message(string_literal("Not enough arguments for command 'compile'\n")); } auto build_mode = BuildMode::debug_none; @@ -364,7 +376,7 @@ void entry_point(Slice arguments, Slice environment) build_mode = (BuildMode)i; if (build_mode == BuildMode::count) { - fail_with_message(string_literal("Invalid build mode\n")); + bb_fail_with_message(string_literal("Invalid build mode\n")); } } @@ -381,7 +393,7 @@ void entry_point(Slice arguments, Slice environment) } else { - fail_with_message(string_literal("Wrong value for has_debug_info\n")); + bb_fail_with_message(string_literal("Wrong value for has_debug_info\n")); } } @@ -399,17 +411,13 @@ void entry_point(Slice arguments, Slice environment) // TODO: provide more arguments if (arguments.length != 2) { - fail_with_message(string_literal("error: 'test' command takes no arguments")); + bb_fail_with_message(string_literal("error: 'test' command takes no arguments")); } bool has_debug_info_array[] = {true, false}; - String test_matrix[array_length(names)][build_mode_count][2]; - - auto name_i = 0; for (auto name: names) { - auto build_mode_i = 0; for (BuildMode build_mode = BuildMode::debug_none; build_mode < BuildMode::count; build_mode = (BuildMode)((backing_type(BuildMode))build_mode + 1)) { for (bool has_debug_info : has_debug_info_array) @@ -426,35 +434,12 @@ void entry_point(Slice arguments, Slice environment) .silent = true, }); - test_matrix[name_i][build_mode_i][!has_debug_info] = executable_path; - - arena_restore(arena, position); - } - - build_mode_i += 1; - } - - name_i += 1; - } - - print(string_literal("Compiled tests successfully!\nRunning tests...\n")); - - u64 test_count = 0; - for (auto& a : test_matrix) - { - for (auto& b: a) - { - for (String executable_path : b) - { - unused(executable_path); - unused(test_count); - assert(executable_path.pointer[executable_path.length]); char* const arguments[] = { (char*)executable_path.pointer, 0, }; - Slice arg_slice = array_to_slice(arguments); + Slice arg_slice = array_to_slice(arguments); arg_slice.length -= 1; auto execution = os_execute(arena, arg_slice, environment, {}); auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0; @@ -463,16 +448,59 @@ void entry_point(Slice arguments, Slice environment) print(string_literal("Test failed: ")); print(executable_path); print(string_literal("\n")); - trap(); + bb_fail(); } - test_count += 1; + + arena_restore(arena, position); + } + } + } + + for (BuildMode build_mode = BuildMode::debug_none; build_mode < BuildMode::count; build_mode = (BuildMode)((backing_type(BuildMode))build_mode + 1)) + { + for (bool has_debug_info : has_debug_info_array) + { + auto compiler = compile_file(arena, { + .relative_file_path = string_literal("src/compiler.bbb"), + .build_mode = build_mode, + .has_debug_info = has_debug_info, + .silent = true, + }); + + for (auto name: names) + { + BuildMode build_mode = BuildMode::debug_none; + bool has_debug_info = true; + String relative_file_path_parts[] = { string_literal("tests/"), name, string_literal(".bbb") }; + auto relative_file_path = arena_join_string(arena, array_to_slice(relative_file_path_parts)); + const char* const arguments[] = + { + (char*)compiler.pointer, + "compile", + (char*)relative_file_path.pointer, + (char*)build_mode_to_string(build_mode).pointer, + has_debug_info ? "true" : "false", + 0, + }; + Slice arg_slice = array_to_slice(arguments); + arg_slice.length -= 1; + auto execution = os_execute(arena, arg_slice, environment, {}); + auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0; + if (!success) + { + print(string_literal("Standalone test failed: ")); + print(name); + print(string_literal("\n")); + bb_fail(); + } + break; } } } } break; case Command::count: { - fail_with_message(string_literal("error: Invalid command\n")); + bb_fail_with_message(string_literal("error: Invalid command\n")); } break; } } diff --git a/src/compiler.h b/src/compiler.h index 297f484..0e5817e 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -518,6 +518,16 @@ fn u64 get_byte_size(Type* type) auto result = get_byte_size(type->bits.backing_type); return result; } break; + case TypeId::alias: + { + auto result = get_byte_size(type->alias.type); + return result; + } break; + case TypeId::union_type: + { + auto result = type->union_type.byte_size; + return result; + } break; default: trap(); } } @@ -777,6 +787,7 @@ enum class UnaryId va_end, bitwise_not, dereference, + pointer_from_int, }; struct ValueUnary @@ -787,6 +798,7 @@ struct ValueUnary enum class UnaryTypeId { + align_of, byte_size, integer_max, }; @@ -973,12 +985,15 @@ struct Value { case ValueId::constant_integer: case ValueId::enum_literal: + case ValueId::unary_type: + case ValueId::string_literal: return true; case ValueId::unary: case ValueId::binary: case ValueId::field_access: case ValueId::array_expression: case ValueId::call: + case ValueId::select: return false; case ValueId::variable_reference: { @@ -1132,6 +1147,7 @@ struct Module String path; String executable; Sliceobjects; + Slicelibraries; Target target; BuildMode build_mode; @@ -1200,6 +1216,7 @@ struct Options String executable; String name; Slice objects; + Slice libraries; Target target; BuildMode build_mode; bool has_debug_info; @@ -1427,7 +1444,9 @@ fn Type* get_array_type(Module* module, Type* element_type, u64 element_count) { assert(array_type->id == TypeId::array); auto* candidate_element_type = array_type->array.element_type; - if (candidate_element_type == element_type) + auto candidate_element_count = array_type->array.element_count; + + if (candidate_element_type == element_type && candidate_element_count == element_count) { return array_type; } diff --git a/src/emitter.cpp b/src/emitter.cpp index be4fb2f..d538179 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -15,6 +15,41 @@ enum class TypeKind }; fn void analyze_block(Module* module, Block* block); fn void emit_local_storage(Module* module, Variable* variable); +fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, Value* right); +fn void emit_macro_instantiation(Module* module, Value* value); + +fn void emit_block(Module* module, LLVMBasicBlockRef basic_block) +{ + auto current_basic_block = LLVMGetInsertBlock(module->llvm.builder); + if (current_basic_block) + { + if (!LLVMGetBasicBlockTerminator(current_basic_block)) + { + LLVMBuildBr(module->llvm.builder, basic_block); + } + } + + if (current_basic_block && LLVMGetBasicBlockParent(current_basic_block)) + { + LLVMInsertExistingBasicBlockAfterInsertBlock(module->llvm.builder, basic_block); + } + else + { + LLVMAppendExistingBasicBlock(module->current_function->variable.storage->llvm, basic_block); + + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, basic_block); +} + +fn LLVMValueRef emit_intrinsic_call(Module* module, IntrinsicIndex index, Slice argument_types, Slice argument_values) +{ + auto intrinsic_id = module->llvm.intrinsic_table[(backing_type(IntrinsicIndex))index]; + auto intrinsic_function = LLVMGetIntrinsicDeclaration(module->llvm.module, intrinsic_id.n, argument_types.pointer, argument_types.length); + auto intrinsic_function_type = LLVMIntrinsicGetType(module->llvm.context, intrinsic_id.n, argument_types.pointer, argument_types.length); + auto call = LLVMBuildCall2(module->llvm.builder, intrinsic_function_type, intrinsic_function, argument_values.pointer, argument_values.length, ""); + return call; +} fn EvaluationKind get_evaluation_kind(Type* type) { @@ -77,6 +112,8 @@ fn bool type_is_signed(Type* type) return type_is_signed(type->bits.backing_type); case TypeId::pointer: // TODO: pointers should be signed? return false; + case TypeId::alias: + return type_is_signed(type->alias.type); default: unreachable(); } } @@ -1743,6 +1780,7 @@ struct GEPOptions fn LLVMValueRef create_gep(Module* module, GEPOptions options) { + assert(options.indices.length); auto* gep_function = options.inbounds ? &LLVMBuildInBoundsGEP2 : &LLVMBuildGEP2; auto gep = gep_function(module->llvm.builder, options.type, options.pointer, options.indices.pointer, (u32)options.indices.length, ""); return gep; @@ -1989,6 +2027,7 @@ fn bool unary_is_boolean(UnaryId id) case UnaryId::va_end: case UnaryId::bitwise_not: case UnaryId::dereference: + case UnaryId::pointer_from_int: return false; } } @@ -2250,6 +2289,10 @@ fn Value* clone_value(Module* module, Scope* scope, Value* old_value) .id = old_value->unary.id, }; } break; + case ValueId::unary_type: + { + result->unary_type = old_value->unary_type; + } break; case ValueId::unreachable: break; case ValueId::slice_expression: @@ -2263,6 +2306,23 @@ fn Value* clone_value(Module* module, Scope* scope, Value* old_value) .end = old_end ? clone_value(module, scope, old_end) : 0, }; } break; + case ValueId::call: + { + auto callable = clone_value(module, scope, old_value->call.callable); + auto old_arguments = old_value->call.arguments; + auto arguments = new_value_array(module, old_arguments.length); + + for (u64 i = 0; i < arguments.length; i += 1) + { + arguments[i] = clone_value(module, scope, old_arguments[i]); + } + + result->call = { + .callable = callable, + .arguments = arguments, + .function_type = old_value->call.function_type, + }; + } break; default: trap(); } } @@ -2354,7 +2414,6 @@ fn void copy_block(Module* module, Scope* parent_scope, BlockCopy copy) Statement* last_statement = 0; for (Statement* old_statement = source->first_statement; old_statement; old_statement = old_statement->next) { - assert((u64)old_statement->next != 4); auto statement = clone_statement(module, scope, old_statement); assert(!statement->next); if (last_statement) @@ -2365,12 +2424,7 @@ fn void copy_block(Module* module, Scope* parent_scope, BlockCopy copy) else { last_statement = statement; - source->first_statement = statement; - } - - if ((u64)old_statement->next == 0x4) - { - breakpoint(); + destination->first_statement = statement; } } } @@ -2688,6 +2742,33 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) value_type = string_type; } break; + case UnaryId::pointer_from_int: + { + if (!expected_type) + { + report_error(); + } + + if (expected_type->id != TypeId::pointer) + { + report_error(); + } + + analyze_type(module, unary_value, 0); + auto unary_value_type = unary_value->type; + if (unary_value_type->id != TypeId::integer) + { + report_error(); + } + + // TODO: is this correct? + if (get_bit_size(unary_value_type) != 64) + { + report_error(); + } + + value_type = expected_type; + } break; default: { auto is_boolean = unary_is_boolean(unary_id); @@ -2708,50 +2789,50 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) } break; case ValueId::unary_type: { - auto unary_type = value->unary_type.type; + auto unary_type = resolve_type(module, value->unary_type.type); + value->unary_type.type = unary_type; auto unary_type_id = value->unary_type.id; + + if (expected_type) + { + value_type = expected_type; + } + else + { + value_type = unary_type; + } + + assert(value_type); + if (value_type->id != TypeId::integer) + { + report_error(); + } + + u64 value; + auto max_value = integer_max_value(value_type->integer.bit_count, value_type->integer.is_signed); switch (unary_type_id) { + case UnaryTypeId::align_of: + { + value = get_byte_alignment(unary_type); + } break; case UnaryTypeId::byte_size: { - if (!expected_type) - { - report_error(); - } - if (expected_type->id != TypeId::integer) - { - report_error(); - } - - auto size = get_byte_size(unary_type); - auto max_value = integer_max_value(unary_type->integer.bit_count, unary_type->integer.is_signed); - if (size > max_value) - { - report_error(); - } - - value_type = expected_type; + value = get_byte_size(unary_type); } break; case UnaryTypeId::integer_max: { - if (unary_type->id != TypeId::integer) - { - report_error(); - } - - if (expected_type) - { - if (expected_type->id != TypeId::integer) - { - report_error(); - } - } - - value_type = expected_type ? expected_type : unary_type; - typecheck(module, expected_type, value_type); + value = integer_max_value(unary_type->integer.bit_count, unary_type->integer.is_signed); } break; } + + if (value > max_value) + { + report_error(); + } + + typecheck(module, expected_type, value_type); } break; case ValueId::binary: { @@ -2845,6 +2926,11 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) auto values = value->array_initialization.values; if (expected_type) { + if (expected_type->id != TypeId::array) + { + report_error(); + } + if (expected_type->array.element_count == 0) { expected_type->array.element_count = values.length; @@ -3589,7 +3675,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) .type = u64_type, .pointer = index_alloca, }); - + LLVMValueRef value_array_indices[] = { u64_zero, content_index_load, @@ -3926,6 +4012,19 @@ fn ValueTypePair enter_struct_pointer_for_coerced_access(Module* module, LLVMVal return { source_value, source_type }; } +fn LLVMValueRef coerce_integer_or_pointer_to_integer_or_pointer(Module* module, LLVMValueRef source, Type* source_type, Type* destination_type) +{ + unused(module); + unused(source_type); + unused(destination_type); + if (source_type != destination_type) + { + trap(); + } + + return source; +} + fn LLVMValueRef create_coerced_load(Module* module, LLVMValueRef source, Type* source_type, Type* destination_type) { LLVMValueRef result = 0; @@ -4022,7 +4121,13 @@ fn void create_coerced_store(Module* module, LLVMValueRef source_value, Type* so } else if (type_is_integer_backing(source_type)) { - trap(); + auto int_type = integer_type(module, { .bit_count = (u32)destination_size * 8, .is_signed = false }); + auto value = coerce_integer_or_pointer_to_integer_or_pointer(module, source_value, source_type, int_type); + create_store(module, { + .source = value, + .destination = destination_value, + .type = int_type, + }); } else { @@ -4030,6 +4135,204 @@ fn void create_coerced_store(Module* module, LLVMValueRef source_value, Type* so } } +struct SliceEmitResult +{ + LLVMValueRef values[2]; +}; + +fn LLVMValueRef emit_slice_result(Module* module, SliceEmitResult slice, LLVMTypeRef slice_type) +{ + auto result = LLVMGetPoison(slice_type); + result = LLVMBuildInsertValue(module->llvm.builder, result, slice.values[0], 0, ""); + result = LLVMBuildInsertValue(module->llvm.builder, result, slice.values[1], 1, ""); + return result; +} + +fn SliceEmitResult emit_slice_expression(Module* module, Value* value) +{ + switch (value->id) + { + case ValueId::slice_expression: + { + auto value_type = value->type; + assert(value_type); + assert(type_is_slice(value_type)); + auto slice_pointer_type = value_type->structure.fields[0].type; + assert(slice_pointer_type->id == TypeId::pointer); + auto slice_element_type = slice_pointer_type->pointer.element_type; + + auto index_type = uint64(module); + resolve_type_in_place(module, index_type); + auto llvm_index_type = index_type->llvm.abi; + auto index_zero = LLVMConstInt(llvm_index_type, 0, 0); + + auto array_like = value->slice_expression.array_like; + auto start = value->slice_expression.start; + auto end = value->slice_expression.end; + + assert(array_like->kind == ValueKind::left); + emit_value(module, array_like, TypeKind::memory); + + auto pointer_type = array_like->type; + assert(pointer_type->id == TypeId::pointer); + auto sliceable_type = pointer_type->pointer.element_type; + bool has_start = start; + if (start && start->id == ValueId::constant_integer && start->constant_integer.value == 0) + { + has_start = false; + } + + if (start) + { + emit_value(module, start, TypeKind::memory); + } + + if (end) + { + emit_value(module, end, TypeKind::memory); + } + + switch (sliceable_type->id) + { + case TypeId::pointer: + { + auto element_type = sliceable_type->pointer.element_type; + auto pointer_load = create_load(module, { + .type = sliceable_type, + .pointer = array_like->llvm, + }); + + auto slice_pointer = pointer_load; + if (has_start) + { + LLVMValueRef indices[] = { start->llvm }; + slice_pointer = create_gep(module, { + .type = element_type->llvm.memory, + .pointer = pointer_load, + .indices = array_to_slice(indices), + }); + } + + auto slice_length = end->llvm; + + if (has_start) + { + slice_length = LLVMBuildSub(module->llvm.builder, slice_length, start->llvm, ""); + } + return { slice_pointer, slice_length }; + } break; + case TypeId::structure: + { + assert(sliceable_type->structure.is_slice); + auto slice_load = create_load(module, { + .type = sliceable_type, + .pointer = array_like->llvm, + }); + auto old_slice_pointer = LLVMBuildExtractValue(module->llvm.builder, slice_load, 0, ""); + auto slice_pointer = old_slice_pointer; + + if (has_start) + { + LLVMValueRef indices[] = { start->llvm }; + slice_pointer = create_gep(module, { + .type = slice_element_type->llvm.memory, + .pointer = old_slice_pointer, + .indices = array_to_slice(indices), + }); + } + + auto slice_end = end ? end->llvm : LLVMBuildExtractValue(module->llvm.builder, slice_load, 1, ""); + auto slice_length = slice_end; + if (has_start) + { + slice_length = LLVMBuildSub(module->llvm.builder, slice_end, start->llvm, ""); + } + + return { slice_pointer, slice_length }; + } break; + case TypeId::array: + { + assert(sliceable_type->array.element_type == slice_element_type); + LLVMValueRef slice_pointer = array_like->llvm; + if (has_start) + { + LLVMValueRef indices[] = { index_zero, start->llvm }; + slice_pointer = create_gep(module, { + .type = sliceable_type->llvm.memory, + .pointer = slice_pointer, + .indices = array_to_slice(indices), + }); + } + + LLVMValueRef slice_length = 0; + if (has_start) + { + trap(); + } + else if (end) + { + slice_length = end->llvm; + } + else + { + auto element_count = sliceable_type->array.element_count; + slice_length = LLVMConstInt(llvm_index_type, element_count, 0); + } + + assert(slice_length); + return { slice_pointer, slice_length }; + } break; + default: unreachable(); + } + } break; + default: unreachable(); + } +} + + +fn SliceEmitResult emit_string_literal(Module* module, Value* value) +{ + auto resolved_value_type = resolve_alias(module, value->type); + switch (value->id) + { + case ValueId::string_literal: + { + bool null_terminate = true; + auto length = value->string_literal.length; + auto constant_string = LLVMConstStringInContext2(module->llvm.context, (char*)value->string_literal.pointer, length, !null_terminate); + auto u8_type = uint8(module); + resolve_type_in_place(module, u8_type); + auto string_type = LLVMArrayType2(u8_type->llvm.abi, length + null_terminate); + auto is_constant = true; + LLVMValueRef before = 0; + LLVMThreadLocalMode tlm = LLVMNotThreadLocal; + bool externally_initialized = false; + unsigned address_space = 0; + auto global = llvm_module_create_global_variable(module->llvm.module, string_type, is_constant, LLVMInternalLinkage, constant_string, string_literal("conststring"), before, tlm, address_space, externally_initialized); + LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); + auto slice_type = get_slice_type(module, u8_type); + + if (resolved_value_type->id != TypeId::structure) + { + report_error(); + } + + if (!resolved_value_type->structure.is_slice) + { + report_error(); + } + + if (slice_type != resolved_value_type) + { + report_error(); + } + + return { global, LLVMConstInt(slice_type->structure.fields[1].type->llvm.abi, length, false) }; + } break; + default: unreachable(); + } +} + fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type) { switch (value->id) @@ -4159,7 +4462,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, { semantic_argument_type = semantic_call_argument_value->type; argument_abi = abi_system_v_classify_argument(module, &available_registers.system_v, llvm_abi_argument_type_buffer_slice, abi_argument_type_buffer_slice, { - .type = semantic_argument_type, + .type = resolve_alias(module, semantic_argument_type), .abi_start = abi_argument_count, .is_named_argument = false, }); @@ -4460,28 +4763,36 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, llvm_abi_argument_value_buffer[abi_argument_count] = global; abi_argument_count += 1; - do_continue = true; } else { + auto pointer_type = get_pointer_type(module, semantic_call_argument_value->type); + switch (semantic_call_argument_value->id) { case ValueId::variable_reference: { - auto pointer_type = get_pointer_type(module, semantic_call_argument_value->type); semantic_call_argument_value->type = 0; semantic_call_argument_value->kind = ValueKind::left; analyze_value(module, semantic_call_argument_value, pointer_type, TypeKind::memory); llvm_abi_argument_value_buffer[abi_argument_count] = semantic_call_argument_value->llvm; abi_argument_count += 1; - do_continue = true; } break; default: { - trap(); + assert(abi_argument_type->id == TypeId::pointer); + assert(abi_argument_type->pointer.element_type == semantic_call_argument_value->type); + auto alloca = create_alloca(module, { + .type = semantic_call_argument_value->type, + }); + emit_assignment(module, alloca, pointer_type, semantic_call_argument_value); + llvm_abi_argument_value_buffer[abi_argument_count] = alloca; + abi_argument_count += 1; } break; } } + + do_continue = true; } else { @@ -4622,427 +4933,6 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, } } -fn LLVMValueRef emit_intrinsic_call(Module* module, IntrinsicIndex index, Slice argument_types, Slice argument_values) -{ - auto intrinsic_id = module->llvm.intrinsic_table[(backing_type(IntrinsicIndex))index]; - auto intrinsic_function = LLVMGetIntrinsicDeclaration(module->llvm.module, intrinsic_id.n, argument_types.pointer, argument_types.length); - auto intrinsic_function_type = LLVMIntrinsicGetType(module->llvm.context, intrinsic_id.n, argument_types.pointer, argument_types.length); - auto call = LLVMBuildCall2(module->llvm.builder, intrinsic_function_type, intrinsic_function, argument_values.pointer, argument_values.length, ""); - return call; -} - -fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type, TypeKind type_kind) -{ - unused(left_type); - - switch (value->id) - { - case ValueId::field_access: - { - auto aggregate = value->field_access.aggregate; - auto field_name = value->field_access.field_name; - - emit_value(module, aggregate, TypeKind::memory); - - assert(aggregate->kind == ValueKind::left); - auto aggregate_type = aggregate->type; - assert(aggregate_type->id == TypeId::pointer); - auto aggregate_element_type = aggregate_type->pointer.element_type; - - Type* real_aggregate_type = aggregate_element_type->id == TypeId::pointer ? aggregate_element_type->pointer.element_type : aggregate_element_type; - auto resolved_aggregate_type = resolve_alias(module, real_aggregate_type); - 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) - { - v = create_load(module, { - .type = aggregate_element_type, - .pointer = aggregate->llvm, - }); - } - else - { - v = aggregate->llvm; - } - - switch (resolved_aggregate_type->id) - { - case TypeId::structure: - case TypeId::union_type: - { - struct StructLikeFieldAccess - { - Type* type; - u32 field_index; - LLVMTypeRef struct_type; - }; - - StructLikeFieldAccess field_access; - switch (resolved_aggregate_type->id) - { - case TypeId::structure: - { - u32 field_index; - auto fields = resolved_aggregate_type->structure.fields; - auto field_count = (u32)fields.length; - for (field_index = 0; field_index < field_count; field_index += 1) - { - auto& field = fields[field_index]; - if (field_name.equal(field.name)) - { - break; - } - } - - if (field_index == field_count) - { - report_error(); - } - - field_access = { - .type = resolved_aggregate_type->structure.fields[field_index].type, - .field_index = field_index, - .struct_type = resolved_aggregate_type->llvm.memory, - }; - } break; - case TypeId::union_type: - { - auto fields = resolved_aggregate_type->union_type.fields; - u32 field_index; - auto field_count = (u32)fields.length; - for (field_index = 0; field_index < field_count; field_index += 1) - { - auto& field = fields[field_index]; - if (field_name.equal(field.name)) - { - break; - } - } - - if (field_index == field_count) - { - report_error(); - } - - auto field_type = resolved_aggregate_type->union_type.fields[field_index].type; - auto struct_type = LLVMStructTypeInContext(module->llvm.context, &field_type->llvm.memory, 1, false); - assert(struct_type); - - field_access = { - .type = field_type, - .field_index = 0, - .struct_type = struct_type, - }; - } break; - default: unreachable(); - } - - auto gep = LLVMBuildStructGEP2(module->llvm.builder, field_access.struct_type, v, field_access.field_index, ""); - - if (left_llvm) - { - trap(); - } - else - { - switch (value->kind) - { - case ValueKind::left: - { - return gep; - } break; - case ValueKind::right: - { - auto load = create_load(module, { - .type = field_access.type, - .pointer = gep, - }); - return load; - } break; - } - } - } break; - case TypeId::bits: - { - auto fields = resolved_aggregate_type->bits.fields; - u64 i; - for (i = 0; i < fields.length; i += 1) - { - auto& field = fields[i]; - if (field_name.equal(field.name)) - { - break; - } - } - - assert(i < fields.length); - - auto& field = fields[i]; - auto field_type = field.type; - resolve_type_in_place(module, field_type); - - auto load = create_load(module, { - .type = resolved_aggregate_type, - .pointer = v, - }); - auto shift = LLVMBuildLShr(module->llvm.builder, load, LLVMConstInt(resolved_aggregate_type->llvm.abi, field.offset, false), ""); - auto trunc = LLVMBuildTrunc(module->llvm.builder, shift, field_type->llvm.abi, ""); - if (left_llvm) - { - trap(); - } - - return trunc; - } break; - case TypeId::array: - { - assert(value->field_access.field_name.equal(string_literal("length"))); - auto array_length_type = get_llvm_type(value->type, type_kind); - auto result = LLVMConstInt(array_length_type, resolved_aggregate_type->array.element_count, false); - return result; - } 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(); - } -} - -struct SliceEmitResult -{ - LLVMValueRef values[2]; -}; - -fn LLVMValueRef emit_slice_result(Module* module, SliceEmitResult slice, LLVMTypeRef slice_type) -{ - auto result = LLVMGetPoison(slice_type); - result = LLVMBuildInsertValue(module->llvm.builder, result, slice.values[0], 0, ""); - result = LLVMBuildInsertValue(module->llvm.builder, result, slice.values[1], 1, ""); - return result; -} - -fn SliceEmitResult emit_slice_expression(Module* module, Value* value) -{ - switch (value->id) - { - case ValueId::slice_expression: - { - auto value_type = value->type; - assert(value_type); - assert(type_is_slice(value_type)); - auto slice_pointer_type = value_type->structure.fields[0].type; - assert(slice_pointer_type->id == TypeId::pointer); - auto slice_element_type = slice_pointer_type->pointer.element_type; - - auto index_type = uint64(module); - resolve_type_in_place(module, index_type); - auto llvm_index_type = index_type->llvm.abi; - auto index_zero = LLVMConstInt(llvm_index_type, 0, 0); - - auto array_like = value->slice_expression.array_like; - auto start = value->slice_expression.start; - auto end = value->slice_expression.end; - - assert(array_like->kind == ValueKind::left); - emit_value(module, array_like, TypeKind::memory); - - auto pointer_type = array_like->type; - assert(pointer_type->id == TypeId::pointer); - auto sliceable_type = pointer_type->pointer.element_type; - bool has_start = start && start->id == ValueId::constant_integer && start->constant_integer.value != 0; - - if (start) - { - emit_value(module, start, TypeKind::memory); - } - - if (end) - { - emit_value(module, end, TypeKind::memory); - } - - switch (sliceable_type->id) - { - case TypeId::pointer: - { - auto element_type = sliceable_type->pointer.element_type; - auto pointer_load = create_load(module, { - .type = sliceable_type, - .pointer = array_like->llvm, - }); - - auto slice_pointer = pointer_load; - if (has_start) - { - LLVMValueRef indices[] = { start->llvm }; - slice_pointer = create_gep(module, { - .type = element_type->llvm.memory, - .pointer = pointer_load, - .indices = array_to_slice(indices), - }); - } - - auto slice_length = end->llvm; - - if (has_start) - { - slice_length = LLVMBuildSub(module->llvm.builder, slice_length, start->llvm, ""); - } - return { slice_pointer, slice_length }; - } break; - case TypeId::structure: - { - assert(sliceable_type->structure.is_slice); - auto slice_load = create_load(module, { - .type = sliceable_type, - .pointer = array_like->llvm, - }); - auto old_slice_pointer = LLVMBuildExtractValue(module->llvm.builder, slice_load, 0, ""); - auto slice_pointer = old_slice_pointer; - - if (has_start) - { - LLVMValueRef indices[] = { start->llvm }; - slice_pointer = create_gep(module, { - .type = slice_element_type->llvm.memory, - .pointer = old_slice_pointer, - .indices = array_to_slice(indices), - }); - } - - auto slice_end = end ? end->llvm : LLVMBuildExtractValue(module->llvm.builder, slice_load, 1, ""); - auto slice_length = slice_end; - if (has_start) - { - slice_length = LLVMBuildSub(module->llvm.builder, slice_end, start->llvm, ""); - } - - return { slice_pointer, slice_length }; - } break; - case TypeId::array: - { - assert(sliceable_type->array.element_type == slice_element_type); - LLVMValueRef slice_pointer = array_like->llvm; - if (has_start) - { - LLVMValueRef indices[] = { index_zero, start->llvm }; - slice_pointer = create_gep(module, { - .type = sliceable_type->llvm.memory, - .pointer = slice_pointer, - .indices = array_to_slice(indices), - }); - } - - LLVMValueRef slice_length = 0; - if (has_start) - { - trap(); - } - else if (end) - { - slice_length = end->llvm; - } - else - { - auto element_count = sliceable_type->array.element_count; - slice_length = LLVMConstInt(llvm_index_type, element_count, 0); - } - - assert(slice_length); - return { slice_pointer, slice_length }; - } break; - default: unreachable(); - } - } break; - default: unreachable(); - } -} - -fn SliceEmitResult emit_string_literal(Module* module, Value* value) -{ - auto resolved_value_type = resolve_alias(module, value->type); - switch (value->id) - { - case ValueId::string_literal: - { - bool null_terminate = true; - auto length = value->string_literal.length; - auto constant_string = LLVMConstStringInContext2(module->llvm.context, (char*)value->string_literal.pointer, length, !null_terminate); - auto u8_type = uint8(module); - resolve_type_in_place(module, u8_type); - auto string_type = LLVMArrayType2(u8_type->llvm.abi, length + null_terminate); - auto is_constant = true; - LLVMValueRef before = 0; - LLVMThreadLocalMode tlm = LLVMNotThreadLocal; - bool externally_initialized = false; - unsigned address_space = 0; - auto global = llvm_module_create_global_variable(module->llvm.module, string_type, is_constant, LLVMInternalLinkage, constant_string, string_literal("conststring"), before, tlm, address_space, externally_initialized); - LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); - auto slice_type = get_slice_type(module, u8_type); - - if (resolved_value_type->id != TypeId::structure) - { - report_error(); - } - - if (!resolved_value_type->structure.is_slice) - { - report_error(); - } - - if (slice_type != resolved_value_type) - { - report_error(); - } - - return { global, LLVMConstNull(slice_type->structure.fields[1].type->llvm.abi) }; - } break; - default: unreachable(); - } -} - -fn void emit_block(Module* module, LLVMBasicBlockRef basic_block) -{ - auto current_basic_block = LLVMGetInsertBlock(module->llvm.builder); - if (current_basic_block) - { - if (!LLVMGetBasicBlockTerminator(current_basic_block)) - { - LLVMBuildBr(module->llvm.builder, basic_block); - } - } - - if (current_basic_block && LLVMGetBasicBlockParent(current_basic_block)) - { - LLVMInsertExistingBasicBlockAfterInsertBlock(module->llvm.builder, basic_block); - } - else - { - LLVMAppendExistingBasicBlock(module->current_function->variable.storage->llvm, basic_block); - - } - - LLVMPositionBuilderAtEnd(module->llvm.builder, basic_block); -} - fn LLVMValueRef emit_va_arg_from_memory(Module* module, LLVMValueRef va_list_pointer, Type* va_list_struct, Type* argument_type) { assert(va_list_struct->id == TypeId::structure); @@ -5076,6 +4966,7 @@ fn LLVMValueRef emit_va_arg_from_memory(Module* module, LLVMValueRef va_list_poi return overflow_arg_area; } + fn LLVMValueRef emit_va_arg(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type) { switch (value->id) @@ -5317,285 +5208,201 @@ fn LLVMValueRef emit_va_arg(Module* module, Value* value, LLVMValueRef left_llvm } } -fn LLVMValueRef emit_binary(Module* module, LLVMValueRef left, Type* left_type, LLVMValueRef right, Type* right_type, BinaryId id, Type* resolved_value_type) +fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type, TypeKind type_kind) { - switch (resolved_value_type->id) - { - case TypeId::integer: - { - switch (id) - { - case BinaryId::shift_right: - if (resolved_value_type->integer.is_signed) - { - return LLVMBuildAShr(module->llvm.builder, left, right, ""); - } - else - { - return LLVMBuildLShr(module->llvm.builder, left, right, ""); - } - break; - case BinaryId::div: - if (resolved_value_type->integer.is_signed) - { - return LLVMBuildSDiv(module->llvm.builder, left, right, ""); - } - else - { - return LLVMBuildUDiv(module->llvm.builder, left, right, ""); - } - break; - case BinaryId::rem: - if (resolved_value_type->integer.is_signed) - { - return LLVMBuildSRem(module->llvm.builder, left, right, ""); - } - else - { - return LLVMBuildURem(module->llvm.builder, left, right, ""); - } - break; - case BinaryId::compare_equal: - case BinaryId::compare_not_equal: - case BinaryId::compare_greater: - case BinaryId::compare_less: - case BinaryId::compare_greater_equal: - case BinaryId::compare_less_equal: - { - LLVMIntPredicate predicate; - assert(left_type == right_type); - auto left_signed = type_is_signed(left_type); - auto right_signed = type_is_signed(right_type); - assert(left_signed == right_signed); - auto is_signed = left_signed; + unused(left_type); - switch (id) - { - case BinaryId::compare_equal: predicate = LLVMIntEQ; break; - case BinaryId::compare_not_equal: predicate = LLVMIntNE; break; - case BinaryId::compare_greater: predicate = is_signed ? LLVMIntSGT : LLVMIntUGT; break; - case BinaryId::compare_less: predicate = is_signed ? LLVMIntSLT : LLVMIntULT; break; - case BinaryId::compare_greater_equal: predicate = is_signed ? LLVMIntSGE : LLVMIntUGE; break; - case BinaryId::compare_less_equal: predicate = is_signed ? LLVMIntSLE : LLVMIntULE; break; - default: unreachable(); - } - return LLVMBuildICmp(module->llvm.builder, predicate, left, right, ""); - } break; - case BinaryId::add: return LLVMBuildAdd(module->llvm.builder, left, right, ""); break; - case BinaryId::sub: return LLVMBuildSub(module->llvm.builder, left, right, ""); break; - case BinaryId::mul: return LLVMBuildMul(module->llvm.builder, left, right, ""); break; - case BinaryId::bitwise_and: return LLVMBuildAnd(module->llvm.builder, left, right, ""); break; - case BinaryId::bitwise_or: return LLVMBuildOr(module->llvm.builder, left, right, ""); break; - case BinaryId::bitwise_xor: return LLVMBuildXor(module->llvm.builder, left, right, ""); break; - case BinaryId::shift_left: return LLVMBuildShl(module->llvm.builder, left, right, ""); break; - default: unreachable(); - } - } break; - case TypeId::pointer: - { - auto element_type = resolved_value_type->pointer.element_type; - resolve_type_in_place(module, element_type); - - if (id != BinaryId::add && id != BinaryId::sub) - { - report_error(); - } - - LLVMValueRef index = right; - if (id == BinaryId::sub) - { - index = LLVMBuildNeg(module->llvm.builder, index, ""); - } - - LLVMValueRef indices[] = { index }; - - return create_gep(module, { - .type = element_type->llvm.abi, - .pointer = left, - .indices = array_to_slice(indices), - }); - } break; - default: unreachable(); - } -} - -fn void emit_local_storage(Module* module, Variable* variable) -{ - assert(!variable->storage); - auto value_type = variable->type; - resolve_type_in_place(module, value_type); - auto pointer_type = get_pointer_type(module, value_type); - auto storage = new_value(module); - auto alloca = create_alloca(module, { - .type = value_type, - .name = variable->name, - }); - *storage = Value{ - .type = pointer_type, - .id = ValueId::local, - .llvm = alloca, - }; - variable->storage = storage; -} - -fn LLVMMetadataRef null_expression(Module* module) -{ - return LLVMDIBuilderCreateExpression(module->llvm.di_builder, 0, 0); -} - -fn void end_debug_local(Module* module, Variable* variable, LLVMMetadataRef llvm_local) -{ - auto debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, variable->line, variable->column, variable->scope->llvm, module->llvm.inlined_at); - LLVMSetCurrentDebugLocation2(module->llvm.builder, debug_location); - auto basic_block = LLVMGetInsertBlock(module->llvm.builder); - assert(basic_block); - LLVMDIBuilderInsertDeclareRecordAtEnd(module->llvm.di_builder, variable->storage->llvm, llvm_local, null_expression(module), debug_location, basic_block); -} - -fn void emit_local_variable(Module* module, Local* local) -{ - emit_local_storage(module, &local->variable); - assert(local->variable.storage); - - if (module->has_debug_info) - { - auto debug_type = local->variable.type->llvm.debug; - assert(debug_type); - bool always_preserve = true; - LLVMDIFlags flags = {}; - - auto scope = local->variable.scope->llvm; - auto bit_alignment = get_byte_alignment(local->variable.storage->type->pointer.element_type) * 8; - auto local_variable = LLVMDIBuilderCreateAutoVariable(module->llvm.di_builder, scope, (char*)local->variable.name.pointer, local->variable.name.length, module->llvm.file, local->variable.line, debug_type, always_preserve, flags, bit_alignment); - - end_debug_local(module, &local->variable, local_variable); - } -} - -fn void emit_argument(Module* module, Argument* argument) -{ - emit_local_storage(module, &argument->variable); - assert(argument->variable.storage); - - if (module->has_debug_info) - { - auto debug_type = argument->variable.type->llvm.debug; - assert(debug_type); - auto scope = argument->variable.scope->llvm; - auto always_preserve = true; - LLVMDIFlags flags = {}; - auto argument_variable = LLVMDIBuilderCreateParameterVariable(module->llvm.di_builder, scope, (char*)argument->variable.name.pointer, argument->variable.name.length, argument->index, module->llvm.file, argument->variable.line, debug_type, always_preserve, flags); - - end_debug_local(module, &argument->variable, argument_variable); - } -} - - - -fn void emit_macro_instantiation(Module* module, Value* value) -{ switch (value->id) { - case ValueId::macro_instantiation: + case ValueId::field_access: { - auto current_function = module->current_function; - if (!current_function) + auto aggregate = value->field_access.aggregate; + auto field_name = value->field_access.field_name; + + emit_value(module, aggregate, TypeKind::memory); + + assert(aggregate->kind == ValueKind::left); + auto aggregate_type = aggregate->type; + assert(aggregate_type->id == TypeId::pointer); + auto aggregate_element_type = aggregate_type->pointer.element_type; + + Type* real_aggregate_type = aggregate_element_type->id == TypeId::pointer ? aggregate_element_type->pointer.element_type : aggregate_element_type; + auto resolved_aggregate_type = resolve_alias(module, real_aggregate_type); + 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) { - report_error(); - } - module->current_function = 0; - - auto old_macro_instantiation = module->current_macro_instantiation; - assert(!old_macro_instantiation); - auto macro_instantiation = &value->macro_instantiation; - module->current_macro_instantiation = macro_instantiation; - - - for (Value* instantiation_argument: macro_instantiation->instantiation_arguments) - { - emit_value(module, instantiation_argument, TypeKind::abi); - } - - LLVMMetadataRef caller_debug_location = 0; - if (module->has_debug_info) - { - assert(!module->llvm.inlined_at); - caller_debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, macro_instantiation->line, macro_instantiation->column, macro_instantiation->scope.parent->llvm, 0); - } - auto older_inlined_at = module->llvm.inlined_at; - assert(!older_inlined_at); - 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); - - LLVMBuildBr(module->llvm.builder, entry_block); - LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); - - LLVMValueRef return_alloca = 0; - auto return_type = macro_instantiation->return_type; - if (return_type->id != TypeId::void_type && return_type->id != TypeId::noreturn) - { - return_alloca = create_alloca(module, { - .type = return_type, - .name = string_literal("macro.return"), + v = create_load(module, { + .type = aggregate_element_type, + .pointer = aggregate->llvm, }); } - 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); - assert(!macro_instantiation->return_block); - macro_instantiation->return_block = return_block; - - auto declaration_arguments = macro_instantiation->declaration_arguments; - auto instantiation_arguments = macro_instantiation->instantiation_arguments; - assert(declaration_arguments.length == instantiation_arguments.length); - - for (u64 i = 0; i < declaration_arguments.length; i += 1) + else { - auto* declaration_argument = &declaration_arguments[i]; - auto* instantiation_argument = instantiation_arguments[i]; + v = aggregate->llvm; + } - emit_argument(module, declaration_argument); - - auto type = declaration_argument->variable.type; - auto resolved_type = resolve_alias(module, type); - auto evaluation_kind = get_evaluation_kind(resolved_type); - auto llvm_instantiation_argument = instantiation_argument->llvm; - auto llvm_declaration_argument = declaration_argument->variable.storage->llvm; - switch (evaluation_kind) - { - case EvaluationKind::scalar: + switch (resolved_aggregate_type->id) + { + case TypeId::structure: + case TypeId::union_type: + { + struct StructLikeFieldAccess { - create_store(module, { - .source = llvm_instantiation_argument, - .destination = llvm_declaration_argument, - .type = type, - }); - } break; - default: - trap(); - } - } - - analyze_block(module, macro_instantiation->block); + Type* type; + u32 field_index; + LLVMTypeRef struct_type; + }; - if (LLVMGetInsertBlock(module->llvm.builder)) - { - LLVMBuildBr(module->llvm.builder, return_block); + StructLikeFieldAccess field_access; + switch (resolved_aggregate_type->id) + { + case TypeId::structure: + { + u32 field_index; + auto fields = resolved_aggregate_type->structure.fields; + auto field_count = (u32)fields.length; + for (field_index = 0; field_index < field_count; field_index += 1) + { + auto& field = fields[field_index]; + if (field_name.equal(field.name)) + { + break; + } + } + + if (field_index == field_count) + { + report_error(); + } + + field_access = { + .type = resolved_aggregate_type->structure.fields[field_index].type, + .field_index = field_index, + .struct_type = resolved_aggregate_type->llvm.memory, + }; + } break; + case TypeId::union_type: + { + auto fields = resolved_aggregate_type->union_type.fields; + u32 field_index; + auto field_count = (u32)fields.length; + for (field_index = 0; field_index < field_count; field_index += 1) + { + auto& field = fields[field_index]; + if (field_name.equal(field.name)) + { + break; + } + } + + if (field_index == field_count) + { + report_error(); + } + + auto field_type = resolved_aggregate_type->union_type.fields[field_index].type; + auto struct_type = LLVMStructTypeInContext(module->llvm.context, &field_type->llvm.memory, 1, false); + assert(struct_type); + + field_access = { + .type = field_type, + .field_index = 0, + .struct_type = struct_type, + }; + } break; + default: unreachable(); + } + + auto gep = LLVMBuildStructGEP2(module->llvm.builder, field_access.struct_type, v, field_access.field_index, ""); + + if (left_llvm) + { + assert(get_evaluation_kind(field_access.type) == EvaluationKind::aggregate); + auto alignment = get_byte_alignment(field_access.type); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, gep, alignment, LLVMConstInt(u64_type->llvm.abi, get_byte_size(field_access.type), false)); + return gep; + } + else + { + switch (value->kind) + { + case ValueKind::left: + { + return gep; + } break; + case ValueKind::right: + { + auto load = create_load(module, { + .type = field_access.type, + .pointer = gep, + }); + return load; + } break; + } + } + } break; + case TypeId::bits: + { + auto fields = resolved_aggregate_type->bits.fields; + u64 i; + for (i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + if (field_name.equal(field.name)) + { + break; + } + } + + assert(i < fields.length); + + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place(module, field_type); + + auto load = create_load(module, { + .type = resolved_aggregate_type, + .pointer = v, + }); + auto shift = LLVMBuildLShr(module->llvm.builder, load, LLVMConstInt(resolved_aggregate_type->llvm.abi, field.offset, false), ""); + auto trunc = LLVMBuildTrunc(module->llvm.builder, shift, field_type->llvm.abi, ""); + if (left_llvm) + { + trap(); + } + + return trunc; + } break; + case TypeId::array: + { + assert(value->field_access.field_name.equal(string_literal("length"))); + auto array_length_type = get_llvm_type(value->type, type_kind); + auto result = LLVMConstInt(array_length_type, resolved_aggregate_type->array.element_count, false); + return result; + } break; + default: unreachable(); } - LLVMPositionBuilderAtEnd(module->llvm.builder, return_block); + trap(); - // END OF SCOPE - if (module->has_debug_info) - { - LLVMSetCurrentDebugLocation2(module->llvm.builder, caller_debug_location); - } - module->llvm.inlined_at = older_inlined_at; - module->current_macro_instantiation = old_macro_instantiation; - module->current_function = current_function; + // 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(); } @@ -5636,6 +5443,11 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, { case ValueId::array_initialization: { + auto values = right->array_initialization.values; + assert(resolved_value_type->id == TypeId::array); + auto uint64_type = uint64(module); + resolve_type_in_place(module, uint64_type); + if (right->array_initialization.is_constant) { emit_value(module, right, TypeKind::memory); @@ -5654,19 +5466,32 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, auto alignment = get_byte_alignment(resolved_value_type); LLVMSetAlignment(global, alignment); - auto uint64_type = uint64(module); - resolve_type_in_place(module, uint64_type); - auto element_type = resolved_value_type->array.element_type; auto element_count = resolved_value_type->array.element_count; - assert(right->array_initialization.values.length == element_count); + assert(values.length == element_count); u64 memcpy_size = get_byte_size(element_type) * element_count; LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, global, alignment, LLVMConstInt(uint64_type->llvm.abi, memcpy_size, false)); } else { - trap(); + auto u64_zero = LLVMConstNull(uint64_type->llvm.abi); + auto pointer_to_element_type = get_pointer_type(module, resolved_value_type->array.element_type); + + for (u64 i = 0; i < values.length; i += 1) + { + LLVMValueRef indices[] = { + u64_zero, + LLVMConstInt(uint64_type->llvm.abi, i, false), + }; + auto alloca_gep = create_gep(module, { + .type = resolved_value_type->llvm.memory, + .pointer = left_llvm, + .indices = array_to_slice(indices), + }); + + emit_assignment(module, alloca_gep, pointer_to_element_type, values[i]); + } } } break; case ValueId::string_literal: @@ -5762,7 +5587,32 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, if (zero) { - trap(); + u64 buffer_field_count = sizeof(field_mask) * 8; + auto raw_end_uninitialized_field_count = clz(field_mask); + auto unused_buffer_field_count = buffer_field_count - fields.length; + auto end_uninitialized_field_count = raw_end_uninitialized_field_count - unused_buffer_field_count; + auto initialized_field_count = __builtin_popcount(field_mask); + auto uninitialized_field_count = fields.length - initialized_field_count; + + if (uninitialized_field_count != end_uninitialized_field_count) + { + trap(); + } + + if (end_uninitialized_field_count == 0) + { + report_error(); + } + + auto field_index_offset = fields.length - end_uninitialized_field_count; + auto destination_pointer = LLVMBuildStructGEP2(module->llvm.builder, resolved_value_type->llvm.abi, left_llvm, field_index_offset, ""); + auto start_field = &fields[field_index_offset]; + auto memset_size = get_byte_size(resolved_value_type) - start_field->offset; + auto u8_type = uint8(module); + auto u64_type = uint64(module); + resolve_type_in_place(module, u8_type); + resolve_type_in_place(module, u64_type); + LLVMBuildMemSet(module->llvm.builder, destination_pointer, LLVMConstNull(u8_type->llvm.memory), LLVMConstInt(u64_type->llvm.memory, memset_size, false), 1); } } break; case TypeId::union_type: @@ -5892,6 +5742,21 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, resolve_type_in_place(module, u64_type); LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, right->macro_instantiation.return_alloca, alignment, LLVMConstInt(u64_type->llvm.abi, size, false)); } break; + case ValueId::unary: + case ValueId::select: + { + emit_value(module, right, TypeKind::memory); + create_store(module, { + .source = right->llvm, + .destination = left_llvm, + .type = resolved_value_type, + }); + } break; + case ValueId::field_access: + { + auto value = emit_field_access(module, right, left_llvm, left_type, TypeKind::memory); + right->llvm = value; + } break; default: unreachable(); } } break; @@ -5899,6 +5764,293 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, } } +fn LLVMValueRef emit_binary(Module* module, LLVMValueRef left, Type* left_type, LLVMValueRef right, Type* right_type, BinaryId id, Type* resolved_value_type) +{ + switch (resolved_value_type->id) + { + case TypeId::integer: + { + switch (id) + { + case BinaryId::shift_right: + if (resolved_value_type->integer.is_signed) + { + return LLVMBuildAShr(module->llvm.builder, left, right, ""); + } + else + { + return LLVMBuildLShr(module->llvm.builder, left, right, ""); + } + break; + case BinaryId::div: + if (resolved_value_type->integer.is_signed) + { + return LLVMBuildSDiv(module->llvm.builder, left, right, ""); + } + else + { + return LLVMBuildUDiv(module->llvm.builder, left, right, ""); + } + break; + case BinaryId::rem: + if (resolved_value_type->integer.is_signed) + { + return LLVMBuildSRem(module->llvm.builder, left, right, ""); + } + else + { + return LLVMBuildURem(module->llvm.builder, left, right, ""); + } + break; + case BinaryId::compare_equal: + case BinaryId::compare_not_equal: + case BinaryId::compare_greater: + case BinaryId::compare_less: + case BinaryId::compare_greater_equal: + case BinaryId::compare_less_equal: + { + LLVMIntPredicate predicate; + assert(left_type == right_type); + auto left_signed = type_is_signed(left_type); + auto right_signed = type_is_signed(right_type); + assert(left_signed == right_signed); + auto is_signed = left_signed; + + switch (id) + { + case BinaryId::compare_equal: predicate = LLVMIntEQ; break; + case BinaryId::compare_not_equal: predicate = LLVMIntNE; break; + case BinaryId::compare_greater: predicate = is_signed ? LLVMIntSGT : LLVMIntUGT; break; + case BinaryId::compare_less: predicate = is_signed ? LLVMIntSLT : LLVMIntULT; break; + case BinaryId::compare_greater_equal: predicate = is_signed ? LLVMIntSGE : LLVMIntUGE; break; + case BinaryId::compare_less_equal: predicate = is_signed ? LLVMIntSLE : LLVMIntULE; break; + default: unreachable(); + } + return LLVMBuildICmp(module->llvm.builder, predicate, left, right, ""); + } break; + case BinaryId::add: return LLVMBuildAdd(module->llvm.builder, left, right, ""); break; + case BinaryId::sub: return LLVMBuildSub(module->llvm.builder, left, right, ""); break; + case BinaryId::mul: return LLVMBuildMul(module->llvm.builder, left, right, ""); break; + case BinaryId::logical_and: + case BinaryId::bitwise_and: return LLVMBuildAnd(module->llvm.builder, left, right, ""); break; + case BinaryId::logical_or: + case BinaryId::bitwise_or: return LLVMBuildOr(module->llvm.builder, left, right, ""); break; + case BinaryId::bitwise_xor: return LLVMBuildXor(module->llvm.builder, left, right, ""); break; + case BinaryId::shift_left: return LLVMBuildShl(module->llvm.builder, left, right, ""); break; + default: unreachable(); + } + } break; + case TypeId::pointer: + { + auto element_type = resolved_value_type->pointer.element_type; + resolve_type_in_place(module, element_type); + + if (id != BinaryId::add && id != BinaryId::sub) + { + report_error(); + } + + LLVMValueRef index = right; + if (id == BinaryId::sub) + { + index = LLVMBuildNeg(module->llvm.builder, index, ""); + } + + LLVMValueRef indices[] = { index }; + + return create_gep(module, { + .type = element_type->llvm.abi, + .pointer = left, + .indices = array_to_slice(indices), + }); + } break; + default: unreachable(); + } +} + +fn void emit_local_storage(Module* module, Variable* variable) +{ + assert(!variable->storage); + auto value_type = variable->type; + resolve_type_in_place(module, value_type); + auto pointer_type = get_pointer_type(module, value_type); + auto storage = new_value(module); + assert(variable->name.pointer); + assert(variable->name.length); + auto alloca = create_alloca(module, { + .type = value_type, + .name = variable->name, + }); + *storage = Value{ + .type = pointer_type, + .id = ValueId::local, + .llvm = alloca, + }; + variable->storage = storage; +} + +fn LLVMMetadataRef null_expression(Module* module) +{ + return LLVMDIBuilderCreateExpression(module->llvm.di_builder, 0, 0); +} + +fn void end_debug_local(Module* module, Variable* variable, LLVMMetadataRef llvm_local) +{ + auto debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, variable->line, variable->column, variable->scope->llvm, module->llvm.inlined_at); + LLVMSetCurrentDebugLocation2(module->llvm.builder, debug_location); + auto basic_block = LLVMGetInsertBlock(module->llvm.builder); + assert(basic_block); + LLVMDIBuilderInsertDeclareRecordAtEnd(module->llvm.di_builder, variable->storage->llvm, llvm_local, null_expression(module), debug_location, basic_block); +} + +fn void emit_local_variable(Module* module, Local* local) +{ + emit_local_storage(module, &local->variable); + assert(local->variable.storage); + + if (module->has_debug_info) + { + auto debug_type = local->variable.type->llvm.debug; + assert(debug_type); + bool always_preserve = true; + LLVMDIFlags flags = {}; + + auto scope = local->variable.scope->llvm; + auto bit_alignment = get_byte_alignment(local->variable.storage->type->pointer.element_type) * 8; + auto local_variable = LLVMDIBuilderCreateAutoVariable(module->llvm.di_builder, scope, (char*)local->variable.name.pointer, local->variable.name.length, module->llvm.file, local->variable.line, debug_type, always_preserve, flags, bit_alignment); + + end_debug_local(module, &local->variable, local_variable); + } +} + +fn void emit_argument(Module* module, Argument* argument) +{ + emit_local_storage(module, &argument->variable); + assert(argument->variable.storage); + + if (module->has_debug_info) + { + auto debug_type = argument->variable.type->llvm.debug; + assert(debug_type); + auto scope = argument->variable.scope->llvm; + auto always_preserve = true; + LLVMDIFlags flags = {}; + auto argument_variable = LLVMDIBuilderCreateParameterVariable(module->llvm.di_builder, scope, (char*)argument->variable.name.pointer, argument->variable.name.length, argument->index, module->llvm.file, argument->variable.line, debug_type, always_preserve, flags); + + end_debug_local(module, &argument->variable, argument_variable); + } +} + +fn void emit_macro_instantiation(Module* module, Value* value) +{ + switch (value->id) + { + case ValueId::macro_instantiation: + { + auto current_function = module->current_function; + if (!current_function) + { + report_error(); + } + module->current_function = 0; + + auto old_macro_instantiation = module->current_macro_instantiation; + assert(!old_macro_instantiation); + auto macro_instantiation = &value->macro_instantiation; + module->current_macro_instantiation = macro_instantiation; + + + for (Value* instantiation_argument: macro_instantiation->instantiation_arguments) + { + emit_value(module, instantiation_argument, TypeKind::abi); + } + + LLVMMetadataRef caller_debug_location = 0; + if (module->has_debug_info) + { + assert(!module->llvm.inlined_at); + caller_debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, macro_instantiation->line, macro_instantiation->column, macro_instantiation->scope.parent->llvm, 0); + } + auto older_inlined_at = module->llvm.inlined_at; + assert(!older_inlined_at); + 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); + + LLVMBuildBr(module->llvm.builder, entry_block); + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + LLVMValueRef return_alloca = 0; + auto return_type = macro_instantiation->return_type; + if (return_type->id != TypeId::void_type && return_type->id != TypeId::noreturn) + { + return_alloca = create_alloca(module, { + .type = return_type, + .name = string_literal("macro.return"), + }); + } + 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); + assert(!macro_instantiation->return_block); + macro_instantiation->return_block = return_block; + + auto declaration_arguments = macro_instantiation->declaration_arguments; + auto instantiation_arguments = macro_instantiation->instantiation_arguments; + assert(declaration_arguments.length == instantiation_arguments.length); + + for (u64 i = 0; i < declaration_arguments.length; i += 1) + { + auto* declaration_argument = &declaration_arguments[i]; + auto* instantiation_argument = instantiation_arguments[i]; + + emit_argument(module, declaration_argument); + + auto type = declaration_argument->variable.type; + auto resolved_type = resolve_alias(module, type); + auto evaluation_kind = get_evaluation_kind(resolved_type); + auto llvm_instantiation_argument = instantiation_argument->llvm; + auto llvm_declaration_argument = declaration_argument->variable.storage->llvm; + switch (evaluation_kind) + { + case EvaluationKind::scalar: + { + create_store(module, { + .source = llvm_instantiation_argument, + .destination = llvm_declaration_argument, + .type = type, + }); + } break; + default: + trap(); + } + } + + analyze_block(module, macro_instantiation->block); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + LLVMBuildBr(module->llvm.builder, return_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, return_block); + + // END OF SCOPE + if (module->has_debug_info) + { + LLVMSetCurrentDebugLocation2(module->llvm.builder, caller_debug_location); + } + module->llvm.inlined_at = older_inlined_at; + module->current_macro_instantiation = old_macro_instantiation; + module->current_function = current_function; + } break; + default: unreachable(); + } +} + + 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) @@ -6055,7 +6207,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) { auto pointer_type = unary_value->type; assert(pointer_type->id == TypeId::pointer); - auto child_type = pointer_type->pointer.element_type; + auto child_type = resolve_alias(module, pointer_type->pointer.element_type); assert(child_type == resolved_value_type); auto load = create_load(module, LoadOptions{ .type = child_type, @@ -6068,6 +6220,10 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) trap(); } } break; + case UnaryId::pointer_from_int: + { + llvm_value = LLVMBuildIntToPtr(module->llvm.builder, llvm_unary_value, resolved_value_type->llvm.abi, ""); + } break; } } break; case ValueId::unary_type: @@ -6079,6 +6235,12 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) switch (unary_type_id) { + case UnaryTypeId::align_of: + { + assert(resolved_value_type->id == TypeId::integer); + auto constant_integer = LLVMConstInt(resolved_value_type->llvm.abi, get_byte_alignment(unary_type), false); + llvm_value = constant_integer; + } break; case UnaryTypeId::byte_size: { assert(resolved_value_type->id == TypeId::integer); @@ -6634,6 +6796,9 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) { auto fields = resolved_value_type->bits.fields; Type* backing_type = resolved_value_type->bits.backing_type; + resolve_type_in_place(module, backing_type); + auto abi_type = get_llvm_type(backing_type, type_kind); + if (is_constant) { u64 bits_value = 0; @@ -6659,7 +6824,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) unreachable(); } - auto field = fields[declaration_index]; + const auto& field = fields[declaration_index]; u64 field_value; switch (value->id) { @@ -6673,13 +6838,42 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) bits_value |= field_value << field.offset; } - resolve_type_in_place(module, backing_type); - - llvm_value = LLVMConstInt(get_llvm_type(backing_type, type_kind), bits_value, false); + llvm_value = LLVMConstInt(abi_type, bits_value, false); } else { - trap(); + llvm_value = LLVMConstNull(abi_type); + + for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + { + auto value = values[initialization_index]; + auto name = names[initialization_index]; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + { + auto& field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + if (declaration_index == fields.length) + { + unreachable(); + } + + const auto& field = fields[declaration_index]; + + emit_value(module, value, TypeKind::memory); + + auto extended = LLVMBuildZExt(module->llvm.builder, value->llvm, abi_type, ""); + auto shl = LLVMBuildShl(module->llvm.builder, extended, LLVMConstInt(abi_type, field.offset, false), ""); + auto or_value = LLVMBuildOr(module->llvm.builder, llvm_value, shl, ""); + llvm_value = or_value; + } } } break; default: unreachable(); @@ -6770,12 +6964,17 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) } break; } } break; + case ValueId::undefined: + { + llvm_value = LLVMGetPoison(get_llvm_type(resolved_value_type, type_kind)); + } break; default: unreachable(); } assert(llvm_value); value->llvm = llvm_value; } + fn void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind) { analyze_type(module, value, expected_type); @@ -6817,11 +7016,13 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 { case StatementId::return_st: { + auto return_value = statement->return_st; + if (module->current_function) { + assert(!module->current_macro_instantiation); auto& function_type = parent_function_global->variable.storage->type->pointer.element_type->function; auto& return_abi = function_type.return_abi; - auto return_value = statement->return_st; switch (return_abi.semantic_type->id) { @@ -6866,7 +7067,13 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 } else if (module->current_macro_instantiation) { - trap(); + auto macro_instantiation = module->current_macro_instantiation; + auto return_type = macro_instantiation->return_type; + assert(return_type); + analyze_type(module, return_value, return_type); + emit_assignment(module, macro_instantiation->return_alloca, get_pointer_type(module, return_type), return_value); + LLVMBuildBr(module->llvm.builder, macro_instantiation->return_block); + LLVMClearInsertionPosition(module->llvm.builder); } else { @@ -6899,19 +7106,17 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto condition_type = condition->type; LLVMValueRef llvm_condition = 0; - switch (condition_type->id) + assert(condition_type->id == TypeId::integer || condition_type->id == TypeId::pointer); + + llvm_condition = condition->llvm; + + if (!(condition_type->id == TypeId::integer && condition_type->integer.bit_count == 1)) { - case TypeId::integer: - { - llvm_condition = condition->llvm; - if (condition_type->integer.bit_count != 1) - { - llvm_condition = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, llvm_condition, LLVMConstNull(condition_type->llvm.abi), ""); - } - } break; - default: report_error(); + llvm_condition = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, llvm_condition, LLVMConstNull(condition_type->llvm.abi), ""); } + assert(llvm_condition); + LLVMBuildCondBr(module->llvm.builder, llvm_condition, taken_block, not_taken_block); LLVMPositionBuilderAtEnd(module->llvm.builder, taken_block); @@ -7214,6 +7419,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto previous_continue_block = module->llvm.continue_block; auto previous_exit_block = module->llvm.exit_block; + module->llvm.continue_block = continue_block; module->llvm.exit_block = exit_block; @@ -7305,10 +7511,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 { assert(aggregate_type->structure.is_slice); - auto gep = create_gep(module, { - .type = aggregate_type->llvm.abi, - .pointer = value->llvm, - }); + auto gep = LLVMBuildStructGEP2(module->llvm.builder, aggregate_type->llvm.abi, value->llvm, 1, "slice.length.pointer"); auto load = create_load(module, { .type = index_type, .pointer = gep, @@ -7594,6 +7797,169 @@ fn void emit_debug_argument(Module* module, Argument* argument, LLVMBasicBlockRe LLVMDIBuilderInsertDeclareRecordAtEnd(module->llvm.di_builder, argument->variable.storage->llvm, parameter_variable, LLVMDIBuilderCreateExpression(module->llvm.di_builder, 0, 0), debug_location, basic_block); } +struct ObjectGenerate +{ + String path; + BBLLVMOptimizationLevel optimization_level; + bool run_optimization_passes; + bool has_debug_info; +}; + +fn BBLLVMCodeGenerationPipelineResult generate_object(LLVMModuleRef module, LLVMTargetMachineRef target_machine, ObjectGenerate options) +{ + llvm_module_set_target(module, target_machine); + + if (options.run_optimization_passes) + { + // BBLLVM + bool prefer_speed = options.optimization_level == BBLLVMOptimizationLevel::O2 || options.optimization_level == BBLLVMOptimizationLevel::O3; + BBLLVMOptimizationPipelineOptions optimization_options = { + .optimization_level = (u64)options.optimization_level, + .debug_info = options.has_debug_info, + .loop_unrolling = prefer_speed, + .loop_interleaving = prefer_speed, + .loop_vectorization = prefer_speed, + .slp_vectorization = prefer_speed, + .merge_functions = prefer_speed, + .call_graph_profile = false, + .unified_lto = false, + .assignment_tracking = options.has_debug_info, + .verify_module = true, + }; + llvm_module_run_optimization_pipeline(module, target_machine, optimization_options); + } + + BBLLVMCodeGenerationPipelineOptions code_generation_options = { + .output_file_path = options.path, + .code_generation_file_type = (u64)BBLLVMCodeGenerationFileType::object_file, + .optimize_when_possible = options.optimization_level > BBLLVMOptimizationLevel::O0, + .verify_module = true, + }; + auto result = llvm_module_run_code_generation_pipeline(module, target_machine, &code_generation_options); + return result; +} + +struct ArgBuilder +{ + const char* args[128]; + u32 argument_count = 0; + + void add(const char* arg) + { + assert(argument_count < array_length(args)); + args[argument_count] = arg; + argument_count += 1; + } + + Slice flush() + { + assert(argument_count < array_length(args)); + args[argument_count] = 0; + return { args, argument_count }; + } +}; + +void link(Module* module) +{ + Arena* arena = module->arena; + ArgBuilder builder; + builder.add("ld.lld"); + builder.add("--error-limit=0"); + builder.add("-o"); + assert(module->executable.pointer[module->executable.length] == 0); + builder.add((char*)module->executable.pointer); + for (String object: module->objects) + { + assert(object.pointer[object.length] == 0); + builder.add((char*)object.pointer); + } + + for (String library: module->libraries) + { + assert(library.pointer[library.length] == 0); + builder.add((char*)library.pointer); + } + + String candidate_library_paths[] = { + string_literal("/usr/lib"), + string_literal("/usr/lib/x86_64-linux-gnu"), + }; + + u64 index; + String scrt1_object_path = {}; + for (index = 0; index < array_length(candidate_library_paths); index += 1) + { + auto directory_path = candidate_library_paths[index]; + String parts[] = { + directory_path, + string_literal("/Scrt1.o"), + }; + scrt1_object_path = arena_join_string(arena, array_to_slice(parts)); + auto file = os_open(scrt1_object_path, { .read = 1}, {}); + if (file >= 0) + { + os_close(file); + break; + } + } + + if (index == array_length(candidate_library_paths)) + { + report_error(); + } + + { + String parts[] = { + string_literal("-L"), + candidate_library_paths[index], + }; + + builder.add((char*)arena_join_string(arena, array_to_slice(parts)).pointer); + } + + auto link_libcpp = false; + if (link_libcpp) + { + builder.add("-lstdc++"); + } + + auto link_libc = true; + auto dynamic_linker = true; + + if (dynamic_linker) + { + builder.add("-dynamic-linker"); + auto dynamic_linker_path = "/usr/lib64/ld-linux-x86-64.so.2"; + builder.add(dynamic_linker_path); + } + + if (link_libc) + { + assert(scrt1_object_path.pointer); + builder.add((char*)scrt1_object_path.pointer); + builder.add("-lc"); + } + + auto args = builder.flush(); + auto result = lld_elf_link(args.pointer, args.length, true, false); + if (!result.success) + { + print(string_literal("Command failed:\n")); + for (auto arg : args) + { + auto a = c_string_to_slice(arg); + print(a); + print(string_literal(" ")); + } + print(string_literal("\n")); + assert(result.stdout_string.length == 0); + assert(result.stderr_string.length != 0); + print(result.stderr_string); + print(string_literal("\n")); + exit(1); + } +} + void emit(Module* module) { assert(!module->current_function); @@ -7677,7 +8043,7 @@ void emit(Module* module) for (u64 i = 0; i < semantic_argument_count; i += 1) { auto& abi = function_type->argument_abis[i]; - auto semantic_argument_type = function_type->semantic_argument_types[i]; + auto semantic_argument_type = resolve_alias(module, function_type->semantic_argument_types[i]); auto is_named_argument = i < semantic_argument_count; assert(is_named_argument); @@ -7929,7 +8295,7 @@ void emit(Module* module) } } - auto alloca = create_alloca(module, { .type = argument_abi.semantic_type }); + auto alloca = create_alloca(module, { .type = argument_abi.semantic_type, .name = argument->variable.name }); LLVMValueRef pointer; Type* pointer_type; if (argument_abi.attributes.direct.offset > 0) @@ -7989,6 +8355,14 @@ void emit(Module* module) } } } + else + { + assert(argument_abi.abi_count == 1); + auto abi_argument_type = function_type->abi_argument_types[argument_abi.abi_start]; + auto destination_size = get_byte_size(pointer_type) - argument_abi.attributes.direct.offset; + auto is_volatile = false; + create_coerced_store(module, argument_abi_arguments[0], abi_argument_type, pointer, pointer_type, destination_size, is_volatile); + } semantic_argument_storage = alloca; } @@ -8227,11 +8601,81 @@ void emit(Module* module) dump_module(module); print(string_literal("\n==========================\nLLVM VERIFICATION ERROR\n==========================\n")); print(verification_error_message); - fail(); + bb_fail(); } if (!module->silent) { dump_module(module); } + + BBLLVMCodeGenerationOptimizationLevel code_generation_optimization_level; + switch (module->build_mode) + { + case BuildMode::debug_none: + case BuildMode::debug: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::none; + break; + case BuildMode::soft_optimize: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::less; + break; + case BuildMode::optimize_for_speed: + case BuildMode::optimize_for_size: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::normal; + break; + case BuildMode::aggressively_optimize_for_speed: + case BuildMode::aggressively_optimize_for_size: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::aggressive; + break; + case BuildMode::count: + unreachable(); + } + BBLLVMTargetMachineCreate target_machine_options = { + .target_triple = llvm_default_target_triple(), + .cpu_model = llvm_host_cpu_name(), + .cpu_features = llvm_host_cpu_features(), + .relocation_model = BBLLVMRelocationModel::default_relocation, + .code_model = BBLLVMCodeModel::none, + .optimization_level = code_generation_optimization_level, + }; + String error_message = {}; + auto target_machine = llvm_create_target_machine(&target_machine_options, &error_message); + assert(target_machine); + BBLLVMOptimizationLevel optimization_level; + switch (module->build_mode) + { + case BuildMode::debug_none: + case BuildMode::debug: + optimization_level = BBLLVMOptimizationLevel::O0; + break; + case BuildMode::soft_optimize: + optimization_level = BBLLVMOptimizationLevel::O1; + break; + case BuildMode::optimize_for_speed: + optimization_level = BBLLVMOptimizationLevel::O2; + break; + case BuildMode::optimize_for_size: + optimization_level = BBLLVMOptimizationLevel::Os; + break; + case BuildMode::aggressively_optimize_for_speed: + optimization_level = BBLLVMOptimizationLevel::O3; + break; + case BuildMode::aggressively_optimize_for_size: + optimization_level = BBLLVMOptimizationLevel::Oz; + break; + case BuildMode::count: + unreachable(); + } + auto object_generation_result = generate_object(module->llvm.module, target_machine, { + .path = module->objects[0], + .optimization_level = optimization_level, + .run_optimization_passes = module->build_mode != BuildMode::debug_none, + .has_debug_info = module->has_debug_info, + }); + if (object_generation_result != BBLLVMCodeGenerationPipelineResult::success) + { + report_error(); + } + + link(module); } diff --git a/src/lib.cpp b/src/lib.cpp index 7822d61..be0a933 100644 --- a/src/lib.cpp +++ b/src/lib.cpp @@ -35,7 +35,7 @@ struct Stat extern "C" s32 fstat(s32, Stat*); extern "C" s32 fork(); extern "C" s32 dup2(s32, s32); -extern "C" s32 execve(const char* path_name, char* const argv[], char* const envp[]); +extern "C" s32 execve(const char* path_name, const char* const argv[], char* const envp[]); extern "C" s32 waitpid(s32 pid, int* wstatus, int options); u64 os_file_size(s32 fd) @@ -75,7 +75,7 @@ fn bool IFSIGNALED(u32 s) { return (s & 0xffff) - 1 < 0xff; } -Execution os_execute(Arena* arena, Slice arguments, Slice environment, ExecuteOptions options) +Execution os_execute(Arena* arena, Slice arguments, Slice environment, ExecuteOptions options) { unused(arena); assert(arguments.pointer[arguments.length] == 0); diff --git a/src/lib.h b/src/lib.h index e1d6e45..424974f 100644 --- a/src/lib.h +++ b/src/lib.h @@ -633,20 +633,8 @@ fn String file_read(Arena* arena, String file_path) return result; } -[[noreturn]] fn void fail() -{ - if (os_is_debugger_present()) - { - trap(); - } - exit(1); -} - -[[noreturn]] fn void fail_with_message(String string) -{ - print(string); - fail(); -} +#define bb_fail() os_is_debugger_present() ? trap() : exit(1) +#define bb_fail_with_message(message) (print(message), bb_fail()) fn u64 next_power_of_two(u64 n) { @@ -727,4 +715,4 @@ struct Execution u32 termination_code; }; -Execution os_execute(Arena* arena, Slice arguments, Slice environment, ExecuteOptions options); +Execution os_execute(Arena* arena, Slice arguments, Slice environment, ExecuteOptions options); diff --git a/src/llvm.cpp b/src/llvm.cpp index 57ba474..228dd00 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -922,268 +922,15 @@ EXPORT String llvm_host_cpu_features() return { result, length }; } -enum class BBLLVMEmitDwarfUnwindType : u8 -{ - always = 0, - no_compact_unwind = 1, - normal = 2, -}; - -enum class BBLLVMDwarfDirectory : u8 -{ - disable = 0, - enable = 1, - normal = 2, -}; - -enum class BBLLVMDebugCompressionType : u8 -{ - none = 0, - zlib = 1, - zstd = 2, -}; - -#define BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT (7) - -struct BBLLVMMCTargetOptions -{ - String abi_name; - String assembly_language; - String split_dwarf_file; - String as_secure_log_file; - const char* argv0; - String* argv_pointer; - u64 argv_count; - String* integrated_assembler_search_path_pointer; - u64 integrated_assembler_search_path_count; - u32 relax_all:1; - u32 no_exec_stack:1; - u32 fatal_warnings:1; - u32 no_warn:1; - u32 no_deprecated_warn:1; - u32 no_type_check:1; - u32 save_temp_labels:1; - u32 incremental_linker_compatible:1; - u32 fdpic:1; - u32 show_mc_encoding:1; - u32 show_mc_inst:1; - u32 asm_verbose:1; - u32 preserve_asm_comments:1; - u32 dwarf64:1; - u32 crel:1; - u32 x86_relax_relocations:1; - u32 x86_sse2_avx:1; - u32 emit_dwarf_unwind:2; - u32 use_dwarf_directory:2; - u32 debug_compression_type:2; - u32 emit_compact_unwind_non_canonical:1; - u32 ppc_use_full_register_names:1; - u32 reserved:BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT; -}; - -static_assert(sizeof(BBLLVMMCTargetOptions) == 112); -static_assert(BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT == 7); - -enum class BBLLVMCodeModel : u8 -{ - none = 0, - tiny = 1, - small = 2, - kernel = 3, - medium = 4, - large = 5, -}; - -enum class BBLLVMRelocationModel : u8 -{ - default_relocation = 0, - static_relocation = 1, - pic = 2, - dynamic_no_pic = 3, - ropi = 4, - rwpi = 5, - ropi_rwpi = 6, -}; - -enum class BBLLVMCodeGenerationOptimizationLevel : u8 -{ - none = 0, // -O0 - less = 1, // -O1 - normal = 2, // -O2, -Os - aggressive = 3 // -O3 -}; - -enum class BBLLVMGlobalISelAbortMode : u8 -{ - disable = 0, - enable = 1, - disable_with_diag = 2, -}; - -enum class BBLLVMSwiftAsyncFramePointerMode : u8 -{ - deployment_based = 0, - always = 1, - never = 2, -}; - -enum class BBLLVMBasicBlockSection : u8 -{ - all = 0, - list = 1, - preset = 2, - none = 3, -}; - -enum class BBLLVMFloatAbi : u8 -{ - normal = 0, - soft = 1, - hard = 2, -}; - -enum class BBLLVMFPOpFusion : u8 -{ - fast = 0, - standard = 1, - strict = 2, -}; - -enum class BBLLVMThreadModel : u8 -{ - posix = 0, - single = 1, -}; - -enum class BBLLVMEAbi : u8 -{ - unknown = 0, - normal = 1, - eabi4 = 2, - eabi5 = 3, - gnu = 4, -}; - -enum class BBLLVMDebuggerKind : u8 -{ - normal = 0, - gdb = 1, - lldb = 2, - sce = 3, - dbx = 4, -}; - -enum class BBLLVMExceptionHandling : u8 -{ - none = 0, - dwarf_cfi = 1, - setjmp_longjmp = 2, - arm = 3, - win_eh = 4, - wasm = 5, - aix = 6, - zos = 7, -}; - -#define BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT (21) - -struct BBLLVMTargetOptions -{ - u64 unsafe_fp_math:1; - u64 no_infs_fp_math:1; - u64 no_nans_fp_math:1; - u64 no_trapping_fp_math:1; - u64 no_signed_zeroes_fp_math:1; - u64 approx_func_fp_math:1; - u64 enable_aix_extended_altivec_abi:1; - u64 honor_sign_dependent_rounding_fp_math:1; - u64 no_zeroes_in_bss:1; - u64 guaranteed_tail_call_optimization:1; - u64 stack_symbol_ordering:1; - u64 enable_fast_isel:1; - u64 enable_global_isel:1; - u64 global_isel_abort_mode:2; - u64 swift_async_frame_pointer:2; - u64 use_init_array:1; - u64 disable_integrated_assembler:1; - u64 function_sections:1; - u64 data_sections:1; - u64 ignore_xcoff_visibility:1; - u64 xcoff_traceback_table:1; - u64 unique_section_names:1; - u64 unique_basic_block_section_names:1; - u64 separate_named_sections:1; - u64 trap_unreachable:1; - u64 no_trap_after_noreturn:1; - u64 tls_size:8; - u64 emulated_tls:1; - u64 enable_tls_descriptors:1; - u64 enable_ipra:1; - u64 emit_stack_size_section:1; - u64 enable_machine_outliner:1; - u64 enable_machine_function_splitter:1; - u64 supports_default_outlining:1; - u64 emit_address_significance_table:1; - u64 bb_address_map:1; - u64 bb_sections:3; - u64 emit_call_site_information:1; - u64 supports_debug_entry_values:1; - u64 enable_debug_entry_values:1; - u64 value_tracking_variable_locations:1; - u64 force_dwarf_frame_section:1; - u64 xray_function_index:1; - u64 debug_strict_dwarf:1; - u64 hotpatch:1; - u64 ppc_gen_scalar_mass_entries:1; - u64 jmc_instrument:1; - u64 enable_cfi_fixup:1; - u64 mis_expect:1; - u64 xcoff_read_only_pointers:1; - u64 float_abi:2; - u64 thread_model:1; - u32 fp_op_fusion_mode:2; - u32 eabi_version:3; - u32 debugger_kind:3; - u32 exception_handling:3; - u32 reserved:BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT; - unsigned loop_alignment; - int binutils_version[2]; - - BBLLVMMCTargetOptions mc; -}; - -static_assert(sizeof(BBLLVMTargetOptions) == 136); -static_assert(BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT == 21); - -#define BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT (4) - -struct BBLLVMTargetMachineCreate -{ - BBLLVMTargetOptions target_options; - String target_triple; - String cpu_model; - String cpu_features; - BBLLVMRelocationModel relocation_model; - BBLLVMCodeModel code_model; - BBLLVMCodeGenerationOptimizationLevel optimization_level; - bool jit; - u8 reserved[BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT]; -}; - -static_assert(sizeof(BBLLVMTargetMachineCreate) == 192); -static_assert(BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT == 4); - -EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachineCreate& create, String* error_message) +EXPORT LLVMTargetMachineRef llvm_create_target_machine(const BBLLVMTargetMachineCreate* create, String* error_message) { std::string error_message_string; - const llvm::Target* target = llvm::TargetRegistry::lookupTarget(string_ref(create.target_triple), error_message_string); - - llvm::TargetMachine* target_machine; + const llvm::Target* target = llvm::TargetRegistry::lookupTarget(string_ref(create->target_triple), error_message_string); if (target) { std::optional code_model; - switch (create.code_model) + switch (create->code_model) { case BBLLVMCodeModel::none: code_model = std::nullopt; break; case BBLLVMCodeModel::tiny: code_model = llvm::CodeModel::Tiny; break; @@ -1195,7 +942,7 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine std::optional relocation_model; - switch (create.relocation_model) + switch (create->relocation_model) { case BBLLVMRelocationModel::default_relocation: relocation_model = std::nullopt; break; case BBLLVMRelocationModel::static_relocation: relocation_model = llvm::Reloc::Static; break; @@ -1207,7 +954,7 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine } llvm::CodeGenOptLevel optimization_level; - switch (create.optimization_level) + switch (create->optimization_level) { case BBLLVMCodeGenerationOptimizationLevel::none: optimization_level = llvm::CodeGenOptLevel::None; break; case BBLLVMCodeGenerationOptimizationLevel::less: optimization_level = llvm::CodeGenOptLevel::Less; break; @@ -1218,28 +965,28 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine // INFO: This calls the default constructor, so all LLVM defaults are set and we only override what we control llvm::TargetOptions target_options; - target_options.UnsafeFPMath = create.target_options.unsafe_fp_math; - target_options.NoInfsFPMath = create.target_options.no_infs_fp_math; - target_options.NoNaNsFPMath = create.target_options.no_nans_fp_math; - target_options.NoTrappingFPMath = create.target_options.no_trapping_fp_math; - target_options.NoSignedZerosFPMath = create.target_options.no_signed_zeroes_fp_math; - target_options.ApproxFuncFPMath = create.target_options.approx_func_fp_math; - target_options.EnableAIXExtendedAltivecABI = create.target_options.enable_aix_extended_altivec_abi; - target_options.HonorSignDependentRoundingFPMathOption = create.target_options.honor_sign_dependent_rounding_fp_math; - target_options.NoZerosInBSS = create.target_options.no_zeroes_in_bss; - target_options.GuaranteedTailCallOpt = create.target_options.guaranteed_tail_call_optimization; - target_options.StackSymbolOrdering = create.target_options.stack_symbol_ordering; - target_options.EnableFastISel = create.target_options.enable_fast_isel; - target_options.EnableGlobalISel = create.target_options.enable_global_isel; + target_options.UnsafeFPMath = create->target_options.unsafe_fp_math; + target_options.NoInfsFPMath = create->target_options.no_infs_fp_math; + target_options.NoNaNsFPMath = create->target_options.no_nans_fp_math; + target_options.NoTrappingFPMath = create->target_options.no_trapping_fp_math; + target_options.NoSignedZerosFPMath = create->target_options.no_signed_zeroes_fp_math; + target_options.ApproxFuncFPMath = create->target_options.approx_func_fp_math; + target_options.EnableAIXExtendedAltivecABI = create->target_options.enable_aix_extended_altivec_abi; + target_options.HonorSignDependentRoundingFPMathOption = create->target_options.honor_sign_dependent_rounding_fp_math; + target_options.NoZerosInBSS = create->target_options.no_zeroes_in_bss; + target_options.GuaranteedTailCallOpt = create->target_options.guaranteed_tail_call_optimization; + target_options.StackSymbolOrdering = create->target_options.stack_symbol_ordering; + target_options.EnableFastISel = create->target_options.enable_fast_isel; + target_options.EnableGlobalISel = create->target_options.enable_global_isel; - auto global_isel_abort_mode = (BBLLVMGlobalISelAbortMode)create.target_options.global_isel_abort_mode; + auto global_isel_abort_mode = (BBLLVMGlobalISelAbortMode)create->target_options.global_isel_abort_mode; switch (global_isel_abort_mode) { case BBLLVMGlobalISelAbortMode::disable: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::Disable; break; case BBLLVMGlobalISelAbortMode::enable: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::Enable; break; case BBLLVMGlobalISelAbortMode::disable_with_diag: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::DisableWithDiag; break; } - auto swift_async_frame_pointer = (BBLLVMSwiftAsyncFramePointerMode)create.target_options.swift_async_frame_pointer; + auto swift_async_frame_pointer = (BBLLVMSwiftAsyncFramePointerMode)create->target_options.swift_async_frame_pointer; switch (swift_async_frame_pointer) { case BBLLVMSwiftAsyncFramePointerMode::deployment_based: target_options.SwiftAsyncFramePointer = llvm::SwiftAsyncFramePointerMode::DeploymentBased; break; @@ -1247,33 +994,33 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine case BBLLVMSwiftAsyncFramePointerMode::never: target_options.SwiftAsyncFramePointer = llvm::SwiftAsyncFramePointerMode::Never; break; } - target_options.UseInitArray = create.target_options.use_init_array; - target_options.DisableIntegratedAS = create.target_options.disable_integrated_assembler; - target_options.FunctionSections = create.target_options.function_sections; - target_options.DataSections = create.target_options.data_sections; - target_options.IgnoreXCOFFVisibility = create.target_options.ignore_xcoff_visibility; - target_options.XCOFFTracebackTable = create.target_options.xcoff_traceback_table; - target_options.UniqueSectionNames = create.target_options.unique_section_names; - target_options.UniqueBasicBlockSectionNames = create.target_options.unique_basic_block_section_names; + target_options.UseInitArray = create->target_options.use_init_array; + target_options.DisableIntegratedAS = create->target_options.disable_integrated_assembler; + target_options.FunctionSections = create->target_options.function_sections; + target_options.DataSections = create->target_options.data_sections; + target_options.IgnoreXCOFFVisibility = create->target_options.ignore_xcoff_visibility; + target_options.XCOFFTracebackTable = create->target_options.xcoff_traceback_table; + target_options.UniqueSectionNames = create->target_options.unique_section_names; + target_options.UniqueBasicBlockSectionNames = create->target_options.unique_basic_block_section_names; #if LLVM_VERSION_MAJOR >= 19 - target_options.SeparateNamedSections = create.target_options.separate_named_sections; + target_options.SeparateNamedSections = create->target_options.separate_named_sections; #endif - target_options.TrapUnreachable = create.target_options.trap_unreachable; - target_options.NoTrapAfterNoreturn = create.target_options.no_trap_after_noreturn; - target_options.TLSSize = create.target_options.tls_size; - target_options.EmulatedTLS = create.target_options.emulated_tls; - target_options.EnableTLSDESC = create.target_options.enable_tls_descriptors; - target_options.EnableIPRA = create.target_options.enable_ipra; - target_options.EmitStackSizeSection = create.target_options.emit_stack_size_section; - target_options.EnableMachineOutliner = create.target_options.enable_machine_outliner; - target_options.EnableMachineFunctionSplitter = create.target_options.enable_machine_function_splitter; - target_options.SupportsDefaultOutlining = create.target_options.supports_default_outlining; - target_options.EmitAddrsig = create.target_options.emit_address_significance_table; + target_options.TrapUnreachable = create->target_options.trap_unreachable; + target_options.NoTrapAfterNoreturn = create->target_options.no_trap_after_noreturn; + target_options.TLSSize = create->target_options.tls_size; + target_options.EmulatedTLS = create->target_options.emulated_tls; + target_options.EnableTLSDESC = create->target_options.enable_tls_descriptors; + target_options.EnableIPRA = create->target_options.enable_ipra; + target_options.EmitStackSizeSection = create->target_options.emit_stack_size_section; + target_options.EnableMachineOutliner = create->target_options.enable_machine_outliner; + target_options.EnableMachineFunctionSplitter = create->target_options.enable_machine_function_splitter; + target_options.SupportsDefaultOutlining = create->target_options.supports_default_outlining; + target_options.EmitAddrsig = create->target_options.emit_address_significance_table; #if LLVM_VERSION_MAJOR >= 19 - target_options.BBAddrMap = create.target_options.bb_address_map; + target_options.BBAddrMap = create->target_options.bb_address_map; #endif - auto bb_sections = (BBLLVMBasicBlockSection) create.target_options.bb_sections; + auto bb_sections = (BBLLVMBasicBlockSection) create->target_options.bb_sections; switch (bb_sections) { case BBLLVMBasicBlockSection::all: target_options.BBSections = llvm::BasicBlockSection::All; break; @@ -1282,21 +1029,21 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine case BBLLVMBasicBlockSection::none: target_options.BBSections = llvm::BasicBlockSection::None; break; } - target_options.EmitCallSiteInfo = create.target_options.emit_call_site_information; - target_options.SupportsDebugEntryValues = create.target_options.supports_debug_entry_values; - target_options.EnableDebugEntryValues = create.target_options.enable_debug_entry_values; - target_options.ValueTrackingVariableLocations = create.target_options.value_tracking_variable_locations; - target_options.ForceDwarfFrameSection = create.target_options.force_dwarf_frame_section; - target_options.XRayFunctionIndex = create.target_options.xray_function_index; - target_options.DebugStrictDwarf = create.target_options.debug_strict_dwarf; - target_options.Hotpatch = create.target_options.hotpatch; - target_options.PPCGenScalarMASSEntries = create.target_options.ppc_gen_scalar_mass_entries; - target_options.JMCInstrument = create.target_options.jmc_instrument; - target_options.EnableCFIFixup = create.target_options.enable_cfi_fixup; - target_options.MisExpect = create.target_options.mis_expect; - target_options.XCOFFReadOnlyPointers = create.target_options.xcoff_read_only_pointers; + target_options.EmitCallSiteInfo = create->target_options.emit_call_site_information; + target_options.SupportsDebugEntryValues = create->target_options.supports_debug_entry_values; + target_options.EnableDebugEntryValues = create->target_options.enable_debug_entry_values; + target_options.ValueTrackingVariableLocations = create->target_options.value_tracking_variable_locations; + target_options.ForceDwarfFrameSection = create->target_options.force_dwarf_frame_section; + target_options.XRayFunctionIndex = create->target_options.xray_function_index; + target_options.DebugStrictDwarf = create->target_options.debug_strict_dwarf; + target_options.Hotpatch = create->target_options.hotpatch; + target_options.PPCGenScalarMASSEntries = create->target_options.ppc_gen_scalar_mass_entries; + target_options.JMCInstrument = create->target_options.jmc_instrument; + target_options.EnableCFIFixup = create->target_options.enable_cfi_fixup; + target_options.MisExpect = create->target_options.mis_expect; + target_options.XCOFFReadOnlyPointers = create->target_options.xcoff_read_only_pointers; - auto float_abi = (BBLLVMFloatAbi) create.target_options.float_abi; + auto float_abi = (BBLLVMFloatAbi) create->target_options.float_abi; switch (float_abi) { case BBLLVMFloatAbi::normal: target_options.FloatABIType = llvm::FloatABI::Default; break; @@ -1304,14 +1051,14 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine case BBLLVMFloatAbi::hard: target_options.FloatABIType = llvm::FloatABI::Hard; break; } - auto thread_model = (BBLLVMThreadModel) create.target_options.thread_model; + auto thread_model = (BBLLVMThreadModel) create->target_options.thread_model; switch (thread_model) { case BBLLVMThreadModel::posix: target_options.ThreadModel = llvm::ThreadModel::POSIX; break; case BBLLVMThreadModel::single: target_options.ThreadModel = llvm::ThreadModel::Single; break; } - auto fp_op_fusion_mode = (BBLLVMFPOpFusion) create.target_options.fp_op_fusion_mode; + auto fp_op_fusion_mode = (BBLLVMFPOpFusion) create->target_options.fp_op_fusion_mode; switch (fp_op_fusion_mode) { case BBLLVMFPOpFusion::fast: target_options.AllowFPOpFusion = llvm::FPOpFusion::Fast; break; @@ -1319,7 +1066,7 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine case BBLLVMFPOpFusion::strict: target_options.AllowFPOpFusion = llvm::FPOpFusion::Strict; break; } - auto eabi_version = (BBLLVMEAbi) create.target_options.eabi_version; + auto eabi_version = (BBLLVMEAbi) create->target_options.eabi_version; switch (eabi_version) { case BBLLVMEAbi::unknown: target_options.EABIVersion = llvm::EABI::Unknown; break; @@ -1329,7 +1076,7 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine case BBLLVMEAbi::gnu: target_options.EABIVersion = llvm::EABI::GNU; break; } - auto debugger_kind = (BBLLVMDebuggerKind) create.target_options.debugger_kind; + auto debugger_kind = (BBLLVMDebuggerKind) create->target_options.debugger_kind; switch (debugger_kind) { case BBLLVMDebuggerKind::normal: target_options.DebuggerTuning = llvm::DebuggerKind::Default; break; @@ -1339,7 +1086,7 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine case BBLLVMDebuggerKind::dbx: target_options.DebuggerTuning = llvm::DebuggerKind::DBX; break; } - auto exception_handling = (BBLLVMExceptionHandling) create.target_options.exception_handling; + auto exception_handling = (BBLLVMExceptionHandling) create->target_options.exception_handling; switch (exception_handling) { case BBLLVMExceptionHandling::none: target_options.ExceptionModel = llvm::ExceptionHandling::None; break; @@ -1352,66 +1099,66 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine case BBLLVMExceptionHandling::zos: target_options.ExceptionModel = llvm::ExceptionHandling::ZOS; break; } - target_options.LoopAlignment = create.target_options.loop_alignment; - target_options.BinutilsVersion = { create.target_options.binutils_version[0], create.target_options.binutils_version[1] }; + target_options.LoopAlignment = create->target_options.loop_alignment; + target_options.BinutilsVersion = { create->target_options.binutils_version[0], create->target_options.binutils_version[1] }; - if (create.target_options.mc.abi_name.length) + if (create->target_options.mc.abi_name.length) { - target_options.MCOptions.ABIName = { (char*)create.target_options.mc.abi_name.pointer, create.target_options.mc.abi_name.length }; + target_options.MCOptions.ABIName = { (char*)create->target_options.mc.abi_name.pointer, create->target_options.mc.abi_name.length }; } - if (create.target_options.mc.assembly_language.length) + if (create->target_options.mc.assembly_language.length) { - target_options.MCOptions.AssemblyLanguage = { (char*)create.target_options.mc.assembly_language.pointer, create.target_options.mc.assembly_language.length }; + target_options.MCOptions.AssemblyLanguage = { (char*)create->target_options.mc.assembly_language.pointer, create->target_options.mc.assembly_language.length }; } - if (create.target_options.mc.split_dwarf_file.length) + if (create->target_options.mc.split_dwarf_file.length) { - target_options.MCOptions.SplitDwarfFile = { (char*)create.target_options.mc.split_dwarf_file.pointer, create.target_options.mc.split_dwarf_file.length }; + target_options.MCOptions.SplitDwarfFile = { (char*)create->target_options.mc.split_dwarf_file.pointer, create->target_options.mc.split_dwarf_file.length }; } - if (create.target_options.mc.as_secure_log_file.length) + if (create->target_options.mc.as_secure_log_file.length) { - target_options.MCOptions.AsSecureLogFile = { (char*)create.target_options.mc.as_secure_log_file.pointer, create.target_options.mc.as_secure_log_file.length }; + target_options.MCOptions.AsSecureLogFile = { (char*)create->target_options.mc.as_secure_log_file.pointer, create->target_options.mc.as_secure_log_file.length }; } - if (create.target_options.mc.argv_count) + if (create->target_options.mc.argv_count) { - target_options.MCOptions.Argv0 = create.target_options.mc.argv0; + target_options.MCOptions.Argv0 = create->target_options.mc.argv0; // TODO: __builtin_trap(); } - if (create.target_options.mc.integrated_assembler_search_path_count) + if (create->target_options.mc.integrated_assembler_search_path_count) { // TODO: __builtin_trap(); } - target_options.MCOptions.MCRelaxAll = create.target_options.mc.relax_all; - target_options.MCOptions.MCNoExecStack = create.target_options.mc.no_exec_stack; - target_options.MCOptions.MCFatalWarnings = create.target_options.mc.fatal_warnings; - target_options.MCOptions.MCNoWarn = create.target_options.mc.no_warn; - target_options.MCOptions.MCNoDeprecatedWarn = create.target_options.mc.no_deprecated_warn; - target_options.MCOptions.MCNoTypeCheck = create.target_options.mc.no_type_check; - target_options.MCOptions.MCSaveTempLabels = create.target_options.mc.save_temp_labels; - target_options.MCOptions.MCIncrementalLinkerCompatible = create.target_options.mc.incremental_linker_compatible; + target_options.MCOptions.MCRelaxAll = create->target_options.mc.relax_all; + target_options.MCOptions.MCNoExecStack = create->target_options.mc.no_exec_stack; + target_options.MCOptions.MCFatalWarnings = create->target_options.mc.fatal_warnings; + target_options.MCOptions.MCNoWarn = create->target_options.mc.no_warn; + target_options.MCOptions.MCNoDeprecatedWarn = create->target_options.mc.no_deprecated_warn; + target_options.MCOptions.MCNoTypeCheck = create->target_options.mc.no_type_check; + target_options.MCOptions.MCSaveTempLabels = create->target_options.mc.save_temp_labels; + target_options.MCOptions.MCIncrementalLinkerCompatible = create->target_options.mc.incremental_linker_compatible; #if LLVM_VERSION_MAJOR >= 19 - target_options.MCOptions.FDPIC = create.target_options.mc.fdpic; + target_options.MCOptions.FDPIC = create->target_options.mc.fdpic; #endif - target_options.MCOptions.ShowMCEncoding = create.target_options.mc.show_mc_encoding; - target_options.MCOptions.ShowMCInst = create.target_options.mc.show_mc_inst; - target_options.MCOptions.AsmVerbose = create.target_options.mc.asm_verbose; - target_options.MCOptions.PreserveAsmComments = create.target_options.mc.preserve_asm_comments; - target_options.MCOptions.Dwarf64 = create.target_options.mc.dwarf64; + target_options.MCOptions.ShowMCEncoding = create->target_options.mc.show_mc_encoding; + target_options.MCOptions.ShowMCInst = create->target_options.mc.show_mc_inst; + target_options.MCOptions.AsmVerbose = create->target_options.mc.asm_verbose; + target_options.MCOptions.PreserveAsmComments = create->target_options.mc.preserve_asm_comments; + target_options.MCOptions.Dwarf64 = create->target_options.mc.dwarf64; #if LLVM_VERSION_MAJOR >= 19 - target_options.MCOptions.Crel = create.target_options.mc.crel; - target_options.MCOptions.X86RelaxRelocations = create.target_options.mc.x86_relax_relocations; - target_options.MCOptions.X86Sse2Avx = create.target_options.mc.x86_sse2_avx; + target_options.MCOptions.Crel = create->target_options.mc.crel; + target_options.MCOptions.X86RelaxRelocations = create->target_options.mc.x86_relax_relocations; + target_options.MCOptions.X86Sse2Avx = create->target_options.mc.x86_sse2_avx; #endif - auto emit_dwarf_unwind = (BBLLVMEmitDwarfUnwindType) create.target_options.mc.emit_dwarf_unwind; + auto emit_dwarf_unwind = (BBLLVMEmitDwarfUnwindType) create->target_options.mc.emit_dwarf_unwind; switch (emit_dwarf_unwind) { case BBLLVMEmitDwarfUnwindType::always: target_options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::Always; break; @@ -1419,7 +1166,7 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine case BBLLVMEmitDwarfUnwindType::normal: target_options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::Default; break; } - auto use_dwarf_directory = (BBLLVMDwarfDirectory) create.target_options.mc.use_dwarf_directory; + auto use_dwarf_directory = (BBLLVMDwarfDirectory) create->target_options.mc.use_dwarf_directory; switch (use_dwarf_directory) { case BBLLVMDwarfDirectory::disable: target_options.MCOptions.MCUseDwarfDirectory = llvm::MCTargetOptions::DwarfDirectory::DisableDwarfDirectory; break; @@ -1428,7 +1175,7 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine } #if LLVM_VERSION_MAJOR >= 19 - auto debug_compression_type = (BBLLVMDebugCompressionType) create.target_options.mc.debug_compression_type; + auto debug_compression_type = (BBLLVMDebugCompressionType) create->target_options.mc.debug_compression_type; switch (debug_compression_type) { case BBLLVMDebugCompressionType::none: target_options.MCOptions.CompressDebugSections = llvm::DebugCompressionType::None; break; @@ -1437,10 +1184,10 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine } #endif - target_options.MCOptions.EmitCompactUnwindNonCanonical = create.target_options.mc.emit_compact_unwind_non_canonical; - target_options.MCOptions.PPCUseFullRegisterNames = create.target_options.mc.ppc_use_full_register_names; + target_options.MCOptions.EmitCompactUnwindNonCanonical = create->target_options.mc.emit_compact_unwind_non_canonical; + target_options.MCOptions.PPCUseFullRegisterNames = create->target_options.mc.ppc_use_full_register_names; - target_machine = target->createTargetMachine(string_ref(create.target_triple), string_ref(create.cpu_model), string_ref(create.cpu_features), target_options, relocation_model, code_model, optimization_level, create.jit); + return reinterpret_cast(const_cast(target->createTargetMachine(string_ref(create->target_triple), string_ref(create->cpu_model), string_ref(create->cpu_features), target_options, relocation_model, code_model, optimization_level, create->jit))); } else { @@ -1449,52 +1196,23 @@ EXPORT llvm::TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachine memcpy(result, error_message_string.c_str(), length); *error_message = { result, length }; - - target_machine = 0; + return 0; } - - return target_machine; } -EXPORT void llvm_module_set_target(llvm::Module& module, llvm::TargetMachine& target_machine) +EXPORT void llvm_module_set_target(LLVMModuleRef m, LLVMTargetMachineRef tm) { - module.setDataLayout(target_machine.createDataLayout()); - auto& triple_string = target_machine.getTargetTriple().getTriple(); - module.setTargetTriple(llvm::StringRef(triple_string)); + auto module = llvm::unwrap(m); + auto target_machine = (llvm::TargetMachine*)tm; + module->setDataLayout(target_machine->createDataLayout()); + auto& triple_string = target_machine->getTargetTriple().getTriple(); + module->setTargetTriple(llvm::StringRef(triple_string)); } -enum class BBLLVMOptimizationLevel : u8 -{ - O0 = 0, - O1 = 1, - O2 = 2, - O3 = 3, - Os = 4, - Oz = 5, -}; - -#define BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (51) -struct BBLLVMOptimizationPipelineOptions -{ - u64 optimization_level:3; - u64 debug_info:1; - u64 loop_unrolling:1; - u64 loop_interleaving:1; - u64 loop_vectorization:1; - u64 slp_vectorization:1; - u64 merge_functions:1; - u64 call_graph_profile:1; - u64 unified_lto:1; - u64 assignment_tracking:1; - u64 verify_module:1; - u64 reserved:BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; -}; - -static_assert(sizeof(BBLLVMOptimizationPipelineOptions) == sizeof(u64)); -static_assert(BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 51); - -EXPORT void llvm_module_run_optimization_pipeline(llvm::Module& module, llvm::TargetMachine& target_machine, BBLLVMOptimizationPipelineOptions options) +EXPORT void llvm_module_run_optimization_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, BBLLVMOptimizationPipelineOptions options) { + auto module = llvm::unwrap(m); + auto target_machine = (llvm::TargetMachine*)tm; // TODO: PGO // TODO: CS profile @@ -1514,7 +1232,7 @@ EXPORT void llvm_module_run_optimization_pipeline(llvm::Module& module, llvm::Ta llvm::CGSCCAnalysisManager cgscc_analysis_manager; llvm::ModuleAnalysisManager module_analysis_manager; - llvm::PassBuilder pass_builder(&target_machine, pipeline_tuning_options); + llvm::PassBuilder pass_builder(target_machine, pipeline_tuning_options); if (options.assignment_tracking && options.debug_info != 0) { @@ -1524,7 +1242,7 @@ EXPORT void llvm_module_run_optimization_pipeline(llvm::Module& module, llvm::Ta }); } - llvm::Triple target_triple = target_machine.getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 + llvm::Triple target_triple = target_machine->getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 // TODO: add library (?) std::unique_ptr TLII(llvm::driver::createTLII(target_triple, llvm::driver::VectorLibrary::NoLibrary)); function_analysis_manager.registerPass([&] { return llvm::TargetLibraryAnalysis(*TLII); }); @@ -1570,60 +1288,34 @@ EXPORT void llvm_module_run_optimization_pipeline(llvm::Module& module, llvm::Ta // TODO: if emit bitcode/IR - module_pass_manager.run(module, module_analysis_manager); + module_pass_manager.run(*module, module_analysis_manager); } -enum class BBLLVMCodeGenerationFileType : u8 +EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, const BBLLVMCodeGenerationPipelineOptions* options) { - assembly_file = 0, - object_file = 1, - null = 2, -}; + auto module = llvm::unwrap(m); + auto target_machine = (llvm::TargetMachine*)tm; -#define BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (60) - -struct BBLLVMCodeGenerationPipelineOptions -{ - String output_dwarf_file_path; - String output_file_path; - u64 code_generation_file_type:2; - u64 optimize_when_possible:1; - u64 verify_module:1; - u64 reserved: BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; -}; - -static_assert(sizeof(BBLLVMCodeGenerationPipelineOptions) == 5 * sizeof(u64)); -static_assert(BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 60); - -enum class BBLLVMCodeGenerationPipelineResult : u8 -{ - success = 0, - failed_to_create_file = 1, - failed_to_add_emit_passes = 2, -}; - -EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(llvm::Module& module, llvm::TargetMachine& target_machine, BBLLVMCodeGenerationPipelineOptions options) -{ // We still use the legacy PM to run the codegen pipeline since the new PM // does not work with the codegen pipeline. // FIXME: make the new PM work with the codegen pipeline. llvm::legacy::PassManager CodeGenPasses; #if LLVM_VERSION_MAJOR >= 19 - if (options.optimize_when_possible) + if (options->optimize_when_possible) { - CodeGenPasses.add(createTargetTransformInfoWrapperPass(target_machine.getTargetIRAnalysis())); + CodeGenPasses.add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis())); } #endif llvm::raw_pwrite_stream* dwarf_object_file = 0; - if (options.output_dwarf_file_path.length) + if (options->output_dwarf_file_path.length) { __builtin_trap(); } - if (options.optimize_when_possible) + if (options->optimize_when_possible) { - llvm::Triple target_triple = target_machine.getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 + llvm::Triple target_triple = target_machine->getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 // TODO: add library (?) std::unique_ptr TLII(llvm::driver::createTLII(target_triple, llvm::driver::VectorLibrary::NoLibrary)); CodeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*TLII)); @@ -1631,11 +1323,11 @@ EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeli std::unique_ptr stream; - if (options.output_file_path.length) + if (options->output_file_path.length) { 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(string_ref(options->output_file_path), error_code, llvm::sys::fs::OF_None); if (error_code) { @@ -1648,33 +1340,24 @@ EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeli } llvm::CodeGenFileType file_type; - switch ((BBLLVMCodeGenerationFileType)options.code_generation_file_type) + switch ((BBLLVMCodeGenerationFileType)options->code_generation_file_type) { case BBLLVMCodeGenerationFileType::assembly_file: file_type = llvm::CodeGenFileType::AssemblyFile; break; case BBLLVMCodeGenerationFileType::object_file: file_type = llvm::CodeGenFileType::ObjectFile; break; case BBLLVMCodeGenerationFileType::null: file_type = llvm::CodeGenFileType::Null; break; } - auto disable_verify = !options.verify_module; - if (target_machine.addPassesToEmitFile(CodeGenPasses, *stream, dwarf_object_file, file_type, disable_verify)) + auto disable_verify = !options->verify_module; + if (target_machine->addPassesToEmitFile(CodeGenPasses, *stream, dwarf_object_file, file_type, disable_verify)) { return BBLLVMCodeGenerationPipelineResult::failed_to_add_emit_passes; } - CodeGenPasses.run(module); + CodeGenPasses.run(*module); return BBLLVMCodeGenerationPipelineResult::success; } -struct LLDResult -{ - String stdout_string; - String stderr_string; - bool success; -}; - -#define lld_api_args() const char** argument_pointer, u64 argument_count, bool exit_early, bool disable_output -#define lld_api_function_decl(link_name) LLDResult lld_ ## link_name ## _link(lld_api_args()) #define lld_api_function_signature(name) bool name(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) #define lld_link_decl(link_name) \ diff --git a/src/llvm.h b/src/llvm.h index 94a3d99..c56107b 100644 --- a/src/llvm.h +++ b/src/llvm.h @@ -2,10 +2,77 @@ #include #include +#include #include #include #include -#include +#include + +struct LLDResult +{ + String stdout_string; + String stderr_string; + bool success; +}; + +enum class BBLLVMCodeGenerationPipelineResult : u8 +{ + success = 0, + failed_to_create_file = 1, + failed_to_add_emit_passes = 2, +}; + +enum class BBLLVMCodeGenerationFileType : u8 +{ + assembly_file = 0, + object_file = 1, + null = 2, +}; + +#define BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (60) + +struct BBLLVMCodeGenerationPipelineOptions +{ + String output_dwarf_file_path; + String output_file_path; + u64 code_generation_file_type:2; + u64 optimize_when_possible:1; + u64 verify_module:1; + u64 reserved: BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; +}; + +static_assert(sizeof(BBLLVMCodeGenerationPipelineOptions) == 5 * sizeof(u64)); +static_assert(BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 60); + +enum class BBLLVMOptimizationLevel : u8 +{ + O0 = 0, + O1 = 1, + O2 = 2, + O3 = 3, + Os = 4, + Oz = 5, +}; + +#define BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (51) +struct BBLLVMOptimizationPipelineOptions +{ + u64 optimization_level:3; + u64 debug_info:1; + u64 loop_unrolling:1; + u64 loop_interleaving:1; + u64 loop_vectorization:1; + u64 slp_vectorization:1; + u64 merge_functions:1; + u64 call_graph_profile:1; + u64 unified_lto:1; + u64 assignment_tracking:1; + u64 verify_module:1; + u64 reserved:BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; +}; + +static_assert(sizeof(BBLLVMOptimizationPipelineOptions) == sizeof(u64)); +static_assert(BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 51); enum class BBLLVMUWTableKind : u64 { @@ -284,6 +351,256 @@ struct DIFlags static_assert(sizeof(DIFlags) == sizeof(u32)); +enum class BBLLVMEmitDwarfUnwindType : u8 +{ + always = 0, + no_compact_unwind = 1, + normal = 2, +}; + +enum class BBLLVMDwarfDirectory : u8 +{ + disable = 0, + enable = 1, + normal = 2, +}; + +enum class BBLLVMDebugCompressionType : u8 +{ + none = 0, + zlib = 1, + zstd = 2, +}; + +#define BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT (7) + +struct BBLLVMMCTargetOptions +{ + String abi_name; + String assembly_language; + String split_dwarf_file; + String as_secure_log_file; + const char* argv0; + String* argv_pointer; + u64 argv_count; + String* integrated_assembler_search_path_pointer; + u64 integrated_assembler_search_path_count; + u32 relax_all:1; + u32 no_exec_stack:1; + u32 fatal_warnings:1; + u32 no_warn:1; + u32 no_deprecated_warn:1; + u32 no_type_check:1; + u32 save_temp_labels:1; + u32 incremental_linker_compatible:1; + u32 fdpic:1; + u32 show_mc_encoding:1; + u32 show_mc_inst:1; + u32 asm_verbose:1; + u32 preserve_asm_comments:1 = true; + u32 dwarf64:1; + u32 crel:1; + u32 x86_relax_relocations:1; + u32 x86_sse2_avx:1; + u32 emit_dwarf_unwind:2 = 2; + u32 use_dwarf_directory:2 = 2; + u32 debug_compression_type:2 = 0; + u32 emit_compact_unwind_non_canonical:1; + u32 ppc_use_full_register_names:1; + u32 reserved:BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT; +}; + +static_assert(sizeof(BBLLVMMCTargetOptions) == 112); +static_assert(BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT == 7); + +enum class BBLLVMCodeModel : u8 +{ + none = 0, + tiny = 1, + small = 2, + kernel = 3, + medium = 4, + large = 5, +}; + +enum class BBLLVMRelocationModel : u8 +{ + default_relocation = 0, + static_relocation = 1, + pic = 2, + dynamic_no_pic = 3, + ropi = 4, + rwpi = 5, + ropi_rwpi = 6, +}; + +enum class BBLLVMCodeGenerationOptimizationLevel : u8 +{ + none = 0, // -O0 + less = 1, // -O1 + normal = 2, // -O2, -Os + aggressive = 3 // -O3 +}; + +enum class BBLLVMGlobalISelAbortMode : u8 +{ + disable = 0, + enable = 1, + disable_with_diag = 2, +}; + +enum class BBLLVMSwiftAsyncFramePointerMode : u8 +{ + deployment_based = 0, + always = 1, + never = 2, +}; + +enum class BBLLVMBasicBlockSection : u8 +{ + all = 0, + list = 1, + preset = 2, + none = 3, +}; + +enum class BBLLVMFloatAbi : u8 +{ + normal = 0, + soft = 1, + hard = 2, +}; + +enum class BBLLVMFPOpFusion : u8 +{ + fast = 0, + standard = 1, + strict = 2, +}; + +enum class BBLLVMThreadModel : u8 +{ + posix = 0, + single = 1, +}; + +enum class BBLLVMEAbi : u8 +{ + unknown = 0, + normal = 1, + eabi4 = 2, + eabi5 = 3, + gnu = 4, +}; + +enum class BBLLVMDebuggerKind : u8 +{ + normal = 0, + gdb = 1, + lldb = 2, + sce = 3, + dbx = 4, +}; + +enum class BBLLVMExceptionHandling : u8 +{ + none = 0, + dwarf_cfi = 1, + setjmp_longjmp = 2, + arm = 3, + win_eh = 4, + wasm = 5, + aix = 6, + zos = 7, +}; + +#define BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT (21) + +struct BBLLVMTargetOptions +{ + u64 unsafe_fp_math:1; + u64 no_infs_fp_math:1; + u64 no_nans_fp_math:1; + u64 no_trapping_fp_math:1 = true; + u64 no_signed_zeroes_fp_math:1; + u64 approx_func_fp_math:1; + u64 enable_aix_extended_altivec_abi:1; + u64 honor_sign_dependent_rounding_fp_math:1; + u64 no_zeroes_in_bss:1; + u64 guaranteed_tail_call_optimization:1; + u64 stack_symbol_ordering:1 = true; + u64 enable_fast_isel:1; + u64 enable_global_isel:1 = 1; + u64 global_isel_abort_mode:2; + u64 swift_async_frame_pointer:2 = 1; + u64 use_init_array:1; + u64 disable_integrated_assembler:1; + u64 function_sections:1; + u64 data_sections:1; + u64 ignore_xcoff_visibility:1; + u64 xcoff_traceback_table:1 = true; + u64 unique_section_names:1 = true; + u64 unique_basic_block_section_names:1; + u64 separate_named_sections:1; + u64 trap_unreachable:1; + u64 no_trap_after_noreturn:1; + u64 tls_size:8; + u64 emulated_tls:1; + u64 enable_tls_descriptors:1; + u64 enable_ipra:1; + u64 emit_stack_size_section:1; + u64 enable_machine_outliner:1; + u64 enable_machine_function_splitter:1; + u64 supports_default_outlining:1; + u64 emit_address_significance_table:1; + u64 bb_address_map:1; + u64 bb_sections:3 = 3; + u64 emit_call_site_information:1; + u64 supports_debug_entry_values:1; + u64 enable_debug_entry_values:1; + u64 value_tracking_variable_locations:1; + u64 force_dwarf_frame_section:1; + u64 xray_function_index:1 = true; + u64 debug_strict_dwarf:1; + u64 hotpatch:1; + u64 ppc_gen_scalar_mass_entries:1; + u64 jmc_instrument:1; + u64 enable_cfi_fixup:1; + u64 mis_expect:1; + u64 xcoff_read_only_pointers:1; + u64 float_abi:2 = 0; + u64 thread_model:1 = 0; + u32 fp_op_fusion_mode:2 = 1; + u32 eabi_version:3 = 1; + u32 debugger_kind:3 = 0; + u32 exception_handling:3 = 0; + u32 reserved:BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT; + unsigned loop_alignment = 0; + int binutils_version[2]; + + BBLLVMMCTargetOptions mc; +}; + +static_assert(sizeof(BBLLVMTargetOptions) == 136); +static_assert(BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT == 21); + +#define BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT (4) + +struct BBLLVMTargetMachineCreate +{ + BBLLVMTargetOptions target_options; + String target_triple; + String cpu_model; + String cpu_features; + BBLLVMRelocationModel relocation_model; + BBLLVMCodeModel code_model; + BBLLVMCodeGenerationOptimizationLevel optimization_level; + bool jit; + u8 reserved[BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT]; +}; + +static_assert(sizeof(BBLLVMTargetMachineCreate) == 192); +static_assert(BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT == 4); fn bool llvm_initialized = false; @@ -318,3 +635,11 @@ extern "C" void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMet extern "C" String llvm_module_to_string(LLVMModuleRef module); +extern "C" LLVMTargetMachineRef llvm_create_target_machine(const BBLLVMTargetMachineCreate* create, String* error_message); +extern "C" void llvm_module_set_target(LLVMModuleRef m, LLVMTargetMachineRef tm); +extern "C" void llvm_module_run_optimization_pipeline(LLVMModuleRef module, LLVMTargetMachineRef target_machine, BBLLVMOptimizationPipelineOptions options); +extern "C" BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, const BBLLVMCodeGenerationPipelineOptions* options); + +#define lld_api_args() const char** argument_pointer, u64 argument_count, bool exit_early, bool disable_output +#define lld_api_function_decl(link_name) LLDResult lld_ ## link_name ## _link(lld_api_args()) +extern "C" lld_api_function_decl(elf); diff --git a/src/parser.cpp b/src/parser.cpp index 69ec62d..4ca302f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -10,6 +10,7 @@ enum class ValueIntrinsic int_from_enum, int_from_pointer, pointer_cast, + pointer_from_int, select, string_to_enum, trap, @@ -741,6 +742,9 @@ fn u8 escape_character(u8 ch) { switch (ch) { + case 'n': return '\n'; + case 't': return '\t'; + case 'r': return '\r'; default: trap(); } } @@ -806,6 +810,7 @@ fn Token tokenize(Module* module) string_literal("int_from_enum"), string_literal("int_from_pointer"), string_literal("pointer_cast"), + string_literal("pointer_from_int"), string_literal("select"), string_literal("string_to_enum"), string_literal("trap"), @@ -815,6 +820,7 @@ fn Token tokenize(Module* module) string_literal("va_arg"), string_literal("va_copy"), }; + static_assert(array_length(value_intrinsics) == (u64)ValueIntrinsic::count); backing_type(ValueIntrinsic) i; for (i = 0; i < (backing_type(ValueIntrinsic))(ValueIntrinsic::count); i += 1) @@ -1025,7 +1031,7 @@ fn Token tokenize(Module* module) case 'x': token_integer_kind = TokenIntegerKind::hexadecimal; break; case 'd': token_integer_kind = TokenIntegerKind::decimal; break; case 'o': token_integer_kind = TokenIntegerKind::octal; break; - case 'b': token_integer_kind = TokenIntegerKind::octal; break; + case 'b': token_integer_kind = TokenIntegerKind::binary; break; default: token_integer_kind = TokenIntegerKind::decimal; break; } auto inferred_decimal = token_integer_kind == TokenIntegerKind::decimal && next_ch != 'd'; @@ -1245,24 +1251,15 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) switch (intrinsic) { - case ValueIntrinsic::align_of: - { - trap(); - } break; case ValueIntrinsic::enum_name: case ValueIntrinsic::extend: case ValueIntrinsic::int_from_enum: case ValueIntrinsic::int_from_pointer: case ValueIntrinsic::truncate: case ValueIntrinsic::pointer_cast: + case ValueIntrinsic::pointer_from_int: case ValueIntrinsic::va_end: { - skip_space(module); - expect_character(module, left_parenthesis); - skip_space(module); - auto argument = parse_value(module, scope, {}); - expect_character(module, right_parenthesis); - UnaryId id; switch (intrinsic) { @@ -1272,10 +1269,17 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) case ValueIntrinsic::int_from_pointer: id = UnaryId::int_from_pointer; 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::va_end: id = UnaryId::va_end; break; default: unreachable(); } + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + auto argument = parse_value(module, scope, {}); + expect_character(module, right_parenthesis); + *result = { .unary = { .value = argument, @@ -1284,6 +1288,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) .id = ValueId::unary, }; } break; + case ValueIntrinsic::align_of: case ValueIntrinsic::byte_size: case ValueIntrinsic::integer_max: { @@ -1298,6 +1303,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) UnaryTypeId id; switch (intrinsic) { + case ValueIntrinsic::align_of: id = UnaryTypeId::align_of; break; case ValueIntrinsic::byte_size: id = UnaryTypeId::byte_size; break; case ValueIntrinsic::integer_max: id = UnaryTypeId::integer_max; break; default: unreachable(); @@ -1563,8 +1569,6 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) fn Precedence get_token_precedence(Token token) { - Precedence precedence; - switch (token.id) { case TokenId::none: unreachable(); @@ -1574,8 +1578,7 @@ fn Precedence get_token_precedence(Token token) case TokenId::right_brace: case TokenId::right_bracket: case TokenId::right_parenthesis: - precedence = Precedence::none; - break; + return Precedence::none; case TokenId::assign: case TokenId::assign_shift_left: case TokenId::assign_shift_right: @@ -1587,22 +1590,17 @@ fn Precedence get_token_precedence(Token token) case TokenId::assign_caret: case TokenId::assign_bar: case TokenId::assign_ampersand: - precedence = Precedence::assignment; - break; + return Precedence::assignment; case TokenId::operator_keyword: // TODO: check if any other operator that is not bitwise is added { switch (token.operator_keyword) { case OperatorKeyword::and_op: - case OperatorKeyword::or_op: - precedence = Precedence::bitwise; - break; case OperatorKeyword::and_op_shortcircuit: - precedence = Precedence::boolean_and; - break; + return Precedence::boolean_and; + case OperatorKeyword::or_op: case OperatorKeyword::or_op_shortcircuit: - precedence = Precedence::boolean_or; - break; + return Precedence::boolean_or; case OperatorKeyword::count: unreachable(); } } break; @@ -1612,36 +1610,28 @@ fn Precedence get_token_precedence(Token token) case TokenId::compare_less_equal: case TokenId::compare_greater: case TokenId::compare_greater_equal: - precedence = Precedence::comparison; - break; + return Precedence::comparison; case TokenId::ampersand: case TokenId::bar: case TokenId::caret: - precedence = Precedence::bitwise; - break; + return Precedence::bitwise; case TokenId::shift_left: case TokenId::shift_right: - precedence = Precedence::shifting; - break; + return Precedence::shifting; case TokenId::plus: case TokenId::dash: - precedence = Precedence::add_like; - break; + return Precedence::add_like; case TokenId::asterisk: case TokenId::forward_slash: case TokenId::percentage: - precedence = Precedence::div_like; - break; + return Precedence::div_like; case TokenId::pointer_dereference: case TokenId::left_parenthesis: case TokenId::left_bracket: case TokenId::dot: - precedence = Precedence::postfix; - break; + return Precedence::postfix; default: trap(); } - - return precedence; } fn Slice parse_call_arguments(Module* module, Scope* scope) @@ -2041,14 +2031,41 @@ fn void print_value(Value* value, u32 identation) switch (value->id) { + case ValueId::unary: + { + switch (value->unary.id) + { + case UnaryId::extend: + { + print(string_literal("extend")); + } break; + default: unreachable(); + } + + print(string_literal("\n")); + + print_value(value->unary.value, identation + 1); + } break; case ValueId::binary: { switch (value->binary.id) { + case BinaryId::compare_equal: + { + print(string_literal("==")); + } break; case BinaryId::compare_not_equal: { print(string_literal("!=")); } break; + case BinaryId::logical_and: + { + print(string_literal("and")); + } break; + case BinaryId::logical_or: + { + print(string_literal("or")); + } break; case BinaryId::logical_and_shortcircuit: { print(string_literal("and?")); @@ -2072,6 +2089,10 @@ fn void print_value(Value* value, u32 identation) { print(string_literal("constant_integer")); } break; + case ValueId::call: + { + print(string_literal("call ")); + } break; default: unreachable(); } @@ -2103,6 +2124,7 @@ fn Statement* parse_statement(Module* module, Scope* scope) skip_space(module); Type* local_type = 0; + if (consume_character_if_match(module, ':')) { skip_space(module); @@ -3321,7 +3343,8 @@ void parse(Module* module) if (module->last_macro_declaration) { assert(module->first_macro_declaration); - trap(); + module->last_macro_declaration->next = macro_declaration; + module->last_macro_declaration = macro_declaration; } else { @@ -3448,7 +3471,7 @@ void parse(Module* module) field_buffer[field_index] = { .name = field_name, .type = field_type, - .offset = byte_size, + .offset = field_byte_offset, .line = field_line, }; diff --git a/tests/concat_logical_or.bbb b/tests/concat_logical_or.bbb new file mode 100644 index 0000000..5b14498 --- /dev/null +++ b/tests/concat_logical_or.bbb @@ -0,0 +1,9 @@ +is_space = fn (ch: u8) u1 +{ + return ch == ' ' or ch == '\n' or ch == '\t' or ch == '\r'; +} + +[export] main = fn [cc(c)] () s32 +{ + return #extend(is_space('f')); +} diff --git a/tests/generic_pointer_array.bbb b/tests/generic_pointer_array.bbb index a9d11fc..31b9765 100644 --- a/tests/generic_pointer_array.bbb +++ b/tests/generic_pointer_array.bbb @@ -6,9 +6,10 @@ foo = macro[T](addr: &u64, count: u64) []T [export] main = fn [cc(c)] () s32 { - >some_var: u64 = 0xaaaaaaaaaaaaaaaa; - >result: []&u8 = foo[&u8](&some_var, 1); - if (#int_from_pointer(result.pointer) != 0xaaaaaaaaaaaaaaaa) #trap(); + >address_raw: u64 = 0xaaaaaaaaaaaaaaaa; + >some_var: &u64 = #pointer_from_int(address_raw); + >result: []&u8 = foo[&u8](some_var, 1); + if (#int_from_pointer(result.pointer) != address_raw) #trap(); if (result.length != 1) #trap(); return 0; } diff --git a/tests/pointer_struct_initialization.bbb b/tests/pointer_struct_initialization.bbb new file mode 100644 index 0000000..eda2b5b --- /dev/null +++ b/tests/pointer_struct_initialization.bbb @@ -0,0 +1,32 @@ +require = fn (ok: u1) void +{ + if (!ok) #trap(); +} + +S = struct +{ + a: u16, + b: u8, + c: u8, + d: u32, +} + +[export] main = fn [cc(c)] () s32 +{ + >s: S = zero; + + >p_s = &s; + p_s.& = { + .a = 1, + .b = 2, + .c = 3, + .d = 4, + }; + + require(s.a == 1); + require(s.b == 2); + require(s.c == 3); + require(s.d == 4); + + return 0; +} diff --git a/tests/slice_array_literal.bbb b/tests/slice_array_literal.bbb new file mode 100644 index 0000000..1f51463 --- /dev/null +++ b/tests/slice_array_literal.bbb @@ -0,0 +1,13 @@ +foo = fn (slices: [][]u8) void +{ + if (slices.length != 3) + { + #trap(); + } +} +[export] main = fn [cc(c)] () s32 +{ + >some_bool: u1 = 0; + foo([ "abc", #select(some_bool, "bcd", "cbd"), "sas", ][..]); + return 0; +} diff --git a/tests/slice_only_start.bbb b/tests/slice_only_start.bbb new file mode 100644 index 0000000..2a6afca --- /dev/null +++ b/tests/slice_only_start.bbb @@ -0,0 +1,11 @@ +[export] main = fn [cc(c)] () s32 +{ + >s = "abcde"; + >index: u64 = 3; + >s_sub = s[index..]; + if (s_sub[0] != 'd') + { + #trap(); + } + return 0; +} diff --git a/tests/strict_array_type.bbb b/tests/strict_array_type.bbb new file mode 100644 index 0000000..66ec212 --- /dev/null +++ b/tests/strict_array_type.bbb @@ -0,0 +1,5 @@ +[export] main = fn [cc(c)] () s32 +{ + >arr: [3]s32 = [3, 1, 0]; + return arr[2]; +}