From 95154512daaa49fac8b0e378fb5574b357b11d80 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Thu, 12 Jun 2025 20:47:12 -0600 Subject: [PATCH] Pass struct test --- src/compiler.bbb | 203 +++++++++++++++++++++++++++++++++++++++++++++-- src/compiler.hpp | 1 + src/emitter.cpp | 49 ++++++++++++ 3 files changed, 248 insertions(+), 5 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 02832a1..6154c9f 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -2501,6 +2501,7 @@ llvm_create_global_variable = fn (module: &LLVMModule, type: &LLVMType, is_const [extern] LLVMConstNeg = fn [cc(c)] (value: &LLVMValue) &LLVMValue; [extern] LLVMConstArray2 = fn [cc(c)] (element_type: &LLVMType, value_pointer: &&LLVMValue, value_count: u64) &LLVMValue; [extern] LLVMConstStringInContext2 = fn [cc(c)] (context: &LLVMContext, pointer: &u8, length: u64, dont_null_terminate: s32) &LLVMValue; +[extern] LLVMConstNamedStruct = fn [cc(c)] (type: &LLVMType, constant_pointer: &&LLVMValue, constant_count: u32) &LLVMValue; [extern] LLVMInstructionEraseFromParent = fn [cc(c)] (value: &LLVMValue) void; [extern] LLVMGetOperand = fn [cc(c)] (value: &LLVMValue, index: u32) &LLVMValue; @@ -2515,6 +2516,7 @@ llvm_create_global_variable = fn (module: &LLVMModule, type: &LLVMType, is_const [extern] LLVMDeleteBasicBlock = fn [cc(c)] (basic_block: &LLVMBasicBlock) void; [extern] LLVMIsABranchInst = fn [cc(c)] (value: &LLVMValue) &LLVMValue; +[extern] LLVMIsAConstant = fn [cc(c)] (value: &LLVMValue) s32; [extern] LLVMIsConditional = fn [cc(c)] (value: &LLVMValue) s32; [extern] LLVMGetSuccessor = fn [cc(c)] (value: &LLVMValue, index: u32) &LLVMBasicBlock; @@ -6070,7 +6072,7 @@ parse = fn (module: &Module) void } >type_it = module.scope.types.first; - >forward_declaration: &Type = zero; + >type_forward_declaration: &Type = zero; while (type_it) { @@ -6078,7 +6080,7 @@ parse = fn (module: &Module) void { if (type_it.id == .forward_declaration) { - forward_declaration = type_it; + type_forward_declaration = type_it; break; } else @@ -6461,7 +6463,102 @@ parse = fn (module: &Module) void }, .struct => { - #trap(); + skip_space(module); + + >struct_type: &Type = undefined; + if (type_forward_declaration) + { + struct_type = type_forward_declaration; + } + else + { + struct_type = new_type(module, { + .id = .forward_declaration, + .name = global_name, + .scope = &module.scope, + zero, + }); + } + + if (consume_character_if_match(module, left_brace)) + { + >field_buffer: [256]Field = undefined; + + >byte_size: u64 = 0; + >byte_alignment: u32 = 1; + >field_count: u64 = 0; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_brace)) + { + break; + } + + >field_index = field_count; + >field_line = get_line(module); + >field_name = parse_identifier(module); + + skip_space(module); + expect_character(module, ':'); + skip_space(module); + + >field_type = parse_type(module, scope); + + >field_byte_size = get_byte_size(field_type); + >field_byte_alignment = get_byte_alignment(field_type); + // Align struct size by field alignment + >field_byte_offset = align_forward(byte_size, #extend(field_byte_alignment)); + + field_buffer[field_index] = { + .name = field_name, + .type = field_type, + .offset = field_byte_offset, + .line = field_line, + }; + + byte_size = field_byte_offset + field_byte_size; + byte_alignment = #max(byte_alignment, field_byte_alignment); + + skip_space(module); + + consume_character_if_match(module, ','); + + field_count += 1; + } + + byte_size = align_forward(byte_size, #extend(byte_alignment)); + assert(byte_size % #extend(byte_alignment) == 0); + + skip_space(module); + consume_character_if_match(module, ';'); + + >fields = arena_allocate_slice[Field](module.arena, field_count); + memcpy(#pointer_cast(fields.pointer), #pointer_cast(&field_buffer), field_count * #byte_size(Field)); + + struct_type.content = { + .struct = { + .fields = fields, + .byte_size = byte_size, + .byte_alignment = byte_alignment, + .line = global_line, + .is_slice = 0, + .next = zero, + }, + }; + struct_type.id = .struct; + } + else + { + if (type_forward_declaration) + { + report_error(); + } + + expect_character(module, ';'); + } }, .typealias => { @@ -8840,7 +8937,69 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi { .struct => { - #trap(); + >fields = aggregate_type.content.struct.fields; + assert(fields.length <= 64); + >is_ordered: u1 = 1; + + >same_values_as_field = fields.length == elements.length; + >is_properly_initialized = same_values_as_field or zero; + + if (zero and same_values_as_field) + { + report_error(); + } + + if (!is_properly_initialized) + { + report_error(); + } + + assert(elements.length <= fields.length); + + for (initialization_index: 0..elements.length) + { + >element = &elements[initialization_index]; + >name = element.name; + >value = element.value; + + >declaration_index: u64 = 0; + + while (declaration_index < fields.length) + { + >field = &fields[declaration_index]; + + if (string_equal(name, field.name)) + { + break; + } + + declaration_index += 1; + } + + if (declaration_index == fields.length) + { + report_error(); + } + + >mask = 1 << declaration_index; + >current_mask = field_mask; + if (current_mask & mask) + { + // Repeated field + report_error(); + } + + field_mask = current_mask | mask; + + is_ordered = is_ordered and declaration_index == initialization_index; + >field = &fields[declaration_index]; + >declaration_type = field.type; + + analyze_type(module, value, declaration_type, { .must_be_constant = analysis.must_be_constant, zero }); + is_constant = is_constant and value_is_constant(value); + } + + value.content.aggregate_initialization.is_constant = is_constant and is_ordered; }, .bits => { @@ -10526,7 +10685,39 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con { .struct => { - #trap(); + >fields = resolved_value_type.content.struct.fields; + assert(fields.length <= 64); + assert(elements.length <= 64); + + if (is_constant) + { + >constant_buffer: [64]&LLVMValue = undefined; + for (i: 0..elements.length) + { + >value = elements[i].value; + emit_value(module, value, .memory, must_be_constant); + >llvm_value = value.llvm; + assert(llvm_value != zero); + assert(LLVMIsAConstant(llvm_value) != 0); + constant_buffer[i] = llvm_value; + } + + >constant_count = elements.length; + + if (is_zero) + { + #trap(); + } + + assert(constant_count == fields.length); + + llvm_value = LLVMConstNamedStruct(get_llvm_type(resolved_value_type, type_kind), &constant_buffer[0], #truncate(constant_count)); + } + else + { + // TODO: shouldn't this be a left value? + report_error(); + } }, .union => { @@ -12469,6 +12660,8 @@ names: [_][]u8 = [ "bits_no_backing_type", "bits_return_u1", "bits_zero", + "comparison", + "global_struct", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/compiler.hpp b/src/compiler.hpp index ba16c7c..1f7cb18 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -853,6 +853,7 @@ struct ValueConstantInteger struct FunctionLLVM { + LLVMValueRef alloca_insertion_point; LLVMBasicBlockRef return_block; LLVMValueRef return_alloca; }; diff --git a/src/emitter.cpp b/src/emitter.cpp index a966deb..850672f 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -1949,6 +1949,25 @@ struct AllocaOptions u32 alignment; }; +fn Global* get_current_function(Module* module) +{ + Global* parent_function_global; + if (module->current_function) + { + parent_function_global = module->current_function; + } + else if (module->current_macro_instantiation) + { + parent_function_global = module->current_macro_instantiation->instantiation_function; + } + else + { + report_error(); + } + + return parent_function_global; +} + fn LLVMValueRef create_alloca(Module* module, AllocaOptions options) { auto abi_type = options.type; @@ -1964,7 +1983,15 @@ fn LLVMValueRef create_alloca(Module* module, AllocaOptions options) alignment = get_byte_alignment(abi_type); } + auto original_block = LLVMGetInsertBlock(module->llvm.builder); + auto function = get_current_function(module); + auto debug_location = LLVMGetCurrentDebugLocation2(module->llvm.builder); + LLVMPositionBuilderBefore(module->llvm.builder, function->variable.storage->function.llvm.alloca_insertion_point); + LLVMSetCurrentDebugLocation2(module->llvm.builder, 0); + auto alloca = llvm_builder_create_alloca(module->llvm.builder, abi_type->llvm.memory, alignment, options.name); + LLVMPositionBuilderAtEnd(module->llvm.builder, original_block); + LLVMSetCurrentDebugLocation2(module->llvm.builder, debug_location); return alloca; } @@ -3070,6 +3097,12 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal auto* entry_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "entry"); LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + auto u32_type = uint32(module); + resolve_type_in_place(module, u32_type); + auto current_function = get_current_function(module); + auto old_alloca_insertion_point = current_function->variable.storage->function.llvm.alloca_insertion_point; + current_function->variable.storage->function.llvm.alloca_insertion_point = LLVMBuildAlloca(module->llvm.builder, u32_type->llvm.abi, "alloca_insert_point"); + auto alloca = create_alloca(module, { .type = string_type, .name = string_literal("retval"), @@ -3137,6 +3170,8 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal enum_to_string = llvm_function; enum_type->enumerator.enum_to_string_function = enum_to_string; + + current_function->variable.storage->function.llvm.alloca_insertion_point = old_alloca_insertion_point; } assert(enum_to_string); @@ -4260,6 +4295,12 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + auto current_function = get_current_function(module); + auto old_alloca_insertion_point = current_function->variable.storage->function.llvm.alloca_insertion_point; + auto u32_type = uint32(module); + resolve_type_in_place(module, u32_type); + current_function->variable.storage->function.llvm.alloca_insertion_point = LLVMBuildAlloca(module->llvm.builder, u32_type->llvm.abi, "alloca_insert_point"); + LLVMValueRef arguments[2]; LLVMGetParams(llvm_function, arguments); @@ -4468,6 +4509,8 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal enum_type->enumerator.string_to_enum_function = llvm_function; enum_type->enumerator.string_to_enum_struct_type = struct_type; + + current_function->variable.storage->function.llvm.alloca_insertion_point = old_alloca_insertion_point; } auto struct_type = enum_type->enumerator.string_to_enum_struct_type; @@ -9507,6 +9550,10 @@ void emit(Module* module) LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); LLVMSetCurrentDebugLocation2(module->llvm.builder, 0); + auto u32_type = uint32(module); + resolve_type_in_place(module, u32_type); + global->variable.storage->function.llvm.alloca_insertion_point = LLVMBuildAlloca(module->llvm.builder, u32_type->llvm.abi, "alloca_insert_point"); + auto return_abi_kind = function_type->abi.return_abi.flags.kind; switch (return_abi_kind) { @@ -9893,6 +9940,8 @@ void emit(Module* module) LLVMBuildRet(module->llvm.builder, return_value); } + LLVMInstructionEraseFromParent(global->variable.storage->function.llvm.alloca_insertion_point); + // END OF SCOPE module->current_function = 0; }