From 9d6d3935e3404576a9c1c5ea1c60b8d9d554def7 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 | 1071 ++++++++++++++++++++++++++++++++++++++++++++-- src/emitter.cpp | 20 +- src/llvm.cpp | 10 - src/llvm.hpp | 2 - 4 files changed, 1048 insertions(+), 55 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index b7e6b00..2b87340 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -847,7 +847,8 @@ LLVMAttributeIndex = enum u32 LLVMLinkage = enum u32 { - external, // Externally visible function available_externally, + external, // Externally visible function + available_externally, link_once_any, // Keep one copy of function when linking (inline)*/ link_once_odr, // Same, but only replaced by something @@ -1315,11 +1316,19 @@ TypeFunction = struct next: &Type, } +TypeArray = struct +{ + element_type: &Type, + element_count: u64, + next: &Type, +} + TypeContent = union { integer: TypeInteger, function: TypeFunction, pointer: TypePointer, + array: TypeArray, } TypeLLVM = struct @@ -1417,6 +1426,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 +1462,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 +1528,11 @@ ValueId = enum variable, local, unary_type, + macro_reference, + call, + array_initialization, + array_expression, + slice_expression, } ValueConstantInteger = struct @@ -1673,6 +1692,32 @@ ValueBinary = struct id: BinaryId, } +ValueCall = struct +{ + callable: &Value, + arguments: []&Value, + function_type: &Type, +} + +ValueArrayInitialization = struct +{ + values: []&Value, + is_constant: u1, +} + +ValueArrayExpression = struct +{ + array_like: &Value, + index: &Value, +} + +ValueSliceExpression = struct +{ + array_like: &Value, + start: &Value, + end: &Value, +} + ValueContent = union { constant_integer: ValueConstantInteger, @@ -1681,6 +1726,10 @@ ValueContent = union binary: ValueBinary, variable: &Variable, unary_type: ValueUnaryType, + call: ValueCall, + array_initialization: ValueArrayInitialization, + array_expression: ValueArrayExpression, + slice_expression: ValueSliceExpression, } ValueKind = enum @@ -2055,6 +2104,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; @@ -2071,6 +2121,7 @@ LLVMICmpPredicate = enum u32 [extern] LLVMBuildRetVoid = fn [cc(c)] (builder: &LLVMBuilder) &LLVMValue; [extern] LLVMBuildBr = fn [cc(c)] (builder: &LLVMBuilder, target_block: &LLVMBasicBlock) &LLVMValue; +[extern] LLVMBuildCondBr = fn [cc(c)] (builder: &LLVMBuilder, condition: &LLVMValue, taken_block: &LLVMBasicBlock, not_taken_block: &LLVMBasicBlock) &LLVMValue; [extern] LLVMBuildIntCast2 = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, signed: s32, name: &u8) &LLVMValue; [extern] LLVMBuildNeg = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, name: &u8) &LLVMValue; @@ -2094,10 +2145,13 @@ 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; +[extern] LLVMConstNull = fn [cc(c)] (type: &LLVMType) &LLVMValue; [extern] LLVMConstInt = fn [cc(c)] (type: &LLVMType, value: u64, is_signed: s32) &LLVMValue; [extern] LLVMConstNeg = fn [cc(c)] (value: &LLVMValue) &LLVMValue; @@ -2106,6 +2160,10 @@ LLVMICmpPredicate = enum u32 [extern] LLVMGetFirstUse = fn [cc(c)] (value: &LLVMValue) &LLVMUse; [extern] LLVMGetNextUse = fn [cc(c)] (use: &LLVMUse) &LLVMUse; [extern] LLVMGetUser = fn [cc(c)] (use: &LLVMUse) &LLVMValue; +[extern] LLVMGetFirstInstruction = fn [cc(c)] (basic_block: &LLVMBasicBlock) &LLVMValue; +[extern] LLVMReplaceAllUsesWith = fn [cc(c)] (old_value: &LLVMValue, new_value: &LLVMValue) void; + +[extern] LLVMDeleteBasicBlock = fn [cc(c)] (basic_block: &LLVMBasicBlock) void; [extern] LLVMIsABranchInst = fn [cc(c)] (value: &LLVMValue) &LLVMValue; [extern] LLVMIsConditional = fn [cc(c)] (value: &LLVMValue) s32; @@ -2129,6 +2187,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 +2380,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 @@ -2798,6 +2868,11 @@ TypeKeyword = enum enum_array, } +get_slice_type = fn (module: &Module, element_type: &Type) &Type +{ + #trap(); +} + parse_type = fn (module: &Module, scope: &Scope) &Type { >start_character = module.content[module.offset]; @@ -2901,6 +2976,69 @@ parse_type = fn (module: &Module, scope: &Scope) &Type } else if (start_character == left_bracket) { + module.offset += 1; + skip_space(module); + + >is_slice = consume_character_if_match(module, right_bracket); + if (is_slice) + { + skip_space(module); + >element_type = parse_type(module, scope); + >slice_type = get_slice_type(module, element_type); + return slice_type; + } + else + { + >checkpoint = get_checkpoint(module); + >length_inferred: u1 = 0; + if (consume_character_if_match(module, '_')) + { + skip_space(module); + length_inferred = consume_character_if_match(module, ']'); + } + + >length_value: &Value = zero; + >element_count: u64 = 0; + >resolved: u1 = 0; + + if (!length_inferred) + { + #trap(); + } + + skip_space(module); + + >element_type = parse_type(module, scope); + + if (length_inferred) + { + assert(!length_value); + >result = new_type(module, { + .content = { + .array = { + .element_type = element_type, + .element_count = 0, + zero, + }, + }, + .id = .array, + .name = "", + .scope = element_type.scope, + zero, + }); + return result; + } + else + { + if (!resolved) + { + report_error(); + } + + #trap(); + } + #trap(); + } #trap(); } else if (start_character == '#') @@ -3233,7 +3371,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 +3606,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 => { @@ -3689,7 +3888,70 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .left_bracket => { - #trap(); + >element_count: u64 = 0; + >value_buffer: [64]&Value = undefined; + + skip_space(module); + + >checkpoint = get_checkpoint(module); + >is_aggregate_initialization: u1 = 0; + + if (consume_character_if_match(module, '.')) + { + >identifier = parse_identifier(module); + + skip_space(module); + is_aggregate_initialization = consume_character_if_match(module, '='); + + if (!is_aggregate_initialization) + { + if (!consume_character_if_match(module, ',')) + { + report_error(); + } + } + } + + set_checkpoint(module, checkpoint); + + if (is_aggregate_initialization) + { + #trap(); + } + else + { + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + + >value = parse_value(module, scope, zero); + value_buffer[element_count] = value; + element_count += 1; + + consume_character_if_match(module, ','); + } + + >values = arena_allocate_slice[&Value](module.arena, element_count); + memcpy(#pointer_cast(values.pointer), #pointer_cast(&value_buffer), element_count * #byte_size(&Value)); + + result = new_value(module); + + result.& = { + .content = { + .array_initialization = { + .values = values, + .is_constant = 0, // This is analyzed later + }, + }, + .id = .array_initialization, + zero, + }; + } }, .dot => { @@ -3804,6 +4066,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,15 +4184,116 @@ parse_right = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .pointer_dereference => { - #trap(); + result = new_value(module); + result.& = { + .content = { + .unary = { + .value = left, + .id = .dereference, + }, + }, + .id = .unary, + .kind = .right, + zero, + }; }, .left_parenthesis => { - #trap(); + result = new_value(module); + + switch (left.id) + { + .macro_reference => + { + #trap(); + }, + else => + { + >arguments = parse_call_arguments(module, scope); + result.& = { + .content = { + .call = { + .callable = left, + .arguments = arguments, + zero, + }, + }, + .id = .call, + .kind = .right, + zero, + }; + }, + } }, .left_bracket => { - #trap(); + skip_space(module); + + result = new_value(module); + + if (left.id == .macro_reference) + { + #trap(); + } + else + { + left.kind = .left; + + >start_value: &Value = zero; + >start = !(module.content[module.offset] == '.' and module.content[module.offset + 1] == '.'); + if (start) + { + start_value = parse_value(module, scope, zero); + } + + >is_array = consume_character_if_match(module, right_bracket); + + if (is_array) + { + if (!start_value) + { + report_error(); + } + + >index = start_value; + result.& = { + .content = { + .array_expression = { + .array_like = left, + .index = index, + }, + }, + .id = .array_expression, + .kind = builder.kind, + zero, + }; + } + else + { + expect_character(module, '.'); + expect_character(module, '.'); + + >end_value: &Value = zero; + + if (!consume_character_if_match(module, right_bracket)) + { + end_value = parse_value(module, scope, zero); + expect_character(module, right_bracket); + } + + result.& = { + .content = { + .slice_expression = { + .array_like = left, + .start = start_value, + .end = end_value, + }, + }, + .id = .slice_expression, + zero, + }; + } + } }, .dot => { @@ -3978,6 +4376,8 @@ StatementStartKeyword = enum continue, } +parse_block = fn (module: &Module, parent_scope: &Scope) &Block; + parse_statement = fn (module: &Module, scope: &Scope) &Statement { >require_semicolon: u1 = 1; @@ -4038,12 +4438,19 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement }, '#' => { - statement.content.expression = parse_value(module, scope, zero); + statement.content = { + .expression = parse_value(module, scope, zero), + }; statement.id = .expression; }, left_brace => { - #trap(); + >block = parse_block(module, scope); + statement.content = { + .block = block, + }; + statement.id = .block; + require_semicolon = 0; }, else => { @@ -4073,7 +4480,51 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement }, .if => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + >condition = parse_value(module, scope, zero); + + skip_space(module); + expect_character(module, right_parenthesis); + skip_space(module); + + >if_statement = parse_statement(module, scope); + + skip_space(module); + + >is_else: u1 = 0; + >else_statement: &Statement = zero; + + if (is_identifier_start(module.content[module.offset])) + { + >checkpoint = get_checkpoint(module); + >identifier = parse_identifier(module); + + is_else = string_equal(identifier, "else"); + + if (is_else) + { + skip_space(module); + else_statement = parse_statement(module, scope); + } + else + { + set_checkpoint(module, checkpoint); + } + } + + require_semicolon = 0; + + statement.content = { + .if = { + .condition = condition, + .if = if_statement, + .else = else_statement, + }, + }; + statement.id = .if; }, .for => { @@ -4601,6 +5052,10 @@ resolve_type_in_place_abi = fn (module: &Module, type: &Type) void { result = module.llvm.pointer_type; }, + .array => + { + #trap(); + }, else => { #trap(); @@ -4936,6 +5391,54 @@ abi_system_v_classify_return_type = fn (module: &Module, semantic_return_type: & return result; } +format_integer_decimal = fn (buffer: []u8, v: u64) u64 +{ + >byte_count: u64 = 0; + >value = v; + + if (value != 0) + { + >reverse_buffer: [64]u8 = undefined; + >reverse_index: u64 = 0; + + while (value != 0) + { + >digit_value: u8 = #truncate(value % 10); + >ascii_character = digit_value + '0'; + value /= 10; + reverse_buffer[reverse_index] = ascii_character; + reverse_index += 1; + } + + while (reverse_index != 0) + { + reverse_index -= 1; + buffer[byte_count] = reverse_buffer[reverse_index]; + byte_count += 1; + } + } + else + { + buffer[0] = '0'; + byte_count = 1; + } + + return byte_count; +} + +array_name = fn (module: &Module, element_type: &Type, element_count: u64) []u8 +{ + >buffer: [512]u8 = undefined; + >buffer_slice = buffer[..]; + + >i: u64 = 0; + + buffer[i] = left_bracket; + i += 1; + + i += format_integer_decimal(buffer_slice[i..], element_count); +} + LLVMAttributeCallback = typealias fn [cc(c)] (&LLVMValue, u32, &LLVMAttribute) void; add_enum_attribute = fn (module: &Module, attribute_index: LLVMAttributeIndex, attribute_value: u64, add_callback: &LLVMAttributeCallback, value: &LLVMValue, index: u32) &LLVMAttribute @@ -5023,7 +5526,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 +5535,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; @@ -5294,7 +5797,15 @@ TypeAnalysis = struct indexing_type: &Type, must_be_constant: u1, } + analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysis: TypeAnalysis) void; +emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_constant: u1) void; + +analyze_value = fn (module: &Module, value: &Value, expected_type: &Type, type_kind: TypeKind, must_be_constant: u1) void +{ + analyze_type(module, value, expected_type, { .must_be_constant = must_be_constant, zero }); + emit_value(module, value, type_kind, must_be_constant); +} analyze_binary_type = fn (module: &Module, left: &Value, right: &Value, is_boolean: u1, expected_type: &Type, must_be_constant: u1) void { @@ -5488,7 +5999,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 +6054,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 +6146,144 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi typecheck(module, expected_type, value_type); }, + .call => + { + >call = &value.content.call; + >callable = call.callable; + analyze_type(module, callable, zero, { .must_be_constant = analysis.must_be_constant, zero }); + + >function_type: &Type = zero; + + switch (callable.id) + { + .variable => + { + >variable = callable.content.variable; + >variable_type = variable.type; + + switch (variable_type.id) + { + .function => + { + function_type = variable_type; + }, + .pointer => + { + >element_type = resolve_alias(module, variable_type.content.pointer.element_type); + + switch (element_type.id) + { + .function => + { + function_type = element_type; + }, + else => { report_error(); }, + } + }, + else => + { + report_error(); + }, + } + }, + else => + { + report_error(); + }, + } + + assert(function_type != zero); + assert(function_type.id == .function); + call.function_type = function_type; + + >semantic_argument_types = function_type.content.function.base.semantic_argument_types; + >call_arguments = call.arguments; + + if (function_type.content.function.base.is_variable_argument) + { + if (call_arguments.length < semantic_argument_types.length) + { + report_error(); + } + } + else + { + if (call_arguments.length != semantic_argument_types.length) + { + report_error(); + } + } + + for (i: 0..semantic_argument_types.length) + { + >argument_type = semantic_argument_types[i]; + >call_argument = call_arguments[i]; + analyze_type(module, call_argument, argument_type, { .must_be_constant = analysis.must_be_constant, zero }); + check_types(module, argument_type, call_argument.type); + } + + for (i: semantic_argument_types.length..call_arguments.length) + { + >call_argument = call_arguments[i]; + analyze_type(module, call_argument, zero, { .must_be_constant = analysis.must_be_constant, zero }); + } + + >semantic_return_type = function_type.content.function.base.semantic_return_type; + + typecheck(module, expected_type, semantic_return_type); + value_type = semantic_return_type; + }, + .array_initialization => + { + >values = value.content.array_initialization.values; + + if (expected_type != zero) + { + if (expected_type.id != .array) + { + report_error(); + } + + >element_type = expected_type.content.array.element_type; + + if (expected_type.content.array.element_count == 0) + { + // TODO: use existing types? + >element_count = values.length; + expected_type.content.array.element_count = element_count; + assert(string_equal(expected_type.name, "")); + expected_type.name = array_name(module, element_type, element_count); + } + else + { + if (expected_type.content.array.element_count != values.length) + { + report_error(); + } + } + + >is_constant: u1 = 1; + + for (value: values) + { + analyze_type(module, value, element_type, { .must_be_constant = analysis.must_be_constant, zero }); + is_constant = is_constant and value_is_constant(value); + } + + value.content.array_initialization.is_constant = is_constant; + + if (value.kind == .left) // TODO: possible? + { + report_error(); + } + + value_type = expected_type; + } + else + { + #trap(); + } + }, else => { #trap(); @@ -5728,6 +6405,239 @@ emit_binary = fn (module: &Module, left: &LLVMValue, left_type: &Type, right: &L } } +type_is_abi_equal = fn (module: &Module, a: &Type, b: &Type) u1 +{ + resolve_type_in_place(module, a); + resolve_type_in_place(module, b); + + >result = a == b; + if (!result) + { + result = a.llvm.abi == b.llvm.abi; + } + + return result; +} + +emit_call = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type: &Type) &LLVMValue +{ + assert(value.id == .call); + + >call = &value.content.call; + + >raw_function_type = call.function_type; + >callable = call.callable; + >call_arguments = call.arguments; + + >llvm_callable: &LLVMValue = zero; + + switch (callable.id) + { + .variable => + { + >variable = callable.content.variable; + >variable_type = variable.type; + >llvm_value = variable.storage.llvm; + + switch (variable_type.id) + { + .pointer => + { + >element_type = resolve_alias(module, variable_type.content.pointer.element_type); + + switch (element_type.id) + { + .function => + { + llvm_callable = create_load(module, { + .type = get_pointer_type(module, raw_function_type), + .pointer = llvm_value, + zero, + }); + }, + else => { report_error(); }, + } + }, + .function => { llvm_callable = llvm_value; }, + } + }, + else => { report_error(); }, + } + + assert(llvm_callable != zero); + + >llvm_abi_argument_value_buffer: [64]&LLVMValue = undefined; + >llvm_abi_argument_type_buffer: [64]&LLVMType = undefined; + >abi_argument_type_buffer: [64]&Type = undefined; + >argument_abi_buffer: [64]AbiInformation = undefined; + >llvm_abi_argument_type_buffer_slice = llvm_abi_argument_type_buffer[..]; + >abi_argument_type_buffer_slice = abi_argument_type_buffer[..]; + + >abi_argument_count: u16 = 0; + + >uses_in_alloca: u1 = 0; + if (uses_in_alloca) + { + #trap(); + } + + >llvm_indirect_return_value: &LLVMValue = zero; + + >return_abi = &raw_function_type.content.function.abi.return_abi; + >return_abi_kind = return_abi.flags.kind; + + switch (return_abi_kind) + { + .indirect, + .in_alloca, + .coerce_and_expand, + => + { + #trap(); + }, + else => {}, + } + + >available_registers = raw_function_type.content.function.abi.available_registers; + >declaration_semantic_argument_types = raw_function_type.content.function.base.semantic_argument_types; + + for (call_argument_index: 0..call_arguments.length) + { + >is_named_argument = call_argument_index < declaration_semantic_argument_types.length; + >semantic_call_argument_value = call_arguments[call_argument_index]; + + >semantic_argument_type: &Type = undefined; + >argument_abi: AbiInformation = undefined; + + if (is_named_argument) + { + argument_abi = raw_function_type.content.function.abi.argument_abis[call_argument_index]; + semantic_argument_type = argument_abi.semantic_type; + } + else + { + #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); + + if (return_abi.attributes.direct.offset == 0 and type_is_abi_equal(module, return_abi.semantic_type, coerce_to_type)) + { + >evaluation_kind = get_evaluation_kind(coerce_to_type); + + switch (evaluation_kind) + { + .scalar => { return llvm_call; }, + .aggregate => {}, + .complex => { unreachable; }, + } + + #trap(); + } + + #trap(); + }, + .indirect => + { + assert(llvm_indirect_return_value != zero); + return llvm_indirect_return_value; + }, + else => { unreachable; }, + } +} + emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_constant: u1) void { assert(value.type != zero); @@ -5826,6 +6736,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 +6860,10 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con }, } }, + .call => + { + llvm_value = emit_call(module, value, zero, zero); + }, else => { #trap(); @@ -6037,6 +6980,27 @@ emit_local = fn (module: &Module, local: &Local) void } } +emit_condition = fn (module: &Module, condition: &Value) &LLVMValue +{ + >llvm_condition = condition.llvm; + >condition_type = condition.type; + assert(llvm_condition != zero); + assert(condition_type != zero); + + assert(condition_type.id == .integer or condition_type.id == .pointer); + + if (!(condition_type.id == .integer and condition_type.content.integer.bit_count == 1)) + { + llvm_condition = LLVMBuildICmp(module.llvm.builder, .ne, llvm_condition, LLVMConstNull(condition_type.llvm.abi), ""); + } + + assert(llvm_condition != zero); + + return llvm_condition; +} + +analyze_block = fn (module: &Module, block: &Block) void; + analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) void { >parent_function_global: &Global = undefined; @@ -6139,6 +7103,46 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v emit_local(module, local); emit_assignment(module, local.variable.storage.llvm, local.variable.storage.type, local.initial_value); }, + .if => + { + >condition = statement.content.if.condition; + >taken_statement = statement.content.if.if; + >not_taken_statement = statement.content.if.else; + + >taken_block = llvm_context_create_basic_block(module.llvm.context, "if.taken", llvm_function); + >not_taken_block = llvm_context_create_basic_block(module.llvm.context, "if.not_taken", llvm_function); + >exit_block = llvm_context_create_basic_block(module.llvm.context, "if.exit", llvm_function); + + analyze_value(module, condition, zero, .abi, 0); + >llvm_condition = emit_condition(module, condition); + + LLVMBuildCondBr(module.llvm.builder, llvm_condition, taken_block, not_taken_block); + LLVMPositionBuilderAtEnd(module.llvm.builder, taken_block); + + analyze_statement(module, scope, taken_statement); + + if (LLVMGetInsertBlock(module.llvm.builder) != zero) + { + LLVMBuildBr(module.llvm.builder, exit_block); + } + + LLVMPositionBuilderAtEnd(module.llvm.builder, not_taken_block); + if (not_taken_statement != zero) + { + analyze_statement(module, scope, not_taken_statement); + } + + if (LLVMGetInsertBlock(module.llvm.builder) != zero) + { + LLVMBuildBr(module.llvm.builder, exit_block); + } + + LLVMPositionBuilderAtEnd(module.llvm.builder, exit_block); + }, + .block => + { + analyze_block(module, statement.content.block); + }, else => { #trap(); @@ -6179,20 +7183,6 @@ emit_block = fn (module: &Module, basic_block: &LLVMBasicBlock) void LLVMPositionBuilderAtEnd(module.llvm.builder, basic_block); } -type_is_abi_equal = fn (module: &Module, a: &Type, b: &Type) u1 -{ - resolve_type_in_place(module, a); - resolve_type_in_place(module, b); - - >result = a == b; - if (!result) - { - result = a.llvm.abi == b.llvm.abi; - } - - return result; -} - LLVMObjectGenerate = struct { path: []u8, @@ -6624,7 +7614,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, @@ -6773,7 +7763,15 @@ emit = fn (module: &Module) void if (current_basic_block) { assert(!LLVMGetBasicBlockTerminator(current_basic_block)); - #trap(); + if (!LLVMGetFirstInstruction(current_basic_block) or !LLVMGetFirstUse(#pointer_cast(current_basic_block))) + { + LLVMReplaceAllUsesWith(#pointer_cast(return_block), #pointer_cast(current_basic_block)); + LLVMDeleteBasicBlock(return_block); + } + else + { + #trap(); + } } else { @@ -7184,6 +8182,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..656684f 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -3231,6 +3231,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal if (expected_type->array.element_count == 0) { + // TODO: use existing types? expected_type->array.element_count = values.length; assert(expected_type->name.equal(string_literal(""))); expected_type->name = array_name(module, expected_type->array.element_type, expected_type->array.element_count); @@ -5081,11 +5082,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) @@ -8036,7 +8032,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto condition = statement->if_st.condition; analyze_value(module, condition, 0, TypeKind::abi, false); - auto llvm_condition = emit_condition(module, statement->if_st.condition); + auto llvm_condition = emit_condition(module, condition); LLVMBuildCondBr(module->llvm.builder, llvm_condition, taken_block, not_taken_block); LLVMPositionBuilderAtEnd(module->llvm.builder, taken_block); @@ -8125,12 +8121,12 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 LLVMBuildBr(module->llvm.builder, entry_block); - if (llvm_value_use_empty((LLVMValueRef)body_block)) + if (!LLVMGetFirstUse((LLVMValueRef)body_block)) { trap(); } - if (llvm_value_use_empty((LLVMValueRef)exit_block)) + if (!LLVMGetFirstUse((LLVMValueRef)exit_block)) { trap(); } @@ -8621,8 +8617,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), }); @@ -9552,7 +9550,7 @@ void emit(Module* module) { assert(!LLVMGetBasicBlockTerminator(current_basic_block)); - if (llvm_basic_block_is_empty(current_basic_block) || llvm_value_use_empty((LLVMValueRef)current_basic_block)) + if (!LLVMGetFirstInstruction(current_basic_block) || !LLVMGetFirstUse((LLVMValueRef)current_basic_block)) { LLVMReplaceAllUsesWith((LLVMValueRef)return_block, (LLVMValueRef)current_basic_block); LLVMDeleteBasicBlock(return_block); @@ -9633,7 +9631,7 @@ void emit(Module* module) auto alloca = LLVMGetOperand(store, 1); assert(alloca == return_alloca); LLVMInstructionEraseFromParent(store); - assert(llvm_value_use_empty(alloca)); + assert(!LLVMGetFirstUse(alloca)); LLVMInstructionEraseFromParent(alloca); } else diff --git a/src/llvm.cpp b/src/llvm.cpp index 8520149..1fb2844 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -169,16 +169,6 @@ EXPORT LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LL return wrap(store); } -EXPORT bool llvm_value_use_empty(LLVMValueRef value) -{ - return llvm::unwrap(value)->use_empty(); -} - -EXPORT bool llvm_basic_block_is_empty(LLVMBasicBlockRef basic_block) -{ - return llvm::unwrap(basic_block)->empty(); -} - EXPORT LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef b, LLVMTypeRef type, unsigned address_space, u32 alignment, String name) { auto& builder = *llvm::unwrap(b); diff --git a/src/llvm.hpp b/src/llvm.hpp index f2faa1e..f267a49 100644 --- a/src/llvm.hpp +++ b/src/llvm.hpp @@ -133,10 +133,8 @@ extern "C" LLVMBasicBlockRef llvm_context_create_basic_block(LLVMContextRef cont extern "C" LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMValueRef before, LLVMThreadLocalMode thread_local_mode, unsigned address_space, bool externally_initialized); extern "C" LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef builder, LLVMTypeRef type, unsigned address_space, u32 alignment, String name); -extern "C" bool llvm_basic_block_is_empty(LLVMBasicBlockRef basic_block); extern "C" LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LLVMValueRef ra, LLVMTypeRef et); -extern "C" bool llvm_value_use_empty(LLVMValueRef value); extern "C" bool llvm_function_verify(LLVMValueRef function_value, String* error_message); extern "C" bool llvm_module_verify(LLVMModuleRef m, String* error_message);