From 51cad46ad621518d35794cd9af9d0b0a53ff7301 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 17 Jun 2025 05:44:10 -0600 Subject: [PATCH 01/18] Pass 'basic_macro' --- src/compiler.bbb | 866 ++++++++++++++++++++++++++++++++++++++++++----- src/compiler.hpp | 1 - src/parser.cpp | 6 +- 3 files changed, 791 insertions(+), 82 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index ee6352f..b2825a9 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1045,6 +1045,8 @@ Value = struct; Local = struct; Block = struct; Module = struct; +MacroDeclaration = struct; +MacroInstantiation = struct; ScopeKind = enum { @@ -1779,7 +1781,7 @@ ValueId = enum variable, local, unary_type, - macro_reference, + macro, call, array_initialization, array_expression, @@ -1797,6 +1799,7 @@ ValueId = enum undefined, select, string_to_enum, + macro_instantiation, } ValueConstantInteger = struct @@ -2023,6 +2026,41 @@ ValueStringToEnum = struct string: &Value, } +ConstantArgumentId = enum +{ + value, + type, +} + +ConstantArgumentContent = union +{ + value: &Value, + type: &Type, +} + +ConstantArgument = struct +{ + name: []u8, + content: ConstantArgumentContent, + id: ConstantArgumentId, +} + +MacroInstantiation = struct +{ + declaration: &MacroDeclaration, + instantiation_function: &Global, + declaration_arguments: []Argument, + instantiation_arguments: []&Value, + constant_arguments: []ConstantArgument, + return_type: &Type, + block: &Block, + scope: Scope, + line: u32, + column: u32, + return_alloca: &LLVMValue, + return_block: &LLVMBasicBlock, +} + ValueContent = union { constant_integer: ValueConstantInteger, @@ -2042,6 +2080,8 @@ ValueContent = union aggregate_initialization: ValueAggregateInitialization, select: ValueSelect, string_to_enum: ValueStringToEnum, + macro: &MacroDeclaration, + macro_instantiation: MacroInstantiation, } ValueKind = enum @@ -2126,12 +2166,18 @@ void_offset: u64 = i128_offset + 2; MacroDeclaration = struct { - foo: u32, + arguments: []Argument, + constant_arguments: []ConstantArgument, + return_type: &Type, + block: &Block, + name: []u8, + scope: Scope, + next: &MacroDeclaration, } -MacroInstantiation = struct +macro_declaration_is_generic = fn (declaration: &MacroDeclaration) u1 { - foo: u32, + return declaration.constant_arguments.length != 0; } [extern] LLVMInitializeX86TargetInfo = fn [cc(c)] () void; @@ -2464,6 +2510,7 @@ LLVMICmpPredicate = enum u32 [extern] LLVMSetSubprogram = fn [cc(c)] (function: &LLVMValue, subprogram: &LLVMMetadata) void; [extern] LLVMGetSubprogram = fn [cc(c)] (function: &LLVMValue) &LLVMMetadata; +[extern] llvm_subprogram_replace_type = fn [cc(c)] (subprogram: &LLVMMetadata, subroutine_type: &LLVMMetadata) void; [extern] LLVMLookupIntrinsicID = fn [cc(c)] (name_pointer: &u8, name_length: u64) LLVMIntrinsicId; [extern] LLVMGetIntrinsicDeclaration = fn [cc(c)] (module: &LLVMModule, intrinsic_id: LLVMIntrinsicId, argument_type_pointer: &&LLVMType, argument_type_count: u64) &LLVMValue; @@ -4905,6 +4952,18 @@ tokenize = fn (module: &Module) Token parse_precedence = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value; +scope_to_macro_declaration = fn (scope: &Scope) &MacroDeclaration +{ + assert(scope.kind == .macro_declaration); + return #field_parent_pointer(scope, "scope"); +} + +scope_to_macro_instantiation = fn (scope: &Scope) &MacroInstantiation +{ + assert(scope.kind == .macro_instantiation); + return #field_parent_pointer(scope, "scope"); +} + reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: []u8, kind: ValueKind) &Value { assert(!string_equal(identifier, "")); @@ -4936,9 +4995,22 @@ reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: [ >macro_declaration = module.first_macro_declaration; - while (macro_declaration != zero) + while (macro_declaration) { - #trap(); + if (string_equal(identifier, macro_declaration.name)) + { + >result = new_value(module); + result.& = { + .content = { + .macro = macro_declaration, + }, + .id = .macro, + zero, + }; + return result; + } + + macro_declaration = macro_declaration.next; } }, .function => @@ -4995,11 +5067,39 @@ reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: [ }, .macro_declaration => { - #trap(); + assert(scope.parent != zero); + >macro_declaration = scope_to_macro_declaration(scope); + + for (&constant_argument: macro_declaration.constant_arguments) + { + if (string_equal(identifier, constant_argument.name)) + { + #trap(); + } + } + + for (&argument: macro_declaration.arguments) + { + if (string_equal(identifier, argument.variable.name)) + { + variable = &argument.variable; + break; + } + } }, .macro_instantiation => { - #trap(); + assert(scope.parent != zero); + >macro_instantiation = scope_to_macro_instantiation(scope); + + for (&argument: macro_instantiation.declaration_arguments) + { + if (string_equal(identifier, argument.variable.name)) + { + variable = &argument.variable; + break; + } + } }, } @@ -5761,9 +5861,44 @@ parse_right = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value switch (left.id) { - .macro_reference => + .macro => { - #trap(); + >declaration = left.content.macro; + + if (macro_declaration_is_generic(declaration)) + { + report_error(); + } + + >instantiation_line = get_line(module); + >instantiation_column = get_column(module); + + >arguments = parse_call_arguments(module, scope); + + result.& = { + .content = { + .macro_instantiation = { + .declaration = declaration, + .instantiation_function = module.current_function, + .declaration_arguments = zero, + .instantiation_arguments = arguments, + .constant_arguments = zero, + .return_type = declaration.return_type, + .scope = { + .parent = scope, + .line = declaration.scope.line, + .column = declaration.scope.column, + .kind = .macro_instantiation, + zero, + }, + .line = instantiation_line, + .column = instantiation_column, + zero, + }, + }, + .id = .macro_instantiation, + zero, + }; }, else => { @@ -5789,7 +5924,7 @@ parse_right = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value result = new_value(module); - if (left.id == .macro_reference) + if (left.id == .macro) { #trap(); } @@ -7016,7 +7151,118 @@ parse = fn (module: &Module) void }, .macro => { - #trap(); + >constant_argument_buffer: [64]ConstantArgument = undefined; + >constant_argument_count: u64 = 0; + + >is_generic = consume_character_if_match(module, left_bracket); + >macro_declaration = arena_allocate[MacroDeclaration](module.arena, 1); + macro_declaration.& = { + .name = global_name, + .scope = { + .parent = scope, + .line = global_line, + .column = global_column, + .kind = .macro_declaration, + zero, + }, + zero, + }; + + if (is_generic) + { + // END OF SCOPE + if (constant_argument_count == 0) + { + report_error(); + } + #trap(); + } + else + { + assert(constant_argument_count == 0); + } + + expect_character(module, left_parenthesis); + + macro_declaration.constant_arguments = arena_allocate_slice[ConstantArgument](module.arena, constant_argument_count); + memcpy(#pointer_cast(macro_declaration.constant_arguments.pointer), #pointer_cast(&constant_argument_buffer), constant_argument_count * #byte_size(ConstantArgument)); + + if (module.first_macro_declaration) + { + assert(module.first_macro_declaration != zero); + module.last_macro_declaration.next = macro_declaration; + } + else + { + assert(!module.first_macro_declaration); + module.first_macro_declaration = macro_declaration; + } + + module.last_macro_declaration = macro_declaration; + module.current_macro_declaration = macro_declaration; + + >scope = ¯o_declaration.scope; + + >argument_buffer: [64]Argument = undefined; + >argument_count: u64 = 0; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_parenthesis)) + { + break; + } + + >argument_index = argument_count; + + >argument_line = get_line(module); + >argument_column = get_column(module); + + >argument_name = arena_duplicate_string(module.arena, parse_identifier(module)); + + skip_space(module); + expect_character(module, ':'); + skip_space(module); + + >argument_type = parse_type(module, scope); + + >argument = &argument_buffer[argument_index]; + argument.& = { + .variable = { + .type = argument_type, + .scope = scope, + .name = argument_name, + .line = argument_line, + .column = argument_column, + zero, + }, + .index = #truncate(argument_index + 1), + }; + + argument_count += 1; + + skip_space(module); + consume_character_if_match(module, ','); + } + + skip_space(module); + + >return_type = parse_type(module, scope); + macro_declaration.return_type = return_type; + + >arguments = arena_allocate_slice[Argument](module.arena, argument_count); + memcpy(#pointer_cast(arguments.pointer), #pointer_cast(&argument_buffer), argument_count * #byte_size(Argument)); + macro_declaration.arguments = arguments; + + skip_space(module); + + >block = parse_block(module, scope); + macro_declaration.block = block; + + // END OF SCOPE + module.current_macro_declaration = zero; }, .opaque => { @@ -8909,6 +9155,7 @@ TypeAnalysis = struct must_be_constant: u1, } +analyze_block = fn (module: &Module, block: &Block) void; analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysis: TypeAnalysis) void; emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_constant: u1) void; emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, right: &Value) void; @@ -9084,6 +9331,149 @@ get_enum_name_array_global = fn (module: &Module, enum_type: &Type) &Global return enum_type.content.enum.name_array; } +resolve_type = fn (module: &Module, type: &Type) &Type +{ + >result: &Type = zero; + + switch (type.id) + { + .unresolved => + { + #trap(); + }, + .integer => + { + result = type; + }, + else => + { + #trap(); + }, + } + + assert(result != zero); + return result; +} + +clone_value = fn (module: &Module, scope: &Scope, old_value: &Value) &Value +{ + assert(old_value != zero); + + >result: &Value = zero; + + if (old_value.id == .variable) + { + result = reference_identifier(module, scope, old_value.content.variable.name, old_value.kind); + } + else + { + result = new_value(module); + result.& = old_value.&; + + switch (old_value.id) + { + .variable => + { + unreachable; + }, + .binary => + { + >left = clone_value(module, scope, old_value.content.binary.left); + >right = clone_value(module, scope, old_value.content.binary.right); + + result.content = { + .binary = { + .left = left, + .right = right, + .id = old_value.content.binary.id, + }, + }; + }, + else => + { + #trap(); + }, + } + } + + assert(result != zero); + + return result; +} + +clone_statement = fn (module: &Module, scope: &Scope, old_statement: &Statement) &Statement +{ + >new_statement = arena_allocate[Statement](module.arena, 1); + new_statement.& = zero; + >old_statement_id = old_statement.id; + new_statement.id = old_statement_id; // TODO: is this right? + new_statement.line = old_statement.line; + new_statement.column = old_statement.column; + + switch (old_statement_id) + { + .return => + { + >old_return_value = old_statement.content.return; + >return_value: &Value = zero; + if (old_return_value) + { + return_value = clone_value(module, scope, old_return_value); + } + + new_statement.content = { + .return = return_value, + }; + }, + else => + { + #trap(); + }, + } + + return new_statement; +} + +BlockCopy = struct +{ + source: &Block, + destination: &Block, +} + +copy_block = fn (module: &Module, parent_scope: &Scope, copy: BlockCopy) void +{ + >source = copy.source; + >destination = copy.destination; + + destination.& = zero; + >scope = &destination.scope; + scope.& = source.scope; + scope.parent = parent_scope; + assert(!scope.llvm); + + >last_statement: &Statement = zero; + >old_statement = source.first_statement; + + while (old_statement) + { + >statement = clone_statement(module, scope, old_statement); + assert(!statement.next); + + if (last_statement) + { + last_statement.next = last_statement; + } + else + { + destination.first_statement = statement; + } + + last_statement = statement; + + old_statement = old_statement.next; + } +} + analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysis: TypeAnalysis) void { assert(!value.type); @@ -10647,6 +11037,163 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi value_type = expected_type; }, + .macro_instantiation => + { + if (module.current_macro_declaration) + { + report_error(); + } + + >current_function = module.current_function; + if (!current_function) + { + report_error(); + } + + module.current_function = zero; + + >current_macro_instantiation = module.current_macro_instantiation; + assert(!current_macro_instantiation); // TODO + >macro_instantiation = &value.content.macro_instantiation; + module.current_macro_instantiation = macro_instantiation; + + >declaration = macro_instantiation.declaration; + >declaration_arguments = declaration.arguments; + >instantiation_declaration_arguments = arena_allocate_slice[Argument](module.arena, declaration_arguments.length); + macro_instantiation.declaration_arguments = instantiation_declaration_arguments; + + >subprogram: &LLVMMetadata = zero; + if (module.has_debug_info) + { + >subroutine_type: &LLVMMetadata = zero; + >is_local_to_unit: u1 = 1; + >is_definition: u1 = 1; + >flags: LLVMDIFlags = zero; + >is_optimized = build_mode_is_optimized(module.build_mode); + subprogram = LLVMDIBuilderCreateFunction(module.llvm.di_builder, module.scope.llvm, declaration.name.pointer, declaration.name.length, declaration.name.pointer, declaration.name.length, module.llvm.file, macro_instantiation.scope.line, subroutine_type, #extend(is_local_to_unit), #extend(is_definition), macro_instantiation.scope.line, flags, #extend(is_optimized)); + } + + macro_instantiation.scope.llvm = subprogram; + + // First copy + for (i: 0..declaration_arguments.length) + { + >instantiation_declaration_argument = &instantiation_declaration_arguments[i]; + >declaration_argument = &declaration_arguments[i]; + instantiation_declaration_argument.& = { + .variable = { + .type = declaration_argument.variable.type, + .scope = ¯o_instantiation.scope, + .name = declaration_argument.variable.name, + .line = declaration_argument.variable.line, + .column = declaration_argument.variable.column, + zero, + }, + .index = declaration_argument.index, + }; + } + + >declaration_constant_arguments = declaration.constant_arguments; + >instantiation_constant_arguments = macro_instantiation.constant_arguments; + + for (i: 0..declaration_constant_arguments.length) + { + >declaration_constant_argument = &declaration_constant_arguments[i]; + >instantiation_constant_argument = &instantiation_constant_arguments[i]; + + assert(declaration_constant_argument.id == instantiation_constant_argument.id); + + instantiation_constant_argument.name = declaration_constant_argument.name; + + switch (declaration_constant_argument.id) + { + .value => + { + #trap(); + }, + .type => + { + #trap(); + }, + } + } + + value_type = resolve_type(module, declaration.return_type); + assert(value_type.id != .unresolved); + macro_instantiation.return_type = value_type; + + for (&argument: macro_instantiation.declaration_arguments) + { + argument.variable.type = resolve_type(module, argument.variable.type); + } + + >instantiation_arguments = macro_instantiation.instantiation_arguments; + if (instantiation_arguments.length != instantiation_declaration_arguments.length) + { + report_error(); + } + + if (module.has_debug_info) + { + for (i: 0..instantiation_arguments.length) + { + >instantiation_argument = instantiation_arguments[i]; + >declaration_argument = &instantiation_declaration_arguments[i]; + + >argument_type = declaration_argument.variable.type; + assert(argument_type != zero); + analyze_type(module, instantiation_argument, argument_type, { .must_be_constant = analysis.must_be_constant, zero }); + } + + >type_buffer: [64]&LLVMMetadata = undefined; + >type_count = instantiation_arguments.length + 1; + + resolve_type_in_place_debug(module, value_type); + type_buffer[0] = value_type.llvm.debug; + + for (i: 0..instantiation_declaration_arguments.length) + { + >declaration_argument = &instantiation_declaration_arguments[i]; + >type = declaration_argument.variable.type; + resolve_type_in_place_debug(module, type); + type_buffer[i + 1] = type.llvm.debug; + } + + LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); + >flags: LLVMDIFlags = zero; + >subroutine_type = LLVMDIBuilderCreateSubroutineType(module.llvm.di_builder, module.llvm.file, &type_buffer[0], #truncate(type_count), flags); + assert(macro_instantiation.scope.llvm != zero); + llvm_subprogram_replace_type(subprogram, subroutine_type); + } + + assert(!macro_instantiation.block); + macro_instantiation.block = arena_allocate[Block](module.arena, 1); + + copy_block(module, ¯o_instantiation.scope, { + .source = declaration.block, + .destination = macro_instantiation.block, + }); + + resolve_type_in_place(module, value_type); + typecheck(module, expected_type, value_type); + + if (!module.has_debug_info) + { + for (i: 0..instantiation_arguments.length) + { + >instantiation_argument = instantiation_arguments[i]; + >declaration_argument = &instantiation_declaration_arguments[i]; + + >argument_type = declaration_argument.variable.type; + assert(argument_type != zero); + analyze_type(module, instantiation_argument, argument_type, { .must_be_constant = analysis.must_be_constant, zero }); + } + } + + // END OF SCOPE + module.current_macro_instantiation = current_macro_instantiation; + module.current_function = current_function; + }, else => { #trap(); @@ -12476,6 +13023,198 @@ emit_string_literal = fn (module: &Module, value: &Value) [2]&LLVMValue return [ global, LLVMConstInt(uint64(module).llvm.abi, length, 0) ]; } +emit_local_storage = fn (module: &Module, variable: &Variable) void +{ + assert(!variable.storage); + assert(variable.name.pointer != zero); + assert(variable.name.length != zero); + + >value_type = variable.type; + resolve_type_in_place(module, value_type); + >pointer_type = get_pointer_type(module, value_type); + + >alloca = create_alloca(module, { + .type = value_type, + .name = variable.name, + zero, + }); + + >storage = new_value(module); + storage.& = { + .type = pointer_type, + .id = .local, + .llvm = alloca, + zero, + }; + + variable.storage = storage; +} + +null_expression = fn (module: &Module) &LLVMMetadata +{ + return LLVMDIBuilderCreateExpression(module.llvm.di_builder, zero, 0); +} + +end_debug_local = fn (module: &Module, variable: &Variable, llvm_local: &LLVMMetadata) void +{ + >debug_location = LLVMDIBuilderCreateDebugLocation(module.llvm.context, variable.line, variable.column, variable.scope.llvm, module.llvm.inlined_at); + LLVMSetCurrentDebugLocation2(module.llvm.builder, debug_location); + >basic_block = LLVMGetInsertBlock(module.llvm.builder); + assert(basic_block != zero); + LLVMDIBuilderInsertDeclareRecordAtEnd(module.llvm.di_builder, variable.storage.llvm, llvm_local, null_expression(module), debug_location, basic_block); +} + +emit_local = fn (module: &Module, local: &Local) void +{ + emit_local_storage(module, &local.variable); + assert(local.variable.storage != zero); + + if (module.has_debug_info) + { + >debug_type = local.variable.type.llvm.debug; + assert(debug_type != zero); + + >always_preserve: s32 = 1; + >flags: LLVMDIFlags = zero; + >scope = local.variable.scope.llvm; + >bit_alignment = get_byte_alignment(local.variable.type) * 8; + >name = local.variable.name; + >local_variable = LLVMDIBuilderCreateAutoVariable(module.llvm.di_builder, scope, name.pointer, name.length, module.llvm.file, local.variable.line, debug_type, always_preserve, flags, bit_alignment); + + end_debug_local(module, &local.variable, local_variable); + } +} + +emit_argument = fn (module: &Module, argument: &Argument) void +{ + emit_local_storage(module, &argument.variable); + assert(argument.variable.storage != zero); + + if (module.has_debug_info) + { + >debug_type = argument.variable.type.llvm.debug; + assert(debug_type != zero); + >scope = argument.variable.scope.llvm; + assert(scope != zero); + >always_preserve: u1 = 1; + >flags: LLVMDIFlags = zero; + >argument_variable = LLVMDIBuilderCreateParameterVariable(module.llvm.di_builder, scope, argument.variable.name.pointer, argument.variable.name.length, argument.index, module.llvm.file, argument.variable.line, debug_type, #extend(always_preserve), flags); + + end_debug_local(module, &argument.variable, argument_variable); + } +} + +emit_macro_instantiation = fn (module: &Module, value: &Value) void +{ + assert(value.id == .macro_instantiation); + >current_function = module.current_function; + if (!current_function) + { + report_error(); + } + module.current_function = zero; + + >old_macro_instantiation = module.current_macro_instantiation; + assert(!old_macro_instantiation); + >macro_instantiation = &value.content.macro_instantiation; + module.current_macro_instantiation = macro_instantiation; + + >caller_debug_location: &LLVMMetadata = zero; + 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, zero); + LLVMSetCurrentDebugLocation2(module.llvm.builder, caller_debug_location); + } + + for (instantiation_argument: macro_instantiation.instantiation_arguments) + { + emit_value(module, instantiation_argument, .abi, 0); + } + + >older_inlined_at = module.llvm.inlined_at; + assert(!older_inlined_at); + module.llvm.inlined_at = caller_debug_location; + + >llvm_function = current_function.variable.storage.llvm; + >entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "macro.entry"); + + LLVMBuildBr(module.llvm.builder, entry_block); + LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); + + >return_alloca: &LLVMValue = zero; + >return_type = macro_instantiation.return_type; + + if (return_type.id != .void and return_type.id != .noreturn) + { + return_alloca = create_alloca(module, { + .type = return_type, + .name = "macro.return", + zero, + }); + } + + assert(!macro_instantiation.return_alloca != zero); + macro_instantiation.return_alloca = return_alloca; + + >return_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "macro.return_block"); + assert(!macro_instantiation.return_block); + macro_instantiation.return_block = return_block; + + >declaration_arguments = macro_instantiation.declaration_arguments; + >instantiation_arguments = macro_instantiation.instantiation_arguments; + assert(declaration_arguments.length == instantiation_arguments.length); + + for (i: 0..declaration_arguments.length) + { + >declaration_argument = &declaration_arguments[i]; + >instantiation_argument = instantiation_arguments[i]; + + emit_argument(module, declaration_argument); + + >type = declaration_argument.variable.type; + >resolved_type = resolve_alias(module, type); + >evaluation_kind = get_evaluation_kind(resolved_type); + >llvm_instantiation_argument = instantiation_argument.llvm; + >llvm_declaration_argument = declaration_argument.variable.storage.llvm; + + switch (evaluation_kind) + { + .scalar => + { + create_store(module, { + .source = llvm_instantiation_argument, + .destination = llvm_declaration_argument, + .type = type, + zero, + }); + }, + .aggregate => + { + #trap(); + }, + .complex => + { + #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 + module.llvm.inlined_at = older_inlined_at; + module.current_macro_instantiation = old_macro_instantiation; + module.current_function = current_function; +} + ShortcircuitingOperation = enum { and, @@ -12493,7 +13232,7 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con } else if (module.current_macro_instantiation) { - #trap(); + parent_function_global = module.current_macro_instantiation.instantiation_function; } else { @@ -13375,6 +14114,29 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con else => { unreachable; }, } }, + .macro_instantiation => + { + emit_macro_instantiation(module, value); + + >macro_instantiation = &value.content.macro_instantiation; + >return_type = macro_instantiation.return_type; + >return_alloca = macro_instantiation.return_alloca; + + // TODO: make this more serious + switch (return_type.id) + { + .void, .noreturn => { return; }, // Return early to omit the assert + else => + { + llvm_value = create_load(module, { + .type = return_type, + .pointer = return_alloca, + .kind = type_kind, + zero, + }); + }, + } + }, else => { #trap(); @@ -13394,7 +14156,7 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, } else if (module.current_macro_instantiation) { - #trap(); + parent_function_global = module.current_macro_instantiation.instantiation_function; } else { @@ -13736,70 +14498,6 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, } } -emit_local_storage = fn (module: &Module, variable: &Variable) void -{ - assert(!variable.storage); - assert(variable.name.pointer != zero); - assert(variable.name.length != zero); - - >value_type = variable.type; - resolve_type_in_place(module, value_type); - >pointer_type = get_pointer_type(module, value_type); - - >alloca = create_alloca(module, { - .type = value_type, - .name = variable.name, - zero, - }); - - >storage = new_value(module); - storage.& = { - .type = pointer_type, - .id = .local, - .llvm = alloca, - zero, - }; - - variable.storage = storage; -} - -null_expression = fn (module: &Module) &LLVMMetadata -{ - return LLVMDIBuilderCreateExpression(module.llvm.di_builder, zero, 0); -} - -end_debug_local = fn (module: &Module, variable: &Variable, llvm_local: &LLVMMetadata) void -{ - >debug_location = LLVMDIBuilderCreateDebugLocation(module.llvm.context, variable.line, variable.column, variable.scope.llvm, module.llvm.inlined_at); - LLVMSetCurrentDebugLocation2(module.llvm.builder, debug_location); - >basic_block = LLVMGetInsertBlock(module.llvm.builder); - assert(basic_block != zero); - LLVMDIBuilderInsertDeclareRecordAtEnd(module.llvm.di_builder, variable.storage.llvm, llvm_local, null_expression(module), debug_location, basic_block); -} - -emit_local = fn (module: &Module, local: &Local) void -{ - emit_local_storage(module, &local.variable); - assert(local.variable.storage != zero); - - if (module.has_debug_info) - { - >debug_type = local.variable.type.llvm.debug; - assert(debug_type != zero); - - >always_preserve: s32 = 1; - >flags: LLVMDIFlags = zero; - >scope = local.variable.scope.llvm; - >bit_alignment = get_byte_alignment(local.variable.type) * 8; - >name = local.variable.name; - >local_variable = LLVMDIBuilderCreateAutoVariable(module.llvm.di_builder, scope, name.pointer, name.length, module.llvm.file, local.variable.line, debug_type, always_preserve, flags, bit_alignment); - - end_debug_local(module, &local.variable, local_variable); - } -} - -analyze_block = fn (module: &Module, block: &Block) void; - analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) void { >parent_function_global: &Global = undefined; @@ -13810,7 +14508,7 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v } else if (module.current_macro_instantiation) { - #trap(); + parent_function_global = module.current_macro_instantiation.instantiation_function; } else { @@ -13882,7 +14580,14 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v } else if (module.current_macro_instantiation) { - #trap(); + >macro_instantiation = module.current_macro_instantiation; + >return_type = macro_instantiation.return_type; + assert(return_type != zero); + + analyze_type(module, return_value, return_type, zero); + 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 { @@ -16223,6 +16928,7 @@ names: [_][]u8 = "pointer_struct_initialization", "slice_array_literal", "slice_only_start", + "basic_macro", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/compiler.hpp b/src/compiler.hpp index 1f7cb18..24d4233 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -1029,7 +1029,6 @@ struct MacroDeclaration { Slice arguments; Slice constant_arguments; - TypeList types; Type* return_type; Block* block; String name; diff --git a/src/parser.cpp b/src/parser.cpp index 020e74e..61a7e8c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2693,7 +2693,11 @@ fn Statement* parse_statement(Module* module, Scope* scope) } break; case StatementStartKeyword::return_st: { - auto return_value = parse_value(module, scope, {}); + Value* return_value = 0; + if (module->content[module->offset] != ';') + { + return_value = parse_value(module, scope, {}); + } statement->return_st = return_value; statement->id = StatementId::return_st; } break; -- 2.43.0 From 2d2dd435126e7288a30e520b9512a49a60687824 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 17 Jun 2025 10:05:09 -0600 Subject: [PATCH 02/18] Pass 'generic_macro' --- src/compiler.bbb | 226 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 222 insertions(+), 4 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index b2825a9..5d75c7f 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1477,6 +1477,10 @@ type_is_signed = fn (type: &Type) u1 >backing_type = type.content.bits.backing_type; return type_is_signed(backing_type); }, + .alias => + { + return type_is_signed(type.content.alias.type); + }, else => { #trap(); @@ -1671,6 +1675,10 @@ get_byte_alignment = fn (type: &Type) u32 { return type.content.union.byte_alignment; }, + .alias => + { + return get_byte_alignment(type.content.alias.type); + }, else => { #trap(); @@ -1703,6 +1711,11 @@ get_bit_size = fn (type: &Type) u64 >byte_size = get_byte_size(type); return byte_size * 8; }, + .alias => + { + >bit_size = get_bit_size(type.content.alias.type); + return bit_size; + }, else => { #trap(); @@ -2492,6 +2505,7 @@ LLVMICmpPredicate = enum u32 [extern] LLVMDIBuilderCreateBitFieldMemberType = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, bit_size: u64, bit_offset: u64, storage_bit_offset: u64, flags: LLVMDIFlags, type: &LLVMMetadata) &LLVMMetadata; [extern] LLVMDIBuilderCreateStructType = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, bit_size: u64, bit_alignment: u32, flags: LLVMDIFlags, derived_from: &LLVMMetadata, element_pointer: &&LLVMMetadata, element_count: u32, runtime_language: u32, vtable_holder: &LLVMMetadata, unique_identifier_pointer: &u8, unique_identifier_length: u64) &LLVMMetadata; [extern] LLVMDIBuilderCreateUnionType = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, bit_size: u64, bit_alignment: u32, flags: LLVMDIFlags, element_pointer: &&LLVMMetadata, element_count: u32, runtime_language: u32, unique_identifier_pointer: &u8, unique_identifier_length: u64) &LLVMMetadata; +[extern] LLVMDIBuilderCreateTypedef = fn [cc(c)] (di_builder: &LLVMDIBuilder, type: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, scope: &LLVMMetadata, alignment: u32) &LLVMMetadata; [extern] LLVMDIBuilderCreateFunction = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, linkage_name_pointer: &u8, linkage_name_length: u64, file: &LLVMMetadata, line: u32, type: &LLVMMetadata, is_local_to_unit: s32, is_definition: s32, scope_line: u32, flags: LLVMDIFlags, is_optimized: s32) &LLVMMetadata; [extern] LLVMDIBuilderFinalizeSubprogram = fn [cc(c)] (di_builder: &LLVMDIBuilder, subprogram: &LLVMMetadata) void; @@ -5926,7 +5940,96 @@ parse_right = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value if (left.id == .macro) { - #trap(); + >declaration = left.content.macro; + + if (!macro_declaration_is_generic(declaration)) + { + report_error(); + } + + >instantiation_line = get_line(module); + >instantiation_column = get_column(module); + >original_constant_argument_count = declaration.constant_arguments.length; + >constant_arguments = arena_allocate_slice[ConstantArgument](module.arena, original_constant_argument_count); + >constant_argument_count: u64 = 0; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + + >constant_argument_index = constant_argument_count; + if (constant_argument_index == original_constant_argument_count) + { + report_error(); + } + + >constant_argument = declaration.constant_arguments[constant_argument_index]; + + switch (constant_argument.id) + { + .value => + { + #trap(); + }, + .type => + { + >argument_type = parse_type(module, scope); + constant_arguments[constant_argument_index] = { + .name = constant_argument.name, + .content = { + .type = argument_type, + }, + .id = .type, + }; + }, + } + + constant_argument_count += 1; + + skip_space(module); + consume_character_if_match(module, ','); + } + + skip_space(module); + + expect_character(module, left_parenthesis); + + if (original_constant_argument_count != constant_argument_count) + { + report_error(); + } + + >instantiation_arguments = parse_call_arguments(module, scope); + + result.& = { + .content = { + .macro_instantiation = { + .declaration = declaration, + .instantiation_function = module.current_function, + .declaration_arguments = zero, + .instantiation_arguments = instantiation_arguments, + .constant_arguments = constant_arguments, + .return_type = declaration.return_type, + .scope = { + .parent = scope, + .line = declaration.scope.line, + .column = declaration.scope.column, + .kind = .macro_instantiation, + zero, + }, + .line = instantiation_line, + .column = instantiation_column, + zero, + }, + }, + .id = .macro_instantiation, + zero, + }; } else { @@ -7170,12 +7273,55 @@ parse = fn (module: &Module) void if (is_generic) { + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + + >argument_name = arena_duplicate_string(module.arena, parse_identifier(module)); + + skip_space(module); + + >has_value = consume_character_if_match(module, ':'); + + >constant_argument_index = constant_argument_count; + + if (has_value) + { + #trap(); + } + else + { + >ty = new_type(module, { + .id = .unresolved, + .name = argument_name, + .scope = ¯o_declaration.scope, + zero, + }); + + constant_argument_buffer[constant_argument_index] = { + .name = argument_name, + .content = { + .type = ty, + }, + .id = .type, + }; + } + + constant_argument_count += 1; + } + + skip_space(module); + // END OF SCOPE if (constant_argument_count == 0) { report_error(); } - #trap(); } else { @@ -7591,6 +7737,12 @@ resolve_type_in_place_abi = fn (module: &Module, type: &Type) void >size = get_byte_size(type); assert(llvm_size == size); }, + .alias => + { + >aliased = type.content.alias.type; + resolve_type_in_place_abi(module, aliased); + result = aliased.llvm.abi; + }, else => { #trap(); @@ -7651,6 +7803,12 @@ resolve_type_in_place_memory = fn (module: &Module, type: &Type) void >size = get_byte_size(type); assert(llvm_size == size); }, + .alias => + { + >aliased = type.content.alias.type; + resolve_type_in_place_memory(module, aliased); + result = aliased.llvm.memory; + }, else => { #trap(); @@ -7836,6 +7994,14 @@ resolve_type_in_place_debug = fn (module: &Module, type: &Type) void result = union_type; }, + .alias => + { + >aliased = type.content.alias.type; + resolve_type_in_place_debug(module, aliased); + + >alignment = get_byte_alignment(aliased); + result = LLVMDIBuilderCreateTypedef(module.llvm.di_builder, aliased.llvm.debug, type.name.pointer, type.name.length, module.llvm.file, type.content.alias.line, type.content.alias.scope.llvm, alignment * 8); + }, else => { #trap(); @@ -9339,7 +9505,37 @@ resolve_type = fn (module: &Module, type: &Type) &Type { .unresolved => { - #trap(); + assert(!module.current_macro_declaration); + + >instantiation = module.current_macro_instantiation; + if (!instantiation) + { + report_error(); + } + + >declaration = instantiation.declaration; + >declaration_arguments = declaration.constant_arguments; + >instantiation_arguments = instantiation.constant_arguments; + assert(declaration_arguments.length == instantiation_arguments.length); + + for (i: 0..declaration_arguments.length) + { + >declaration_argument = &declaration_arguments[i]; + >instantiation_argument = &instantiation_arguments[i]; + assert(instantiation_argument.id == declaration_argument.id); + + if (declaration_argument.id == .type and type == declaration_argument.content.type) + { + assert(string_equal(declaration_argument.name, instantiation_argument.name)); + result = instantiation_argument.content.type; + break; + } + } + + if (!result) + { + report_error(); + } }, .integer => { @@ -11113,7 +11309,28 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi }, .type => { - #trap(); + >declaration_type = declaration_constant_argument.content.type; + assert(declaration_type.id == .unresolved); + + >old_instantiation_type = instantiation_constant_argument.content.type; + + >instantiation_type = new_type(module, { + .content = { + .alias = { + .type = old_instantiation_type, + .scope = ¯o_instantiation.scope, + .line = macro_instantiation.line, + }, + }, + .id = .alias, + .name = declaration_constant_argument.name, + .scope = ¯o_instantiation.scope, + zero, + }); + + instantiation_constant_argument.content = { + .type = instantiation_type, + }; }, } } @@ -16929,6 +17146,7 @@ names: [_][]u8 = "slice_array_literal", "slice_only_start", "basic_macro", + "generic_macro", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 62c9909d469cb1009a5f1dd4cf0c2fa27fab0797 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 17 Jun 2025 19:28:09 -0600 Subject: [PATCH 03/18] Pass 'generic_pointer_macro' --- src/compiler.bbb | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 5d75c7f..3b79a32 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -9541,6 +9541,18 @@ resolve_type = fn (module: &Module, type: &Type) &Type { result = type; }, + .pointer => + { + >old_element_type = type.content.pointer.element_type; + >element_type = resolve_type(module, old_element_type); + + result = type; + + if (element_type != old_element_type) + { + result = get_pointer_type(module, element_type); + } + }, else => { #trap(); @@ -9585,6 +9597,16 @@ clone_value = fn (module: &Module, scope: &Scope, old_value: &Value) &Value }, }; }, + .unary => + { + >unary_value = clone_value(module, scope, old_value.content.unary.value); + result.content = { + .unary = { + .value = unary_value, + .id = old_value.content.unary.id, + }, + }; + }, else => { #trap(); @@ -12600,7 +12622,11 @@ emit_field_access = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, l if (real_aggregate_type != aggregate_element_type) { - #trap(); + v = create_load(module, { + .type = aggregate_element_type, + .pointer = aggregate.llvm, + zero, + }); } else { @@ -17147,6 +17173,7 @@ names: [_][]u8 = "slice_only_start", "basic_macro", "generic_macro", + "generic_pointer_macro", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From f48cd83c4eb21a33da9ce6a313a44b63525752f7 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 17 Jun 2025 19:47:27 -0600 Subject: [PATCH 04/18] Pass 'noreturn_macro' --- src/compiler.bbb | 63 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 3b79a32..635adcf 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -9537,7 +9537,10 @@ resolve_type = fn (module: &Module, type: &Type) &Type report_error(); } }, - .integer => + .void, + .noreturn, + .integer, + => { result = type; }, @@ -9563,6 +9566,14 @@ resolve_type = fn (module: &Module, type: &Type) &Type return result; } +BlockCopy = struct +{ + source: &Block, + destination: &Block, +} + +copy_block = fn (module: &Module, parent_scope: &Scope, copy: BlockCopy) void; + clone_value = fn (module: &Module, scope: &Scope, old_value: &Value) &Value { assert(old_value != zero); @@ -9607,6 +9618,7 @@ clone_value = fn (module: &Module, scope: &Scope, old_value: &Value) &Value }, }; }, + .unreachable => {}, else => { #trap(); @@ -9643,6 +9655,43 @@ clone_statement = fn (module: &Module, scope: &Scope, old_statement: &Statement) .return = return_value, }; }, + .if => + { + >condition = clone_value(module, scope, old_statement.content.if.condition); + >if_statement = clone_statement(module, scope, old_statement.content.if.if); + >else_statement = old_statement.content.if.else; + if (else_statement) + { + else_statement = clone_statement(module, scope, else_statement); + } + + new_statement.content = { + .if = { + .condition = condition, + .if = if_statement, + .else = else_statement, + }, + }; + }, + .block => + { + >block = arena_allocate[Block](module.arena, 1); + copy_block(module, scope, { + .source = old_statement.content.block, + .destination = block, + }); + + new_statement.content = { + .block = block, + }; + }, + .expression => + { + >value = clone_value(module, scope, old_statement.content.expression); + new_statement.content = { + .expression = value, + }; + }, else => { #trap(); @@ -9652,12 +9701,6 @@ clone_statement = fn (module: &Module, scope: &Scope, old_statement: &Statement) return new_statement; } -BlockCopy = struct -{ - source: &Block, - destination: &Block, -} - copy_block = fn (module: &Module, parent_scope: &Scope, copy: BlockCopy) void { >source = copy.source; @@ -10049,6 +10092,7 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi .ampersand, .exclamation, .va_end, + .bitwise_not, => { >is_boolean = unary_is_boolean(unary_id); @@ -13763,6 +13807,10 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con LLVMSetInstructionCallConv(call, .fast); llvm_value = call; }, + .bitwise_not => + { + llvm_value = LLVMBuildNot(module.llvm.builder, llvm_unary_value, ""); + }, else => { #trap(); }, } }, @@ -17174,6 +17222,7 @@ names: [_][]u8 = "basic_macro", "generic_macro", "generic_pointer_macro", + "noreturn_macro", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 52004cc1179685bc57371c36d30e6baa16724818 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 17 Jun 2025 20:28:58 -0600 Subject: [PATCH 05/18] Pass 'generic_pointer_array' --- src/compiler.bbb | 111 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 635adcf..3767702 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -9556,6 +9556,23 @@ resolve_type = fn (module: &Module, type: &Type) &Type result = get_pointer_type(module, element_type); } }, + .struct => + { + result = type; + + if (type_is_slice(type)) + { + >old_pointer_type = type.content.struct.fields[0].type; + assert(old_pointer_type.id == .pointer); + >old_element_type = old_pointer_type.content.pointer.element_type; + >element_type = resolve_type(module, old_element_type); + + if (element_type != old_element_type) + { + result = get_slice_type(module, element_type); + } + } + }, else => { #trap(); @@ -9619,6 +9636,30 @@ clone_value = fn (module: &Module, scope: &Scope, old_value: &Value) &Value }; }, .unreachable => {}, + .slice_expression => + { + >array_like = clone_value(module, scope, old_value.content.slice_expression.array_like); + >start = old_value.content.slice_expression.start; + >end = old_value.content.slice_expression.end; + + if (start) + { + start = clone_value(module, scope, start); + } + + if (end) + { + end = clone_value(module, scope, end); + } + + result.content = { + .slice_expression = { + .array_like = array_like, + .start = start, + .end = end, + }, + }; + }, else => { #trap(); @@ -9692,6 +9733,36 @@ clone_statement = fn (module: &Module, scope: &Scope, old_statement: &Statement) .expression = value, }; }, + .local => + { + >local_old = old_statement.content.local; + >local_new = new_local(module, scope); + assert(!local_old.variable.storage); + >initial_value = clone_value(module, scope, local_old.initial_value); + + >local_type = local_old.variable.type; + if (local_type) + { + local_type = resolve_type(module, local_type); + } + + local_new.& = { + .variable = { + .type = local_type, + .scope = scope, + .name = local_old.variable.name, + .line = local_old.variable.line, + .column = local_old.variable.column, + zero, + }, + .initial_value = initial_value, + zero, + }; + + new_statement.content = { + .local = local_new, + }; + }, else => { #trap(); @@ -9722,7 +9793,7 @@ copy_block = fn (module: &Module, parent_scope: &Scope, copy: BlockCopy) void if (last_statement) { - last_statement.next = last_statement; + last_statement.next = statement; } else { @@ -10063,7 +10134,31 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi }, .pointer_from_int => { - #trap(); + if (!expected_type) + { + report_error(); + } + + if (expected_type.id != .pointer) + { + report_error(); + } + + analyze_type(module, unary_value, zero, { .must_be_constant = analysis.must_be_constant, zero }); + + >unary_value_type = unary_value.type; + if (unary_value_type.id != .integer) + { + report_error(); + } + + // TODO: is this correct? + if (get_bit_size(unary_value_type) != 64) + { + report_error(); + } + + value_type = expected_type; }, .enum_from_int => { @@ -14776,6 +14871,17 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, zero, }); }, + .macro_instantiation => + { + emit_macro_instantiation(module, right); + >size = get_byte_size(resolved_value_type); + >alignment = get_byte_alignment(resolved_value_type); + + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + + LLVMBuildMemCpy(module.llvm.builder, left_llvm, alignment, right.content.macro_instantiation.return_alloca, alignment, LLVMConstInt(u64_type.llvm.abi, size, 0)); + }, else => { #trap(); @@ -17223,6 +17329,7 @@ names: [_][]u8 = "generic_macro", "generic_pointer_macro", "noreturn_macro", + "generic_pointer_array", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 422adc46933db4d1071d50a3948e242e8d0b55d5 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 17 Jun 2025 20:30:18 -0600 Subject: [PATCH 06/18] Pass 'self_referential_struct' --- src/compiler.bbb | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler.bbb b/src/compiler.bbb index 3767702..204fa0c 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -17330,6 +17330,7 @@ names: [_][]u8 = "generic_pointer_macro", "noreturn_macro", "generic_pointer_array", + "self_referential_struct", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 134339002b0c82aec4851ab7ceb21d8adec1236a Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 17 Jun 2025 20:31:49 -0600 Subject: [PATCH 07/18] Pass 'forward_declared_type' --- src/compiler.bbb | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler.bbb b/src/compiler.bbb index 204fa0c..dce2fc7 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -17331,6 +17331,7 @@ names: [_][]u8 = "noreturn_macro", "generic_pointer_array", "self_referential_struct", + "forward_declared_type", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 08f8bf723ee0f1367a242d8d6623d2d121890fcf Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 18 Jun 2025 06:39:14 -0600 Subject: [PATCH 08/18] Pass 'enum_array' --- src/compiler.bbb | 207 +++++++++++++++++++++++++++++++++++++++++++++-- src/compiler.hpp | 5 +- src/emitter.cpp | 5 +- 3 files changed, 208 insertions(+), 9 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index dce2fc7..db01990 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1632,6 +1632,17 @@ get_byte_size = fn (type: &Type) u64 >result = type.content.union.byte_size; return result; }, + .enum_array => + { + >enum_type = type.content.enum_array.enum_type; + >element_type = type.content.enum_array.element_type; + assert(enum_type.id == .enum); + >element_count = enum_type.content.enum.fields.length; + >element_size = get_byte_size(element_type); + + >result = element_size * element_count; + return result; + }, else => { #trap(); @@ -1679,6 +1690,10 @@ get_byte_alignment = fn (type: &Type) u32 { return get_byte_alignment(type.content.alias.type); }, + .enum_array => + { + return get_byte_alignment(type.content.enum_array.element_type); + }, else => { #trap(); @@ -3983,6 +3998,54 @@ FunctionKeyword = enum cc, } +get_enum_array_type = fn (module: &Module, enum_type: &Type, element_type: &Type) &Type +{ + assert(enum_type != zero); + assert(element_type != zero); + + >last_enum_type = module.first_enum_array_type; + + while (last_enum_type) + { + assert(last_enum_type.id == .enum_array); + + if (last_enum_type.content.enum_array.enum_type == enum_type and last_enum_type.content.enum_array.element_type == element_type) + { + return last_enum_type; + } + + >next = last_enum_type.content.enum_array.next; + + if (!next) + { + break; + } + + last_enum_type = next; + } + + assert(enum_type.scope != zero); + assert(element_type.scope != zero); + + >scope = #select(element_type.scope.kind == .global, enum_type.scope, element_type.scope); + + >enum_array_type = new_type(module, { + .content = { + .enum_array = { + .enum_type = enum_type, + .element_type = element_type, + zero, + }, + }, + .id = .enum_array, + .name = arena_join_string(module.arena, [ "enum_array[", enum_type.name, "](", element_type.name, ")" ][..]), + .scope = scope, + zero, + }); + + return enum_array_type; +} + resolve_alias = fn (module: &Module, type: &Type) &Type { >result: &Type = zero; @@ -4055,6 +4118,22 @@ resolve_alias = fn (module: &Module, type: &Type) &Type { result = resolve_alias(module, type.content.alias.type); }, + .enum_array => + { + >old_enum_type = type.content.enum_array.enum_type; + >old_element_type = type.content.enum_array.element_type; + >enum_type = resolve_alias(module, old_enum_type); + >element_type = resolve_alias(module, old_element_type); + + if (old_enum_type == enum_type and old_element_type == element_type) + { + result = type; + } + else + { + result = get_enum_array_type(module, enum_type, element_type); + } + }, else => { #trap(); @@ -4332,7 +4411,17 @@ parse_type = fn (module: &Module, scope: &Scope) &Type }, .enum_array => { - #trap(); + skip_space(module); + expect_character(module, left_bracket); + >enum_type = parse_type(module, scope); + expect_character(module, right_bracket); + + expect_character(module, left_parenthesis); + >element_type = parse_type(module, scope); + expect_character(module, right_parenthesis); + + >enum_array_type = get_enum_array_type(module, enum_type, element_type); + return enum_array_type; }, } } @@ -5534,7 +5623,7 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value if (is_aggregate_initialization) { - #trap(); + result = parse_aggregate_initialization(module, scope, builder, right_bracket); } else { @@ -7743,6 +7832,24 @@ resolve_type_in_place_abi = fn (module: &Module, type: &Type) void resolve_type_in_place_abi(module, aliased); result = aliased.llvm.abi; }, + .enum_array => + { + >enum_type = type.content.enum_array.enum_type; + assert(enum_type.id == .enum); + + >element_type = type.content.enum_array.element_type; + resolve_type_in_place_memory(module, element_type); + + >element_count = enum_type.content.enum.fields.length; + assert(element_count != 0); + + >array_type = LLVMArrayType2(element_type.llvm.memory, element_count); + result = array_type; + + >llvm_size = LLVMStoreSizeOfType(module.llvm.target_data_layout, result); + >size = get_byte_size(type); + assert(llvm_size == size); + }, else => { #trap(); @@ -8002,6 +8109,18 @@ resolve_type_in_place_debug = fn (module: &Module, type: &Type) void >alignment = get_byte_alignment(aliased); result = LLVMDIBuilderCreateTypedef(module.llvm.di_builder, aliased.llvm.debug, type.name.pointer, type.name.length, module.llvm.file, type.content.alias.line, type.content.alias.scope.llvm, alignment * 8); }, + .enum_array => + { + >enum_type = type.content.enum_array.enum_type; + assert(enum_type.id == .enum); + >element_type = type.content.enum_array.element_type; + >element_count = enum_type.content.enum.fields.length; + resolve_type_in_place_debug(module, element_type); + assert(element_count != 0); + >bit_alignment = get_byte_alignment(type) * 8; + >array_type = LLVMDIBuilderCreateArrayType(module.llvm.di_builder, element_count, bit_alignment, element_type.llvm.debug, zero, 0); + result = array_type; + }, else => { #trap(); @@ -10506,7 +10625,7 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi }, .enum_array => { - #trap(); + element_type = pointer_element_type.content.enum_array.element_type; }, else => { unreachable; }, } @@ -11004,7 +11123,72 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi }, .enum_array => { - #trap(); + >is_ordered: u1 = 1; + >enum_type = aggregate_type.content.enum_array.enum_type; + >element_type = aggregate_type.content.enum_array.element_type; + if (enum_type.id != .enum) + { + report_error(); + } + + >fields = enum_type.content.enum.fields; + + assert(fields.length <= 64); + + >same_values_as_field = fields.length == elements.length; + >is_properly_initialized = same_values_as_field or is_zero; + + if (is_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]; + >value = element.value; + >name = element.name; + + >result_field: &EnumField = zero; + + for (&field: fields) + { + if (string_equal(name, field.name)) + { + result_field = field; + break; + } + } + + if (!result_field) + { + report_error(); + } + + >declaration_index: u64 = #extend(result_field - fields.pointer); + >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; + + analyze_type(module, value, element_type, { .must_be_constant = analysis.must_be_constant, zero }); + is_constant = is_constant and value_is_constant(value); + } + + value.content.aggregate_initialization.is_constant = is_constant and is_ordered; }, else => { report_error(); }, } @@ -14107,7 +14291,7 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con }, .enum_array => { - #trap(); + element_type = array_type.content.enum_array.element_type; }, else => { unreachable; }, } @@ -14413,7 +14597,17 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con }, .enum_array => { - #trap(); + assert(is_constant); + assert(elements.length <= 64); + >value_buffer: [64]&Value = undefined; + + for (i: 0..elements.length) + { + value_buffer[i] = elements[i].value; + } + >values = value_buffer[..elements.length]; + >element_type = resolved_value_type.content.enum_array.element_type; + llvm_value = emit_constant_array(module, values, element_type); }, else => { unreachable; }, } @@ -17332,6 +17526,7 @@ names: [_][]u8 = "generic_pointer_array", "self_referential_struct", "forward_declared_type", + "enum_array", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/compiler.hpp b/src/compiler.hpp index 24d4233..e6df8e9 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -1933,12 +1933,13 @@ fn Type* get_enum_array_type(Module* module, Type* enum_type, Type* element_type return last_enum_type; } - if (!last_enum_type->enum_array.next) + auto next = last_enum_type->enum_array.next; + if (!next) { break; } - last_enum_type = last_enum_type->enum_array.next; + last_enum_type = next; } } diff --git a/src/emitter.cpp b/src/emitter.cpp index afc64b7..9cfad5e 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -4095,7 +4095,10 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal bool is_ordered = true; auto enum_type = aggregate_type->enum_array.enum_type; auto element_type = aggregate_type->enum_array.element_type; - assert(enum_type->id == TypeId::enumerator); + if (enum_type->id != TypeId::enumerator) + { + report_error(); + } auto fields = enum_type->enumerator.fields; assert(fields.length <= 64); -- 2.43.0 From a36fdc88f064bb223ba8d6cfe5b1c872d245136a Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 18 Jun 2025 07:55:09 -0600 Subject: [PATCH 09/18] Pass 'opaque' --- src/compiler.bbb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index db01990..b6bf931 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -7501,7 +7501,14 @@ parse = fn (module: &Module) void }, .opaque => { - #trap(); + skip_space(module); + expect_character(module, ';'); + new_type(module, { + .id = .opaque, + .name = global_name, + .scope = &module.scope, + zero, + }); }, .struct => { @@ -8121,6 +8128,10 @@ resolve_type_in_place_debug = fn (module: &Module, type: &Type) void >array_type = LLVMDIBuilderCreateArrayType(module.llvm.di_builder, element_count, bit_alignment, element_type.llvm.debug, zero, 0); result = array_type; }, + .opaque => + { + return; + }, else => { #trap(); @@ -17527,6 +17538,7 @@ names: [_][]u8 = "self_referential_struct", "forward_declared_type", "enum_array", + "opaque", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 54f86ac8f3cda6da3dc571da6f5215ba75077727 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 18 Jun 2025 07:58:48 -0600 Subject: [PATCH 10/18] Pass 'basic_struct_passing' --- src/compiler.bbb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/compiler.bbb b/src/compiler.bbb index b6bf931..724f4b9 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1731,6 +1731,11 @@ get_bit_size = fn (type: &Type) u64 >bit_size = get_bit_size(type.content.alias.type); return bit_size; }, + .union => + { + >result = type.content.union.byte_size * 8; + return result; + }, else => { #trap(); @@ -2182,6 +2187,10 @@ value_is_constant = fn (value: &Value) u1 { return 0; }, + .zero => + { + return 1; + }, else => { #trap(); @@ -17539,6 +17548,7 @@ names: [_][]u8 = "forward_declared_type", "enum_array", "opaque", + "basic_struct_passing", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 3fdd2826b1929666f1f1aab9b2a0c1b749c61e7a Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 18 Jun 2025 08:13:14 -0600 Subject: [PATCH 11/18] Pass 'enum_arbitrary_abi' --- src/compiler.bbb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 724f4b9..7b805c2 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1783,6 +1783,11 @@ is_promotable_integer_type_for_abi = fn (type: &Type) u1 >backing_type = type.content.bits.backing_type; return is_promotable_integer_type_for_abi(backing_type); }, + .enum => + { + >backing_type = type.content.enum.backing_type; + return is_promotable_integer_type_for_abi(backing_type); + }, else => { #trap(); @@ -8404,6 +8409,10 @@ abi_system_v_classify_type = fn (type: &Type, options: AbiSystemVClassifyArgumen { return abi_system_v_classify_type(type.content.bits.backing_type, options); }, + .enum => + { + return abi_system_v_classify_type(type.content.enum.backing_type, options); + }, else => { #trap(); @@ -8596,7 +8605,8 @@ abi_system_v_get_integer_type_at_offset = fn (module: &Module, type: &Type, offs }, .enum => { - #trap(); + >backing_type = type.content.enum.backing_type; + return abi_system_v_get_integer_type_at_offset(module, backing_type, offset, #select(source_type == type, backing_type, source_type), source_offset); }, .array => { @@ -17549,6 +17559,7 @@ names: [_][]u8 = "enum_array", "opaque", "basic_struct_passing", + "enum_arbitrary_abi", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 27f9cf3c935fa951c3d19d641da7e5861be8fd83 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 18 Jun 2025 08:15:37 -0600 Subject: [PATCH 12/18] Pass 'enum_debug_info' --- src/compiler.bbb | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler.bbb b/src/compiler.bbb index 7b805c2..6f57f19 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -17560,6 +17560,7 @@ names: [_][]u8 = "opaque", "basic_struct_passing", "enum_arbitrary_abi", + "enum_debug_info", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From e74bfcabe29ec09da664e92742aa9cda726fa4cc Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 18 Jun 2025 08:21:57 -0600 Subject: [PATCH 13/18] Pass 'return_array' --- src/compiler.bbb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 6f57f19..bed0436 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -8610,7 +8610,10 @@ abi_system_v_get_integer_type_at_offset = fn (module: &Module, type: &Type, offs }, .array => { - #trap(); + >element_type = type.content.array.element_type; + >element_size = get_byte_size(element_type); + >element_offset = (offset / element_size) * element_size; + return abi_system_v_get_integer_type_at_offset(module, element_type, offset - element_offset, source_type, source_offset); }, else => { #trap(); }, } @@ -17561,6 +17564,7 @@ names: [_][]u8 = "basic_struct_passing", "enum_arbitrary_abi", "enum_debug_info", + "return_array", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 788f8a853f9e8fe029b2437fbd39430b1c99d3f9 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 18 Jun 2025 08:24:48 -0600 Subject: [PATCH 14/18] Pass 'bool_pair' --- src/compiler.bbb | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler.bbb b/src/compiler.bbb index bed0436..7af53c2 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -17565,6 +17565,7 @@ names: [_][]u8 = "enum_arbitrary_abi", "enum_debug_info", "return_array", + "bool_pair", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 0e1d2796235262177830ea2620ede626af07fdeb Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 18 Jun 2025 08:41:39 -0600 Subject: [PATCH 15/18] Pass 'min_max' --- src/compiler.bbb | 90 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 7af53c2..1fa87cd 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -5595,7 +5595,47 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .max, .min => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + >left = parse_value(module, scope, zero); + + skip_space(module); + expect_character(module, ','); + skip_space(module); + + >right = parse_value(module, scope, zero); + + skip_space(module); + expect_character(module, right_parenthesis); + + >binary_id: BinaryId = undefined; + + switch (intrinsic) + { + .max => + { + binary_id = .max; + }, + .min => + { + binary_id = .min; + }, + else => { unreachable; }, + } + + result.& = { + .content = { + .binary = { + .left = left, + .right = right, + .id = binary_id, + }, + }, + .id = .binary, + zero, + }; }, .build_mode => { @@ -11814,6 +11854,15 @@ get_llvm_type = fn (type: &Type, kind: TypeKind) &LLVMType } } +emit_intrinsic_call = fn (module: &Module, index: LLVMIntrinsicIndex, argument_types: []&LLVMType, argument_values: []&LLVMValue) &LLVMValue +{ + >intrinsic_id = module.llvm.intrinsic_table[index]; + >intrinsic_function = LLVMGetIntrinsicDeclaration(module.llvm.module, intrinsic_id, argument_types.pointer, argument_types.length); + >intrinsic_function_type = LLVMIntrinsicGetType(module.llvm.context, intrinsic_id, argument_types.pointer, argument_types.length); + >call = LLVMBuildCall2(module.llvm.builder, intrinsic_function_type, intrinsic_function, argument_values.pointer, #truncate(argument_values.length), ""); + return call; +} + emit_binary = fn (module: &Module, left: &LLVMValue, left_type: &Type, right: &LLVMValue, right_type: &Type, id: BinaryId, resolved_value_type: &Type) &LLVMValue { switch (resolved_value_type.id) @@ -11830,7 +11879,34 @@ emit_binary = fn (module: &Module, left: &LLVMValue, left_type: &Type, right: &L { .max, .min => { - #trap(); + >intrinsic_index: LLVMIntrinsicIndex = undefined; + + switch (resolved_value_type.id) + { + .integer => + { + >signed = resolved_value_type.content.integer.signed; + + switch (id) + { + .max => + { + intrinsic_index = #select(signed, ."llvm.smax", ."llvm.umax"); + }, + .min => + { + intrinsic_index = #select(signed, ."llvm.smin", ."llvm.umin"); + }, + else => { unreachable; }, + } + }, + else => { report_error(); }, + } + + >argument_types: [_]&LLVMType = [ resolved_value_type.llvm.abi ]; + >argument_values: [_]&LLVMValue = [ left, right ]; + >llvm_value = emit_intrinsic_call(module, intrinsic_index, argument_types[..], argument_values[..]); + return llvm_value; }, .shift_right => { @@ -12941,15 +13017,6 @@ emit_constant_array = fn (module: &Module, elements: []&Value, element_type: &Ty return constant_array; } -emit_intrinsic_call = fn (module: &Module, index: LLVMIntrinsicIndex, argument_types: []&LLVMType, argument_values: []&LLVMValue) &LLVMValue -{ - >intrinsic_id = module.llvm.intrinsic_table[index]; - >intrinsic_function = LLVMGetIntrinsicDeclaration(module.llvm.module, intrinsic_id, argument_types.pointer, argument_types.length); - >intrinsic_function_type = LLVMIntrinsicGetType(module.llvm.context, intrinsic_id, argument_types.pointer, argument_types.length); - >call = LLVMBuildCall2(module.llvm.builder, intrinsic_function_type, intrinsic_function, argument_values.pointer, #truncate(argument_values.length), ""); - return call; -} - StructLikeFieldAccess = struct { type: &Type, @@ -17566,6 +17633,7 @@ names: [_][]u8 = "enum_debug_info", "return_array", "bool_pair", + "min_max", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From b5c5f9682f5dec5980c2d9abe4f7a052b3ef83c7 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 18 Jun 2025 16:44:29 -0600 Subject: [PATCH 16/18] Pass 'field_parent_pointer' --- src/compiler.bbb | 131 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 1fa87cd..48f5178 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1838,6 +1838,7 @@ ValueId = enum select, string_to_enum, macro_instantiation, + field_parent_pointer, } ValueConstantInteger = struct @@ -2099,6 +2100,12 @@ MacroInstantiation = struct return_block: &LLVMBasicBlock, } +ValueFieldParentPointer = struct +{ + pointer: &Value, + name: []u8, +} + ValueContent = union { constant_integer: ValueConstantInteger, @@ -2120,6 +2127,7 @@ ValueContent = union string_to_enum: ValueStringToEnum, macro: &MacroDeclaration, macro_instantiation: MacroInstantiation, + field_parent_pointer: ValueFieldParentPointer, } ValueKind = enum @@ -5643,7 +5651,30 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .field_parent_pointer => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + + >field_pointer = parse_value(module, scope, zero); + + skip_space(module); + expect_character(module, ','); + skip_space(module); + + >field_name = parse_string_literal(module); + + skip_space(module); + expect_character(module, right_parenthesis); + + result.& = { + .content = { + .field_parent_pointer = { + .pointer = field_pointer, + .name = field_name, + }, + }, + .id = .field_parent_pointer, + zero, + }; }, } }, @@ -11829,6 +11860,53 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi module.current_macro_instantiation = current_macro_instantiation; module.current_function = current_function; }, + .field_parent_pointer => + { + >field_pointer = value.content.field_parent_pointer.pointer; + >field_name = value.content.field_parent_pointer.name; + + if (!expected_type) + { + report_error(); + } + + value_type = expected_type; + + if (value_type.id != .pointer) + { + report_error(); + } + + >aggregate_type = value_type.content.pointer.element_type; + + >field_type: &Type = zero; + + switch (aggregate_type.id) + { + .struct => + { + >fields = aggregate_type.content.struct.fields; + + for (&field: fields) + { + if (string_equal(field_name, field.name)) + { + field_type = field.type; + break; + } + } + }, + else => { #trap(); }, + } + + if (!field_type) + { + report_error(); + } + + >pointer_to_field = get_pointer_type(module, field_type); + analyze_type(module, field_pointer, pointer_to_field, zero); + }, else => { #trap(); @@ -14817,6 +14895,56 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con }, } }, + .field_parent_pointer => + { + >field_pointer = value.content.field_parent_pointer.pointer; + >field_name = value.content.field_parent_pointer.name; + + emit_value(module, field_pointer, .memory, 0); + + >llvm_field_pointer = field_pointer.llvm; + assert(llvm_field_pointer != zero); + + assert(resolved_value_type.id == .pointer); + >aggregate_type = resolved_value_type.content.pointer.element_type; + + switch (aggregate_type.id) + { + .struct => + { + >fields = aggregate_type.content.struct.fields; + + >result_field: &Field = zero; + + for (&field: fields) + { + if (string_equal(field_name, field.name)) + { + result_field = field; + break; + } + } + + assert(result_field != zero); + >offset = result_field.offset; + + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + + >llvm_u64 = u64_type.llvm.abi; + + >address_int = LLVMBuildPtrToInt(module.llvm.builder, llvm_field_pointer, llvm_u64, ""); + address_int = LLVMBuildSub(module.llvm.builder, address_int, LLVMConstInt(llvm_u64, offset, 0), ""); + + >address_pointer = LLVMBuildIntToPtr(module.llvm.builder, address_int, resolved_value_type.llvm.abi, ""); + llvm_value = address_pointer; + }, + else => + { + report_error(); + }, + } + }, else => { #trap(); @@ -17634,6 +17762,7 @@ names: [_][]u8 = "return_array", "bool_pair", "min_max", + "field_parent_pointer", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 88978ef50b833faf4f20c00d823559690edf7f9c Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 18 Jun 2025 21:06:32 -0600 Subject: [PATCH 17/18] Pass 'leading_trailing_zeroes' --- src/compiler.bbb | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 48f5178..18234e0 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -820,6 +820,8 @@ LLVMTargetMachineOptions = opaque; LLVMIntrinsicIndex = enum u32 { + "llvm.ctlz", + "llvm.cttz", "llvm.smax", "llvm.smin", "llvm.trap", @@ -1880,6 +1882,8 @@ UnaryId = enum dereference, pointer_from_int, enum_from_int, + leading_zeroes, + trailing_zeroes, } unary_is_boolean = fn (id: UnaryId) u1 @@ -3936,12 +3940,14 @@ ValueIntrinsic = enum integer_max, int_from_enum, int_from_pointer, + leading_zeroes, max, min, pointer_cast, pointer_from_int, select, string_to_enum, + trailing_zeroes, trap, truncate, va_start, @@ -5415,6 +5421,8 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value .pointer_cast, .pointer_from_int, .va_end, + .leading_zeroes, + .trailing_zeroes, => { >id: UnaryId = undefined; @@ -5426,9 +5434,11 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value .extend => { id = .extend; }, .int_from_enum => { id = .int_from_enum; }, .int_from_pointer => { id = .int_from_pointer; }, - .truncate => { id = .truncate; }, + .leading_zeroes => { id = .leading_zeroes; }, .pointer_cast => { id = .pointer_cast; }, .pointer_from_int => { id = .pointer_from_int; }, + .trailing_zeroes => { id = .trailing_zeroes; }, + .truncate => { id = .truncate; }, .va_end => { id = .va_end; }, else => { unreachable; }, } @@ -5664,7 +5674,6 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value skip_space(module); expect_character(module, right_parenthesis); - result.& = { .content = { .field_parent_pointer = { @@ -5676,6 +5685,7 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value zero, }; }, + else => { unreachable; }, } }, .left_bracket => @@ -10411,6 +10421,8 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi .exclamation, .va_end, .bitwise_not, + .leading_zeroes, + .trailing_zeroes, => { >is_boolean = unary_is_boolean(unary_id); @@ -14268,6 +14280,27 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con { llvm_value = LLVMBuildNot(module.llvm.builder, llvm_unary_value, ""); }, + .leading_zeroes, + .trailing_zeroes, + => + { + >intrinsic_index: LLVMIntrinsicIndex = undefined; + + switch (unary_id) + { + .leading_zeroes => { intrinsic_index = ."llvm.ctlz"; }, + .trailing_zeroes => { intrinsic_index = ."llvm.cttz"; }, + else => { unreachable; }, + } + + >u1_type = uint1(module); + resolve_type_in_place(module, u1_type); + + >zero_is_poison = LLVMConstNull(u1_type.llvm.abi); + >types: [_]&LLVMType = [ destination_type ]; + >values: [_]&LLVMValue = [ llvm_unary_value, zero_is_poison ]; + llvm_value = emit_intrinsic_call(module, intrinsic_index, types[..], values[..]); + }, else => { #trap(); }, } }, @@ -17763,6 +17796,7 @@ names: [_][]u8 = "bool_pair", "min_max", "field_parent_pointer", + "leading_trailing_zeroes", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 14f856b550fa9c315ddc0e162c6e227bb9741d23 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Thu, 19 Jun 2025 07:07:13 -0600 Subject: [PATCH 18/18] Pass 'pointer_sub' --- src/compiler.bbb | 318 ++++++++++++++++++++++++++++++++++++++++++----- src/emitter.cpp | 15 +-- 2 files changed, 294 insertions(+), 39 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 18234e0..ace2f3d 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1458,6 +1458,88 @@ Type = struct llvm: TypeLLVM, } +receives_type = fn (value: &Value) u1 +{ + switch (value.id) + { + .constant_integer, + .enum_literal, + .string_literal, + .zero, + => + { + return 1; + }, + .array_expression, + .call, + => + { + return 0; + }, + .variable => + { + return value.kind == .left and value.id == .global; + }, + .field_access => + { + >aggregate = value.content.field_access.aggregate; + >field_name = value.content.field_access.field_name; + + return string_equal(field_name, "length") and? aggregate.id == .variable and? aggregate.kind == .left and? aggregate.content.variable.type.id == .array; + }, + .unary => + { + >unary_id = value.content.unary.id; + >unary_value = value.content.unary.value; + + switch (unary_id) + { + .extend, + .truncate, + .pointer_cast, + .pointer_from_int, + => + { + return 1; + }, + .int_from_pointer, + .ampersand, + .dereference, + .va_end, + .leading_zeroes, + .trailing_zeroes, + .exclamation, + .int_from_enum, + .enum_from_int, + .enum_name, + => + { + return 0; + }, + .minus, + .plus, + .bitwise_not, + => + { + return receives_type(unary_value); + }, + } + }, + .binary => + { + >left = value.content.binary.left; + >right = value.content.binary.right; + >binary_id = value.content.binary.id; + + return receives_type(left) and receives_type(right); + }, + else => + { + #trap(); + }, + } +} + type_is_signed = fn (type: &Type) u1 { switch (type.id) @@ -1553,7 +1635,7 @@ is_arbitrary_bit_integer = fn (type: &Type) u1 switch (bit_count) { - 8, 16, 32, 64, 128 => { return 0; }, + 1, 8, 16, 32, 64, 128 => { return 0; }, else => { return 1; }, } }, @@ -1765,6 +1847,10 @@ is_integral_or_enumeration_type = fn (type: &Type) u1 { return 0; }, + .alias => + { + return is_integral_or_enumeration_type(type.content.alias.type); + }, else => { unreachable; @@ -1851,6 +1937,7 @@ ValueConstantInteger = struct ValueFunctionLLVM = struct { + alloca_insertion_point: &LLVMValue, return_block: &LLVMBasicBlock, return_alloca: &LLVMValue, } @@ -2583,8 +2670,10 @@ LLVMICmpPredicate = enum u32 [extern] LLVMAppendBasicBlockInContext = fn [cc(c)] (context: &LLVMContext, function: &LLVMValue, name: &u8) &LLVMBasicBlock; +[extern] LLVMPositionBuilderBefore = fn [cc(c)] (builder: &LLVMBuilder, instruction: &LLVMValue) void; [extern] LLVMPositionBuilderAtEnd = fn [cc(c)] (builder: &LLVMBuilder, basic_block: &LLVMBasicBlock) void; [extern] LLVMClearInsertionPosition = fn [cc(c)] (builder: &LLVMBuilder) void; +[extern] LLVMGetCurrentDebugLocation2 = fn [cc(c)] (builder: &LLVMBuilder) &LLVMMetadata; [extern] LLVMSetCurrentDebugLocation2 = fn [cc(c)] (builder: &LLVMBuilder, debug_location: &LLVMMetadata) void; [extern] LLVMSetAlignment = fn [cc(c)] (value: &LLVMValue, alignment: u32) void; @@ -3102,6 +3191,11 @@ sint32 = fn (module: &Module) &Type return integer_type(module, { .bit_count = 32, .signed = 1 }); } +sint64 = fn (module: &Module) &Type +{ + return integer_type(module, { .bit_count = 64, .signed = 1 }); +} + void_type = fn (module: &Module) &Type { return module.scope.types.first + void_offset; @@ -9397,6 +9491,22 @@ ResolvedCallingConvention = enum win64, } +get_current_function = fn (module: &Module) &Global +{ + >current_function = module.current_function; + + if (!current_function) + { + >macro_instantiation = module.current_macro_instantiation; + if (macro_instantiation) + { + current_function = macro_instantiation.instantiation_function; + } + } + + return current_function; +} + AllocaOptions = struct { type: &Type, @@ -9415,7 +9525,17 @@ create_alloca = fn (module: &Module, options: AllocaOptions) &LLVMValue alignment = get_byte_alignment(abi_type); } + >original_block = LLVMGetInsertBlock(module.llvm.builder); + >function = get_current_function(module); + >debug_location = LLVMGetCurrentDebugLocation2(module.llvm.builder); + >alloca_insertion_point = function.variable.storage.content.function.llvm.alloca_insertion_point; + LLVMPositionBuilderBefore(module.llvm.builder, alloca_insertion_point); + LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); + >alloca = llvm_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; } @@ -9565,56 +9685,82 @@ analyze_value = fn (module: &Module, value: &Value, expected_type: &Type, type_k emit_value(module, value, type_kind, must_be_constant); } -analyze_binary_type = fn (module: &Module, left: &Value, right: &Value, is_boolean: u1, expected_type: &Type, must_be_constant: u1) void +analyze_binary_type = fn (module: &Module, left: &Value, right: &Value, is_boolean: u1, expected_type: &Type, must_be_constant: u1, is_sub: u1) void { >left_constant = value_is_constant(left); >right_constant = value_is_constant(right); + >left_receives_type = receives_type(left); + >right_receives_type = receives_type(right); - if (!expected_type) + if (!expected_type and left_receives_type and right_receives_type) { - if (left_constant != zero and right_constant) + if (left.id == right.id) { - if (left.type == zero and right.type == zero) + switch (left.id) { - >string_literal = left.id == .string_literal and right.id == .string_literal; - - if (string_literal) + .string_literal => { - #trap(); - } - else + expected_type = get_slice_type(module, uint8(module)); + }, + else => { report_error(); - } + }, } } - } - - if (is_boolean or expected_type == zero) - { - if (left_constant) - { - analyze_type(module, right, zero, { .must_be_constant = must_be_constant, zero }); - analyze_type(module, left, right.type, { .must_be_constant = must_be_constant, zero }); - } else { - analyze_type(module, left, zero, { .must_be_constant = must_be_constant, zero }); - analyze_type(module, right, left.type, { .must_be_constant = must_be_constant, zero }); + report_error(); } } - else if (!is_boolean and expected_type != zero) + + if (!left_receives_type and !right_receives_type) { + analyze_type(module, left, zero, { .must_be_constant = must_be_constant, zero }); + analyze_type(module, right, zero, { .must_be_constant = must_be_constant, zero }); + } + else if (left_receives_type and !right_receives_type) + { + analyze_type(module, right, zero, { .must_be_constant = must_be_constant, zero }); + analyze_type(module, left, right.type, { .must_be_constant = must_be_constant, zero }); + } + else if (!left_receives_type and right_receives_type) + { + analyze_type(module, left, zero, { .must_be_constant = must_be_constant, zero }); + analyze_type(module, right, left.type, { .must_be_constant = must_be_constant, zero }); + } + else if (left_receives_type != zero and right_receives_type != zero) + { + assert(expected_type != zero); + + if (is_boolean) + { + report_error(); + } + analyze_type(module, left, expected_type, { .must_be_constant = must_be_constant, zero }); analyze_type(module, right, expected_type, { .must_be_constant = must_be_constant, zero }); } else { - report_error(); + unreachable; } assert(left.type != zero); assert(right.type != zero); + + if (expected_type) + { + if (expected_type.id == .integer and left.type.id == .pointer and right.type.id == .pointer and is_sub) + { + check_types(module, left.type, right.type); + } + else if (!is_boolean) + { + typecheck(module, expected_type, left.type); + typecheck(module, expected_type, right.type); + } + } } get_va_list_type = fn (module: &Module) &Type @@ -10120,10 +10266,96 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi >id = value.content.binary.id; >is_boolean = binary_is_boolean(id); - analyze_binary_type(module, left, right, is_boolean, expected_type, analysis.must_be_constant); + >is_sub = id == .sub; + analyze_binary_type(module, left, right, is_boolean, expected_type, analysis.must_be_constant, is_sub); check_types(module, left.type, right.type); - value_type = #select(is_boolean, uint1(module), left.type); + if (is_sub and left.type.id == .pointer and right.type.id == .pointer) + { + assert(left.type == right.type); + + >u64_type = uint64(module); + >s64_type = sint64(module); + + >left_int_from_pointer = new_value(module); + left_int_from_pointer.& = { + .content = { + .unary = { + .value = left, + .id = .int_from_pointer, + }, + }, + .type = u64_type, + .id = .unary, + zero, + }; + + >right_int_from_pointer = new_value(module); + right_int_from_pointer.& = { + .content = { + .unary = { + .value = right, + .id = .int_from_pointer, + }, + }, + .type = u64_type, + .id = .unary, + zero, + }; + + value.type = s64_type; + value.content.binary.left = left_int_from_pointer; + value.content.binary.right = right_int_from_pointer; + + >sub = new_value(module); + sub.& = value.&; + + >size_constant = new_value(module); + assert(left.type.id == .pointer); + + >element_type = left.type.content.pointer.element_type; + size_constant.& = { + .content = { + .unary_type = { + .type = element_type, + .id = .byte_size, + }, + }, + .id = .unary_type, + zero, + }; + + analyze_type(module, size_constant, s64_type, { .must_be_constant = 1, zero }); + + value.& = { + .content = { + .binary = { + .left = sub, + .right = size_constant, + .id = .div, + }, + }, + .id = .binary, + zero, + }; + + if (expected_type) + { + #trap(); + } + else + { + value_type = s64_type; + } + } + else if (is_boolean) + { + value_type = uint1(module); + } + else + { + value_type = left.type; + } }, .unary => { @@ -10285,7 +10517,9 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi >u32_type = uint32(module); resolve_type_in_place(module, u32_type); - // TODO: alloca insert point + >current_function = get_current_function(module); + >old_alloca_insertion_point = current_function.variable.storage.content.function.llvm.alloca_insertion_point; + current_function.variable.storage.content.function.llvm.alloca_insertion_point = LLVMBuildAlloca(module.llvm.builder, u32_type.llvm.abi, "alloca_insertion_point"); >alloca = create_alloca(module, { .type = string_type, @@ -10358,7 +10592,7 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi enum_to_string_function = llvm_function; enum_type.content.enum.enum_to_string_function = enum_to_string_function; - // TODO: alloca insertion point restoration + current_function.variable.storage.content.function.llvm.alloca_insertion_point = old_alloca_insertion_point; } assert(enum_to_string_function != zero); @@ -11332,7 +11566,7 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi analyze_type(module, condition, zero, { .must_be_constant = analysis.must_be_constant, zero }); >is_boolean: u1 = 0; - analyze_binary_type(module, true_value, false_value, is_boolean, expected_type, analysis.must_be_constant); + analyze_binary_type(module, true_value, false_value, is_boolean, expected_type, analysis.must_be_constant, 0); >true_type = true_value.type; >false_type = false_value.type; @@ -11447,6 +11681,14 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); + >current_function = get_current_function(module); + >old_alloca_insertion_point = current_function.variable.storage.content.function.llvm.alloca_insertion_point; + + >u32_type = uint32(module); + resolve_type_in_place(module, u32_type); + + current_function.variable.storage.content.function.llvm.alloca_insertion_point = LLVMBuildAlloca(module.llvm.builder, u32_type.llvm.abi, "alloca_insertion_point"); + >arguments: [2]&LLVMValue = undefined; LLVMGetParams(llvm_function, &arguments[0]); @@ -11668,6 +11910,8 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi enum_type.content.enum.string_to_enum_function = llvm_function; enum_type.content.enum.string_to_enum_struct_type = struct_type; + + current_function.variable.storage.content.function.llvm.alloca_insertion_point = old_alloca_insertion_point; } >struct_type = enum_type.content.enum.string_to_enum_struct_type; @@ -16210,7 +16454,7 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v } else { - analyze_binary_type(module, start, end, 0, zero, 0); + analyze_binary_type(module, start, end, 0, zero, 0, 0); assert(start.type == end.type); local_type = start.type; } @@ -16614,7 +16858,9 @@ emit = fn (module: &Module) void { >e: LLVMIntrinsicIndex = #enum_from_int(i); >name = #enum_name(e); - module.llvm.intrinsic_table[i] = LLVMLookupIntrinsicID(name.pointer, name.length); + >intrinsic_id = LLVMLookupIntrinsicID(name.pointer, name.length); + assert(intrinsic_id != 0); + module.llvm.intrinsic_table[i] = intrinsic_id; } for (i: 0..module.llvm.attribute_table.length) @@ -16954,6 +17200,11 @@ emit = fn (module: &Module) void LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); + >u32_type = uint32(module); + resolve_type_in_place(module, u32_type); + + global.variable.storage.content.function.llvm.alloca_insertion_point = LLVMBuildAlloca(module.llvm.builder, u32_type.llvm.abi, "alloca_insertion_point"); + >return_abi = &function_type.abi.return_abi; switch (return_abi.flags.kind) { @@ -17370,6 +17621,8 @@ emit = fn (module: &Module) void LLVMBuildRet(module.llvm.builder, return_value); } + LLVMInstructionEraseFromParent(global.variable.storage.content.function.llvm.alloca_insertion_point); + // END OF SCOPE module.current_function = zero; } @@ -17797,6 +18050,7 @@ names: [_][]u8 = "min_max", "field_parent_pointer", "leading_trailing_zeroes", + "pointer_sub", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/emitter.cpp b/src/emitter.cpp index 9cfad5e..dfcc596 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -43,6 +43,7 @@ enum class TypeKind abi, memory, }; + 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); @@ -340,6 +341,7 @@ fn bool is_arbitrary_bit_integer(Type* type) { case TypeId::integer: switch (type->integer.bit_count) { + case 1: case 8: case 16: case 32: @@ -1622,7 +1624,10 @@ fn AbiInformation abi_system_v_get_indirect_result(Module* module, Type* type, u { if (is_promotable_integer_type_for_abi(type)) { - trap(); + return abi_system_v_get_extend({ + .semantic_type = type, + .sign = type_is_signed(type), + }); } else { @@ -1654,7 +1659,6 @@ fn AbiInformation abi_system_v_get_indirect_result(Module* module, Type* type, u } } - struct AbiSystemVClassifyArgumentTypeOptions { u32 available_gpr; @@ -1949,7 +1953,7 @@ struct AllocaOptions fn Global* get_current_function(Module* module) { - Global* parent_function_global; + Global* parent_function_global = 0; if (module->current_function) { parent_function_global = module->current_function; @@ -1958,10 +1962,6 @@ fn Global* get_current_function(Module* module) { parent_function_global = module->current_macro_instantiation->instantiation_function; } - else - { - report_error(); - } return parent_function_global; } @@ -9375,6 +9375,7 @@ void emit(Module* module) abi_argument_type_count += 1; } + for (u64 i = 0; i < semantic_argument_count; i += 1) { auto& abi = function_type->abi.argument_abis[i]; -- 2.43.0