From 67983af01517b0e6e58468f2adba141ba062d4ce Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Sun, 8 Jun 2025 09:07:52 -0600 Subject: [PATCH] wip --- src/compiler.bbb | 545 ++++++++++++++++++++++++++++++++++++++++++++++- src/emitter.cpp | 9 +- 2 files changed, 537 insertions(+), 17 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index b7e6b00..1b44fe9 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1417,6 +1417,10 @@ get_byte_size = fn (type: &Type) u64 assert(byte_count == 1 or byte_count == 2 or byte_count == 4 or byte_count == 8 or byte_count == 16); return byte_count; }, + .pointer => + { + return 8; + }, else => { #trap(); @@ -1449,6 +1453,7 @@ get_byte_alignment = fn (type: &Type) u32 assert(aligned_byte_count == 1 or aligned_byte_count == 2 or aligned_byte_count == 4 or aligned_byte_count == 8 or aligned_byte_count == 16); return aligned_byte_count; }, + .pointer => { return 8; }, else => { #trap(); @@ -1514,6 +1519,8 @@ ValueId = enum variable, local, unary_type, + macro_reference, + call, } ValueConstantInteger = struct @@ -1673,6 +1680,13 @@ ValueBinary = struct id: BinaryId, } +ValueCall = struct +{ + callable: &Value, + arguments: []&Value, + function_type: &Type, +} + ValueContent = union { constant_integer: ValueConstantInteger, @@ -1681,6 +1695,7 @@ ValueContent = union binary: ValueBinary, variable: &Variable, unary_type: ValueUnaryType, + call: ValueCall, } ValueKind = enum @@ -2055,6 +2070,7 @@ LLVMICmpPredicate = enum u32 [extern] LLVMCreateTypeAttribute = fn [cc(c)] (context: &LLVMContext, attribute_id: LLVMAttributeId, type: &LLVMType) &LLVMAttribute; [extern] LLVMCreateStringAttribute = fn [cc(c)] (context: &LLVMContext, attribute_key_pointer: &u8, attribute_key_length: u64, attribute_value_pointer: &u8, attribute_value_length: u64) &LLVMAttribute; [extern] LLVMAddAttributeAtIndex = fn [cc(c)] (value: &LLVMValue, index: u32, attribute: &LLVMAttribute) void; +[extern] LLVMAddCallSiteAttribute = fn [cc(c)] (value: &LLVMValue, index: u32, attribute: &LLVMAttribute) void; [extern] LLVMGetParams = fn [cc(c)] (function: &LLVMValue, parameter_pointer: &&LLVMValue) void; @@ -2094,6 +2110,8 @@ LLVMICmpPredicate = enum u32 [extern] LLVMBuildSRem = fn [cc(c)] (builder: &LLVMBuilder, left: &LLVMValue, right: &LLVMValue, name: &u8) &LLVMValue; [extern] LLVMBuildURem = fn [cc(c)] (builder: &LLVMBuilder, left: &LLVMValue, right: &LLVMValue, name: &u8) &LLVMValue; +[extern] LLVMBuildCall2 = fn [cc(c)] (builder: &LLVMBuilder, function_type: &LLVMType, function_value: &LLVMValue, argument_pointer: &&LLVMValue, argument_count: u32, name: &u8) &LLVMValue; + [extern] LLVMGetInsertBlock = fn [cc(c)] (builder: &LLVMBuilder) &LLVMBasicBlock; [extern] LLVMGetBasicBlockTerminator = fn [cc(c)] (basic_block: &LLVMBasicBlock) &LLVMValue; [extern] LLVMGetBasicBlockParent = fn [cc(c)] (basic_block: &LLVMBasicBlock) &LLVMValue; @@ -2129,6 +2147,7 @@ LLVMICmpPredicate = enum u32 [extern] llvm_module_create_function = fn [cc(c)] (module: &LLVMModule, function_type: &LLVMType, linkage_type: LLVMLinkage, address_space: u32, name: []u8) &LLVMValue; [extern] LLVMSetFunctionCallConv = fn [cc(c)] (function: &LLVMValue, calling_convention: LLVMCallingConvention) void; +[extern] LLVMSetInstructionCallConv = fn [cc(c)] (call: &LLVMValue, calling_convention: LLVMCallingConvention) void; [extern] llvm_module_run_optimization_pipeline = fn [cc(c)] (module: &LLVMModule, target_machine: &LLVMTargetMachine, options: &LLVMOptimizationOptions) void; [extern] llvm_module_run_code_generation_pipeline = fn [cc(c)] (module: &LLVMModule, target_machine: &LLVMTargetMachine, options: &LLVMCodeGenerationOptions) LLVMCodeGenerationResult; @@ -2321,8 +2340,19 @@ Statement = struct scope_to_block = fn (scope: &Scope) &Block { assert(scope.kind == .local); - >result: &Block = #field_parent_pointer(scope, "scope"); - return result; + return #field_parent_pointer(scope, "scope"); +} + +scope_to_function = fn (scope: &Scope) &ValueFunction +{ + assert(scope.kind == .function); + return #field_parent_pointer(scope, "scope"); +} + +scope_to_module = fn (scope: &Scope) &Module +{ + assert(scope.kind == .global); + return #field_parent_pointer(scope, "scope"); } new_local = fn (module: &Module, scope: &Scope) &Local @@ -3233,7 +3263,38 @@ tokenize = fn (module: &Module) Token }, '.' => { - #trap(); + >id: TokenId = undefined; + >next_ch = module.content[start_index + 1]; + switch (next_ch) + { + else => { id = .dot; }, + '&' => { id = .pointer_dereference; }, + '.' => + { + switch (module.content[start_index + 2]) + { + '.' => { id = .triple_dot; }, + else => { id = .double_dot; }, + } + }, + } + + >add: u64 = undefined; + + switch (id) + { + .dot => { add = 1; }, + .double_dot, .pointer_dereference => { add = 2; }, + .triple_dot => { add = 3; }, + else => { unreachable; }, + } + + module.offset += add; + + token = { + .id = id, + zero, + }; }, '"' => { @@ -3437,11 +3498,41 @@ reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: [ { .global => { - #trap(); + assert(module == scope_to_module(scope)); + + >global = module.first_global; + + while (global != zero) + { + if (string_equal(identifier, global.variable.name)) + { + variable = &global.variable; + break; + } + + global = global.next; + } + + >macro_declaration = module.first_macro_declaration; + + while (macro_declaration != zero) + { + #trap(); + } }, .function => { - #trap(); + assert(scope.parent != zero); + >function = scope_to_function(scope); + + for (&argument: function.arguments) + { + if (string_equal(identifier, argument.variable.name)) + { + variable = &argument.variable; + break; + } + } }, .local => { @@ -3804,6 +3895,41 @@ get_token_precedence = fn (token: Token) Precedence } } +parse_call_arguments = fn (module: &Module, scope: &Scope) []&Value +{ + >arguments: []&Value = zero; + + >argument_count: u64 = 0; + >argument_buffer: [64]&Value = undefined; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_parenthesis)) + { + break; + } + + >argument = parse_value(module, scope, zero); + >argument_index = argument_count; + argument_buffer[argument_index] = argument; + + skip_space(module); + + consume_character_if_match(module, ','); + + argument_count += 1; + } + + if (argument_count != 0) + { + #trap(); + } + + return arguments; +} + parse_right = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value { >left = builder.left; @@ -3887,11 +4013,46 @@ parse_right = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .pointer_dereference => { - #trap(); + result = new_value(module); + result.& = { + .content = { + .unary = { + .value = left, + .id = .dereference, + }, + }, + .id = .unary, + .kind = .right, + zero, + }; }, .left_parenthesis => { - #trap(); + result = new_value(module); + + switch (left.id) + { + .macro_reference => + { + #trap(); + }, + else => + { + >arguments = parse_call_arguments(module, scope); + result.& = { + .content = { + .call = { + .callable = left, + .arguments = arguments, + zero, + }, + }, + .id = .call, + .kind = .right, + zero, + }; + }, + } }, .left_bracket => { @@ -5023,7 +5184,7 @@ add_value_attribute = fn (module: &Module, value: &LLVMValue, index: u32, add_ca AttributeBuildOptions = struct { - return_abi: AbiInformation, + return_abi: &AbiInformation, argument_abis: []AbiInformation, abi_argument_types: []&Type, abi_return_type: &Type, @@ -5032,7 +5193,7 @@ AttributeBuildOptions = struct emit_attributes = fn (module: &Module, value: &LLVMValue, add_callback: &LLVMAttributeCallback, options: AttributeBuildOptions) void { - >return_abi = &options.return_abi; + >return_abi = options.return_abi; >semantic_return_type = return_abi.semantic_type; resolve_type_in_place(module, semantic_return_type); >abi_return_type = options.abi_return_type; @@ -5488,7 +5649,17 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi }, .dereference => { - #trap(); + analyze_type(module, unary_value, zero, { .must_be_constant = analysis.must_be_constant, zero }); + if (value.kind == .left) + { + report_error(); + } + >pointer_type = unary_value.type; + assert(pointer_type.id == .pointer); + >dereference_type = pointer_type.content.pointer.element_type; + + typecheck(module, expected_type, dereference_type); + value_type = dereference_type; }, .int_from_enum => { @@ -5533,6 +5704,24 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi typecheck(module, expected_type, value_type); }, + // TODO: this is the default case + .ampersand + => + { + >is_boolean = unary_is_boolean(unary_id); + if (is_boolean) + { + analyze_type(module, unary_value, zero, { .must_be_constant = analysis.must_be_constant, zero }); + value_type = uint1(module); + } + else + { + analyze_type(module, unary_value, expected_type, { .must_be_constant = analysis.must_be_constant, zero }); + value_type = unary_value.type; + } + + typecheck(module, expected_type, value_type); + }, else => { // TODO @@ -5607,6 +5796,93 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi typecheck(module, expected_type, value_type); }, + .call => + { + >call = &value.content.call; + >callable = call.callable; + analyze_type(module, callable, zero, { .must_be_constant = analysis.must_be_constant, zero }); + + >function_type: &Type = zero; + + switch (callable.id) + { + .variable => + { + >variable = callable.content.variable; + >variable_type = variable.type; + + switch (variable_type.id) + { + .function => + { + function_type = variable_type; + }, + .pointer => + { + >element_type = resolve_alias(module, variable_type.content.pointer.element_type); + + switch (element_type.id) + { + .function => + { + function_type = element_type; + }, + else => { report_error(); }, + } + }, + else => + { + report_error(); + }, + } + }, + else => + { + report_error(); + }, + } + + assert(function_type != zero); + assert(function_type.id == .function); + call.function_type = function_type; + + >semantic_argument_types = function_type.content.function.base.semantic_argument_types; + >call_arguments = call.arguments; + + if (function_type.content.function.base.is_variable_argument) + { + if (call_arguments.length < semantic_argument_types.length) + { + report_error(); + } + } + else + { + if (call_arguments.length != semantic_argument_types.length) + { + report_error(); + } + } + + for (i: 0..semantic_argument_types.length) + { + >argument_type = semantic_argument_types[i]; + >call_argument = call_arguments[i]; + analyze_type(module, call_argument, argument_type, { .must_be_constant = analysis.must_be_constant, zero }); + check_types(module, argument_type, call_argument.type); + } + + for (i: semantic_argument_types.length..call_arguments.length) + { + >call_argument = call_arguments[i]; + analyze_type(module, call_argument, zero, { .must_be_constant = analysis.must_be_constant, zero }); + } + + >semantic_return_type = function_type.content.function.base.semantic_return_type; + + typecheck(module, expected_type, semantic_return_type); + value_type = semantic_return_type; + }, else => { #trap(); @@ -5728,6 +6004,210 @@ emit_binary = fn (module: &Module, left: &LLVMValue, left_type: &Type, right: &L } } +emit_call = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type: &Type) &LLVMValue +{ + assert(value.id == .call); + + >call = &value.content.call; + + >raw_function_type = call.function_type; + >callable = call.callable; + >call_arguments = call.arguments; + + >llvm_callable: &LLVMValue = zero; + + switch (callable.id) + { + .variable => + { + >variable = callable.content.variable; + >variable_type = variable.type; + >llvm_value = variable.storage.llvm; + + switch (variable_type.id) + { + .pointer => + { + >element_type = resolve_alias(module, variable_type.content.pointer.element_type); + + switch (element_type.id) + { + .function => + { + llvm_callable = create_load(module, { + .type = get_pointer_type(module, raw_function_type), + .pointer = llvm_value, + zero, + }); + }, + else => { report_error(); }, + } + }, + .function => { llvm_callable = llvm_value; }, + } + }, + else => { report_error(); }, + } + + assert(llvm_callable != zero); + + >llvm_abi_argument_value_buffer: [64]&LLVMValue = undefined; + >llvm_abi_argument_type_buffer: [64]&LLVMType = undefined; + >abi_argument_type_buffer: [64]&Type = undefined; + >argument_abi_buffer: [64]AbiInformation = undefined; + >llvm_abi_argument_type_buffer_slice = llvm_abi_argument_type_buffer[..]; + >abi_argument_type_buffer_slice = abi_argument_type_buffer[..]; + + >abi_argument_count: u16 = 0; + + >uses_in_alloca: u1 = 0; + if (uses_in_alloca) + { + #trap(); + } + + >llvm_indirect_return_value: &LLVMValue = zero; + + >return_abi = &raw_function_type.content.function.abi.return_abi; + >return_abi_kind = return_abi.flags.kind; + + switch (return_abi_kind) + { + .indirect, + .in_alloca, + .coerce_and_expand, + => + { + #trap(); + }, + else => {}, + } + + >available_registers = raw_function_type.content.function.abi.available_registers; + >declaration_semantic_argument_types = raw_function_type.content.function.base.semantic_argument_types; + + for (call_argument_index: 0..call_arguments.length) + { + >is_named_argument = call_argument_index < declaration_semantic_argument_types.length; + >semantic_call_argument_value = call_arguments[call_argument_index]; + + >semantic_argument_type: &Type = undefined; + >argument_abi: AbiInformation = undefined; + + if (is_named_argument) + { + argument_abi = raw_function_type.content.function.abi.argument_abis[call_argument_index]; + semantic_argument_type = argument_abi.semantic_type; + } + else + { + #trap(); + } + + assert(semantic_argument_type != zero); + + resolve_type_in_place(module, semantic_argument_type); + + if (is_named_argument) + { + // TODO: better slice single statement + >llvm_abi_argument_types = llvm_abi_argument_type_buffer_slice[#extend(argument_abi.abi_start)..#extend(argument_abi.abi_start + argument_abi.abi_count)]; + >destination_abi_argument_types = abi_argument_type_buffer_slice[#extend(argument_abi.abi_start)..#extend(argument_abi.abi_start + argument_abi.abi_count)]; + >source_abi_argument_types = raw_function_type.content.function.abi.abi_argument_types[#extend(argument_abi.abi_start)..#extend(argument_abi.abi_start + argument_abi.abi_count)]; + + for (i: 0..argument_abi.abi_count) + { + llvm_abi_argument_types[i] = source_abi_argument_types[i].llvm.abi; + destination_abi_argument_types[i] = source_abi_argument_types[i]; + } + } + + argument_abi_buffer[call_argument_index] = argument_abi; + + if (argument_abi.padding.type != zero) + { + #trap(); + } + + assert(abi_argument_count == argument_abi.abi_start); + + switch (argument_abi.flags.kind) + { + .direct, + .extend, + => + { + #trap(); + }, + .indirect, + .indirect_aliased, + => + { + #trap(); + }, + .ignore => { unreachable; }, + else => { #trap(); }, // TODO + } + + // TODO: uncomment this + //assert(abi_argument_count == #extend(argument_abi.abi_start + argument_abi.abi_count)); + } + + >declaration_abi_argument_types = raw_function_type.content.function.abi.abi_argument_types; + + if (raw_function_type.content.function.base.is_variable_argument) + { + assert(declaration_abi_argument_types.length <= #extend(abi_argument_count)); + } + else + { + assert(declaration_abi_argument_types.length == #extend(abi_argument_count)); + } + + assert(raw_function_type.llvm.abi != zero); + + >llvm_call = LLVMBuildCall2(module.llvm.builder, raw_function_type.llvm.abi, llvm_callable, &llvm_abi_argument_value_buffer[0], #extend(abi_argument_count), ""); + >llvm_calling_convention: LLVMCallingConvention = undefined; + switch (raw_function_type.content.function.base.calling_convention) + { + .c => { llvm_calling_convention = .c; }, + } + + LLVMSetInstructionCallConv(llvm_call, llvm_calling_convention); + + >argument_abis = argument_abi_buffer[..call_arguments.length]; + + emit_attributes(module, llvm_call, &LLVMAddCallSiteAttribute, { + .return_abi = return_abi, + .argument_abis = argument_abis, + .abi_argument_types = abi_argument_type_buffer[..#extend(abi_argument_count)], + .abi_return_type = raw_function_type.content.function.abi.abi_return_type, + .attributes = zero, + }); + + switch (return_abi_kind) + { + .ignore => + { + assert(return_abi.semantic_type == noreturn_type(module) or return_abi.semantic_type == void_type(module)); + return llvm_call; + }, + .direct, + .extend, + => + { + >coerce_to_type = abi_get_coerce_to_type(return_abi); + #trap(); + }, + .indirect => + { + assert(llvm_indirect_return_value != zero); + return llvm_indirect_return_value; + }, + else => { unreachable; }, + } +} + emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_constant: u1) void { assert(value.type != zero); @@ -5826,6 +6306,35 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con llvm_value = LLVMBuildTrunc(module.llvm.builder, llvm_unary_value, destination_type, ""); }, + .ampersand => + { + assert(resolved_value_type == resolved_unary_type); + llvm_value = llvm_unary_value; + }, + .dereference => + { + switch (value.kind) + { + .right => + { + >pointer_type = unary_value.type; + assert(pointer_type.id == .pointer); + >child_type = resolve_alias(module, pointer_type.content.pointer.element_type); + assert(child_type == resolved_value_type); + >load = create_load(module, { + .type = child_type, + .pointer = unary_value.llvm, + .kind = type_kind, + zero, + }); + llvm_value = load; + }, + .left => + { + #trap(); + } + } + }, else => { #trap(); }, } }, @@ -5921,6 +6430,11 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con }, } }, + .call => + { + emit_call(module, value, zero, zero); + #trap(); + }, else => { #trap(); @@ -6624,7 +7138,7 @@ emit = fn (module: &Module) void LLVMSetFunctionCallConv(llvm_function, llvm_calling_convention); emit_attributes(module, llvm_function, &LLVMAddAttributeAtIndex, { - .return_abi = function_type.abi.return_abi, + .return_abi = &function_type.abi.return_abi, .argument_abis = function_type.abi.argument_abis, .abi_argument_types = function_type.abi.abi_argument_types, .abi_return_type = function_type.abi.abi_return_type, @@ -7184,6 +7698,15 @@ names: [_][]u8 = [ "extend", "integer_max", "integer_hex", + "basic_pointer", + "basic_call", + "basic_branch", + "basic_array", + "basic_enum", + "basic_slice", + "basic_string", + "basic_varargs", + "basic_while", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/emitter.cpp b/src/emitter.cpp index 2aba2c9..97b0a5c 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -5081,11 +5081,6 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, }); } - if (get_byte_size(semantic_argument_type) > 60 && argument_abi.flags.kind != AbiKind::indirect) - { - trap(); - } - resolve_type_in_place(module, semantic_argument_type); if (is_named_argument) @@ -8621,8 +8616,10 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 LLVMValueRef indices[] = { body_index_load, }; + auto gep_type = aggregate_type->structure.fields[0].type->pointer.element_type; + resolve_type_in_place(module, gep_type); auto gep = create_gep(module, { - .type = aggregate_type->structure.fields[0].type->pointer.element_type->llvm.memory, + .type = gep_type->llvm.memory, .pointer = extract_pointer, .indices = array_to_slice(indices), });