From 4269226470453c61f9da83b6cf1e3b36ba0d8ebc Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 12:37:04 -0600 Subject: [PATCH 01/16] Pass 'return_u64_u64' --- src/compiler.bbb | 159 ++++++++++++++++++++++++++++++++++++++++++++--- src/emitter.cpp | 1 - 2 files changed, 150 insertions(+), 10 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 88034a0..aaf5bc5 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -7523,7 +7523,7 @@ abi_system_v_classify_return_type = fn (module: &Module, semantic_return_type: & if (high_type) { - #trap(); + low_type = get_by_value_argument_pair(module, low_type, high_type); } >result = abi_system_v_get_direct(module, { @@ -9361,6 +9361,67 @@ reanalyze_type_as_left_value = fn (module: &Module, value: &Value) void analyze_type(module, value, expected_type, zero); } +type_is_integer_backing = fn (type: &Type) u1 +{ + #trap(); +} + +create_coerced_store = fn (module: &Module, source_value: &LLVMValue, source_type: &Type, destination_value: &LLVMValue, destination_type: &Type, destination_size: u64, destination_volatile: u1) void +{ + >source_size = get_byte_size(source_type); + + // TODO: this smells badly + // destination_type != uint1(module) + if (!type_is_abi_equal(module, source_type, destination_type) and destination_type.id == .struct) + { + #trap(); + } + + >is_scalable: u1 = 0; + + if (is_scalable or source_size <= destination_size) + { + >destination_alignment = get_byte_alignment(destination_type); + + if (source_type.id == .integer and destination_type.id == .pointer and source_size == align_forward(destination_size, #extend(destination_alignment))) + { + #trap(); + } + else if (source_type.id == .struct) + { + >fields = source_type.content.struct.fields; + + for (i: 0..fields.length) + { + >field = &fields[i]; + + >gep = LLVMBuildStructGEP2(module.llvm.builder, source_type.llvm.abi, destination_value, #truncate(i), ""); + >field_value = LLVMBuildExtractValue(module.llvm.builder, source_value, #truncate(i), ""); + + create_store(module, { + .source = field_value, + .destination = gep, + .type = field.type, + .alignment = destination_alignment, + }); + } + } + else + { + #trap(); + } + } + else if (type_is_integer_backing(source_type)) + { + #trap(); + } + else + { + // Coercion through memory + #trap(); + } +} + emit_call = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type: &Type) &LLVMValue { assert(value.id == .call); @@ -9834,14 +9895,98 @@ emit_call = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type switch (evaluation_kind) { .scalar => { return llvm_call; }, - .aggregate => {}, - .complex => { unreachable; }, + .aggregate => {}, + .complex => { unreachable; }, } + } + // TODO: if + >fixed_vector_type: u1 = 0; + if (fixed_vector_type) + { #trap(); } - #trap(); + >coerce_alloca: &LLVMValue = zero; + + if (left_llvm) + { + assert(left_type.id == .pointer); + assert(left_type.content.pointer.element_type == return_abi.semantic_type); + coerce_alloca = left_llvm; + } + else + { + coerce_alloca = create_alloca(module, { + .type = return_abi.semantic_type, + .name = "coerce", + zero, + }); + } + + >destination_pointer = coerce_alloca; + + if (return_abi.attributes.direct.offset != 0) + { + #trap(); + } + + >destination_type = return_abi.semantic_type; + + >source_value = llvm_call; + >source_type = raw_function_type.content.function.abi.abi_return_type; + >destination_size = get_byte_size(destination_type); + >left_destination_size = destination_size - #extend(return_abi.attributes.direct.offset); + >is_destination_volatile: u1 = 0; + + switch (return_abi.semantic_type.id) + { + .struct => + { + >fields = return_abi.semantic_type.content.struct.fields; + if (fields.length > 0) + { + create_coerced_store(module, source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile); + } + else + { + #trap(); + } + }, + .array => + { + if (get_byte_size(return_abi.semantic_type) <= 8) + { + create_store(module, { + .source = source_value, + .destination = destination_pointer, + .type = source_type, + zero, + }); + } + else + { + create_coerced_store(module, source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile); + } + }, + else => { unreachable; }, + } + + assert(coerce_alloca != zero); + + if (left_llvm) + { + assert(destination_pointer == left_llvm); + return destination_pointer; + } + else + { + switch (value.kind) + { + .right => { #trap(); }, + .left => { #trap(); }, + } + } }, .indirect => { @@ -11797,11 +11942,6 @@ emit_debug_argument = fn (module: &Module, argument: &Argument, basic_block: &LL LLVMDIBuilderInsertDeclareRecordAtEnd(module.llvm.di_builder, argument.variable.storage.llvm, parameter_variable, null_expression(module), debug_location, basic_block); } -create_coerced_store = fn (module: &Module, source_value: &LLVMValue, source_type: &Type, destination_value: &LLVMValue, destination_type: &Type, destination_size: u64, destination_volatile: u1) void -{ - #trap(); -} - emit = fn (module: &Module) void { assert(!module.current_function); @@ -13006,6 +13146,7 @@ names: [_][]u8 = "indirect_varargs", "ret_c_bool", "return_type_builtin", + "return_u64_u64", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/emitter.cpp b/src/emitter.cpp index e64e70b..6ec6266 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -5840,7 +5840,6 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, auto destination_type = return_abi.semantic_type; - auto source_value = llvm_call; auto source_type = raw_function_type->function.abi.abi_return_type; auto destination_size = get_byte_size(destination_type); -- 2.43.0 From 6b56a1b9c039b95d736bdfb25796e948321ceff3 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 13:00:45 -0600 Subject: [PATCH 02/16] Pass 'select' --- src/compiler.bbb | 94 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index aaf5bc5..0f9b81a 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1751,6 +1751,7 @@ ValueId = enum va_arg, unreachable, undefined, + select, } ValueConstantInteger = struct @@ -1964,6 +1965,13 @@ ValueAggregateInitialization = struct is_zero: u1, } +ValueSelect = struct +{ + condition: &Value, + true_value: &Value, + false_value: &Value, +} + ValueContent = union { constant_integer: ValueConstantInteger, @@ -1981,6 +1989,7 @@ ValueContent = union string_literal: []u8, va_arg: ValueVaArg, aggregate_initialization: ValueAggregateInitialization, + select: ValueSelect, } ValueKind = enum @@ -2466,6 +2475,8 @@ llvm_create_global_variable = fn (module: &LLVMModule, type: &LLVMType, is_const [extern] LLVMBuildPhi = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, name: &u8) &LLVMValue; [extern] LLVMAddIncoming = fn [cc(c)] (phi: &LLVMValue, values: &&LLVMValue, blocks: &&LLVMBasicBlock, count: u32) void; +[extern] LLVMBuildSelect = fn [cc(c)] (builder: &LLVMBuilder, condition: &LLVMValue, true_value: &LLVMValue, false_value: &LLVMValue, name: &u8) &LLVMValue; + [extern] LLVMBuildIntToPtr = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, name: &u8) &LLVMValue; [extern] LLVMBuildPtrToInt = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, name: &u8) &LLVMValue; [extern] LLVMBuildIntCast2 = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, signed: s32, name: &u8) &LLVMValue; @@ -5073,7 +5084,36 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .select => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + >condition = parse_value(module, scope, zero); + + expect_character(module, ','); + skip_space(module); + + >true_value = parse_value(module, scope, zero); + + expect_character(module, ','); + skip_space(module); + + >false_value = parse_value(module, scope, zero); + + skip_space(module); + expect_character(module, right_parenthesis); + + result.& = { + .content = { + .select = { + .condition = condition, + .true_value = true_value, + .false_value = false_value, + }, + }, + .id = .select, + zero, + }; }, .string_to_enum => { @@ -9162,6 +9202,28 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi value_type = expected_type; }, + .select => + { + >condition = value.content.select.condition; + >true_value = value.content.select.true_value; + >false_value = value.content.select.false_value; + + 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); + + >true_type = true_value.type; + >false_type = false_value.type; + check_types(module, true_type, false_type); + + assert(true_type == false_type); + + >result_type = true_type; + typecheck(module, expected_type, result_type); + + value_type = result_type; + }, else => { #trap(); @@ -11188,6 +11250,35 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con { llvm_value = LLVMConstNull(get_llvm_type(resolved_value_type, type_kind)); }, + .select => + { + >condition = value.content.select.condition; + >true_value = value.content.select.true_value; + >false_value = value.content.select.false_value; + + emit_value(module, condition, .abi, must_be_constant); + + >llvm_condition = condition.llvm; + >condition_type = condition.type; + + switch (condition_type.id) + { + .integer => + { + >bit_count = condition_type.content.integer.bit_count; + if (bit_count != 1) + { + #trap(); + } + }, + else => { #trap(); }, + } + + emit_value(module, true_value, type_kind, must_be_constant); + emit_value(module, false_value, type_kind, must_be_constant); + + llvm_value = LLVMBuildSelect(module.llvm.builder, llvm_condition, true_value.llvm, false_value.llvm, ""); + }, else => { #trap(); @@ -13147,6 +13238,7 @@ names: [_][]u8 = "ret_c_bool", "return_type_builtin", "return_u64_u64", + "select", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 9e1856d4c5115c0c7e72a7c5066e13ef642ce36c Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 13:13:10 -0600 Subject: [PATCH 03/16] Pass 'slice' --- src/compiler.bbb | 98 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 0f9b81a..2da677e 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -10273,11 +10273,74 @@ emit_slice_expresion = fn (module: &Module, value: &Value) [2]&LLVMValue { .pointer => { - #trap(); + >element_type = sliceable_type.content.pointer.element_type; + >pointer_load = create_load(module, { + .type = sliceable_type, + .pointer = array_like.llvm, + zero, + }); + + >slice_pointer = pointer_load; + if (has_start) + { + slice_pointer = create_gep(module, { + .type = element_type.llvm.memory, + .pointer = pointer_load, + .indices = [ start.llvm ][..], + zero, + }); + } + + >slice_length = end.llvm; + + if (has_start) + { + slice_length = LLVMBuildSub(module.llvm.builder, slice_length, start.llvm, ""); + } + + return [ slice_pointer, slice_length ]; }, .struct => { - #trap(); + assert(type_is_slice(sliceable_type)); + + >slice_load = create_load(module, { + .type = sliceable_type, + .pointer = array_like.llvm, + zero, + }); + + >old_slice_pointer = LLVMBuildExtractValue(module.llvm.builder, slice_load, 0, ""); + >slice_pointer = old_slice_pointer; + + if (has_start) + { + slice_pointer = create_gep(module, { + .type = slice_element_type.llvm.memory, + .pointer = old_slice_pointer, + .indices = [ start.llvm ][..], + zero, + }); + } + + >slice_end: &LLVMValue = undefined; + + if (end) + { + slice_end = end.llvm; + } + else + { + slice_end = LLVMBuildExtractValue(module.llvm.builder, slice_load, 1, ""); + } + + >slice_length = slice_end; + if (has_start) + { + slice_length = LLVMBuildSub(module.llvm.builder, slice_end, start.llvm, ""); + } + + return [ slice_pointer, slice_length ]; }, .array => { @@ -10314,6 +10377,7 @@ emit_slice_expresion = fn (module: &Module, value: &Value) [2]&LLVMValue return [ slice_pointer, slice_length ]; }, + else => { unreachable; }, } } @@ -11424,6 +11488,11 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, emit_intrinsic_call(module, ."llvm.va_start", argument_types[..], argument_values[..]); }, + .va_arg => + { + >result = emit_va_arg(module, right, left_llvm, left_type, llvm_function); + assert(result == left_llvm); + }, .aggregate_initialization => { >elements = right.content.aggregate_initialization.elements; @@ -11456,10 +11525,30 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, >result = emit_call(module, right, left_llvm, left_type); assert(result == left_llvm); }, + .slice_expression => + { + >slice_values = emit_slice_expresion(module, right); + assert(type_is_slice(resolved_value_type)); + >slice_pointer_type = resolved_value_type.content.struct.fields[0].type; + assert(slice_pointer_type.id == .pointer); + create_store(module, { + .source = slice_values[0], + .destination = left_llvm, + .type = slice_pointer_type, + zero, + }); + + >slice_length_destination = LLVMBuildStructGEP2(module.llvm.builder, resolved_value_type.llvm.abi, left_llvm, 1, ""); + create_store(module, { + .source = slice_values[1], + .destination = slice_length_destination, + .type = uint64(module), + zero, + }); + }, else => { - >result = emit_va_arg(module, right, left_llvm, left_type, llvm_function); - assert(result == left_llvm); + #trap(); }, } }, @@ -13239,6 +13328,7 @@ names: [_][]u8 = "return_type_builtin", "return_u64_u64", "select", + "slice", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 6e7949e9bda4998928958049c92a941545d96671 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 13:41:51 -0600 Subject: [PATCH 04/16] Pass 'small_struct_ints' --- src/compiler.bbb | 274 +++++++++++++++++++++++++++++++++++++++++++++-- src/emitter.cpp | 10 +- 2 files changed, 267 insertions(+), 17 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 2da677e..cec7a70 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -7172,7 +7172,26 @@ contains_no_user_data = fn (type: &Type, start: u64, end: u64) u1 .struct => { result = 1; - #trap(); + + >fields = type.content.struct.fields; + + for (&field: fields) + { + >field_offset = field.offset; + + if (field_offset >= end) + { + break; + } + + >field_start = #select(field_offset < start, start - field_offset, 0); + + if (!contains_no_user_data(field.type, field_start, end - field_offset)) + { + result = 0; + break; + } + } }, .array, .enum_array => { @@ -9425,7 +9444,50 @@ reanalyze_type_as_left_value = fn (module: &Module, value: &Value) void type_is_integer_backing = fn (type: &Type) u1 { - #trap(); + switch (type.id) + { + .integer, + .enum, + .bits, + .pointer, + => + { + return 1; + }, + else => { return 0; }, + } +} + +ValueTypePair = struct +{ + value: &LLVMValue, + type: &Type, +} + +enter_struct_pointer_for_coerced_access = fn (module: &Module, source_value: &LLVMValue, source_type: &Type, destination_size: u64) ValueTypePair +{ + >fields = source_type.content.struct.fields; + assert(source_type.id == .struct and fields.length > 0); + >first_field_type = fields[0].type; + >first_field_size = get_byte_size(first_field_type); + >source_size = get_byte_size(source_type); + + if (!(first_field_size < destination_size and first_field_size < source_size)) + { + >gep = LLVMBuildStructGEP2(module.llvm.builder, source_type.llvm.abi, source_value, 0, "coerce.dive"); + if (first_field_type.id == .struct) + { + #trap(); + } + else + { + return { .value = gep, .type = first_field_type }; + } + } + else + { + return { .value = source_value, .type = source_type }; + } } create_coerced_store = fn (module: &Module, source_value: &LLVMValue, source_type: &Type, destination_value: &LLVMValue, destination_type: &Type, destination_size: u64, destination_volatile: u1) void @@ -9434,9 +9496,11 @@ create_coerced_store = fn (module: &Module, source_value: &LLVMValue, source_typ // TODO: this smells badly // destination_type != uint1(module) - if (!type_is_abi_equal(module, source_type, destination_type) and destination_type.id == .struct) + if (destination_type.id == .struct and !type_is_abi_equal(module, source_type, destination_type)) { - #trap(); + >r = enter_struct_pointer_for_coerced_access(module, destination_value, destination_type, source_size); + destination_value = r.value; + destination_type = r.type; } >is_scalable: u1 = 0; @@ -9470,7 +9534,12 @@ create_coerced_store = fn (module: &Module, source_value: &LLVMValue, source_typ } else { - #trap(); + create_store(module, { + .source = source_value, + .destination = destination_value, + .type = destination_type, + .alignment = destination_alignment, + }); } } else if (type_is_integer_backing(source_type)) @@ -9484,6 +9553,95 @@ create_coerced_store = fn (module: &Module, source_value: &LLVMValue, source_typ } } +coerce_integer_or_pointer_to_integer_or_pointer = fn (module: &Module, source: &LLVMValue, source_type: &Type, destination_type: &Type) &LLVMValue +{ + #trap(); +} + +create_coerced_load = fn (module: &Module, source: &LLVMValue, source_type: &Type, destination_type: &Type) &LLVMValue +{ + >result: &LLVMValue = zero; + + if (type_is_abi_equal(module, source_type, destination_type)) + { + #trap(); + } + else + { + >destination_size = get_byte_size(destination_type); + + if (source_type.id == .struct) + { + >src = enter_struct_pointer_for_coerced_access(module, source, source_type, destination_size); + source = src.value; + source_type = src.type; + } + + if (type_is_integer_backing(source_type) and type_is_integer_backing(destination_type)) + { + >load = create_load(module, { + .type = source_type, + .pointer = source, + zero, + }); + >result = coerce_integer_or_pointer_to_integer_or_pointer(module, load, source_type, destination_type); + return result; + } + else + { + >source_size = get_byte_size(source_type); + + >is_source_type_scalable: u1 = 0; + >is_destination_type_scalable: u1 = 0; + + if (!is_source_type_scalable and? !is_source_type_scalable and? source_size >= destination_size) + { + result = create_load(module, { + .type = destination_type, + .pointer = source, + zero, + }); + } + else + { + >scalable_vector_type: u1 = 0; + if (scalable_vector_type) + { + #trap(); + } + else + { + // Coercion through memory + >original_destination_alignment = get_byte_alignment(destination_type); + >source_alignment = get_byte_alignment(source_type); + >destination_alignment = #max(original_destination_alignment, source_alignment); + >destination_alloca = create_alloca(module, { + .type = destination_type, + .name = "coerce", + .alignment = destination_alignment, + }); + + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + + LLVMBuildMemCpy(module.llvm.builder, destination_alloca, destination_alignment, source, source_alignment, LLVMConstInt(u64_type.llvm.abi, source_size, 0)); + + >load = create_load(module, { + .type = destination_type, + .pointer = destination_alloca, + .alignment = destination_alignment, + zero, + }); + result = load; + } + } + } + } + + assert(result != zero); + return result; +} + emit_call = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type: &Type) &LLVMValue { assert(value.id == .call); @@ -9805,7 +9963,85 @@ emit_call = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type } else { - #trap(); + assert(argument_abi.abi_count == 1); + >destination_type = coerce_to_type; + + >v: &LLVMValue = zero; + + switch (src.id) + { + .zero => + { + v = LLVMConstNull(coerce_to_type.llvm.abi); + }, + else => + { + >pointer: &LLVMValue = zero; + >pointer_type: &Type = zero; + + if (src.type.id != .pointer) + { + >type = src.type; + pointer_type = get_pointer_type(module, type); + + if (src.id != .variable) + { + pointer = create_alloca(module, { + .type = type, + .name = "my.coerce", + zero, + }); + + emit_assignment(module, pointer, pointer_type, src); + } + else + { + assert(src.id == .variable); + assert(src.kind == .right); + reanalyze_type_as_left_value(module, src); + } + } + else + { + #trap(); + } + + assert(pointer_type != zero); + assert(pointer_type.id == .pointer); + >element_type = pointer_type.content.pointer.element_type; + + if (!pointer) + { + assert(src.type.id == .pointer); + assert(src.type.llvm.abi == module.llvm.pointer_type); + emit_value(module, src, .memory, 0); + pointer = src.llvm; + } + + >source_type = element_type; + assert(source_type == argument_abi.semantic_type); + >load = create_coerced_load(module, pointer, source_type, destination_type); + + >is_cmse_ns_call: u1 = 0; + if (is_cmse_ns_call) + { + #trap(); + } + + >maybe_undef: u1 = 0; + if (maybe_undef) + { + #trap(); + } + + v = load; + }, + } + + assert(v != zero); + + llvm_abi_argument_value_buffer[abi_argument_count] = v; + abi_argument_count += 1; } } } @@ -12945,15 +13181,30 @@ emit = fn (module: &Module) void else { return_value = create_load(module, { - .type = semantic_return_type, - .pointer = return_alloca, - zero, - }); + .type = semantic_return_type, + .pointer = return_alloca, + zero, + }); } } else { - #trap(); + >source: &LLVMValue = zero; + if (function_type.abi.return_abi.attributes.direct.offset == 0) + { + source = return_alloca; + } + else + { + #trap(); + } + + assert(source != zero); + + >source_type = function_type.abi.return_abi.semantic_type; + >destination_type = coerce_to_type; + >result = create_coerced_load(module, source, source_type, destination_type); + return_value = result; } }, .indirect => @@ -13329,6 +13580,7 @@ names: [_][]u8 = "return_u64_u64", "select", "slice", + "small_struct_ints", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/emitter.cpp b/src/emitter.cpp index 6ec6266..b5601c6 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -609,21 +609,19 @@ fn bool contains_no_user_data(Type* type, u64 start, u64 end) { case TypeId::structure: { - u64 offset = 0; - for (auto& field: type->structure.fields) { - if (offset >= end) + auto field_offset = field.offset; + if (field_offset >= end) { break; } - auto field_start = offset < start ? start - offset : 0; - if (!contains_no_user_data(field.type, field_start, end - offset)) + auto field_start = field_offset < start ? start - field_offset : 0; + if (!contains_no_user_data(field.type, field_start, end - field_offset)) { return false; } - offset += get_byte_size(field.type); } return true; -- 2.43.0 From f07f480a5b0e1c662a82d7b832f5a5ef02577da0 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 15:34:50 -0600 Subject: [PATCH 05/16] Pass 'struct_assignment' --- src/compiler.bbb | 68 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index cec7a70..22db1de 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -2505,6 +2505,7 @@ llvm_create_global_variable = fn (module: &LLVMModule, type: &LLVMType, is_const [extern] LLVMBuildCall2 = fn [cc(c)] (builder: &LLVMBuilder, function_type: &LLVMType, function_value: &LLVMValue, argument_pointer: &&LLVMValue, argument_count: u32, name: &u8) &LLVMValue; [extern] LLVMBuildMemCpy = fn [cc(c)] (builder: &LLVMBuilder, destination: &LLVMValue, destination_alignment: u32, source: &LLVMValue, source_alignment: u32, size: &LLVMValue) &LLVMValue; +[extern] LLVMBuildMemSet = fn [cc(c)] (builder: &LLVMBuilder, pointer: &LLVMValue, value: &LLVMValue, byte_count: &LLVMValue, alignment: u32) &LLVMValue; [extern] LLVMGetInsertBlock = fn [cc(c)] (builder: &LLVMBuilder) &LLVMBasicBlock; [extern] LLVMGetBasicBlockTerminator = fn [cc(c)] (basic_block: &LLVMBasicBlock) &LLVMValue; @@ -11744,16 +11745,80 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, if (is_constant) { emit_value(module, right, .memory, 1); + >linkage: LLVMLinkage = .internal; >thread_local_mode: LLVMThreadLocalMode = .none; >externally_initialized: u1 = 0; >unnamed_address: LLVMUnnamedAddress = .global; + >global = llvm_create_global_variable(module.llvm.module, value_type.llvm.memory, is_constant, linkage, right.llvm, "const.aggregate", thread_local_mode, externally_initialized, alignment, unnamed_address); + LLVMBuildMemCpy(module.llvm.builder, left_llvm, alignment, global, alignment, byte_size_value); } else { - #trap(); + switch (resolved_value_type.id) + { + .struct => + { + >max_field_index: u64 = 0; + >field_mask: u64 = 0; + >fields = resolved_value_type.content.struct.fields; + assert(fields.length <= 64); + + if (is_zero) + { + >u8_type = uint8(module); + resolve_type_in_place(module, u8_type); + LLVMBuildMemSet(module.llvm.builder, left_llvm, LLVMConstNull(u8_type.llvm.memory), byte_size_value, alignment); + } + + for (&element: elements) + { + >name = element.name; + >value = element.value; + + >declaration_index: u64 = 0; + + while (declaration_index < fields.length) + { + >field = &fields[declaration_index]; + + if (string_equal(name, field.name)) + { + break; + } + + declaration_index += 1; + } + + assert(declaration_index < fields.length); + + if (module.has_debug_info) + { + >debug_location = LLVMDIBuilderCreateDebugLocation(module.llvm.context, element.line, element.column, scope.llvm, module.llvm.inlined_at); + LLVMSetCurrentDebugLocation2(module.llvm.builder, debug_location); + } + + field_mask |= 1 << declaration_index; + max_field_index = #max(max_field_index, declaration_index); + + >field = &fields[declaration_index]; + + >destination_pointer = LLVMBuildStructGEP2(module.llvm.builder, resolved_value_type.llvm.memory, left_llvm, #truncate(declaration_index), ""); + + emit_assignment(module, destination_pointer, get_pointer_type(module, field.type), value); + } + }, + .union => + { + #trap(); + }, + else => + { + #trap(); + }, + } } }, .call => @@ -13581,6 +13646,7 @@ names: [_][]u8 = "select", "slice", "small_struct_ints", + "struct_assignment", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 17a33ba3bb82e643ce61691d6cd6133c4387f445 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 15:55:03 -0600 Subject: [PATCH 06/16] Pass 'struct' --- src/compiler.bbb | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 22db1de..23537a5 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -9895,7 +9895,7 @@ emit_call = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type .field_access, => { - #trap(); + reanalyze_type_as_left_value(module, src); }, else => { @@ -9915,7 +9915,15 @@ emit_call = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type if (source_size < destination_size) { - #trap(); + >alloca = create_alloca(module, { + .type = argument_abi.semantic_type, + .name = "coerce", + .alignment = alignment, + }); + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + LLVMBuildMemCpy(module.llvm.builder, alloca, alignment, source, alignment, LLVMConstInt(u64_type.llvm.abi, source_size, 0)); + source = alloca; } assert(coerce_to_type.id == .struct); @@ -9928,7 +9936,26 @@ emit_call = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type { .left => { - #trap(); + for (i: 0..coerce_fields.length) + { + >field = &coerce_fields[i]; + >gep = LLVMBuildStructGEP2(module.llvm.builder, coerce_to_type.llvm.memory, source, #truncate(i), ""); + >maybe_undef: u1 = 0; + if (maybe_undef) + { + #trap(); + } + + >load = create_load(module, { + .type = field.type, + .pointer = gep, + .alignment = alignment, + zero, + }); + + llvm_abi_argument_value_buffer[abi_argument_count] = load; + abi_argument_count += 1; + } }, .right => { @@ -13647,6 +13674,7 @@ names: [_][]u8 = "slice", "small_struct_ints", "struct_assignment", + "struct", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From ffee749e9501bf9913b6974c9d01c2cad6ddfe3a Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 16:08:47 -0600 Subject: [PATCH 07/16] Some struct tests --- src/compiler.bbb | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 23537a5..8dc5cef 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -9073,7 +9073,7 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi >is_ordered: u1 = 1; >same_values_as_field = fields.length == elements.length; - >is_properly_initialized = same_values_as_field or zero; + >is_properly_initialized = same_values_as_field or is_zero; if (zero and same_values_as_field) { @@ -11475,7 +11475,19 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con if (is_zero) { - #trap(); + if (elements.length == fields.length) + { + unreachable; + } + + for (i: elements.length..fields.length) + { + >field = &fields[i]; + >field_type = field.type; + resolve_type_in_place(module, field_type); + constant_buffer[i] = LLVMConstNull(field_type.llvm.memory); + constant_count += 1; + } } assert(constant_count == fields.length); @@ -11874,6 +11886,18 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, zero, }); }, + .zero => + { + >u8_type = uint8(module); + >u64_type = uint64(module); + resolve_type_in_place(module, u8_type); + resolve_type_in_place(module, u64_type); + + >size = get_byte_size(resolved_value_type); + >alignment = get_byte_alignment(resolved_value_type); + // TODO: should we just have typed memset instead: `LLVMConstNull(the_type)`, 1? + LLVMBuildMemSet(module.llvm.builder, left_llvm, LLVMConstNull(u8_type.llvm.memory), LLVMConstInt(u64_type.llvm.memory, size, 0), alignment); + }, else => { #trap(); @@ -13675,6 +13699,9 @@ names: [_][]u8 = "small_struct_ints", "struct_assignment", "struct", + "struct_u64_u64", + "struct_varargs", + "struct_zero", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 4c0b77d8bae321b8afd242ef61387608de7530e8 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 16:15:18 -0600 Subject: [PATCH 08/16] Pass 'unreachable' --- src/compiler.bbb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/compiler.bbb b/src/compiler.bbb index 8dc5cef..77217d8 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -9244,6 +9244,10 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi value_type = result_type; }, + .unreachable => + { + value_type = noreturn_type(module); + }, else => { #trap(); @@ -11619,6 +11623,16 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con llvm_value = LLVMBuildSelect(module.llvm.builder, llvm_condition, true_value.llvm, false_value.llvm, ""); }, + .unreachable => + { + if (module.has_debug_info and !build_mode_is_optimized(module.build_mode)) + { + emit_intrinsic_call(module, ."llvm.trap", zero, zero); + } + + llvm_value = LLVMBuildUnreachable(module.llvm.builder); + LLVMClearInsertionPosition(module.llvm.builder); + }, else => { #trap(); @@ -13702,6 +13716,7 @@ names: [_][]u8 = "struct_u64_u64", "struct_varargs", "struct_zero", + "unreachable", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 5702d601df059ba4465dad572f6e70f578335549 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 18:15:26 -0600 Subject: [PATCH 09/16] Pass 'va_end' --- CMakeLists.txt | 3 +++ src/compiler.bbb | 6 ++++++ src/emitter.cpp | 3 ++- src/parser.cpp | 3 ++- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae44d6b..2e57d33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,3 +51,6 @@ target_link_libraries(bb PUBLIC llvm_bindings) add_compile_options(-Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -Wno-missing-designated-field-initializers -fno-signed-char -fwrapv -fno-strict-aliasing) add_compile_definitions(CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}") add_compile_definitions(BB_CI=${BB_CI}) + +# add_compile_options(-fsanitize=address) +# add_link_options(-fsanitize=address) diff --git a/src/compiler.bbb b/src/compiler.bbb index 77217d8..c280c1e 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -8510,6 +8510,7 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi // TODO: this is the default case .ampersand, .exclamation, + .va_end, => { >is_boolean = unary_is_boolean(unary_id); @@ -11120,6 +11121,10 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con { llvm_value = llvm_unary_value; }, + .va_end => + { + llvm_value = emit_intrinsic_call(module, ."llvm.va_end", [ module.llvm.pointer_type ][..], [ llvm_unary_value ][..]); + }, else => { #trap(); }, } }, @@ -13717,6 +13722,7 @@ names: [_][]u8 = "struct_varargs", "struct_zero", "unreachable", + "varargs", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/emitter.cpp b/src/emitter.cpp index b5601c6..ff5b292 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -7065,7 +7065,8 @@ fn void analyze_block(Module* module, Block* block) fn LLVMValueRef emit_constant_array(Module* module, Slice elements, Type* element_type) { - LLVMValueRef value_buffer[64]; + LLVMValueRef value_buffer[128]; + assert(elements.length <= array_length(value_buffer)); resolve_type_in_place(module, element_type); diff --git a/src/parser.cpp b/src/parser.cpp index e42590e..a8655b8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1937,7 +1937,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) case TokenId::left_bracket: { u64 element_count = 0; - Value* value_buffer[64]; + Value* value_buffer[128]; skip_space(module); @@ -1976,6 +1976,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) } auto value = parse_value(module, scope, {}); + assert(element_count < array_length(value_buffer)); value_buffer[element_count] = value; element_count += 1; -- 2.43.0 From b768586b3c2035d206aab0bf74f98df3c0b9b101 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 18:21:49 -0600 Subject: [PATCH 10/16] Pass some C ABI tests --- src/compiler.bbb | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index c280c1e..13564f4 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -9555,7 +9555,26 @@ create_coerced_store = fn (module: &Module, source_value: &LLVMValue, source_typ else { // Coercion through memory - #trap(); + + >original_destination_alignment = get_byte_alignment(destination_type); + >source_alloca_alignment = #max(original_destination_alignment, get_byte_alignment(source_type)); + >source_alloca = create_alloca(module, { + .type = source_type, + .name = "coerce", + .alignment = source_alloca_alignment, + }); + + create_store(module, { + .source = source_value, + .destination = source_alloca, + .type = source_type, + .alignment = source_alloca_alignment, + }); + + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + + LLVMBuildMemCpy(module.llvm.builder, destination_value, original_destination_alignment, source_alloca, source_alloca_alignment, LLVMConstInt(u64_type.llvm.abi, destination_size, 0)); } } @@ -13723,6 +13742,9 @@ names: [_][]u8 = "struct_zero", "unreachable", "varargs", + "c_abi0", + "c_abi1", + "c_med_struct_ints", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 14ff80bcc55300adc4655e059450f0ff90a8df88 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 18:46:13 -0600 Subject: [PATCH 11/16] Pass 'c_ret_struct_array' --- src/compiler.bbb | 452 ++++++++++++++++++++++++++++++----------------- 1 file changed, 289 insertions(+), 163 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 13564f4..2145037 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -2050,6 +2050,11 @@ value_is_constant = fn (value: &Value) u1 assert(value.type != zero); return value.content.aggregate_initialization.is_constant; }, + .array_initialization => + { + assert(value.type != zero); + return value.content.array_initialization.is_constant; + }, else => { #trap(); @@ -3615,6 +3620,166 @@ get_by_value_argument_pair = fn (module: &Module, low: &Type, high: &Type) &Type return result; } +TokenId = enum +{ + none, + comma, + end_of_statement, + integer, + left_brace, + left_bracket, + left_parenthesis, + right_brace, + right_bracket, + right_parenthesis, + + plus, + dash, + asterisk, + forward_slash, + percentage, + caret, + bar, + ampersand, + exclamation, + + assign_plus, + assign_dash, + assign_asterisk, + assign_forward_slash, + assign_percentage, + assign_caret, + assign_bar, + assign_ampersand, + + value_keyword, + operator_keyword, + identifier, + string_literal, + value_intrinsic, + + shift_left, + shift_right, + assign_shift_left, + assign_shift_right, + + compare_less, + compare_less_equal, + compare_greater, + compare_greater_equal, + compare_equal, + compare_not_equal, + + dot, + double_dot, + triple_dot, + + pointer_dereference, + + assign, + tilde, +} + +TokenIntegerKind = enum +{ + hexadecimal, + decimal, + octal, + binary, + character_literal, +} + +TokenInteger = struct +{ + value: u64, + kind: TokenIntegerKind, +}; + +ValueKeyword = enum +{ + undefined, + unreachable, + zero, +} + +ValueIntrinsic = enum +{ + align_of, + build_mode, + byte_size, + enum_from_int, + enum_name, + enum_values, + extend, + field_parent_pointer, + has_debug_info, + integer_max, + int_from_enum, + int_from_pointer, + max, + min, + pointer_cast, + pointer_from_int, + select, + string_to_enum, + trap, + truncate, + va_start, + va_end, + va_arg, + va_copy, +} + +OperatorKeyword = enum +{ + and, + or, + "and?", + "or?", +} + +TokenContent = union +{ + integer: TokenInteger, + value_keyword: ValueKeyword, + value_intrinsic: ValueIntrinsic, + operator_keyword: OperatorKeyword, + identifier: []u8, + string_literal: []u8, +} + +Token = struct +{ + content: TokenContent, + id: TokenId, +} + +Precedence = enum +{ + none, + assignment, + boolean_or, + boolean_and, + comparison, + bitwise, + shifting, + add_like, + div_like, + prefix, + aggregate_initialization, + postfix, +} + +ValueBuilder = struct +{ + token: Token, + left: &Value, + precedence: Precedence, + kind: ValueKind, + allow_assignment_operators: u1, +} + +parse_value = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value; parse_type = fn (module: &Module, scope: &Scope) &Type; FunctionHeaderArgument = struct @@ -4095,7 +4260,37 @@ parse_type = fn (module: &Module, scope: &Scope) &Type if (!length_inferred) { - #trap(); + set_checkpoint(module, checkpoint); + + length_value = parse_value(module, scope, zero); + assert(length_value != zero); + + if (!value_is_constant(length_value)) + { + report_error(); + } + + switch (length_value.id) + { + .constant_integer => + { + element_count = length_value.content.constant_integer.value; + + if (element_count == 0) + { + report_error(); + } + + resolved = 1; + }, + else => + { + report_error(); + }, + } + + skip_space(module); + expect_character(module, right_bracket); } skip_space(module); @@ -4127,7 +4322,10 @@ parse_type = fn (module: &Module, scope: &Scope) &Type report_error(); } - #trap(); + assert(element_count != 0); + + >array_type = get_array_type(module, element_type, element_count); + return array_type; } } } @@ -4202,140 +4400,6 @@ parse_hexadecimal = fn (module: &Module) u64 return value; } -TokenId = enum -{ - none, - comma, - end_of_statement, - integer, - left_brace, - left_bracket, - left_parenthesis, - right_brace, - right_bracket, - right_parenthesis, - - plus, - dash, - asterisk, - forward_slash, - percentage, - caret, - bar, - ampersand, - exclamation, - - assign_plus, - assign_dash, - assign_asterisk, - assign_forward_slash, - assign_percentage, - assign_caret, - assign_bar, - assign_ampersand, - - value_keyword, - operator_keyword, - identifier, - string_literal, - value_intrinsic, - - shift_left, - shift_right, - assign_shift_left, - assign_shift_right, - - compare_less, - compare_less_equal, - compare_greater, - compare_greater_equal, - compare_equal, - compare_not_equal, - - dot, - double_dot, - triple_dot, - - pointer_dereference, - - assign, - tilde, -} - -TokenIntegerKind = enum -{ - hexadecimal, - decimal, - octal, - binary, - character_literal, -} - -TokenInteger = struct -{ - value: u64, - kind: TokenIntegerKind, -}; - -ValueKeyword = enum -{ - undefined, - unreachable, - zero, -} - -ValueIntrinsic = enum -{ - align_of, - build_mode, - byte_size, - enum_from_int, - enum_name, - enum_values, - extend, - field_parent_pointer, - has_debug_info, - integer_max, - int_from_enum, - int_from_pointer, - max, - min, - pointer_cast, - pointer_from_int, - select, - string_to_enum, - trap, - truncate, - va_start, - va_end, - va_arg, - va_copy, -} - -OperatorKeyword = enum -{ - and, - or, - "and?", - "or?", -} - -TokenContent = union -{ - integer: TokenInteger, - value_keyword: ValueKeyword, - value_intrinsic: ValueIntrinsic, - operator_keyword: OperatorKeyword, - identifier: []u8, - string_literal: []u8, -} - -Token = struct -{ - content: TokenContent, - id: TokenId, -} - tokenize = fn (module: &Module) Token { skip_space(module); @@ -4713,33 +4777,7 @@ tokenize = fn (module: &Module) Token return token; } -Precedence = enum -{ - none, - assignment, - boolean_or, - boolean_and, - comparison, - bitwise, - shifting, - add_like, - div_like, - prefix, - aggregate_initialization, - postfix, -} - -ValueBuilder = struct -{ - token: Token, - left: &Value, - precedence: Precedence, - kind: ValueKind, - allow_assignment_operators: u1, -} - parse_precedence = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value; -parse_value = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value; reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: []u8, kind: ValueKind) &Value { @@ -7152,6 +7190,54 @@ abi_system_v_classify_type = fn (type: &Type, options: AbiSystemVClassifyArgumen } } }, + .array => + { + >byte_size = get_byte_size(type); + + if (byte_size <= 64) + { + if (options.base_offset % #extend(get_byte_alignment(type)) == 0) + { + >element_type = type.content.array.element_type; + >element_size = get_byte_size(element_type); + + result[current_index] = .none; + + >vector_size: u64 = 16; + + if (byte_size > 16 and (byte_size != get_byte_size(element_type) or byte_size > vector_size)) + { + unreachable; + } + else + { + >offset = options.base_offset; + >element_count = type.content.array.element_count; + + for (i: 0..element_count) + { + >element_classes = abi_system_v_classify_type(element_type, { + .base_offset = offset, + .is_variable_argument = options.is_variable_argument, + zero, + }); + offset += element_size; + + result[0] = abi_system_v_merge_class(result[0], element_classes[0]); + result[1] = abi_system_v_merge_class(result[1], element_classes[1]); + + if (result[0] == .memory or result[1] == .memory) + { + break; + } + } + + result = abi_system_v_classify_post_merge(byte_size, result); + assert(result[1] != .sse or result[0] != .sse); + } + } + } + }, else => { #trap(); @@ -7197,7 +7283,46 @@ contains_no_user_data = fn (type: &Type, start: u64, end: u64) u1 .array, .enum_array => { result = 1; - #trap(); + + >element_type: &Type = zero; + >element_count: u64 = 0; + + switch (type.id) + { + .array => + { + element_type = type.content.array.element_type; + element_count = type.content.array.element_count; + }, + .enum_array => + { + #trap(); + }, + else => { unreachable; }, + } + + assert(element_type != zero); + assert(element_count != 0); + + >element_size = get_byte_size(element_type); + + for (i: 0..element_count) + { + >offset = i * element_size; + + if (offset >= end) + { + break; + } + + >element_start = #select(offset < start, start - offset, 0); + + if (!contains_no_user_data(element_type, element_start, end - offset)) + { + result = 0; + break; + } + } }, else => {}, } @@ -13745,6 +13870,7 @@ names: [_][]u8 = "c_abi0", "c_abi1", "c_med_struct_ints", + "c_ret_struct_array", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 7abff8975e93fcc721784aabe0563d440cc26410 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 19:04:35 -0600 Subject: [PATCH 12/16] Pass some more C ABI tests --- src/compiler.bbb | 43 ++++++++++++++++++++++++++++++++++++++++++- src/emitter.cpp | 4 ++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 2145037..8ae67ce 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -10122,7 +10122,44 @@ emit_call = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, left_type { .aggregate_initialization => { - #trap(); + >is_constant = semantic_call_argument_value.content.aggregate_initialization.is_constant; + + if (is_constant) + { + >linkage: LLVMLinkage = .internal; + >thread_local_mode: LLVMThreadLocalMode = .none; + >externally_initialized: u1 = 0; + >alignment = get_byte_alignment(semantic_argument_type); + >unnamed_address: LLVMUnnamedAddress = .global; + + >global = llvm_create_global_variable(module.llvm.module, semantic_argument_type.llvm.memory, is_constant, linkage, semantic_call_argument_value.llvm, "const.struct", thread_local_mode, externally_initialized, alignment, unnamed_address); + + for (i: 0..coerce_fields.length) + { + >gep = LLVMBuildStructGEP2(module.llvm.builder, coerce_to_type.llvm.abi, global, #truncate(i), ""); + >field = &coerce_fields[i]; + + >maybe_undef: u1 = 0; + if (maybe_undef) + { + #trap(); + } + + >load = create_load(module, { + .type = field.type, + .pointer = gep, + .alignment = alignment, + zero, + }); + + llvm_abi_argument_value_buffer[abi_argument_count] = load; + abi_argument_count += 1; + } + } + else + { + #trap(); + } } .zero => { @@ -13871,6 +13908,10 @@ names: [_][]u8 = "c_abi1", "c_med_struct_ints", "c_ret_struct_array", + "c_split_struct_ints", + "c_string_to_slice", + "c_struct_with_array", + "c_function_pointer", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/emitter.cpp b/src/emitter.cpp index ff5b292..2506be8 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -853,7 +853,7 @@ fn AbiSystemVClassifyResult abi_system_v_classify_type(Type* type, AbiSystemVCla u64 vector_size = 16; - if (byte_size > 16 && (byte_size != get_byte_size(element_type) || byte_size > vector_size)) + if (byte_size > 16 && (byte_size != element_size || byte_size > vector_size)) { unreachable(); } @@ -5554,7 +5554,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, bool externally_initialized = false; auto alignment = get_byte_alignment(semantic_argument_type); - auto global = llvm_module_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("conststruct"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); + auto global = llvm_module_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("const.struct"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); for (u32 i = 0; i < coerce_fields.length; i += 1) { -- 2.43.0 From 7cba1a9b1e4f15e32e55a0ed6d183067fa5b3377 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 19:27:04 -0600 Subject: [PATCH 13/16] Pass 'c_abi' --- src/compiler.bbb | 81 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 8 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 8ae67ce..86771f0 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1445,6 +1445,11 @@ type_is_signed = fn (type: &Type) u1 { return 0; }, + .bits => + { + >backing_type = type.content.bits.backing_type; + return type_is_signed(backing_type); + }, else => { #trap(); @@ -1522,11 +1527,11 @@ is_arbitrary_bit_integer = fn (type: &Type) u1 .unresolved => { unreachable; }, .bits => { - #trap(); + return is_arbitrary_bit_integer(type.content.bits.backing_type); }, .enum => { - #trap(); + return is_arbitrary_bit_integer(type.content.enum.backing_type); }, else => { @@ -1704,6 +1709,11 @@ is_promotable_integer_type_for_abi = fn (type: &Type) u1 { return type.content.integer.bit_count < 32; }, + .bits => + { + >backing_type = type.content.bits.backing_type; + return is_promotable_integer_type_for_abi(backing_type); + }, else => { #trap(); @@ -2011,7 +2021,9 @@ value_is_constant = fn (value: &Value) u1 { switch (value.id) { - .constant_integer => + .constant_integer, + .enum_literal, + => { return 1; }, @@ -3023,9 +3035,25 @@ get_array_type = fn (module: &Module, element_type: &Type, element_count: u64) & >array_type = module.first_array_type; - if (array_type != zero) + while (array_type) { - #trap(); + assert(array_type.id == .array); + >candidate_element_type = array_type.content.array.element_type; + >candidate_element_count = array_type.content.array.element_count; + + if (candidate_element_type == element_type and candidate_element_count == element_count) + { + return array_type; + } + + >next = array_type.content.array.next; + + if (!next) + { + break; + } + + array_type = next; } >last_array_type = array_type; @@ -7238,6 +7266,10 @@ abi_system_v_classify_type = fn (type: &Type, options: AbiSystemVClassifyArgumen } } }, + .bits => + { + return abi_system_v_classify_type(type.content.bits.backing_type, options); + }, else => { #trap(); @@ -7425,7 +7457,8 @@ abi_system_v_get_integer_type_at_offset = fn (module: &Module, type: &Type, offs }, .bits => { - #trap(); + >backing_type = type.content.bits.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); }, .enum => { @@ -12098,6 +12131,26 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, // TODO: should we just have typed memset instead: `LLVMConstNull(the_type)`, 1? LLVMBuildMemSet(module.llvm.builder, left_llvm, LLVMConstNull(u8_type.llvm.memory), LLVMConstInt(u64_type.llvm.memory, size, 0), alignment); }, + .variable => + { + >variable = right.content.variable; + switch (right.kind) + { + .left => + { + #trap(); + }, + .right => + { + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + >memcpy_size = get_byte_size(resolved_value_type); + >alignment = get_byte_alignment(resolved_value_type); + + LLVMBuildMemCpy(module.llvm.builder, left_llvm, alignment, variable.storage.llvm, alignment, LLVMConstInt(u64_type.llvm.abi, memcpy_size, 0)); + }, + } + }, else => { #trap(); @@ -13802,8 +13855,12 @@ compile_file = fn (arena: &Arena, compile_options: CompileFile) []u8 >file_path = path_absolute(arena, relative_file_path.pointer); >c_abi_object_path = ""; // TODO - >objects: [][]u8 = undefined; - objects = [ output_object_path ][..]; + >objects = [ output_object_path ][..]; + >c_abi_library = "build/libc_abi.a"; + >llvm_bindings_library = "build/libllvm_bindings.a"; + + >library_buffer: [256][]u8 = undefined; + >library_directory: []u8 = zero; >library_directories: [][]u8 = zero; >library_names: [][]u8 = zero; @@ -13813,6 +13870,10 @@ compile_file = fn (arena: &Arena, compile_options: CompileFile) []u8 { #trap(); } + else if (string_equal(base_name, "c_abi")) + { + library_paths = { .pointer = &c_abi_library, .length = 1 }; + } >options: CompileOptions = { .executable = output_executable_path, @@ -13912,6 +13973,10 @@ names: [_][]u8 = "c_string_to_slice", "c_struct_with_array", "c_function_pointer", + "basic_bool_call", + "abi_enum_bool", + "return_small_struct", + "c_abi", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 6be1f244fe7900d182b935a0f85515d58165a4c3 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 21:12:41 -0600 Subject: [PATCH 14/16] Pass 'string_to_enum' --- src/compiler.bbb | 1810 +++++++++++++++++++++++++++++----------------- src/emitter.cpp | 29 +- 2 files changed, 1155 insertions(+), 684 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 86771f0..c11340a 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1762,6 +1762,7 @@ ValueId = enum unreachable, undefined, select, + string_to_enum, } ValueConstantInteger = struct @@ -1982,6 +1983,12 @@ ValueSelect = struct false_value: &Value, } +ValueStringToEnum = struct +{ + type: &Type, + string: &Value, +} + ValueContent = union { constant_integer: ValueConstantInteger, @@ -2000,6 +2007,7 @@ ValueContent = union va_arg: ValueVaArg, aggregate_initialization: ValueAggregateInitialization, select: ValueSelect, + string_to_enum: ValueStringToEnum, } ValueKind = enum @@ -2373,6 +2381,7 @@ LLVMICmpPredicate = enum u32 [extern] LLVMCreateBuilderInContext = fn (context: &LLVMContext) &LLVMBuilder; [extern] LLVMTypeOf = fn (value: &LLVMValue) &LLVMType; +[extern] LLVMGlobalGetValueType = fn (value: &LLVMValue) &LLVMType; [extern] LLVMVoidTypeInContext = fn [cc(c)] (context: &LLVMContext) &LLVMType; [extern] LLVMPointerTypeInContext = fn [cc(c)] (context: &LLVMContext, address_space: u32) &LLVMType; @@ -2536,6 +2545,7 @@ llvm_create_global_variable = fn (module: &LLVMModule, type: &LLVMType, is_const [extern] LLVMConstArray2 = fn [cc(c)] (element_type: &LLVMType, value_pointer: &&LLVMValue, value_count: u64) &LLVMValue; [extern] LLVMConstStringInContext2 = fn [cc(c)] (context: &LLVMContext, pointer: &u8, length: u64, dont_null_terminate: s32) &LLVMValue; [extern] LLVMConstNamedStruct = fn [cc(c)] (type: &LLVMType, constant_pointer: &&LLVMValue, constant_count: u32) &LLVMValue; +[extern] LLVMConstStructInContext = fn [cc(c)] (context: &LLVMContext, constant_pointer: &&LLVMValue, constant_count: u32, packed: s32) &LLVMValue; [extern] LLVMInstructionEraseFromParent = fn [cc(c)] (value: &LLVMValue) void; [extern] LLVMGetOperand = fn [cc(c)] (value: &LLVMValue, index: u32) &LLVMValue; @@ -2576,6 +2586,7 @@ llvm_module_to_string = fn (module: &LLVMModule) []u8 [extern] LLVMSetTarget = fn [cc(c)] (module: &LLVMModule, target_triple: &u8) void; [extern] LLVMAddFunction = fn [cc(c)] (module: &LLVMModule, name: &u8, function_type: &LLVMType) &LLVMValue; +[extern] LLVMGetNamedFunction = fn [cc(c)] (module: &LLVMModule, name: &u8) &LLVMValue; llvm_module_create_function = fn (module: &LLVMModule, function_type: &LLVMType, linkage_type: LLVMLinkage, name: []u8) &LLVMValue { @@ -2906,6 +2917,11 @@ uint64 = fn (module: &Module) &Type return integer_type(module, { .bit_count = 64, .signed = 0 }); } +sint32 = fn (module: &Module) &Type +{ + return integer_type(module, { .bit_count = 32, .signed = 1 }); +} + void_type = fn (module: &Module) &Type { return module.scope.types.first + void_offset; @@ -5184,7 +5200,31 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .string_to_enum => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + >type = parse_type(module, scope); + + skip_space(module); + expect_character(module, ','); + skip_space(module); + + >string_value = parse_value(module, scope, zero); + + skip_space(module); + expect_character(module, right_parenthesis); + + result.& = { + .content = { + .string_to_enum = { + .type = type, + .string = string_value, + }, + }, + .id = .string_to_enum, + zero, + }; }, // No argument intrinsic call .trap, @@ -8425,6 +8465,82 @@ get_va_list_type = fn (module: &Module) &Type return va_list_type; } +get_enum_name_array_global = fn (module: &Module, enum_type: &Type) &Global +{ + assert(enum_type.id == .enum); + + if (!enum_type.content.enum.name_array) + { + >fields = enum_type.content.enum.fields; + >u8_type = uint8(module); + >u64_type = uint64(module); + resolve_type_in_place(module, u8_type); + resolve_type_in_place(module, u64_type); + + >name_constant_buffer: [64]&LLVMValue = undefined; + + for (i: 0..fields.length) + { + >field = &fields[i]; + + >is_constant: u1 = 1; + >null_terminate: u1 = 1; + >initial_value = LLVMConstStringInContext2(module.llvm.context, field.name.pointer, field.name.length, #extend(!null_terminate)); + >alignment: u32 = 1; + + >name_global = llvm_create_global_variable(module.llvm.module, LLVMArrayType2(u8_type.llvm.abi, field.name.length + #extend(null_terminate)), is_constant, .internal, initial_value, arena_join_string(module.arena, [ "string.", enum_type.name, ".", field.name ][..]), .none, 0, alignment, .global); + + >constants: [_]&LLVMValue = [ + name_global, + LLVMConstInt(u64_type.llvm.abi, field.name.length, 0), + ]; + >slice_constant = LLVMConstStructInContext(module.llvm.context, &constants[0], constants.length, 0); + name_constant_buffer[i] = slice_constant; + } + + >slice_type = get_slice_type(module, u8_type); + >array_element_count = fields.length; + >name_array = LLVMConstArray2(slice_type.llvm.abi, &name_constant_buffer[0], array_element_count); + >name_array_type = LLVMArrayType2(slice_type.llvm.abi, array_element_count); + >is_constant: u1 = 1; + >name_array_variable = llvm_create_global_variable(module.llvm.module, name_array_type, is_constant, .internal, name_array, "name.array.enum", .none, 0, get_byte_alignment(slice_type), .global); + + >global_type = get_array_type(module, slice_type, array_element_count); + resolve_type_in_place(module, global_type); + + >storage_type = get_pointer_type(module, global_type); + resolve_type_in_place(module, storage_type); + + >global_storage = new_value(module); + global_storage.& = { + .type = storage_type, + .id = .global, + .kind = .left, + .llvm = name_array_variable, + zero, + }; + + >global = new_global(module); + global.& = { + .variable = { + .storage = global_storage, + .type = global_type, + .scope = &module.scope, + .name = arena_join_string(module.arena, [ "name.array.enum.", enum_type.name ][..]), + zero, + }, + .linkage = .internal, + zero, + }; + + global.emitted = 1; + + enum_type.content.enum.name_array = global; + } + + return enum_type.content.enum.name_array; +} + analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysis: TypeAnalysis) void { assert(!value.type); @@ -9407,6 +9523,337 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi { value_type = noreturn_type(module); }, + .string_to_enum => + { + >enum_type = value.content.string_to_enum.type; + >enum_string_value = value.content.string_to_enum.string; + + if (enum_type.id != .enum) + { + report_error(); + } + + if (!enum_type.content.enum.string_to_enum_function) + { + resolve_type_in_place(module, enum_type); + + >fields = enum_type.content.enum.fields; + >array_element_count = fields.length; + + >insert_block = LLVMGetInsertBlock(module.llvm.builder); + + >u1_type = uint1(module); + >u8_type = uint8(module); + >u64_type = uint64(module); + resolve_type_in_place(module, u1_type); + resolve_type_in_place(module, u8_type); + resolve_type_in_place(module, u64_type); + + >u64_zero = LLVMConstNull(u64_type.llvm.abi); + + >enum_alignment = get_byte_alignment(enum_type); + >enum_size = get_byte_size(enum_type); + >byte_size = align_forward(enum_size + 1, #extend(enum_alignment)); + + >struct_fields = arena_allocate_slice[Field](module.arena, 2); + + struct_fields[0] = { + .name = "enum_value", + .type = enum_type, + zero, + }; + + struct_fields[1] = { + .name = "is_valid", + .type = u1_type, + .offset = enum_size, + zero, + }; + + >struct_type = new_type(module, { + .content = { + .struct = { + .fields = struct_fields, + .byte_size = byte_size, + .byte_alignment = enum_alignment, + zero, + }, + }, + .id = .struct, + .name = "string_to_enum", + .scope = enum_type.scope, + zero, + }); + resolve_type_in_place(module, struct_type); + + >argument_types: [_]&LLVMType = [ module.llvm.pointer_type, u64_type.llvm.abi ]; + >llvm_function_type = LLVMFunctionType(struct_type.llvm.abi, &argument_types[0], argument_types.length, 0); + >slice_struct_type = get_slice_type(module, u8_type); + + >function_name = arena_join_string(module.arena, [ "string_to_enum.", enum_type.name ][..]); + >llvm_function = llvm_module_create_function(module.llvm.module, llvm_function_type, .internal, function_name); + LLVMSetFunctionCallConv(llvm_function, .fast); + + >name_array_global = get_enum_name_array_global(module, enum_type); + + >enum_value_type = enum_type.llvm.memory; + + >value_constant_buffer: [64]&LLVMValue = undefined; + for (i: 0..fields.length) + { + >field = &fields[i]; + >global_value = LLVMConstInt(enum_value_type, field.value, 0); + value_constant_buffer[i] = global_value; + } + + >value_array = LLVMConstArray2(enum_value_type, &value_constant_buffer[0], array_element_count); + >value_array_variable_type = LLVMArrayType2(enum_value_type, array_element_count); + >is_constant: u1 = 1; + >thread_local_mode: LLVMThreadLocalMode = .none; + >externally_initialized: u1 = 0; + >value_array_variable = llvm_create_global_variable(module.llvm.module, value_array_variable_type, is_constant, .internal, value_array, "value.array.enum", thread_local_mode, externally_initialized, enum_alignment, .global); + + >entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "entry"); + >return_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "return_block"); + >loop_entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "loop.entry"); + >loop_body_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "loop.body"); + >loop_exit_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "loop.exit"); + + LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); + + >arguments: [2]&LLVMValue = undefined; + LLVMGetParams(llvm_function, &arguments[0]); + + >return_value_alloca = create_alloca(module, { + .type = enum_type, + .name = "retval", + zero, + }); + + >return_boolean_alloca = create_alloca(module, { + .type = u8_type, + .name = "retbool", + zero, + }); + + >index_alloca = create_alloca(module, { + .type = u64_type, + .name = "index", + zero, + }); + + create_store(module, { + .source = u64_zero, + .destination = index_alloca, + .type = u64_type, + zero, + }); + + >slice_pointer = arguments[0]; + >slice_length = arguments[1]; + LLVMBuildBr(module.llvm.builder, loop_entry_block); + + LLVMPositionBuilderAtEnd(module.llvm.builder, loop_entry_block); + >index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + zero, + }); + >loop_compare = LLVMBuildICmp(module.llvm.builder, .ult, index_load, LLVMConstInt(u64_type.llvm.abi, array_element_count, 0), ""); + LLVMBuildCondBr(module.llvm.builder, loop_compare, loop_body_block, loop_exit_block); + + LLVMPositionBuilderAtEnd(module.llvm.builder, loop_body_block); + >body_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + zero, + }); + + >array_element_pointer = create_gep(module, { + .type = name_array_global.variable.type.llvm.memory, + .pointer = name_array_global.variable.storage.llvm, + .indices = [ u64_zero, body_index_load ][..], + zero, + }); + + >element_length_pointer = LLVMBuildStructGEP2(module.llvm.builder, slice_struct_type.llvm.abi, array_element_pointer, 1, ""); + >element_length = create_load(module, { + .type = u64_type, + .pointer = element_length_pointer, + zero, + }); + + >length_comparison = LLVMBuildICmp(module.llvm.builder, .eq, slice_length, element_length, ""); + + >length_match_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "length.match"); + >length_mismatch_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "length.mismatch"); + LLVMBuildCondBr(module.llvm.builder, length_comparison, length_match_block, length_mismatch_block); + + LLVMPositionBuilderAtEnd(module.llvm.builder, length_match_block); + + >s32_type = sint32(module); + resolve_type_in_place(module, s32_type); + + >memcmp = module.llvm.memcmp; + if (!memcmp) + { + memcmp = LLVMGetNamedFunction(module.llvm.module, "memcmp"); + if (!memcmp) + { + >arguments: [_]&LLVMType = [ + module.llvm.pointer_type, + module.llvm.pointer_type, + u64_type.llvm.abi, + ]; + >llvm_function_type = LLVMFunctionType(s32_type.llvm.abi, &arguments[0], arguments.length, 0); + >llvm_function = llvm_module_create_function(module.llvm.module, llvm_function_type, .external, "memcmp"); + memcmp = llvm_function; + } + + module.llvm.memcmp = memcmp; + } + + assert(memcmp != zero); + assert(module.llvm.memcmp != zero); + + >length_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + zero, + }); + + >length_array_element_pointer = create_gep(module, { + .type = name_array_global.variable.type.llvm.memory, + .pointer = name_array_global.variable.storage.llvm, + .indices = [ u64_zero, length_index_load ][..], + zero, + }); + + >element_pointer_pointer = LLVMBuildStructGEP2(module.llvm.builder, slice_struct_type.llvm.abi, length_array_element_pointer, 0, ""); + >element_pointer = create_load(module, { + .type = get_pointer_type(module, u8_type), + .pointer = element_pointer_pointer, + zero, + }); + + >memcmp_arguments: [_]&LLVMValue = [ + slice_pointer, + element_pointer, + slice_length, + ]; + >memcmp_return_result = LLVMBuildCall2(module.llvm.builder, LLVMGlobalGetValueType(memcmp), memcmp, &memcmp_arguments[0], memcmp_arguments.length, ""); + >content_comparison = LLVMBuildICmp(module.llvm.builder, .eq, memcmp_return_result, LLVMConstNull(s32_type.llvm.abi), ""); + >content_match_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "content.match"); + LLVMBuildCondBr(module.llvm.builder, content_comparison, content_match_block, length_mismatch_block); + + LLVMPositionBuilderAtEnd(module.llvm.builder, content_match_block); + + >content_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + zero, + }); + + >value_array_element_pointer = create_gep(module, { + .type = value_array_variable_type, + .pointer = value_array_variable, + .indices = [ u64_zero, content_index_load ][..], + zero, + }); + + >enum_value_load = create_load(module, { + .type = enum_type, + .pointer = value_array_element_pointer, + zero, + }); + + create_store(module, { + .source = enum_value_load, + .destination = return_value_alloca, + .type = enum_type, + zero, + }); + + create_store(module, { + .source = LLVMConstInt(u8_type.llvm.abi, 1, 0), + .destination = return_boolean_alloca, + .type = u8_type, + zero, + }); + + LLVMBuildBr(module.llvm.builder, return_block); + + LLVMPositionBuilderAtEnd(module.llvm.builder, length_mismatch_block); + + >inc_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + zero, + }); + + >inc = LLVMBuildAdd(module.llvm.builder, inc_index_load, LLVMConstInt(u64_type.llvm.abi, 1, 0), ""); + + create_store(module, { + .source = inc, + .destination = index_alloca, + .type = u64_type, + zero, + }); + + LLVMBuildBr(module.llvm.builder, loop_entry_block); + + LLVMPositionBuilderAtEnd(module.llvm.builder, loop_exit_block); + + create_store(module, { + .source = LLVMConstNull(enum_type.llvm.memory), + .destination = return_value_alloca, + .type = enum_type, + zero, + }); + create_store(module, { + .source = LLVMConstNull(u8_type.llvm.abi), + .destination = return_boolean_alloca, + .type = u8_type, + zero, + }); + LLVMBuildBr(module.llvm.builder, return_block); + + LLVMPositionBuilderAtEnd(module.llvm.builder, return_block); + >value_load = create_load(module, { + .type = enum_type, + .pointer = return_value_alloca, + .kind = .memory, + zero, + }); + + >return_value = LLVMBuildInsertValue(module.llvm.builder, LLVMGetPoison(struct_type.llvm.memory), value_load, 0, ""); + >bool_load = create_load(module, { + .type = u8_type, + .pointer = return_boolean_alloca, + zero, + }); + + return_value = LLVMBuildInsertValue(module.llvm.builder, return_value, bool_load, 1, ""); + + LLVMBuildRet(module.llvm.builder, return_value); + + // End of scope + LLVMPositionBuilderAtEnd(module.llvm.builder, insert_block); + + enum_type.content.enum.string_to_enum_function = llvm_function; + enum_type.content.enum.string_to_enum_struct_type = struct_type; + } + + >struct_type = enum_type.content.enum.string_to_enum_struct_type; + assert(struct_type != zero); + + typecheck(module, expected_type, struct_type); + + >string_type = get_slice_type(module, uint8(module)); + + analyze_type(module, enum_string_value, string_type, { .must_be_constant = analysis.must_be_constant, zero }); + value_type = struct_type; + }, else => { #trap(); @@ -11852,6 +12299,26 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con llvm_value = LLVMBuildUnreachable(module.llvm.builder); LLVMClearInsertionPosition(module.llvm.builder); }, + .string_to_enum => + { + >enum_type = value.content.string_to_enum.type; + >string_value = value.content.string_to_enum.string; + + emit_value(module, string_value, .memory, must_be_constant); + + >llvm_string_value = string_value.llvm; + + >s2e = enum_type.content.enum.string_to_enum_function; + >first_field = LLVMBuildExtractValue(module.llvm.builder, llvm_string_value, 0, ""); + >second_field = LLVMBuildExtractValue(module.llvm.builder, llvm_string_value, 1, ""); + >fields: [_]&LLVMValue = [ + first_field, + second_field, + ]; + >call = LLVMBuildCall2(module.llvm.builder, LLVMGlobalGetValueType(s2e), s2e, &fields[0], fields.length, ""); + LLVMSetInstructionCallConv(call, .fast); + llvm_value = call; + }, else => { #trap(); @@ -12151,6 +12618,20 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, }, } }, + .string_to_enum => + { + emit_value(module, right, .memory, 0); + + >enum_type = right.content.string_to_enum.type; + >s2e_struct_type = enum_type.content.enum.string_to_enum_struct_type; + + create_store(module, { + .source = right.llvm, + .destination = left_llvm, + .type = s2e_struct_type, + zero, + }); + }, else => { #trap(); @@ -12850,286 +13331,284 @@ emit = fn (module: &Module) void assert(!module.current_macro_instantiation); assert(!module.current_macro_declaration); - if (global.emitted) + if (!global.emitted) { - continue; - } - - switch (global.variable.storage.id) - { - .function, .forward_declared_function => + switch (global.variable.storage.id) { - >global_pointer_type = global.variable.storage.type; - assert(global_pointer_type.id == .pointer); - >global_value_type = global_pointer_type.content.pointer.element_type; - - >function_type = &global_value_type.content.function; - >semantic_argument_types = function_type.base.semantic_argument_types; - >semantic_return_type = function_type.base.semantic_return_type; - function_type.abi.argument_abis = arena_allocate_slice[AbiInformation](module.arena, semantic_argument_types.length); - >llvm_abi_argument_type_buffer: [64]&LLVMType = undefined; - - >resolved_calling_convention: ResolvedCallingConvention = undefined; - >calling_convention = function_type.base.calling_convention; - switch (calling_convention) + .function, .forward_declared_function => { - .c => + >global_pointer_type = global.variable.storage.type; + assert(global_pointer_type.id == .pointer); + >global_value_type = global_pointer_type.content.pointer.element_type; + + >function_type = &global_value_type.content.function; + >semantic_argument_types = function_type.base.semantic_argument_types; + >semantic_return_type = function_type.base.semantic_return_type; + function_type.abi.argument_abis = arena_allocate_slice[AbiInformation](module.arena, semantic_argument_types.length); + >llvm_abi_argument_type_buffer: [64]&LLVMType = undefined; + + >resolved_calling_convention: ResolvedCallingConvention = undefined; + >calling_convention = function_type.base.calling_convention; + switch (calling_convention) { - // TODO: switch on platform - resolved_calling_convention = .system_v; - }, - } + .c => + { + // TODO: switch on platform + resolved_calling_convention = .system_v; + }, + } - >register_call = resolved_calling_convention == .system_v and 0; // TODO: regcall calling convention + >register_call = resolved_calling_convention == .system_v and 0; // TODO: regcall calling convention - switch (resolved_calling_convention) - { - .system_v => + switch (resolved_calling_convention) { - >abi_argument_type_buffer: [64]&Type = undefined; - >abi_argument_type_count: u16 = 0; - - function_type.abi.available_registers = { - .system_v = { - .gpr = #select(register_call, 11, 6), - .sse = #select(register_call, 16, 8), - }, - }; - - function_type.abi.return_abi = abi_system_v_classify_return_type(module, resolve_alias(module, semantic_return_type)); - - >return_abi_kind = function_type.abi.return_abi.flags.kind; - - >abi_return_type: &Type = undefined; - - switch (return_abi_kind) + .system_v => { - .direct, - .extend, - => + >abi_argument_type_buffer: [64]&Type = undefined; + >abi_argument_type_count: u16 = 0; + + function_type.abi.available_registers = { + .system_v = { + .gpr = #select(register_call, 11, 6), + .sse = #select(register_call, 16, 8), + }, + }; + + function_type.abi.return_abi = abi_system_v_classify_return_type(module, resolve_alias(module, semantic_return_type)); + + >return_abi_kind = function_type.abi.return_abi.flags.kind; + + >abi_return_type: &Type = undefined; + + switch (return_abi_kind) { - abi_return_type = function_type.abi.return_abi.coerce_to_type; - }, - .ignore, - .indirect, - => - { - abi_return_type = void_type(module); - }, - else => - { - unreachable; // TODO - }, - } - - assert(abi_return_type != zero); - function_type.abi.abi_return_type = abi_return_type; - resolve_type_in_place(module, abi_return_type); - - if (return_abi_kind == .indirect) - { - assert(!function_type.abi.return_abi.flags.sret_after_this); - function_type.abi.available_registers.system_v.gpr -= 1; - >indirect_type = get_pointer_type(module, function_type.abi.return_abi.semantic_type); - resolve_type_in_place(module, indirect_type); - - >abi_index = abi_argument_type_count; - abi_argument_type_buffer[abi_index] = indirect_type; - llvm_abi_argument_type_buffer[abi_index] = indirect_type.llvm.abi; - - abi_argument_type_count += 1; - } - - if (semantic_argument_types.length != 0) - { - for (i: 0..semantic_argument_types.length) - { - >abi = &function_type.abi.argument_abis[i]; - >semantic_argument_type = function_type.base.semantic_argument_types[i]; - >is_named_argument = i < semantic_argument_types.length; - assert(is_named_argument); - - abi.& = abi_system_v_classify_argument(module, &function_type.abi.available_registers.system_v, llvm_abi_argument_type_buffer[..], abi_argument_type_buffer[..], { - .type = semantic_argument_type, - .abi_start = abi_argument_type_count, - .is_named_argument = is_named_argument, - zero, - }); - - abi_argument_type_count += abi.abi_count; + .direct, + .extend, + => + { + abi_return_type = function_type.abi.return_abi.coerce_to_type; + }, + .ignore, + .indirect, + => + { + abi_return_type = void_type(module); + }, + else => + { + unreachable; // TODO + }, } + assert(abi_return_type != zero); + function_type.abi.abi_return_type = abi_return_type; + resolve_type_in_place(module, abi_return_type); + + if (return_abi_kind == .indirect) + { + assert(!function_type.abi.return_abi.flags.sret_after_this); + function_type.abi.available_registers.system_v.gpr -= 1; + >indirect_type = get_pointer_type(module, function_type.abi.return_abi.semantic_type); + resolve_type_in_place(module, indirect_type); + + >abi_index = abi_argument_type_count; + abi_argument_type_buffer[abi_index] = indirect_type; + llvm_abi_argument_type_buffer[abi_index] = indirect_type.llvm.abi; + + abi_argument_type_count += 1; + } + + if (semantic_argument_types.length != 0) + { + for (i: 0..semantic_argument_types.length) + { + >abi = &function_type.abi.argument_abis[i]; + >semantic_argument_type = function_type.base.semantic_argument_types[i]; + >is_named_argument = i < semantic_argument_types.length; + assert(is_named_argument); + + abi.& = abi_system_v_classify_argument(module, &function_type.abi.available_registers.system_v, llvm_abi_argument_type_buffer[..], abi_argument_type_buffer[..], { + .type = semantic_argument_type, + .abi_start = abi_argument_type_count, + .is_named_argument = is_named_argument, + zero, + }); + + abi_argument_type_count += abi.abi_count; + } + + } + + >abi_argument_types = arena_allocate_slice[&Type](module.arena, #extend(abi_argument_type_count)); + memcpy(#pointer_cast(abi_argument_types.pointer), #pointer_cast(&abi_argument_type_buffer), #extend(#byte_size(&Type) * abi_argument_type_count)); + function_type.abi.abi_argument_types = abi_argument_types; + }, + .win64 => + { +#trap(); + }, + } + + >llvm_function_type = LLVMFunctionType(function_type.abi.abi_return_type.llvm.abi, &llvm_abi_argument_type_buffer[0], #truncate(function_type.abi.abi_argument_types.length), #extend(function_type.base.is_variable_argument)); + + >subroutine_type: &LLVMMetadata = zero; + + if (module.has_debug_info) + { + >debug_argument_type_buffer: [64]&LLVMMetadata = undefined; + >debug_argument_types = debug_argument_type_buffer[..function_type.abi.argument_abis.length + 1 + #extend(function_type.base.is_variable_argument)]; + debug_argument_types[0] = function_type.abi.return_abi.semantic_type.llvm.debug; + assert(debug_argument_types[0] != zero); + + // TODO: support double slicing: sliceable_value[1..][0..length] -- 2 slicing expressions + >debug_argument_type_slice = debug_argument_types[1..]; + debug_argument_type_slice = debug_argument_type_slice[0..function_type.abi.argument_abis.length]; + + for (i: 0..function_type.abi.argument_abis.length) + { + >abi = &function_type.abi.argument_abis[i]; + >debug_argument_type = &debug_argument_type_slice[i]; + debug_argument_type.& = abi.semantic_type.llvm.debug; + assert(debug_argument_type.& != zero); } - >abi_argument_types = arena_allocate_slice[&Type](module.arena, #extend(abi_argument_type_count)); - memcpy(#pointer_cast(abi_argument_types.pointer), #pointer_cast(&abi_argument_type_buffer), #extend(#byte_size(&Type) * abi_argument_type_count)); - function_type.abi.abi_argument_types = abi_argument_types; - }, - .win64 => - { - #trap(); - }, - } + if (function_type.base.is_variable_argument) + { + >void_ty = void_type(module); + assert(void_ty.llvm.debug != zero); + debug_argument_types[function_type.abi.argument_abis.length + 1] = void_ty.llvm.debug; + } - >llvm_function_type = LLVMFunctionType(function_type.abi.abi_return_type.llvm.abi, &llvm_abi_argument_type_buffer[0], #truncate(function_type.abi.abi_argument_types.length), #extend(function_type.base.is_variable_argument)); - - >subroutine_type: &LLVMMetadata = zero; - - if (module.has_debug_info) - { - >debug_argument_type_buffer: [64]&LLVMMetadata = undefined; - >debug_argument_types = debug_argument_type_buffer[..function_type.abi.argument_abis.length + 1 + #extend(function_type.base.is_variable_argument)]; - debug_argument_types[0] = function_type.abi.return_abi.semantic_type.llvm.debug; - assert(debug_argument_types[0] != zero); - - // TODO: support double slicing: sliceable_value[1..][0..length] -- 2 slicing expressions - >debug_argument_type_slice = debug_argument_types[1..]; - debug_argument_type_slice = debug_argument_type_slice[0..function_type.abi.argument_abis.length]; - - for (i: 0..function_type.abi.argument_abis.length) - { - >abi = &function_type.abi.argument_abis[i]; - >debug_argument_type = &debug_argument_type_slice[i]; - debug_argument_type.& = abi.semantic_type.llvm.debug; - assert(debug_argument_type.& != zero); + >flags: LLVMDIFlags = zero; + subroutine_type = LLVMDIBuilderCreateSubroutineType(module.llvm.di_builder, module.llvm.file, debug_argument_types.pointer, #truncate(debug_argument_types.length), flags); } - if (function_type.base.is_variable_argument) + assert(global.variable.storage.type.id == .pointer); + >value_type = global.variable.storage.type.content.pointer.element_type; + value_type.llvm.abi = llvm_function_type; + value_type.llvm.debug = subroutine_type; + + >llvm_linkage: LLVMLinkage = undefined; + + switch (global.linkage) { - >void_ty = void_type(module); - assert(void_ty.llvm.debug != zero); - debug_argument_types[function_type.abi.argument_abis.length + 1] = void_ty.llvm.debug; + .internal => + { + llvm_linkage = .internal; + }, + .external => + { + llvm_linkage = .external; + }, } - >flags: LLVMDIFlags = zero; - subroutine_type = LLVMDIBuilderCreateSubroutineType(module.llvm.di_builder, module.llvm.file, debug_argument_types.pointer, #truncate(debug_argument_types.length), flags); - } - - assert(global.variable.storage.type.id == .pointer); - >value_type = global.variable.storage.type.content.pointer.element_type; - value_type.llvm.abi = llvm_function_type; - value_type.llvm.debug = subroutine_type; - - >llvm_linkage: LLVMLinkage = undefined; - - switch (global.linkage) - { - .internal => + >llvm_function = llvm_module_create_function(module.llvm.module, llvm_function_type, llvm_linkage, global.variable.name); + global.variable.storage.llvm = llvm_function; + >llvm_calling_convention: LLVMCallingConvention = undefined; + switch (calling_convention) { - llvm_linkage = .internal; - }, - .external => + .c => + { + llvm_calling_convention = .c; + }, + } + + LLVMSetFunctionCallConv(llvm_function, llvm_calling_convention); + + emit_attributes(module, llvm_function, &LLVMAddAttributeAtIndex, { + .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, + .attributes = global.variable.storage.content.function.attributes, + }); + + >subprogram: &LLVMMetadata = zero; + + if (module.has_debug_info) { - llvm_linkage = .external; - }, - } + >name = global.variable.name; + >linkage_name = name; + >line = global.variable.line; + >is_local_to_unit = global.linkage == .internal; + >is_definition = global.variable.storage.id == .function; + >scope_line = line + 1; + >flags: LLVMDIFlags = zero; + >is_optimized = build_mode_is_optimized(module.build_mode); + subprogram = LLVMDIBuilderCreateFunction(module.llvm.di_builder, module.scope.llvm, name.pointer, name.length, linkage_name.pointer, linkage_name.length, module.llvm.file, line, subroutine_type, #extend(is_local_to_unit), #extend(is_definition), scope_line, flags, #extend(is_optimized)); + LLVMSetSubprogram(llvm_function, subprogram); + } - >llvm_function = llvm_module_create_function(module.llvm.module, llvm_function_type, llvm_linkage, global.variable.name); - global.variable.storage.llvm = llvm_function; - >llvm_calling_convention: LLVMCallingConvention = undefined; - switch (calling_convention) - { - .c => + switch (global.variable.storage.id) { - llvm_calling_convention = .c; - }, - } - - LLVMSetFunctionCallConv(llvm_function, llvm_calling_convention); - - emit_attributes(module, llvm_function, &LLVMAddAttributeAtIndex, { - .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, - .attributes = global.variable.storage.content.function.attributes, - }); - - >subprogram: &LLVMMetadata = zero; - - if (module.has_debug_info) - { - >name = global.variable.name; - >linkage_name = name; - >line = global.variable.line; - >is_local_to_unit = global.linkage == .internal; - >is_definition = global.variable.storage.id == .function; - >scope_line = line + 1; - >flags: LLVMDIFlags = zero; - >is_optimized = build_mode_is_optimized(module.build_mode); - subprogram = LLVMDIBuilderCreateFunction(module.llvm.di_builder, module.scope.llvm, name.pointer, name.length, linkage_name.pointer, linkage_name.length, module.llvm.file, line, subroutine_type, #extend(is_local_to_unit), #extend(is_definition), scope_line, flags, #extend(is_optimized)); - LLVMSetSubprogram(llvm_function, subprogram); - } - - switch (global.variable.storage.id) - { - .function => + .function => + { + global.variable.storage.content.function.scope.llvm = subprogram; + }, + .forward_declared_function => + { + assert(global.linkage == .external); + if (module.has_debug_info) + { + LLVMDIBuilderFinalizeSubprogram(module.llvm.di_builder, subprogram); + } + }, + else => { unreachable; }, + } + }, + .global => { - global.variable.storage.content.function.scope.llvm = subprogram; - }, - .forward_declared_function => - { - assert(global.linkage == .external); + assert(!module.current_function); + >initial_value = global.initial_value; + analyze_value(module, initial_value, global.variable.type, .memory, 1); + + >initial_value_type = initial_value.type; + + if (!global.variable.type) + { + global.variable.type = initial_value_type; + } + + >global_type = global.variable.type; + + if (global_type != initial_value_type) + { + report_error(); + } + + resolve_type_in_place(module, global_type); + + >is_constant: u1 = 0; + >linkage: LLVMLinkage = undefined; + switch (global.linkage) + { + .internal => { linkage = .internal; }, + .external => { linkage = .external; }, + } + + >thread_local_mode: LLVMThreadLocalMode = .none; + >externally_initialized: u1 = 0; + >alignment = get_byte_alignment(global_type); + >unnamed_address: LLVMUnnamedAddress = .none; + + >llvm_global = llvm_create_global_variable(module.llvm.module, global_type.llvm.memory, is_constant, linkage, initial_value.llvm, global.variable.name, thread_local_mode, externally_initialized, alignment, unnamed_address); + global.variable.storage.llvm = llvm_global; + global.variable.storage.type = get_pointer_type(module, global_type); + if (module.has_debug_info) { - LLVMDIBuilderFinalizeSubprogram(module.llvm.di_builder, subprogram); + >name = global.variable.name; + >linkage_name = name; + >local_to_unit = global.linkage == .internal; + >global_debug = LLVMDIBuilderCreateGlobalVariableExpression(module.llvm.di_builder, module.scope.llvm, name.pointer, name.length, linkage_name.pointer, linkage_name.length, module.llvm.file, global.variable.line, global_type.llvm.debug, #extend(local_to_unit), null_expression(module), zero, alignment * 8); + LLVMGlobalSetMetadata(llvm_global, 0, global_debug); } }, - else => { unreachable; }, - } - }, - .global => - { - assert(!module.current_function); - >initial_value = global.initial_value; - analyze_value(module, initial_value, global.variable.type, .memory, 1); - - >initial_value_type = initial_value.type; - - if (!global.variable.type) - { - global.variable.type = initial_value_type; - } - - >global_type = global.variable.type; - - if (global_type != initial_value_type) - { - report_error(); - } - - resolve_type_in_place(module, global_type); - - >is_constant: u1 = 0; - >linkage: LLVMLinkage = undefined; - switch (global.linkage) - { - .internal => { linkage = .internal; }, - .external => { linkage = .external; }, - } - - >thread_local_mode: LLVMThreadLocalMode = .none; - >externally_initialized: u1 = 0; - >alignment = get_byte_alignment(global_type); - >unnamed_address: LLVMUnnamedAddress = .none; - - >llvm_global = llvm_create_global_variable(module.llvm.module, global_type.llvm.memory, is_constant, linkage, initial_value.llvm, global.variable.name, thread_local_mode, externally_initialized, alignment, unnamed_address); - global.variable.storage.llvm = llvm_global; - global.variable.storage.type = get_pointer_type(module, global_type); - - if (module.has_debug_info) - { - >name = global.variable.name; - >linkage_name = name; - >local_to_unit = global.linkage == .internal; - >global_debug = LLVMDIBuilderCreateGlobalVariableExpression(module.llvm.di_builder, module.scope.llvm, name.pointer, name.length, linkage_name.pointer, linkage_name.length, module.llvm.file, global.variable.line, global_type.llvm.debug, #extend(local_to_unit), null_expression(module), zero, alignment * 8); - LLVMGlobalSetMetadata(llvm_global, 0, global_debug); - } - }, - else => - { - report_error(); - }, + else => + { + report_error(); + }, + } } global = global.next; @@ -13143,456 +13622,454 @@ emit = fn (module: &Module) void assert(!module.current_macro_instantiation); assert(!module.current_macro_declaration); - if (global.emitted) + if (!global.emitted) { - continue; - } - - if (global.variable.storage.id == .function) - { - module.current_function = global; - - >function = global.variable.storage; - assert(function.id == .function); - >pointer_type = function.type; - assert(pointer_type.id == .pointer); - >value_type = pointer_type.content.pointer.element_type; - assert(value_type == global.variable.type); - assert(value_type.id == .function); - >function_type = &value_type.content.function; - >semantic_argument_types = function_type.base.semantic_argument_types; - >llvm_function = function.llvm; - assert(llvm_function != zero); - - >llvm_abi_argument_buffer: [64]&LLVMValue = undefined; - >llvm_abi_arguments = llvm_abi_argument_buffer[..function_type.abi.abi_argument_types.length]; - LLVMGetParams(llvm_function, &llvm_abi_argument_buffer[0]); - - >entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "entry"); - >return_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "return_block"); - function.content.function.llvm.return_block = return_block; - - LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); - LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); - - >return_abi = &function_type.abi.return_abi; - switch (return_abi.flags.kind) + if (global.variable.storage.id == .function) { - .ignore => {}, - .indirect => + module.current_function = global; + + >function = global.variable.storage; + assert(function.id == .function); + >pointer_type = function.type; + assert(pointer_type.id == .pointer); + >value_type = pointer_type.content.pointer.element_type; + assert(value_type == global.variable.type); + assert(value_type.id == .function); + >function_type = &value_type.content.function; + >semantic_argument_types = function_type.base.semantic_argument_types; + >llvm_function = function.llvm; + assert(llvm_function != zero); + + >llvm_abi_argument_buffer: [64]&LLVMValue = undefined; + >llvm_abi_arguments = llvm_abi_argument_buffer[..function_type.abi.abi_argument_types.length]; + LLVMGetParams(llvm_function, &llvm_abi_argument_buffer[0]); + + >entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "entry"); + >return_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "return_block"); + function.content.function.llvm.return_block = return_block; + + LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); + LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); + + >return_abi = &function_type.abi.return_abi; + switch (return_abi.flags.kind) { - >indirect_argument_index = function_type.abi.return_abi.flags.sret_after_this; - if (function_type.abi.return_abi.flags.sret_after_this) - { - #trap(); - } - - global.variable.storage.content.function.llvm.return_alloca = llvm_abi_arguments[indirect_argument_index]; - - if (!function_type.abi.return_abi.flags.indirect_by_value) - { - #trap(); - } - }, - .in_alloca => - { - #trap(); - }, - else => - { - >alloca = create_alloca(module, { - .type = return_abi.semantic_type, - .name = "return_value", - zero, - }); - function.content.function.llvm.return_alloca = alloca; - }, - } - - >arguments = function.content.function.arguments; - >argument_abis = function_type.abi.argument_abis; - assert(arguments.length == argument_abis.length); - - for (i: 0..semantic_argument_types.length) - { - >argument = &arguments[i]; - >argument_abi = &argument_abis[i]; - // TODO: double slice - >argument_abi_arguments = llvm_abi_arguments[#extend(argument_abi.abi_start)..]; - argument_abi_arguments = argument_abi_arguments[..#extend(argument_abi.abi_count)]; - - >semantic_argument_storage: &LLVMValue = zero; - - switch (argument_abi.flags.kind) - { - .direct, .extend => - { - >first_argument = argument_abi_arguments[0]; - >coerce_to_type = abi_get_coerce_to_type(argument_abi); - - if (coerce_to_type.id != .struct and argument_abi.attributes.direct.offset == 0 and type_is_abi_equal(module, coerce_to_type, argument_abi.semantic_type)) + .ignore => {}, + .indirect => { - assert(argument_abi.abi_count == 1); - >is_promoted: u1 = 0; - >v = first_argument; - - if (coerce_to_type.llvm.abi != LLVMTypeOf(v)) + >indirect_argument_index = function_type.abi.return_abi.flags.sret_after_this; + if (function_type.abi.return_abi.flags.sret_after_this) { - #trap(); +#trap(); } - if (is_promoted) - { - #trap(); - } + global.variable.storage.content.function.llvm.return_alloca = llvm_abi_arguments[indirect_argument_index]; - // TODO: this we can get rid of because we handle all of this inside `create_alloca`, load, stores, etc - if (is_arbitrary_bit_integer(argument_abi.semantic_type)) + if (!function_type.abi.return_abi.flags.indirect_by_value) { - >bit_count = get_bit_size(argument_abi.semantic_type); - >abi_bit_count = align_bit_count(bit_count); - >is_signed = type_is_signed(argument_abi.semantic_type); - >destination_type = integer_type(module, { .bit_count = abi_bit_count, .signed = is_signed }); +#trap(); + } + }, + .in_alloca => + { +#trap(); + }, + else => + { + >alloca = create_alloca(module, { + .type = return_abi.semantic_type, + .name = "return_value", + zero, + }); + function.content.function.llvm.return_alloca = alloca; + }, + } + + >arguments = function.content.function.arguments; + >argument_abis = function_type.abi.argument_abis; + assert(arguments.length == argument_abis.length); + + for (i: 0..semantic_argument_types.length) + { + >argument = &arguments[i]; + >argument_abi = &argument_abis[i]; + // TODO: double slice + >argument_abi_arguments = llvm_abi_arguments[#extend(argument_abi.abi_start)..]; + argument_abi_arguments = argument_abi_arguments[..#extend(argument_abi.abi_count)]; + + >semantic_argument_storage: &LLVMValue = zero; + + switch (argument_abi.flags.kind) + { + .direct, .extend => + { + >first_argument = argument_abi_arguments[0]; + >coerce_to_type = abi_get_coerce_to_type(argument_abi); + + if (coerce_to_type.id != .struct and argument_abi.attributes.direct.offset == 0 and type_is_abi_equal(module, coerce_to_type, argument_abi.semantic_type)) + { + assert(argument_abi.abi_count == 1); + >is_promoted: u1 = 0; + >v = first_argument; + + if (coerce_to_type.llvm.abi != LLVMTypeOf(v)) + { +#trap(); + } + + if (is_promoted) + { +#trap(); + } + + // TODO: this we can get rid of because we handle all of this inside `create_alloca`, load, stores, etc + if (is_arbitrary_bit_integer(argument_abi.semantic_type)) + { + >bit_count = get_bit_size(argument_abi.semantic_type); + >abi_bit_count = align_bit_count(bit_count); + >is_signed = type_is_signed(argument_abi.semantic_type); + >destination_type = integer_type(module, { .bit_count = abi_bit_count, .signed = is_signed }); + >alloca = create_alloca(module, { + .type = destination_type, + .name = argument.variable.name, + zero, + }); + + >result: &LLVMValue = undefined; + + if (bit_count < abi_bit_count) + { + >llvm_type = destination_type.llvm.memory; + if (is_signed) + { + result = LLVMBuildSExt(module.llvm.builder, first_argument, llvm_type, ""); + } + else + { + result = LLVMBuildZExt(module.llvm.builder, first_argument, llvm_type, ""); + } + } + else + { +#trap(); + } + + create_store(module, { + .source = result, + .destination = alloca, + .type = destination_type, + zero, + }); + + semantic_argument_storage = alloca; + } + else + { + >alloca = create_alloca(module, { + .type = argument_abi.semantic_type, + .name = argument.variable.name, + zero, + }); + + create_store(module, { + .source = first_argument, + .destination = alloca, + .type = argument_abi.semantic_type, + zero, + }); + + semantic_argument_storage = alloca; + } + } + else + { + >is_fixed_vector_type: u1 = 0; + if (is_fixed_vector_type) + { +#trap(); + } + + if (coerce_to_type.id == .struct and coerce_to_type.content.struct.fields.length > 1 and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) + { + >contains_homogeneous_scalable_vector_types: u1 = 0; + if (contains_homogeneous_scalable_vector_types) + { +#trap(); + } + } + >alloca = create_alloca(module, { - .type = destination_type, + .type = argument_abi.semantic_type, .name = argument.variable.name, zero, }); - >result: &LLVMValue = undefined; + >pointer: &LLVMValue = undefined; + >pointer_type: &Type = undefined; - if (bit_count < abi_bit_count) + if (argument_abi.attributes.direct.offset > 0) { - >llvm_type = destination_type.llvm.memory; - if (is_signed) +#trap(); + } + else + { + pointer = alloca; + pointer_type = argument_abi.semantic_type; + } + + if (coerce_to_type.id == .struct and coerce_to_type.content.struct.fields.length > 1 and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) + { + >struct_size = get_byte_size(coerce_to_type); + >pointer_element_size = get_byte_size(pointer_type); + >is_scalable: u1 = 0; + + if (is_scalable) { - result = LLVMBuildSExt(module.llvm.builder, first_argument, llvm_type, ""); +#trap(); } else { - result = LLVMBuildZExt(module.llvm.builder, first_argument, llvm_type, ""); + >source_size = struct_size; + >destination_size = pointer_element_size; + >address_alignment = get_byte_alignment(argument_abi.semantic_type); + + >address: &LLVMValue = undefined; + + if (source_size <= destination_size) + { + address = alloca; + } + else + { + address = create_alloca(module, { + .type = coerce_to_type, + .name = "coerce", + .alignment = address_alignment, + }); + } + + >fields = coerce_to_type.content.struct.fields; + assert(fields.length == #extend(argument_abi.abi_count)); + + resolve_type_in_place(module, coerce_to_type); + + for (i: 0..fields.length) + { + >field = &fields[i]; + >gep = LLVMBuildStructGEP2(module.llvm.builder, coerce_to_type.llvm.abi, address, #truncate(i), ""); + create_store(module, { + .source = argument_abi_arguments[i], + .destination = gep, + .type = fields[i].type, + zero, + }); + } + + if (source_size > destination_size) + { + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + + >memcpy_size = LLVMConstInt(u64_type.llvm.abi, destination_size, 0); + LLVMBuildMemCpy(module.llvm.builder, pointer, address_alignment, address, address_alignment, memcpy_size); + } } } else { + assert(argument_abi.abi_count == 1); + >abi_argument_type = function_type.abi.abi_argument_types[argument_abi.abi_start]; + >destination_size: u64 = get_byte_size(pointer_type) - #extend(argument_abi.attributes.direct.offset); + >is_volatile: u1 = 0; + create_coerced_store(module, argument_abi_arguments[0], abi_argument_type, pointer, pointer_type, destination_size, is_volatile); + } + + semantic_argument_storage = alloca; + } + }, + .indirect => + { + assert(argument_abi.abi_count == 1); + >evaluation_kind = get_evaluation_kind(argument_abi.semantic_type); + + switch (evaluation_kind) + { + else => + { + if (argument_abi.flags.indirect_realign or argument_abi.flags.kind == .indirect_aliased) + { +#trap(); + } + + >use_indirect_debug_address = !argument_abi.flags.indirect_by_value; + if (use_indirect_debug_address) + { +#trap(); + } + + >llvm_argument = argument_abi_arguments[0]; + semantic_argument_storage = llvm_argument; + }, + .scalar => + { +#trap(); + }, + } + }, + else => + { + unreachable; + }, + } + + assert(semantic_argument_storage != zero); + + >storage = new_value(module); + >value_type = argument.variable.type; + storage.& = { + .type = get_pointer_type(module, value_type), + .id = .argument, + .llvm = semantic_argument_storage, + zero, + }; + argument.variable.storage = storage; + + if (module.has_debug_info) + { + emit_debug_argument(module, argument, entry_block); + } + } + + analyze_block(module, function.content.function.block); + + >current_basic_block = LLVMGetInsertBlock(module.llvm.builder); + + if (current_basic_block) + { + assert(!LLVMGetBasicBlockTerminator(current_basic_block)); + 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 + { + emit_block(module, return_block); + } + } + else + { + >has_single_jump_to_return_block: u1 = 0; + + >first_use = LLVMGetFirstUse(#pointer_cast(return_block)); + >user: &LLVMValue = zero; + + if (first_use) + { + >second_use = LLVMGetNextUse(first_use); + >has_one_use = first_use != zero and first_use == zero; + + if (has_one_use) + { + user = LLVMGetUser(first_use); + has_single_jump_to_return_block = LLVMIsABranchInst(user) != zero and? LLVMIsConditional(user) != 0 and? LLVMGetSuccessor(user, 0) == return_block; + } + } + + if (has_single_jump_to_return_block) + { +#trap(); + } + else + { + emit_block(module, return_block); + } + } + + if (module.has_debug_info) + { + LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); + >subprogram = LLVMGetSubprogram(llvm_function); + LLVMDIBuilderFinalizeSubprogram(module.llvm.di_builder, subprogram); + } + + >semantic_return_type = return_abi.semantic_type; + if (semantic_return_type == noreturn_type(module) or function.content.function.attributes.naked) + { +#trap(); + } + else if (semantic_return_type == void_type(module)) + { + LLVMBuildRetVoid(module.llvm.builder); + } + else + { + >return_value: &LLVMValue = zero; + + switch (return_abi.flags.kind) + { + .direct, .extend => + { + >return_alloca = function.content.function.llvm.return_alloca; + >coerce_to_type = abi_get_coerce_to_type(return_abi); + if (type_is_abi_equal(module, coerce_to_type, semantic_return_type) and? return_abi.attributes.direct.offset == 0) + { + >store = llvm_find_return_value_dominating_store(module.llvm.builder, return_alloca, semantic_return_type.llvm.abi); + if (store) + { + return_value = LLVMGetOperand(store, 0); + >alloca = LLVMGetOperand(store, 1); + assert(alloca == return_alloca); + LLVMInstructionEraseFromParent(store); + LLVMInstructionEraseFromParent(alloca); + } + else + { + return_value = create_load(module, { + .type = semantic_return_type, + .pointer = return_alloca, + zero, + }); + } + } + else + { + >source: &LLVMValue = zero; + if (function_type.abi.return_abi.attributes.direct.offset == 0) + { + source = return_alloca; + } + else + { #trap(); } - create_store(module, { - .source = result, - .destination = alloca, - .type = destination_type, - zero, - }); + assert(source != zero); - semantic_argument_storage = alloca; + >source_type = function_type.abi.return_abi.semantic_type; + >destination_type = coerce_to_type; + >result = create_coerced_load(module, source, source_type, destination_type); + return_value = result; } - else + }, + .indirect => { - >alloca = create_alloca(module, { - .type = argument_abi.semantic_type, - .name = argument.variable.name, - zero, - }); - - create_store(module, { - .source = first_argument, - .destination = alloca, - .type = argument_abi.semantic_type, - zero, - }); - - semantic_argument_storage = alloca; - } - } - else - { - >is_fixed_vector_type: u1 = 0; - if (is_fixed_vector_type) - { - #trap(); - } - - if (coerce_to_type.id == .struct and coerce_to_type.content.struct.fields.length > 1 and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) - { - >contains_homogeneous_scalable_vector_types: u1 = 0; - if (contains_homogeneous_scalable_vector_types) + >evaluation_kind = get_evaluation_kind(function_type.abi.return_abi.semantic_type); + switch (evaluation_kind) { - #trap(); + .scalar => { #trap(); }, + .aggregate => {}, + .complex => { #trap(); }, } - } - - >alloca = create_alloca(module, { - .type = argument_abi.semantic_type, - .name = argument.variable.name, - zero, - }); - - >pointer: &LLVMValue = undefined; - >pointer_type: &Type = undefined; - - if (argument_abi.attributes.direct.offset > 0) - { - #trap(); - } - else - { - pointer = alloca; - pointer_type = argument_abi.semantic_type; - } - - if (coerce_to_type.id == .struct and coerce_to_type.content.struct.fields.length > 1 and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) - { - >struct_size = get_byte_size(coerce_to_type); - >pointer_element_size = get_byte_size(pointer_type); - >is_scalable: u1 = 0; - - if (is_scalable) - { - #trap(); - } - else - { - >source_size = struct_size; - >destination_size = pointer_element_size; - >address_alignment = get_byte_alignment(argument_abi.semantic_type); - - >address: &LLVMValue = undefined; - - if (source_size <= destination_size) - { - address = alloca; - } - else - { - address = create_alloca(module, { - .type = coerce_to_type, - .name = "coerce", - .alignment = address_alignment, - }); - } - - >fields = coerce_to_type.content.struct.fields; - assert(fields.length == #extend(argument_abi.abi_count)); - - resolve_type_in_place(module, coerce_to_type); - - for (i: 0..fields.length) - { - >field = &fields[i]; - >gep = LLVMBuildStructGEP2(module.llvm.builder, coerce_to_type.llvm.abi, address, #truncate(i), ""); - create_store(module, { - .source = argument_abi_arguments[i], - .destination = gep, - .type = fields[i].type, - zero, - }); - } - - if (source_size > destination_size) - { - >u64_type = uint64(module); - resolve_type_in_place(module, u64_type); - - >memcpy_size = LLVMConstInt(u64_type.llvm.abi, destination_size, 0); - LLVMBuildMemCpy(module.llvm.builder, pointer, address_alignment, address, address_alignment, memcpy_size); - } - } - } - else - { - assert(argument_abi.abi_count == 1); - >abi_argument_type = function_type.abi.abi_argument_types[argument_abi.abi_start]; - >destination_size: u64 = get_byte_size(pointer_type) - #extend(argument_abi.attributes.direct.offset); - >is_volatile: u1 = 0; - create_coerced_store(module, argument_abi_arguments[0], abi_argument_type, pointer, pointer_type, destination_size, is_volatile); - } - - semantic_argument_storage = alloca; - } - }, - .indirect => - { - assert(argument_abi.abi_count == 1); - >evaluation_kind = get_evaluation_kind(argument_abi.semantic_type); - - switch (evaluation_kind) - { - else => - { - if (argument_abi.flags.indirect_realign or argument_abi.flags.kind == .indirect_aliased) - { - #trap(); - } - - >use_indirect_debug_address = !argument_abi.flags.indirect_by_value; - if (use_indirect_debug_address) - { - #trap(); - } - - >llvm_argument = argument_abi_arguments[0]; - semantic_argument_storage = llvm_argument; }, - .scalar => - { - #trap(); - }, - } - }, - else => - { - unreachable; - }, - } - - assert(semantic_argument_storage != zero); - - >storage = new_value(module); - >value_type = argument.variable.type; - storage.& = { - .type = get_pointer_type(module, value_type), - .id = .argument, - .llvm = semantic_argument_storage, - zero, - }; - argument.variable.storage = storage; - - if (module.has_debug_info) - { - emit_debug_argument(module, argument, entry_block); - } - } - - analyze_block(module, function.content.function.block); - - >current_basic_block = LLVMGetInsertBlock(module.llvm.builder); - - if (current_basic_block) - { - assert(!LLVMGetBasicBlockTerminator(current_basic_block)); - 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 - { - emit_block(module, return_block); - } - } - else - { - >has_single_jump_to_return_block: u1 = 0; - - >first_use = LLVMGetFirstUse(#pointer_cast(return_block)); - >user: &LLVMValue = zero; - - if (first_use) - { - >second_use = LLVMGetNextUse(first_use); - >has_one_use = first_use != zero and first_use == zero; - - if (has_one_use) - { - user = LLVMGetUser(first_use); - has_single_jump_to_return_block = LLVMIsABranchInst(user) != zero and? LLVMIsConditional(user) != 0 and? LLVMGetSuccessor(user, 0) == return_block; } + + LLVMBuildRet(module.llvm.builder, return_value); } - if (has_single_jump_to_return_block) - { - #trap(); - } - else - { - emit_block(module, return_block); - } + // END OF SCOPE + module.current_function = zero; } - - if (module.has_debug_info) - { - LLVMSetCurrentDebugLocation2(module.llvm.builder, zero); - >subprogram = LLVMGetSubprogram(llvm_function); - LLVMDIBuilderFinalizeSubprogram(module.llvm.di_builder, subprogram); - } - - >semantic_return_type = return_abi.semantic_type; - if (semantic_return_type == noreturn_type(module) or function.content.function.attributes.naked) - { - #trap(); - } - else if (semantic_return_type == void_type(module)) - { - LLVMBuildRetVoid(module.llvm.builder); - } - else - { - >return_value: &LLVMValue = zero; - - switch (return_abi.flags.kind) - { - .direct, .extend => - { - >return_alloca = function.content.function.llvm.return_alloca; - >coerce_to_type = abi_get_coerce_to_type(return_abi); - if (type_is_abi_equal(module, coerce_to_type, semantic_return_type) and? return_abi.attributes.direct.offset == 0) - { - >store = llvm_find_return_value_dominating_store(module.llvm.builder, return_alloca, semantic_return_type.llvm.abi); - if (store) - { - return_value = LLVMGetOperand(store, 0); - >alloca = LLVMGetOperand(store, 1); - assert(alloca == return_alloca); - LLVMInstructionEraseFromParent(store); - LLVMInstructionEraseFromParent(alloca); - } - else - { - return_value = create_load(module, { - .type = semantic_return_type, - .pointer = return_alloca, - zero, - }); - } - } - else - { - >source: &LLVMValue = zero; - if (function_type.abi.return_abi.attributes.direct.offset == 0) - { - source = return_alloca; - } - else - { - #trap(); - } - - assert(source != zero); - - >source_type = function_type.abi.return_abi.semantic_type; - >destination_type = coerce_to_type; - >result = create_coerced_load(module, source, source_type, destination_type); - return_value = result; - } - }, - .indirect => - { - >evaluation_kind = get_evaluation_kind(function_type.abi.return_abi.semantic_type); - switch (evaluation_kind) - { - .scalar => { #trap(); }, - .aggregate => {}, - .complex => { #trap(); }, - } - }, - } - - LLVMBuildRet(module.llvm.builder, return_value); - } - - // END OF SCOPE - module.current_function = zero; } global = global.next; @@ -13977,6 +14454,7 @@ names: [_][]u8 = "abi_enum_bool", "return_small_struct", "c_abi", + "string_to_enum", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/emitter.cpp b/src/emitter.cpp index 2506be8..32fa326 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -9,7 +9,7 @@ fn LLVMValueRef llvm_module_create_function(Arena* arena, LLVMModuleRef module, return function; } -fn LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMThreadLocalMode thread_local_mode, bool externally_initialized, u32 alignment, LLVMUnnamedAddr unnamed_address) +fn LLVMValueRef llvm_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMThreadLocalMode thread_local_mode, bool externally_initialized, u32 alignment, LLVMUnnamedAddr unnamed_address) { assert(name.pointer[name.length] == 0); auto global = LLVMAddGlobal(module, type, (char*)name.pointer); @@ -2539,7 +2539,6 @@ fn Global* get_enum_name_array_global(Module* module, Type* enum_type) auto u64_type = uint64(module); resolve_type_in_place(module, u8_type); resolve_type_in_place(module, u64_type); - LLVMValueRef name_before = 0; LLVMValueRef name_constant_buffer[64]; for (u32 i = 0; i < fields.length; i += 1) @@ -2553,11 +2552,9 @@ fn Global* get_enum_name_array_global(Module* module, Type* enum_type) string_literal("."), field.name, }; - unsigned address_space = 0; auto initial_value = LLVMConstStringInContext2(module->llvm.context, (char*)field.name.pointer, field.name.length, false); u32 alignment = 1; - auto name_global = llvm_module_create_global_variable(module->llvm.module, LLVMArrayType2(u8_type->llvm.abi, field.name.length + null_terminate), is_constant, LLVMInternalLinkage, initial_value, arena_join_string(module->arena, array_to_slice(name_parts)), LLVMNotThreadLocal, false, alignment, LLVMGlobalUnnamedAddr); - name_before = name_global; + auto name_global = llvm_create_global_variable(module->llvm.module, LLVMArrayType2(u8_type->llvm.abi, field.name.length + null_terminate), is_constant, LLVMInternalLinkage, initial_value, arena_join_string(module->arena, array_to_slice(name_parts)), LLVMNotThreadLocal, false, alignment, LLVMGlobalUnnamedAddr); LLVMValueRef constants[] = { name_global, LLVMConstInt(u64_type->llvm.abi, field.name.length, false), @@ -2571,8 +2568,7 @@ fn Global* get_enum_name_array_global(Module* module, Type* enum_type) auto name_array = LLVMConstArray2(slice_type->llvm.abi, name_constant_buffer, array_element_count); auto name_array_type = LLVMArrayType2(slice_type->llvm.abi, array_element_count); auto is_constant = true; - unsigned address_space = 0; - auto name_array_variable = llvm_module_create_global_variable(module->llvm.module, name_array_type, is_constant, LLVMInternalLinkage, name_array, string_literal("name.array.enum"), LLVMNotThreadLocal, false, get_byte_alignment(slice_type), LLVMGlobalUnnamedAddr); + auto name_array_variable = llvm_create_global_variable(module->llvm.module, name_array_type, is_constant, LLVMInternalLinkage, name_array, string_literal("name.array.enum"), LLVMNotThreadLocal, false, get_byte_alignment(slice_type), LLVMGlobalUnnamedAddr); auto global_type = get_array_type(module, slice_type, array_element_count); resolve_type_in_place(module, global_type); @@ -4281,9 +4277,8 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal auto value_array_variable_type = LLVMArrayType2(enum_value_type, array_element_count); auto is_constant = true; LLVMThreadLocalMode thread_local_mode = LLVMNotThreadLocal; - unsigned address_space = 0; auto externally_initialized = false; - auto value_array_variable = llvm_module_create_global_variable(module->llvm.module, value_array_variable_type, is_constant, LLVMInternalLinkage, value_array, string_literal("value.array.enum"), thread_local_mode, externally_initialized, enum_alignment, LLVMGlobalUnnamedAddr); + auto value_array_variable = llvm_create_global_variable(module->llvm.module, value_array_variable_type, is_constant, LLVMInternalLinkage, value_array, string_literal("value.array.enum"), thread_local_mode, externally_initialized, enum_alignment, LLVMGlobalUnnamedAddr); auto* entry_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "entry"); auto* return_block = LLVMAppendBasicBlockInContext(module->llvm.context, llvm_function, "return_block"); @@ -5177,7 +5172,7 @@ fn SliceEmitResult emit_string_literal(Module* module, Value* value) LLVMThreadLocalMode tlm = LLVMNotThreadLocal; bool externally_initialized = false; u32 alignment = 1; - auto global = llvm_module_create_global_variable(module->llvm.module, string_type, is_constant, LLVMInternalLinkage, constant_string, string_literal("conststring"), tlm, externally_initialized, alignment, LLVMGlobalUnnamedAddr); + auto global = llvm_create_global_variable(module->llvm.module, string_type, is_constant, LLVMInternalLinkage, constant_string, string_literal("conststring"), tlm, externally_initialized, alignment, LLVMGlobalUnnamedAddr); return { global, LLVMConstInt(uint64(module)->llvm.abi, length, false) }; } break; @@ -5550,11 +5545,10 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, bool is_constant = true; LLVMLinkage linkage_type = LLVMInternalLinkage; LLVMThreadLocalMode thread_local_mode = {}; - u32 address_space = 0; bool externally_initialized = false; auto alignment = get_byte_alignment(semantic_argument_type); - auto global = llvm_module_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("const.struct"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); + auto global = llvm_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("const.struct"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); for (u32 i = 0; i < coerce_fields.length; i += 1) { @@ -5705,7 +5699,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, bool externally_initialized = false; auto alignment = get_byte_alignment(semantic_argument_type); - auto global = llvm_module_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("indirect.const.aggregate"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); + auto global = llvm_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("indirect.const.aggregate"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); llvm_abi_argument_value_buffer[abi_argument_count] = global; abi_argument_count += 1; @@ -6446,11 +6440,10 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, bool is_constant = true; LLVMLinkage linkage_type = LLVMInternalLinkage; LLVMThreadLocalMode thread_local_mode = {}; - u32 address_space = 0; bool externally_initialized = false; auto alignment = get_byte_alignment(resolved_value_type); - auto global = llvm_module_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("array.init"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); + auto global = llvm_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("array.init"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); u64 memcpy_size = get_byte_size(resolved_value_type); LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, global, alignment, LLVMConstInt(uint64_type->llvm.abi, memcpy_size, false)); @@ -6523,7 +6516,7 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, LLVMLinkage linkage_type = LLVMInternalLinkage; LLVMThreadLocalMode thread_local_mode = LLVMNotThreadLocal; bool externally_initialized = false; - auto global = llvm_module_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("const.aggregate"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); + auto global = llvm_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("const.aggregate"), thread_local_mode, externally_initialized, alignment, LLVMGlobalUnnamedAddr); LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, global, alignment, byte_size_value); } else @@ -7327,7 +7320,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect assert(array_type->id == TypeId::array); resolve_type_in_place(module, array_type); auto alignment = get_byte_alignment(resolved_value_type); - auto value_array_variable = llvm_module_create_global_variable(module->llvm.module, array_type->llvm.memory, is_constant, LLVMInternalLinkage, array_value, string_literal("enum.values"), LLVMNotThreadLocal, 0, alignment, LLVMGlobalUnnamedAddr); + auto value_array_variable = llvm_create_global_variable(module->llvm.module, array_type->llvm.memory, is_constant, LLVMInternalLinkage, array_value, string_literal("enum.values"), LLVMNotThreadLocal, 0, alignment, LLVMGlobalUnnamedAddr); llvm_value = value_array_variable; } break; } @@ -9502,7 +9495,7 @@ void emit(Module* module) bool externally_initialized = false; auto alignment = get_byte_alignment(global_type); - auto global_llvm = llvm_module_create_global_variable(module->llvm.module, global_type->llvm.memory, is_constant, linkage, global->variable.initial_value->llvm, global->variable.name, thread_local_mode, externally_initialized, alignment, LLVMNoUnnamedAddr); + auto global_llvm = llvm_create_global_variable(module->llvm.module, global_type->llvm.memory, is_constant, linkage, global->variable.initial_value->llvm, global->variable.name, thread_local_mode, externally_initialized, alignment, LLVMNoUnnamedAddr); global->variable.storage->llvm = global_llvm; global->variable.storage->type = get_pointer_type(module, global_type); -- 2.43.0 From bb57af76420aa46b2ac65bacf8f907f2dbcb6e84 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 21:43:02 -0600 Subject: [PATCH 15/16] Pass a couple more tests --- src/compiler.bbb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler.bbb b/src/compiler.bbb index c11340a..28f7798 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -14455,6 +14455,8 @@ names: [_][]u8 = "return_small_struct", "c_abi", "string_to_enum", + "empty_if", + "else_if", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 -- 2.43.0 From 81fd0aebfd1865578ada88ed72934f2268bac957 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 13 Jun 2025 22:06:04 -0600 Subject: [PATCH 16/16] Switch statement --- src/compiler.bbb | 336 ++++++++++++++++++++++++++++++++++++++++++++++- src/emitter.cpp | 30 +++++ src/parser.cpp | 10 +- 3 files changed, 370 insertions(+), 6 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 28f7798..447cc4c 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -2498,6 +2498,9 @@ llvm_create_global_variable = fn (module: &LLVMModule, type: &LLVMType, is_const [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] LLVMBuildSwitch = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, else: &LLVMBasicBlock, case_count: u32) &LLVMValue; +[extern] LLVMAddCase = fn [cc(c)] (switch_instruction: &LLVMValue, value: &LLVMValue, block: &LLVMBasicBlock) void; + [extern] LLVMBuildPhi = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, name: &u8) &LLVMValue; [extern] LLVMAddIncoming = fn [cc(c)] (phi: &LLVMValue, values: &&LLVMValue, blocks: &&LLVMBasicBlock, count: u32) void; @@ -2738,10 +2741,29 @@ StatementWhile = struct block: &Block, } +StatementSwitchDiscriminantId = enum +{ + single, + range, +} + +StatementSwitchDiscriminantContent = union +{ + single: &Value, + range: [2]&Value, +} + +StatementSwitchDiscriminant = struct +{ + content: StatementSwitchDiscriminantContent, + id: StatementSwitchDiscriminantId, +} + StatementSwitchClause = struct { - values: []&Value, + values: []StatementSwitchDiscriminant, block: &Block, + basic_block: &LLVMBasicBlock, } StatementSwitch = struct @@ -6014,7 +6036,149 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement }, .switch => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + >discriminant = parse_value(module, scope, zero); + + skip_space(module); + expect_character(module, right_parenthesis); + + skip_space(module); + expect_character(module, left_brace); + + >clause_buffer: [64]StatementSwitchClause = undefined; + >clause_count: u64 = 0; + + while (1) + { + skip_space(module); + + >is_else: u1 = 0; + if (is_identifier_start(module.content[module.offset])) + { + >else_checkpoint = get_checkpoint(module); + >i = parse_identifier(module); + is_else = string_equal(i, "else"); + + if (!is_else) + { + set_checkpoint(module, else_checkpoint); + } + } + + >clause_values: []StatementSwitchDiscriminant = zero; + + if (is_else) + { + skip_space(module); + expect_character(module, '='); + expect_character(module, '>'); + } + else + { + >case_buffer: [64]StatementSwitchDiscriminant = undefined; + >case_count: u64 = 0; + + while (1) + { + >first_case_value = parse_value(module, scope, zero); + + skip_space(module); + + >checkpoint = get_checkpoint(module); + >token = tokenize(module); + + >clause_discriminant: StatementSwitchDiscriminant = undefined; + + switch (token.id) + { + .triple_dot => + { + >last_case_value = parse_value(module, scope, zero); + clause_discriminant = { + .content = { + .range = [ first_case_value, last_case_value ], + }, + .id = .range, + }; + }, + else => + { + if (token.id != .comma) + { + set_checkpoint(module, checkpoint); + } + + clause_discriminant = { + .content = { + .single = first_case_value, + }, + .id = .single, + }; + }, + } + + switch (clause_discriminant.id) + { + .single => { assert(clause_discriminant.content.single != zero); }, + .range => + { + assert(clause_discriminant.content.range[0] != zero); + assert(clause_discriminant.content.range[1] != zero); + }, + } + + case_buffer[case_count] = clause_discriminant; + case_count += 1; + + skip_space(module); + + if (consume_character_if_match(module, '=')) + { + expect_character(module, '>'); + break; + } + } + + clause_values = arena_allocate_slice[StatementSwitchDiscriminant](module.arena, case_count); + memcpy(#pointer_cast(clause_values.pointer), #pointer_cast(&case_buffer), case_count * #byte_size(StatementSwitchDiscriminant)); + } + + skip_space(module); + + >clause_block = parse_block(module, scope); + + clause_buffer[clause_count] = { + .values = clause_values, + .block = clause_block, + zero, + }; + clause_count += 1; + + consume_character_if_match(module, ','); + + skip_space(module); + + if (consume_character_if_match(module, right_brace)) + { + break; + } + } + + >clauses = arena_allocate_slice[StatementSwitchClause](module.arena, clause_count); + memcpy(#pointer_cast(clauses.pointer), #pointer_cast(&clause_buffer), clause_count * #byte_size(StatementSwitchClause)); + + require_semicolon = 0; + + statement.content = { + .switch = { + .discriminant = discriminant, + .clauses = clauses, + }, + }; + statement.id = .switch; }, .break => { @@ -13013,6 +13177,173 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v }, } }, + .switch => + { + >discriminant = statement.content.switch.discriminant; + >clauses = statement.content.switch.clauses; + + >exit_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "switch.exit"); + + analyze_value(module, discriminant, zero, .abi, 0); + + >discriminant_type = discriminant.type; + + >invalid_clause_index: u64 = ~0; + >else_clause_index = invalid_clause_index; + >discriminant_case_count: u64 = 0; + + // TODO: more analysis + switch (discriminant_type.id) + { + .enum, .integer => {}, + else => { report_error(); }, + } + + for (i: 0..clauses.length) + { + >clause = &clauses[i]; + clause.basic_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, #select(clause.values.length == 0, "switch.else_case_block", "switch.case_block")); + discriminant_case_count += clause.values.length; + + if (clause.values.length == 0) + { + if (else_clause_index != invalid_clause_index) + { + // Double else + report_error(); + } + + else_clause_index = i; + } + else + { + for (&value: clause.values) + { + switch (value.id) + { + .single => + { + >v = value.content.single; + assert(v != zero); + analyze_value(module, v, discriminant_type, .abi, 1); + }, + .range => + { + >start = value.content.range[0]; + >end = value.content.range[0]; + + for (v: value.content.range) + { + analyze_value(module, v, discriminant_type, .abi, 1); + } + + if (start.id != end.id) + { + report_error(); + } + + switch (start.id) + { + .constant_integer => + { + if (start.content.constant_integer.value >= end.content.constant_integer.value) + { + report_error(); + } + }, + else => { report_error(); }, + } + }, + } + } + } + } + + >else_block: &LLVMBasicBlock = undefined; + if (else_clause_index != invalid_clause_index) + { + else_block = clauses[else_clause_index].basic_block; + } + else + { + else_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "switch.else_case_block"); + } + + >switch_instruction = LLVMBuildSwitch(module.llvm.builder, discriminant.llvm, else_block, #truncate(discriminant_case_count)); + >all_blocks_terminated: u1 = 1; + + for (&clause: clauses) + { + for (&value: clause.values) + { + switch (value.id) + { + .single => + { + LLVMAddCase(switch_instruction, value.content.single.llvm, clause.basic_block); + }, + .range => + { + >start = value.content.range[0]; + >end = value.content.range[1]; + + LLVMAddCase(switch_instruction, start.llvm, clause.basic_block); + + assert(start.id == end.id); + + switch (start.id) + { + .constant_integer => + { + >start_value = start.content.constant_integer.value; + >end_value = end.content.constant_integer.value; + + for (i: start_value + 1 .. end_value) + { + LLVMAddCase(switch_instruction, LLVMConstInt(start.type.llvm.abi, i, 0), clause.basic_block); + } + }, + else => { unreachable; }, + } + + LLVMAddCase(switch_instruction, end.llvm, clause.basic_block); + }, + } + } + + LLVMPositionBuilderAtEnd(module.llvm.builder, clause.basic_block); + + analyze_block(module, clause.block); + + if (LLVMGetInsertBlock(module.llvm.builder)) + { + all_blocks_terminated = 0; + LLVMBuildBr(module.llvm.builder, exit_block); + LLVMClearInsertionPosition(module.llvm.builder); + } + } + + if (else_clause_index == invalid_clause_index) + { + LLVMPositionBuilderAtEnd(module.llvm.builder, else_block); + + if (module.has_debug_info and !build_mode_is_optimized(module.build_mode)) + { + emit_intrinsic_call(module, ."llvm.trap", zero, zero); + } + + LLVMBuildUnreachable(module.llvm.builder); + LLVMClearInsertionPosition(module.llvm.builder); + } + + LLVMPositionBuilderAtEnd(module.llvm.builder, exit_block); + + if (all_blocks_terminated) + { + LLVMBuildUnreachable(module.llvm.builder); + LLVMClearInsertionPosition(module.llvm.builder); + } + }, else => { #trap(); @@ -14457,6 +14788,7 @@ names: [_][]u8 = "string_to_enum", "empty_if", "else_if", + "else_if_complicated", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/emitter.cpp b/src/emitter.cpp index 32fa326..d00f223 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -5209,6 +5209,22 @@ fn void invalidate_analysis(Module* module, Value* value) { invalidate_analysis(module, value->unary.value); } break; + case ValueId::slice_expression: + { + invalidate_analysis(module, value->slice_expression.array_like); + auto start = value->slice_expression.start; + auto end = value->slice_expression.end; + + if (start) + { + invalidate_analysis(module, start); + } + + if (end) + { + invalidate_analysis(module, end); + } + } break; default: trap(); } @@ -8606,6 +8622,10 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 if (else_clause_index == invalid_clause_index) { LLVMPositionBuilderAtEnd(module->llvm.builder, else_block); + if (module->has_debug_info && !build_mode_is_optimized(module->build_mode)) + { + emit_intrinsic_call(module, IntrinsicIndex::trap, {}, {}); + } LLVMBuildUnreachable(module->llvm.builder); LLVMClearInsertionPosition(module->llvm.builder); } @@ -8669,6 +8689,16 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 Type* aggregate_type = 0; + if (right->kind == ValueKind::left && right->type->id != TypeId::pointer) + { + if (!type_is_slice(right->type)) + { + report_error(); + } + + right->kind = ValueKind::right; + } + switch (right->kind) { case ValueKind::right: diff --git a/src/parser.cpp b/src/parser.cpp index a8655b8..020e74e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2823,7 +2823,7 @@ fn Statement* parse_statement(Module* module, Scope* scope) Value* right_value_buffer[64]; u64 right_value_count = 0; - right_value_buffer[right_value_count] = parse_value(module, scope, { .kind = ValueKind::left }); + right_value_buffer[right_value_count] = parse_value(module, scope, {}); right_value_count += 1; skip_space(module); @@ -2840,15 +2840,17 @@ fn Statement* parse_statement(Module* module, Scope* scope) report_error(); } - right_value_buffer[0]->kind = ValueKind::right; - right_value_buffer[right_value_count] = parse_value(module, scope, {}); right_value_count += 1; expect_character(module, right_parenthesis); kind = ForEachKind::range; } break; - case TokenId::right_parenthesis: kind = ForEachKind::slice; break; + case TokenId::right_parenthesis: + { + right_value_buffer[0]->kind = ValueKind::left; + kind = ForEachKind::slice; + } break; default: report_error(); } -- 2.43.0