From 5e6ac440c75a830de03c0270cc6a82687f8f41b9 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Sat, 31 May 2025 17:56:44 -0600 Subject: [PATCH] wip --- src/compiler.bbb | 515 +++++++++++++++++++++++++++++++++- src/compiler.cpp | 4 + src/compiler.hpp | 19 +- src/emitter.cpp | 571 +++++++++++++++++++++++++------------- src/parser.cpp | 195 ++++++++++--- tests/bool_pair.bbb | 18 ++ tests/enum_debug_info.bbb | 38 +++ tests/min_max.bbb | 10 + tests/return_array.bbb | 22 ++ 9 files changed, 1145 insertions(+), 247 deletions(-) create mode 100644 tests/bool_pair.bbb create mode 100644 tests/enum_debug_info.bbb create mode 100644 tests/min_max.bbb create mode 100644 tests/return_array.bbb diff --git a/src/compiler.bbb b/src/compiler.bbb index 97c841d..75ee552 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -97,6 +97,19 @@ align_forward = fn (value: u64, alignment: u64) u64 return result; } +next_power_of_two = fn (n: u64) u64 +{ + n -= 1; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n |= n >> 32; + n += 1; + return n; +} + string_no_match = #integer_max(u64); c_string_length = fn (c_string: &u8) u64 @@ -551,10 +564,14 @@ LLVMTargetMachineOptions = opaque; LLVMIntrinsicIndex = enum u32 { - trap, - va_start, - va_end, - va_copy, + "llvm.smax", + "llvm.smin", + "llvm.trap", + "llvm.umax", + "llvm.umin", + "llvm.va_start", + "llvm.va_end", + "llvm.va_copy", } CompilerCommand = enum @@ -658,6 +675,7 @@ Type = struct; Value = struct; Local = struct; Block = struct; +Module = struct; ScopeKind = enum { @@ -746,7 +764,7 @@ TypeId = enum TypeInteger = struct { - bit_count: u32, + bit_count: u64, signed: u1, } @@ -767,9 +785,67 @@ AbiRegisterCount = union system_v: AbiRegisterCountSystemV, }; +AbiInformationPadding = union +{ + type: &Type, + unpadded_coerce_and_expand_type: &Type, +} + +AbiInformationDirectAttributes = struct +{ + offset: u32, + alignment: u32, +} + +AbiInformationIndirectAttributes = struct +{ + alignment: u32, + address_space: u32, +} + +AbiInformationAttributes = union +{ + direct: AbiInformationDirectAttributes, + indirect: AbiInformationIndirectAttributes, + alloca_field_index: u32, +} + +AbiKind = enum +{ + ignore, + direct, + extend, + indirect, + indirect_aliased, + expand, + coerce_and_expand, + in_alloca, +} + +AbiFlags = struct +{ + kind: AbiKind, + padding_in_reg: u1, + in_alloca_sret: u1, + in_alloca_indirect: u1, + in_alloca_by_value: u1, + in_alloca_realign: u1, + sret_after_this: u1, + in_reg: u1, + can_be_flattened: u1, + sign_extension: u1, +} + AbiInformation = struct { - foo: u32, + semantic_type: &Type, + coerce_to_type: &Type, + padding: AbiInformationPadding, + padding_argument_index: u16, + attributes: AbiInformationAttributes, + flags: AbiFlags, + abi_start: u16, + abi_count: u16, } TypeFunction = struct @@ -802,12 +878,88 @@ Type = struct scope: &Scope, } +align_bit_count = fn (bit_count: u64) u64 +{ + >aligned_bit_count = #max(next_power_of_two(bit_count), 8); + assert(aligned_bit_count % 8 == 0); + return aligned_bit_count; +} + +aligned_byte_count_from_bit_count = fn (bit_count: u64) u64 +{ + >aligned_bit_count = align_bit_count(bit_count); + return aligned_bit_count / 8; +} + +get_byte_size = fn (type: &Type) u64 +{ + switch (type.id) + { + .integer => + { + >byte_count = aligned_byte_count_from_bit_count(type.content.integer.bit_count); + assert(byte_count == 1 or byte_count == 2 or byte_count == 4 or byte_count == 8 or byte_count == 16); + return byte_count; + }, + else => + { + #trap(); + }, + } +} + +is_integral_or_enumeration_type = fn (type: &Type) u1 +{ + switch (type.id) + { + .integer, .bits, .enum => + { + return 1; + }, + .array, .struct => + { + return 0; + }, + else => + { + unreachable; + }, + } +} + +is_promotable_integer_type_for_abi = fn (type: &Type) u1 +{ + switch (type.id) + { + .integer => + { + return type.content.integer.bit_count < 32; + }, + else => + { + #trap(); + } + } +} + +resolve_type_in_place_abi = fn (module: &Module, type: &Type) void; +resolve_type_in_place_memory = fn (module: &Module, type: &Type) void; +resolve_type_in_place_debug = fn (module: &Module, type: &Type) void; + +resolve_type_in_place = fn (module: &Module, type: &Type) void +{ + resolve_type_in_place_abi(module, type); + resolve_type_in_place_memory(module, type); + resolve_type_in_place_debug(module, type); +} + ValueId = enum { infer_or_ignore, external_function, function, constant_integer, + global, } ValueConstantInteger = struct @@ -1223,7 +1375,7 @@ integer_type = fn (module: &Module, integer: TypeInteger) &Type { assert(integer.bit_count != 0); assert(integer.bit_count <= 64); - >index = #select(integer.bit_count == 128, i128_offset + #extend(integer.signed), #extend(integer.bit_count) - 1 + 64 * #extend(integer.signed)); + >index = #select(integer.bit_count == 128, i128_offset + #extend(integer.signed), integer.bit_count - 1 + 64 * #extend(integer.signed)); >result = module.scope.types.first + index; assert(result.id == .integer); assert(result.content.integer.bit_count == integer.bit_count); @@ -1685,7 +1837,7 @@ parse_type = fn (module: &Module, scope: &Scope) &Type } } - >result = integer_type(module, { .bit_count = #truncate(bit_count), .signed = is_signed }); + >result = integer_type(module, { .bit_count = bit_count, .signed = is_signed }); return result; } else @@ -2784,6 +2936,278 @@ parse = fn (module: &Module) void } } +resolve_alias = fn (module: &Module, type: &Type) &Type +{ + >result: &Type = zero; + + switch (type.id) + { + .void, + .integer, + => + { + result = type; + }, + else => + { + #trap(); + }, + } + + assert(result != zero); + + return result; +} + +resolve_type_in_place_abi = fn (module: &Module, type: &Type) void +{ + #trap(); +} + +resolve_type_in_place_memory = fn (module: &Module, type: &Type) void +{ + #trap(); +} + +resolve_type_in_place_debug = fn (module: &Module, type: &Type) void +{ + #trap(); +} + +AbiSystemVClass = enum +{ + none, + integer, + sse, + sse_up, + x87, + x87_up, + complex_x87, + memory, +} + +AbiSystemVClassifyArgument = struct +{ + base_offset: u64, + is_variable_argument: u1, + is_register_call: u1, +} + +abi_system_v_classify_type = fn (type: &Type, options: AbiSystemVClassifyArgument) [2]AbiSystemVClass +{ + >result: [2]AbiSystemVClass = zero; + + >is_memory = options.base_offset >= 8; + >current_index: u64 = #extend(is_memory); + >not_current_index: u64 = #extend(!is_memory); + assert(current_index != not_current_index); + result[current_index] = .memory; + + switch (type.id) + { + .void, .noreturn => + { + result[current_index] = .none; + }, + .integer => + { + >bit_count = type.content.integer.bit_count; + if (bit_count <= 64) + { + result[current_index] = .integer; + } + else if (bit_count == 128) + { + #trap(); + } + else + { + report_error(); + } + }, + else => + { + #trap(); + }, + } + + return result; +} + +contains_no_user_data = fn (type: &Type, start: u64, end: u64) u1 +{ + >byte_size = get_byte_size(type); + >result = byte_size <= start; + + if (!result) + { + switch (type.id) + { + .struct => + { + result = 1; + #trap(); + }, + .array, .enum_array => + { + result = 1; + #trap(); + }, + else => {}, + } + } + + return result; +} + +abi_system_v_get_integer_type_at_offset = fn (module: &Module, type: &Type, offset: u64, source_type: &Type, source_offset: u64) &Type +{ + switch (type.id) + { + .integer => + { + if (offset == 0) + { + >bit_count = type.content.integer.bit_count; + >start = source_offset + get_byte_size(type); + >end = source_offset + 8; + + if (bit_count == 64) + { + return type; + } + + if (bit_count == 32 or bit_count == 16 or bit_count == 8) + { + if (contains_no_user_data(source_type, start, end)) + { + return type; + } + } + } + }, + } + + >source_size = get_byte_size(source_type); + assert(source_size != source_offset); + >byte_count = source_size - source_offset; + >bit_count = #select(byte_count > 8, 64, byte_count * 8); + >result = integer_type(module, { .bit_count = bit_count, .signed = 0 }); + return result; +} + +AbiSystemVDirect = struct +{ + semantic_type: &Type, + type: &Type, + padding: &Type, + offset: u32, + alignment: u32, + cannot_be_flattened: u1, +} + +abi_system_v_get_direct = fn (module: &Module, direct: AbiSystemVDirect) AbiInformation +{ + >result: AbiInformation = { + .semantic_type = direct.semantic_type, + .flags = { + .kind = .direct, + zero, + }, + zero, + }; + + resolve_type_in_place(module, direct.semantic_type); + resolve_type_in_place(module, direct.type); + + if (direct.padding) + { + resolve_type_in_place(module, direct.padding); + } + + #trap(); +} + +abi_system_v_classify_return_type = fn (module: &Module, semantic_return_type: &Type) AbiInformation +{ + >classes = abi_system_v_classify_type(semantic_return_type, zero); + assert(classes[1] != .memory or classes[0] == .memory); + assert(classes[1] != .sse_up or classes[0] == .sse); + + >low_type: &Type = zero; + + switch (classes[0]) + { + .none => + { + if (classes[1] == .none) + { + #trap(); + } + else + { + report_error(); + } + }, + .integer => + { + low_type = abi_system_v_get_integer_type_at_offset(module, semantic_return_type, 0, semantic_return_type, 0); + + if (classes[1] == .none and low_type.id == .integer) + { + if (is_integral_or_enumeration_type(semantic_return_type) and? is_promotable_integer_type_for_abi(semantic_return_type)) + { + #trap(); + } + } + }, + else => + { + #trap(); + }, + } + + >high_type: &Type = zero; + + switch (classes[1]) + { + .none => {}, + .integer => + { + >high_offset: u64 = 8; + high_type = abi_system_v_get_integer_type_at_offset(module, semantic_return_type, high_offset, semantic_return_type, high_offset); + + if (classes[0] == .none) + { + #trap(); + } + }, + else => + { + #trap(); + }, + } + + if (high_type) + { + #trap(); + } + + >result = abi_system_v_get_direct(module, { + .semantic_type = semantic_return_type, + .type = low_type, + zero, + }); + + return result; +} + +ResolvedCallingConvention = enum +{ + system_v, + win64, +} + emit = fn (module: &Module) void { assert(!module.current_function); @@ -2889,6 +3313,79 @@ emit = fn (module: &Module) void >name = #enum_name(e); module.llvm.intrinsic_table[i] = LLVMLookupIntrinsicID(name.pointer, name.length); } + + >global = module.first_global; + + while (global) + { + assert(!module.current_function); + assert(!module.current_macro_instantiation); + assert(!module.current_macro_declaration); + + if (global.emitted) + { + continue; + } + + >global_pointer_type = global.variable.storage.type; + assert(global_pointer_type.id == .pointer); + >global_value_type = global_pointer_type.content.pointer.element_type; + + switch (global.variable.storage.id) + { + .function, .external_function => + { + >function_type = &global_value_type.content.function; + >semantic_argument_types = function_type.semantic_argument_types; + >semantic_return_type = function_type.semantic_return_type; + function_type.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; + switch (function_type.calling_convention) + { + .c => + { + // TODO: switch on platform + resolved_calling_convention = .system_v; + }, + } + + >is_reg_call = resolved_calling_convention == .system_v and 0; // TODO: regcall calling convention + + switch (resolved_calling_convention) + { + .system_v => + { + function_type.available_registers = { + .system_v = { + .gpr = #select(is_reg_call, 11, 6), + .sse = #select(is_reg_call, 16, 8), + }, + }; + function_type.return_abi = abi_system_v_classify_return_type(module, resolve_alias(module, semantic_return_type)); + #trap(); + }, + .win64 => + { + #trap(); + }, + } + }, + .global => + { + #trap(); + }, + else => + { + report_error(); + }, + } + + global = global.next; + } + + #trap(); } compile = fn (arena: &Arena, options: CompileOptions) void @@ -2919,7 +3416,7 @@ compile = fn (arena: &Arena, options: CompileOptions) void type_it.& = { .content = { .integer = { - .bit_count = #truncate(bit_count), + .bit_count = bit_count, .signed = sign, }, }, diff --git a/src/compiler.cpp b/src/compiler.cpp index 0df0659..23dd87c 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -435,6 +435,10 @@ global_variable String names[] = string_literal("opaque"), string_literal("basic_struct_passing"), string_literal("enum_arbitrary_abi"), + string_literal("enum_debug_info"), + string_literal("return_array"), + string_literal("bool_pair"), + string_literal("min_max"), }; void entry_point(Slice arguments, Slice envp) diff --git a/src/compiler.hpp b/src/compiler.hpp index ff9ae29..6c7e3c2 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -629,6 +629,11 @@ fn u64 get_bit_size(Type* type) case TypeId::integer: return type->integer.bit_count; case TypeId::enumerator: return get_bit_size(type->enumerator.backing_type); case TypeId::alias: return get_bit_size(type->alias.type); + case TypeId::array: return get_byte_size(type->array.element_type) * type->array.element_count * 8; + case TypeId::pointer: return 64; + case TypeId::structure: return type->structure.byte_size * 8; + case TypeId::union_type: return type->union_type.byte_size * 8; + case TypeId::enum_array: return get_byte_size(type->enum_array.element_type) * type->enum_array.enum_type->enumerator.fields.length * 8; default: trap(); } } @@ -793,7 +798,7 @@ struct Block enum class ValueId { infer_or_ignore, - external_function, + forward_declared_function, function, constant_integer, unary, @@ -905,6 +910,8 @@ enum class BinaryId logical_or, logical_and_shortcircuit, logical_or_shortcircuit, + max, + min, }; struct ValueBinary @@ -1078,6 +1085,7 @@ struct Value case ValueId::array_expression: case ValueId::call: case ValueId::select: + case ValueId::slice_expression: return false; case ValueId::variable_reference: { @@ -1164,7 +1172,11 @@ struct LLVMIntrinsicId enum class IntrinsicIndex { + smax, + smin, trap, + umax, + umin, va_start, va_end, va_copy, @@ -1172,7 +1184,11 @@ enum class IntrinsicIndex }; global_variable String intrinsic_names[] = { + string_literal("llvm.smax"), + string_literal("llvm.smin"), string_literal("llvm.trap"), + string_literal("llvm.umax"), + string_literal("llvm.umin"), string_literal("llvm.va_start"), string_literal("llvm.va_end"), string_literal("llvm.va_copy"), @@ -1213,6 +1229,7 @@ struct Module Type* first_pair_struct_type; Type* first_array_type; Type* first_enum_array_type; + Type* first_function_type; Type* va_list_type; diff --git a/src/emitter.cpp b/src/emitter.cpp index 464114c..ea3d624 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -153,6 +153,7 @@ fn bool is_integral_or_enumeration_type(Type* type) case TypeId::bits: case TypeId::enumerator: return true; + case TypeId::array: case TypeId::structure: return false; default: unreachable(); @@ -667,31 +668,25 @@ fn Type* abi_system_v_get_integer_type_at_offset(Module* module, Type* type, u32 { case TypeId::integer: { - auto bit_count = type->integer.bit_count; - switch (bit_count) + if (offset == 0) { - case 64: return type; - case 32: case 16: case 8: - { - assert(offset == 0); - auto start = source_offset + get_byte_size(type); - auto end = source_offset + 8; + auto bit_count = type->integer.bit_count; + auto start = source_offset + get_byte_size(type); + auto end = source_offset + 8; - if (contains_no_user_data(source_type, start, end)) - { - return type; - } - } break; - default: - { - auto original_byte_count = get_byte_size(type); - assert(original_byte_count != source_offset); - auto byte_count = MIN(original_byte_count - source_offset, 8); - auto bit_count = byte_count * 8; - - auto result_type = integer_type(module, { .bit_count = (u32)bit_count, .is_signed = false }); - return result_type; - } break; + bool type_contains_no_user_data = contains_no_user_data(source_type, start, end); + switch (bit_count) + { + case 64: return type; + case 32: case 16: case 8: + { + if (type_contains_no_user_data) + { + return type; + } + } break; + default: break; + } } } break; case TypeId::pointer: @@ -738,10 +733,18 @@ fn Type* abi_system_v_get_integer_type_at_offset(Module* module, Type* type, u32 auto backing_type = type->enumerator.backing_type; return abi_system_v_get_integer_type_at_offset(module, backing_type, offset, source_type == type ? backing_type : source_type, source_offset); } break; + case TypeId::array: + { + auto element_type = type->array.element_type; + auto element_size = get_byte_size(element_type); + auto element_offset = (offset / element_size) * element_size; + return abi_system_v_get_integer_type_at_offset(module, element_type, offset - element_offset, source_type, source_offset); + } break; default: unreachable(); } auto source_size = get_byte_size(source_type); + assert(source_size != source_offset); auto byte_count = source_size - source_offset; u32 bit_count = byte_count > 8 ? 64 : byte_count * 8; auto result = integer_type(module, { .bit_count = bit_count, .is_signed = false }); @@ -1103,7 +1106,7 @@ fn void resolve_type_in_place_debug(Module* module, Type* type) assert(array_element_count); resolve_type_in_place_debug(module, array_element_type); auto bit_alignment = get_byte_alignment(type) * 8; - auto array_type = LLVMDIBuilderCreateArrayType(module->llvm.di_builder, array_element_count, bit_alignment, array_element_type->llvm.debug, 0, 0); + auto array_type = LLVMDIBuilderCreateArrayType(module->llvm.di_builder, get_bit_size(type), bit_alignment, array_element_type->llvm.debug, 0, 0); result = array_type; } break; case TypeId::enumerator: @@ -1119,7 +1122,10 @@ fn void resolve_type_in_place_debug(Module* module, Type* type) field_buffer[i] = enum_field; } - result = LLVMDIBuilderCreateEnumerationType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->enumerator.line, get_bit_size(type), get_byte_alignment(type) * 8, field_buffer, type->enumerator.fields.length, backing_type->llvm.debug); + auto debug_aligned_type = align_integer_type(module, backing_type); + resolve_type_in_place_debug(module, debug_aligned_type); + + result = LLVMDIBuilderCreateEnumerationType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->enumerator.line, get_bit_size(type), get_byte_alignment(type) * 8, field_buffer, type->enumerator.fields.length, debug_aligned_type->llvm.debug); } break; case TypeId::structure: { @@ -1136,7 +1142,7 @@ fn void resolve_type_in_place_debug(Module* module, Type* type) auto& field = fields[i]; auto field_type = field.type; resolve_type_in_place_debug(module, field_type); - auto member_type = LLVMDIBuilderCreateMemberType(module->llvm.di_builder, module->scope.llvm, (char*)field.name.pointer, field.name.length, module->llvm.file, field.line, get_byte_size(field_type) * 8, get_byte_alignment(field_type) * 8, field.offset * 8, flags, field_type->llvm.debug); + auto member_type = LLVMDIBuilderCreateMemberType(module->llvm.di_builder, module->scope.llvm, (char*)field.name.pointer, field.name.length, module->llvm.file, field.line, get_bit_size(field_type), get_byte_alignment(field_type) * 8, field.offset * 8, flags, field_type->llvm.debug); llvm_type_buffer[i] = member_type; } @@ -1762,7 +1768,7 @@ fn AbiInformation abi_system_v_get_indirect_return_result(Type* type) } } -fn AbiInformation abi_system_classify_return_type(Module* module, Type* semantic_return_type) +fn AbiInformation abi_system_v_classify_return_type(Module* module, Type* semantic_return_type) { auto type_classes = abi_system_v_classify_type(semantic_return_type, {}); auto low_class = type_classes.r[0]; @@ -2242,6 +2248,8 @@ fn bool binary_is_boolean(BinaryId id) case BinaryId::bitwise_xor: case BinaryId::shift_left: case BinaryId::shift_right: + case BinaryId::max: + case BinaryId::min: return false; case BinaryId::compare_equal: case BinaryId::compare_not_equal: @@ -2278,8 +2286,8 @@ enum class IndexType struct TypeAnalysis { + Type* indexing_type; bool must_be_constant; - bool is_index; }; fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnalysis analysis); @@ -2692,7 +2700,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal { if (!expected_type) { - if (analysis.is_index) + if (analysis.indexing_type) { expected_type = uint64(module); } @@ -3268,7 +3276,9 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal } auto pointer_element_type = array_like_type->pointer.element_type; - analyze_type(module, value->array_expression.index, 0, { .must_be_constant = analysis.must_be_constant, .is_index = true }); + auto indexing_type = pointer_element_type->id == TypeId::enum_array ? pointer_element_type->enum_array.enum_type : uint64(module); + + analyze_type(module, value->array_expression.index, 0, { .indexing_type = indexing_type, .must_be_constant = analysis.must_be_constant }); Type* element_type = 0; switch (pointer_element_type->id) @@ -3311,6 +3321,11 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal } break; case ValueId::enum_literal: { + if (!expected_type) + { + expected_type = analysis.indexing_type; + } + if (!expected_type) { report_error(); @@ -3572,12 +3587,31 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal // Right now 0, 1, 2, 3 => constant values, rest zeroed is constant because `declaration_index == initialization_index` // With constant initialization values 2, 3, 4 and rest zeroed, the aggregate initialization because `declaration_index != initialization_index`, that is, the first initialization index (0) does not match the declaration index (2). The same case can be applied for cases (1, 3) and (2, 4) - switch (resolved_type->id) + Type* aggregate_type = 0; + switch (value->kind) + { + case ValueKind::left: + { + if (resolved_type->id != TypeId::pointer) + { + report_error(); + } + + aggregate_type = resolved_type->pointer.element_type; + } break; + case ValueKind::right: + { + aggregate_type = resolved_type; + } break; + default: + } + + switch (aggregate_type->id) { case TypeId::structure: { bool is_ordered = true; - auto fields = resolved_type->structure.fields; + auto fields = aggregate_type->structure.fields; assert(fields.length <= 64); auto same_values_as_field = fields.length == elements.length; @@ -3637,7 +3671,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal } break; case TypeId::bits: { - auto fields = resolved_type->bits.fields; + auto fields = aggregate_type->bits.fields; assert(fields.length <= 64); auto same_values_as_field = fields.length == elements.length; @@ -3704,7 +3738,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal auto initialization_name = elements[0].name; u64 i; - auto fields = resolved_type->union_type.fields; + auto fields = aggregate_type->union_type.fields; for (i = 0; i < fields.length; i += 1) { auto& field = fields[i]; @@ -3725,8 +3759,8 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal case TypeId::enum_array: { bool is_ordered = true; - auto enum_type = resolved_type->enum_array.enum_type; - auto element_type = resolved_type->enum_array.element_type; + auto enum_type = aggregate_type->enum_array.enum_type; + auto element_type = aggregate_type->enum_array.element_type; assert(enum_type->id == TypeId::enumerator); auto fields = enum_type->enumerator.fields; @@ -4379,10 +4413,20 @@ fn ValueTypePair enter_struct_pointer_for_coerced_access(Module* module, LLVMVal if (!(first_field_size < destination_size && first_field_size < source_size)) { - trap(); + auto gep = LLVMBuildStructGEP2(module->llvm.builder, source_type->llvm.abi, source_value, 0, "coerce.dive"); + if (first_field_type->id == TypeId::structure) + { + trap(); + } + else + { + return { gep, first_field_type }; + } + } + else + { + return { source_value, source_type }; } - - return { source_value, source_type }; } fn LLVMValueRef coerce_integer_or_pointer_to_integer_or_pointer(Module* module, LLVMValueRef source, Type* source_type, Type* destination_type) @@ -4418,7 +4462,12 @@ fn LLVMValueRef create_coerced_load(Module* module, LLVMValueRef source, Type* s if (type_is_integer_backing(source_type) && type_is_integer_backing(destination_type)) { - trap(); + auto load = create_load(module, { + .type = source_type, + .pointer = source, + }); + auto result = coerce_integer_or_pointer_to_integer_or_pointer(module, load, source_type, destination_type); + return result; } else { @@ -4749,6 +4798,42 @@ fn SliceEmitResult emit_string_literal(Module* module, Value* value) } } +fn void invalidate_analysis(Module* module, Value* value) +{ + switch (value->id) + { + case ValueId::variable_reference: + case ValueId::constant_integer: + break; + case ValueId::aggregate_initialization: + { + auto elements = value->aggregate_initialization.elements; + for (auto& element : elements) + { + invalidate_analysis(module, element.value); + } + } break; + case ValueId::field_access: + { + invalidate_analysis(module, value->field_access.aggregate); + } break; + default: trap(); + } + + value->type = 0; +} + +fn void reanalyze_type_as_left_value(Module* module, Value* value) +{ + assert(value->type); + assert(value->kind == ValueKind::right); + auto original_type = value->type; + invalidate_analysis(module, value); + value->kind = ValueKind::left; + auto expected_type = value->id == ValueId::aggregate_initialization ? get_pointer_type(module, original_type) : 0; + analyze_type(module, value, expected_type, {}); +} + fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type) { switch (value->id) @@ -4972,17 +5057,27 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, } else { - if (src->kind == ValueKind::right) + if (src->kind == ValueKind::right && !src->is_constant()) { - if (src->id == ValueId::variable_reference) + if (!type_is_slice(src->type)) { - src->type = 0; - src->kind = ValueKind::left; - analyze_type(module, src, 0, {}); + switch (src->id) + { + case ValueId::aggregate_initialization: + case ValueId::variable_reference: + case ValueId::field_access: + { + reanalyze_type_as_left_value(module, src); + } break; + default: + { + trap(); + } break; + } } } - emit_value(module, semantic_call_argument_value, TypeKind::memory, false); + emit_value(module, src, TypeKind::memory, false); auto destination_size = get_byte_size(coerce_to_type); auto source_size = get_byte_size(argument_abi.semantic_type); auto alignment = get_byte_alignment(argument_abi.semantic_type); @@ -5083,6 +5178,16 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, trap(); } } break; + case ValueId::zero: + { + for (u32 i = 0; i < coerce_fields.length; i += 1) + { + auto& field = coerce_fields[i]; + auto field_type = field.type; + llvm_abi_argument_value_buffer[abi_argument_count] = LLVMConstNull(field_type->llvm.abi); + abi_argument_count += 1; + } + } break; default: trap(); } } @@ -5124,9 +5229,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, assert(src->type->id == TypeId::structure); assert(src->id == ValueId::variable_reference); assert(src->kind == ValueKind::right); - src->type = 0; - src->kind = ValueKind::left; - analyze_type(module, src, pointer_type, {}); + reanalyze_type_as_left_value(module, src); } } else @@ -5222,9 +5325,8 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, { case ValueId::variable_reference: { - semantic_call_argument_value->type = 0; - semantic_call_argument_value->kind = ValueKind::left; - analyze_value(module, semantic_call_argument_value, pointer_type, TypeKind::memory, false); + reanalyze_type_as_left_value(module, semantic_call_argument_value); + emit_value(module, semantic_call_argument_value, TypeKind::memory, false); llvm_abi_argument_value_buffer[abi_argument_count] = semantic_call_argument_value->llvm; abi_argument_count += 1; } break; @@ -5342,19 +5444,42 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, auto destination_type = return_abi.semantic_type; - assert(return_abi.semantic_type->id == TypeId::structure); - if (return_abi.semantic_type->structure.fields.length > 0) + + auto source_value = llvm_call; + auto source_type = raw_function_type->function.abi_return_type; + auto destination_size = get_byte_size(destination_type); + auto left_destination_size = destination_size - return_abi.attributes.direct.offset; + auto is_destination_volatile = false; + + switch (return_abi.semantic_type->id) { - auto source_value = llvm_call; - auto source_type = raw_function_type->function.abi_return_type; - auto destination_size = get_byte_size(destination_type); - auto left_destination_size = destination_size - return_abi.attributes.direct.offset; - auto is_destination_volatile = false; - create_coerced_store(module, source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile); - } - else - { - trap(); + case TypeId::structure: + { + if (return_abi.semantic_type->structure.fields.length > 0) + { + create_coerced_store(module, source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile); + } + else + { + trap(); + } + } break; + case TypeId::array: + { + if (get_byte_size(return_abi.semantic_type) <= 8) + { + create_store(module, { + .source = source_value, + .destination = destination_pointer, + .type = source_type, + }); + } + else + { + trap(); + } + } break; + default: unreachable(); } assert(coerce_alloca); @@ -6229,6 +6354,35 @@ fn LLVMValueRef emit_binary(Module* module, LLVMValueRef left, Type* left_type, { switch (id) { + case BinaryId::max: + case BinaryId::min: + { + IntrinsicIndex intrinsic; + switch (resolved_value_type->id) + { + case TypeId::integer: + { + auto is_signed = resolved_value_type->integer.is_signed; + switch (id) + { + case BinaryId::max: + { + intrinsic = is_signed ? IntrinsicIndex::smax : IntrinsicIndex::umax; + } break; + case BinaryId::min: + { + intrinsic = is_signed ? IntrinsicIndex::smin : IntrinsicIndex::umin; + } break; + default: unreachable(); + } + } break; + default: report_error(); + } + LLVMTypeRef argument_types[] = { resolved_value_type->llvm.abi }; + LLVMValueRef argument_values[] = { left, right }; + auto llvm_value = emit_intrinsic_call(module, intrinsic, array_to_slice(argument_types), array_to_slice(argument_values)); + return llvm_value; + } break; case BinaryId::shift_right: if (resolved_value_type->integer.is_signed) { @@ -7206,156 +7360,184 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect auto is_constant = value->aggregate_initialization.is_constant; auto zero = value->aggregate_initialization.zero; - switch (resolved_value_type->id) + switch (value->kind) { - case TypeId::structure: + case ValueKind::left: { - auto fields = resolved_value_type->structure.fields; - - if (is_constant) + if (resolved_value_type->id != TypeId::pointer) { - LLVMValueRef constant_buffer[64]; - u32 constant_count = (u32)elements.length; - - for (u64 i = 0; i < elements.length; i += 1) - { - auto* value = elements[i].value; - emit_value(module, value, TypeKind::memory, must_be_constant); - auto llvm_value = value->llvm; - assert(llvm_value); - assert(LLVMIsAConstant(llvm_value)); - constant_buffer[i] = llvm_value; - } - - if (zero) - { - if (elements.length == fields.length) - { - unreachable(); - } - - for (u64 i = elements.length; i < fields.length; i += 1) - { - auto& field = fields[i]; - auto 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); - - llvm_value = LLVMConstNamedStruct(get_llvm_type(resolved_value_type, type_kind), constant_buffer, constant_count); - } - else - { - trap(); + report_error(); } + + auto aggregate_type = resolved_value_type->pointer.element_type; + + auto alloca = create_alloca(module, { + .type = aggregate_type, + }); + auto resolved_pointer_type = resolved_value_type; + auto old_type = value->type; + // Overwrite type so asserts are not triggered + value->type = aggregate_type; + emit_assignment(module, alloca, resolved_pointer_type, value); + value->type = old_type; + llvm_value = alloca; } break; - case TypeId::union_type: + case ValueKind::right: { - trap(); - } break; - case TypeId::bits: - { - auto fields = resolved_value_type->bits.fields; - Type* backing_type = resolved_value_type->bits.backing_type; - resolve_type_in_place(module, backing_type); - auto abi_type = get_llvm_type(backing_type, type_kind); - - if (is_constant) + switch (resolved_value_type->id) { - u64 bits_value = 0; - - for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) - { - auto value = elements[initialization_index].value; - auto name = elements[initialization_index].name; - - u32 declaration_index; - for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + case TypeId::structure: { - auto& field = fields[declaration_index]; + auto fields = resolved_value_type->structure.fields; - if (name.equal(field.name)) + if (is_constant) { - break; - } - } + LLVMValueRef constant_buffer[64]; + u32 constant_count = (u32)elements.length; - if (declaration_index == fields.length) - { - unreachable(); - } - - const auto& field = fields[declaration_index]; - u64 field_value; - switch (value->id) - { - case ValueId::constant_integer: + for (u64 i = 0; i < elements.length; i += 1) { - field_value = value->constant_integer.value; - } break; - default: unreachable(); - } + auto* value = elements[i].value; + emit_value(module, value, TypeKind::memory, must_be_constant); + auto llvm_value = value->llvm; + assert(llvm_value); + assert(LLVMIsAConstant(llvm_value)); + constant_buffer[i] = llvm_value; + } - bits_value |= field_value << field.offset; - } + if (zero) + { + if (elements.length == fields.length) + { + unreachable(); + } - llvm_value = LLVMConstInt(abi_type, bits_value, false); - } - else - { - llvm_value = LLVMConstNull(abi_type); + for (u64 i = elements.length; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place(module, field_type); + constant_buffer[i] = LLVMConstNull(field_type->llvm.memory); + constant_count += 1; + } + } - for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) - { - auto value = elements[initialization_index].value; - auto name = elements[initialization_index].name; + assert(constant_count == fields.length); - u32 declaration_index; - for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) - { - auto& field = fields[declaration_index]; - - if (name.equal(field.name)) - { - break; + llvm_value = LLVMConstNamedStruct(get_llvm_type(resolved_value_type, type_kind), constant_buffer, constant_count); } - } - - if (declaration_index == fields.length) + else + { + // TODO: shouldn't this be a left value? + unreachable(); + } + } break; + case TypeId::union_type: { - unreachable(); - } + trap(); + } break; + case TypeId::bits: + { + auto fields = resolved_value_type->bits.fields; + Type* backing_type = resolved_value_type->bits.backing_type; + resolve_type_in_place(module, backing_type); + auto abi_type = get_llvm_type(backing_type, type_kind); - const auto& field = fields[declaration_index]; + if (is_constant) + { + u64 bits_value = 0; - emit_value(module, value, TypeKind::memory, must_be_constant); + for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) + { + auto value = elements[initialization_index].value; + auto name = elements[initialization_index].name; - auto extended = LLVMBuildZExt(module->llvm.builder, value->llvm, abi_type, ""); - auto shl = LLVMBuildShl(module->llvm.builder, extended, LLVMConstInt(abi_type, field.offset, false), ""); - auto or_value = LLVMBuildOr(module->llvm.builder, llvm_value, shl, ""); - llvm_value = or_value; - } + u32 declaration_index; + for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + { + auto& field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + if (declaration_index == fields.length) + { + unreachable(); + } + + const auto& field = fields[declaration_index]; + u64 field_value; + switch (value->id) + { + case ValueId::constant_integer: + { + field_value = value->constant_integer.value; + } break; + default: unreachable(); + } + + bits_value |= field_value << field.offset; + } + + llvm_value = LLVMConstInt(abi_type, bits_value, false); + } + else + { + llvm_value = LLVMConstNull(abi_type); + + for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) + { + auto value = elements[initialization_index].value; + auto name = elements[initialization_index].name; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + { + auto& field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + if (declaration_index == fields.length) + { + unreachable(); + } + + const auto& field = fields[declaration_index]; + + emit_value(module, value, TypeKind::memory, must_be_constant); + + auto extended = LLVMBuildZExt(module->llvm.builder, value->llvm, abi_type, ""); + auto shl = LLVMBuildShl(module->llvm.builder, extended, LLVMConstInt(abi_type, field.offset, false), ""); + auto or_value = LLVMBuildOr(module->llvm.builder, llvm_value, shl, ""); + llvm_value = or_value; + } + } + } break; + case TypeId::enum_array: + { + assert(is_constant); + assert(elements.length <= 64); + Value* value_buffer[64]; + for (u64 i = 0; i < elements.length; i += 1) + { + value_buffer[i] = elements[i].value; + } + Slice values = { value_buffer, elements.length }; + auto element_type = resolved_value_type->enum_array.element_type; + llvm_value = emit_constant_array(module, values, element_type); + } break; + default: unreachable(); } } break; - case TypeId::enum_array: - { - assert(is_constant); - assert(elements.length <= 64); - Value* value_buffer[64]; - for (u64 i = 0; i < elements.length; i += 1) - { - value_buffer[i] = elements[i].value; - } - Slice values = { value_buffer, elements.length }; - auto element_type = resolved_value_type->enum_array.element_type; - llvm_value = emit_constant_array(module, values, element_type); - } break; - default: unreachable(); } + } break; case ValueId::zero: { @@ -8565,8 +8747,13 @@ void emit(Module* module) switch (global->variable.storage->id) { case ValueId::function: - case ValueId::external_function: + case ValueId::forward_declared_function: { + if (global->variable.storage->id == ValueId::forward_declared_function && global->linkage != Linkage::external) + { + report_error(); + } + auto function_type = &global->variable.storage->type->pointer.element_type->function; auto semantic_argument_count = function_type->semantic_argument_types.length; function_type->argument_abis = arena_allocate(module->arena, semantic_argument_count); @@ -8586,7 +8773,7 @@ void emit(Module* module) }, }; auto semantic_return_type = function_type->semantic_return_type; - function_type->return_abi = abi_system_classify_return_type(module, resolve_alias(module, semantic_return_type)); + function_type->return_abi = abi_system_v_classify_return_type(module, resolve_alias(module, semantic_return_type)); auto return_abi_kind = function_type->return_abi.flags.kind; Type* abi_argument_type_buffer[64]; diff --git a/src/parser.cpp b/src/parser.cpp index 73606fb..f114f15 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -10,6 +10,8 @@ enum class ValueIntrinsic integer_max, int_from_enum, int_from_pointer, + max, + min, pointer_cast, pointer_from_int, select, @@ -862,6 +864,8 @@ fn Token tokenize(Module* module) string_literal("integer_max"), string_literal("int_from_enum"), string_literal("int_from_pointer"), + string_literal("max"), + string_literal("min"), string_literal("pointer_cast"), string_literal("pointer_from_int"), string_literal("select"), @@ -1241,6 +1245,7 @@ fn Value* parse_aggregate_initialization(Module* module, Scope* scope, ValueBuil } auto field_index = field_count; + auto checkpoint = get_checkpoint(module); if (consume_character_if_match(module, '.')) { auto name = parse_identifier(module); @@ -1530,6 +1535,37 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) { trap(); } break; + case ValueIntrinsic::min: + case ValueIntrinsic::max: + { + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + auto left = parse_value(module, scope, {}); + skip_space(module); + expect_character(module, ','); + skip_space(module); + auto right = parse_value(module, scope, {}); + skip_space(module); + expect_character(module, right_parenthesis); + + BinaryId binary_id; + switch (intrinsic) + { + case ValueIntrinsic::max: binary_id = BinaryId::max; break; + case ValueIntrinsic::min: binary_id = BinaryId::min; break; + default: unreachable(); + } + + *result = { + .binary = { + .left = left, + .right = right, + .id = binary_id, + }, + .id = ValueId::binary, + }; + } break; case ValueIntrinsic::count: unreachable(); } } break; @@ -1540,7 +1576,26 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) skip_space(module); - if (module->content[module->offset] == '.') + auto checkpoint = get_checkpoint(module); + bool is_aggregate_initialization = false; + if (consume_character_if_match(module, '.')) + { + auto identifier = parse_identifier(module); + + skip_space(module); + is_aggregate_initialization = consume_character_if_match(module, '='); + if (!is_aggregate_initialization) + { + if (!consume_character_if_match(module, ',')) + { + report_error(); + } + } + } + + set_checkpoint(module, checkpoint); + + if (is_aggregate_initialization) { result = parse_aggregate_initialization(module, scope, builder, right_bracket); } @@ -2746,6 +2801,33 @@ fn String parse_name(Module* module) return result; } +struct FunctionTypeCreate +{ + Type* semantic_return_type; + Slice semantic_argument_types; + CallingConvention calling_convention; + bool is_variable_arguments; +}; + +fn Type* get_function_type(Module* module, FunctionTypeCreate create) +{ + Type* last_function_type = module->first_function_type; + + while (last_function_type) + { + trap(); + + if (!last_function_type->next) + { + break; + } + + last_function_type = last_function_type->next; + } + + trap(); +} + void parse(Module* module) { auto scope = &module->scope; @@ -2823,16 +2905,23 @@ void parse(Module* module) auto global_name = parse_identifier(module); + Global* global_forward_declaration = 0; Global* last_global = module->first_global; while (last_global) { if (global_name.equal(last_global->variable.name)) { - report_error(); - } + global_forward_declaration = last_global; + if (last_global->variable.storage->id != ValueId::forward_declared_function) + { + report_error(); + } + + if (last_global->linkage == Linkage::external) + { + report_error(); + } - if (!last_global->next) - { break; } @@ -2840,14 +2929,14 @@ void parse(Module* module) } Type* type_it = module->scope.types.first; - Type* forward_declaration = 0; + Type* type_forward_declaration = 0; while (type_it) { if (global_name.equal(type_it->name)) { if (type_it->id == TypeId::forward_declaration) { - forward_declaration = type_it; + type_forward_declaration = type_it; break; } else @@ -2924,6 +3013,12 @@ void parse(Module* module) } auto global_keyword = (GlobalKeyword)i; + + if (global_forward_declaration && global_keyword != GlobalKeyword::function) + { + report_error(); + } + switch (global_keyword) { case GlobalKeyword::bits: @@ -3292,37 +3387,47 @@ void parse(Module* module) auto is_declaration = consume_character_if_match(module, ';'); - auto function_type = type_allocate_init(module, { - .function = { - .semantic_return_type = return_type, - .semantic_argument_types = argument_types, - .calling_convention = calling_convention, - .is_variable_arguments = is_variable_arguments, - }, - .id = TypeId::function, - .name = string_literal(""), - .scope = &module->scope, - }); - auto storage = new_value(module); - *storage = { - .type = get_pointer_type(module, function_type), - .id = ValueId::external_function, - // TODO? .kind = ValueKind::left, - }; - auto global = new_global(module); - *global = { - .variable = { - .storage = storage, - .initial_value = 0, - .type = function_type, - .scope = scope, - .name = global_name, - .line = global_line, - .column = global_column, - }, - .linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal, - }; + Global* global = 0; + if (global_forward_declaration) + { + trap(); + } + else + { + auto function_type = type_allocate_init(module, { + .function = { + .semantic_return_type = return_type, + .semantic_argument_types = argument_types, + .calling_convention = calling_convention, + .is_variable_arguments = is_variable_arguments, + }, + .id = TypeId::function, + .name = string_literal(""), + .scope = &module->scope, + }); + + auto storage = new_value(module); + *storage = { + .type = get_pointer_type(module, function_type), + .id = ValueId::forward_declared_function, + // TODO? .kind = ValueKind::left, + }; + + global = new_global(module); + *global = { + .variable = { + .storage = storage, + .initial_value = 0, + .type = function_type, + .scope = scope, + .name = global_name, + .line = global_line, + .column = global_column, + }, + .linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal, + }; + } if (!is_declaration) { @@ -3340,7 +3445,7 @@ void parse(Module* module) .storage = 0, .initial_value = 0, .type = type, - .scope = &storage->function.scope, + .scope = &global->variable.storage->function.scope, .name = name, .line = line, .column = 0, @@ -3349,7 +3454,7 @@ void parse(Module* module) }; } - storage->function = { + global->variable.storage->function = { .arguments = arguments, .scope = { .parent = scope, @@ -3360,9 +3465,9 @@ void parse(Module* module) .block = 0, .attributes = function_attributes, }; - storage->id = ValueId::function; + global->variable.storage->id = ValueId::function; - storage->function.block = parse_block(module, &storage->function.scope); + global->variable.storage->function.block = parse_block(module, &global->variable.storage->function.scope); module->current_function = 0; } } break; @@ -3532,9 +3637,9 @@ void parse(Module* module) skip_space(module); Type* struct_type; - if (forward_declaration) + if (type_forward_declaration) { - struct_type = forward_declaration; + struct_type = type_forward_declaration; } else { @@ -3646,9 +3751,9 @@ void parse(Module* module) expect_character(module, left_brace); Type* union_type; - if (forward_declaration) + if (type_forward_declaration) { - union_type = forward_declaration; + union_type = type_forward_declaration; } else { diff --git a/tests/bool_pair.bbb b/tests/bool_pair.bbb new file mode 100644 index 0000000..13eed46 --- /dev/null +++ b/tests/bool_pair.bbb @@ -0,0 +1,18 @@ +BoolPair = struct +{ + a: u1, + b: u1, +} + +bool_pair = fn () BoolPair +{ + return { .a = 0, .b = 1 }; +} + +[export] main = fn [cc(c)] () s32 +{ + >result = bool_pair(); + if (result.a) #trap(); + if (!result.b) #trap(); + return 0; +} diff --git a/tests/enum_debug_info.bbb b/tests/enum_debug_info.bbb new file mode 100644 index 0000000..9fb82ec --- /dev/null +++ b/tests/enum_debug_info.bbb @@ -0,0 +1,38 @@ +TypeId = enum +{ + void, + noreturn, + forward_declaration, + integer, + function, + pointer, + array, + enum, + struct, + bits, + alias, + union, + unresolved, + vector, + floating_point, + enum_array, + opaque, +} + +Type = struct +{ + arr: [5]u32, + id: TypeId, + a: [2]u64, + b: u64, +} + +[export] main = fn [cc(c)] () s32 +{ + >t: Type = { + .id = .integer, + zero, + }; + t.arr[0] = 1; + return 0; +} diff --git a/tests/min_max.bbb b/tests/min_max.bbb new file mode 100644 index 0000000..14b3c12 --- /dev/null +++ b/tests/min_max.bbb @@ -0,0 +1,10 @@ +[export] main = fn [cc(c)] () s32 +{ + >a: u32 = 1; + >b: u32 = 2; + >min = #min(a, b); + >max = #max(a, b); + if (min != a) #trap(); + if (max != b) #trap(); + return 0; +} diff --git a/tests/return_array.bbb b/tests/return_array.bbb new file mode 100644 index 0000000..a9674db --- /dev/null +++ b/tests/return_array.bbb @@ -0,0 +1,22 @@ +SomeEnum = enum +{ + a, + b, + c, + d, + e, + f, +} + +foo = fn () [2]SomeEnum +{ + return [ .f, .e ]; +} + +[export] main = fn [cc(c)] () s32 +{ + >result = foo(); + if (result[0] != .f) #trap(); + if (result[1] != .e) #trap(); + return 0; +}