diff --git a/src/compiler.bbb b/src/compiler.bbb index 6333bd6..ee6352f 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1400,6 +1400,29 @@ TypeBits = struct is_implicit_backing_type: u1, } +TypeAlias = struct +{ + type: &Type, + scope: &Scope, + line: u32, +} + +UnionField = struct +{ + type: &Type, + name: []u8, + line: u32, +} + +TypeUnion = struct +{ + fields: []UnionField, + byte_size: u64, + biggest_field: u64, + byte_alignment: u32, + line: u32, +} + TypeContent = union { integer: TypeInteger, @@ -1410,6 +1433,8 @@ TypeContent = union enum: TypeEnum, struct: TypeStruct, bits: TypeBits, + alias: TypeAlias, + union: TypeUnion, } TypeLLVM = struct @@ -1596,6 +1621,11 @@ get_byte_size = fn (type: &Type) u64 >result = get_byte_size(type.content.bits.backing_type); return result; }, + .union => + { + >result = type.content.union.byte_size; + return result; + }, else => { #trap(); @@ -1635,6 +1665,10 @@ get_byte_alignment = fn (type: &Type) u32 >alignment = get_byte_alignment(backing_type); return alignment; }, + .union => + { + return type.content.union.byte_alignment; + }, else => { #trap(); @@ -2076,6 +2110,10 @@ value_is_constant = fn (value: &Value) u1 assert(value.type != zero); return value.content.array_initialization.is_constant; }, + .select => + { + return 0; + }, else => { #trap(); @@ -2407,6 +2445,7 @@ LLVMICmpPredicate = enum u32 [extern] LLVMDIBuilderCreateMemberType = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, bit_size: u64, bit_alignment: u32, bit_offset: u64, flags: LLVMDIFlags, type: &LLVMMetadata) &LLVMMetadata; [extern] LLVMDIBuilderCreateBitFieldMemberType = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, bit_size: u64, bit_offset: u64, storage_bit_offset: u64, flags: LLVMDIFlags, type: &LLVMMetadata) &LLVMMetadata; [extern] LLVMDIBuilderCreateStructType = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, bit_size: u64, bit_alignment: u32, flags: LLVMDIFlags, derived_from: &LLVMMetadata, element_pointer: &&LLVMMetadata, element_count: u32, runtime_language: u32, vtable_holder: &LLVMMetadata, unique_identifier_pointer: &u8, unique_identifier_length: u64) &LLVMMetadata; +[extern] LLVMDIBuilderCreateUnionType = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, bit_size: u64, bit_alignment: u32, flags: LLVMDIFlags, element_pointer: &&LLVMMetadata, element_count: u32, runtime_language: u32, unique_identifier_pointer: &u8, unique_identifier_length: u64) &LLVMMetadata; [extern] LLVMDIBuilderCreateFunction = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, linkage_name_pointer: &u8, linkage_name_length: u64, file: &LLVMMetadata, line: u32, type: &LLVMMetadata, is_local_to_unit: s32, is_definition: s32, scope_line: u32, flags: LLVMDIFlags, is_optimized: s32) &LLVMMetadata; [extern] LLVMDIBuilderFinalizeSubprogram = fn [cc(c)] (di_builder: &LLVMDIBuilder, subprogram: &LLVMMetadata) void; @@ -3951,6 +3990,10 @@ resolve_alias = fn (module: &Module, type: &Type) &Type result = type; } }, + .alias => + { + result = resolve_alias(module, type.content.alias.type); + }, else => { #trap(); @@ -6364,11 +6407,11 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement }, .break => { - #trap(); + statement.id = .break; }, .continue => { - #trap(); + statement.id = .continue; }, } } @@ -7080,11 +7123,111 @@ parse = fn (module: &Module) void }, .typealias => { - #trap(); + >aliased_type = parse_type(module, scope); + + if (!consume_character_if_match(module, ';')) + { + report_error(); + } + + >alias_type = new_type(module, { + .content = { + .alias = { + .type = aliased_type, + .scope = scope, + .line = global_line, + }, + }, + .id = .alias, + .name = global_name, + .scope = scope, + zero, + }); }, .union => { - #trap(); + skip_space(module); + expect_character(module, left_brace); + + >union_type: &Type = undefined; + + if (type_forward_declaration) + { + union_type = type_forward_declaration; + } + else + { + union_type = new_type(module, { + .id = .forward_declaration, + .name = global_name, + .scope = &module.scope, + zero, + }); + } + + >field_count: u64 = 0; + >biggest_field: u64 = 0; + >alignment: u32 = 1; + >byte_size: u64 = 0; + + >field_buffer: [64]UnionField = undefined; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_brace)) + { + break; + } + + >field_index = field_count; + field_count += 1; + + >field_line = get_line(module); + >field_name = parse_identifier(module); + + skip_space(module); + expect_character(module, ':'); + skip_space(module); + + >field_type = parse_type(module, scope); + + >field_alignment = get_byte_alignment(field_type); + >field_size = get_byte_size(field_type); + + field_buffer[field_index] = { + .type = field_type, + .name = field_name, + .line = field_line, + }; + + biggest_field = #select(field_size > byte_size, field_index, biggest_field); + alignment = #max(alignment, field_alignment); + byte_size = #max(byte_size, field_size); + + skip_space(module); + + consume_character_if_match(module, ','); + } + + skip_space(module); + consume_character_if_match(module, ';'); + + >fields = arena_allocate_slice[UnionField](module.arena, field_count); + memcpy(#pointer_cast(fields.pointer), #pointer_cast(&field_buffer), field_count * #byte_size(UnionField)); + + >biggest_size = get_byte_size(fields[biggest_field].type); + assert(biggest_size == byte_size); + + union_type.content.union = { + .fields = fields, + .byte_size = byte_size, + .byte_alignment = alignment, + .line = global_line, + .biggest_field = biggest_field, + }; + union_type.id = .union; }, } } @@ -7192,6 +7335,16 @@ resolve_type_in_place_abi = fn (module: &Module, type: &Type) void >size = get_byte_size(type); assert(llvm_size == size); }, + .union => + { + >biggest_type = type.content.union.fields[type.content.union.biggest_field].type; + resolve_type_in_place_memory(module, biggest_type); + result = LLVMStructTypeInContext(module.llvm.context, &biggest_type.llvm.memory, 1, 0); + + >llvm_size = LLVMStoreSizeOfType(module.llvm.target_data_layout, result); + >size = get_byte_size(type); + assert(llvm_size == size); + }, else => { #trap(); @@ -7242,6 +7395,16 @@ resolve_type_in_place_memory = fn (module: &Module, type: &Type) void resolve_type_in_place_memory(module, backing_type); result = backing_type.llvm.memory; }, + .union => + { + >biggest_type = type.content.union.fields[type.content.union.biggest_field].type; + resolve_type_in_place_memory(module, biggest_type); + result = LLVMStructTypeInContext(module.llvm.context, &biggest_type.llvm.memory, 1, 0); + + >llvm_size = LLVMStoreSizeOfType(module.llvm.target_data_layout, result); + >size = get_byte_size(type); + assert(llvm_size == size); + }, else => { #trap(); @@ -7393,6 +7556,40 @@ resolve_type_in_place_debug = fn (module: &Module, type: &Type) void >struct_type = LLVMDIBuilderCreateStructType(module.llvm.di_builder, module.scope.llvm, type.name.pointer, type.name.length, module.llvm.file, type.content.bits.line, size, alignment, flags, zero, &llvm_type_buffer[0], #truncate(fields.length), runtime_language, vtable_holder, type.name.pointer, type.name.length); result = struct_type; }, + .union => + { + >flags: LLVMDIFlags = zero; + >runtime_language: u32 = 0; + >byte_size = get_byte_size(type); + >alignment = get_byte_alignment(type); + >forward_declaration = LLVMDIBuilderCreateReplaceableCompositeType(module.llvm.di_builder, module.llvm.debug_tag, type.name.pointer, type.name.length, module.scope.llvm, module.llvm.file, type.content.union.line, runtime_language, byte_size * 8, alignment * 8, flags, type.name.pointer, type.name.length); + module.llvm.debug_tag += 1; + type.llvm.debug = forward_declaration; + + >llvm_type_buffer: [64]&LLVMMetadata = undefined; + + >fields = type.content.union.fields; + + for (i: 0..fields.length) + { + >field = &fields[i]; + + >field_type = field.type; + resolve_type_in_place_debug(module, field_type); + + >offset: u64 = 0; + + >member_type = LLVMDIBuilderCreateMemberType(module.llvm.di_builder, module.scope.llvm, field.name.pointer, field.name.length, module.llvm.file, field.line, get_byte_size(field_type) * 8, get_byte_alignment(field_type) * 8, offset, flags, field_type.llvm.debug); + llvm_type_buffer[i] = member_type; + } + + >runtime_language: u32 = 0; + + >union_type = LLVMDIBuilderCreateUnionType(module.llvm.di_builder, module.scope.llvm, type.name.pointer, type.name.length, module.llvm.file, type.content.union.line, byte_size * 8, alignment * 8, flags, &llvm_type_buffer[0], #truncate(fields.length), runtime_language, type.name.pointer, type.name.length); + LLVMMetadataReplaceAllUsesWith(forward_declaration, union_type); + + result = union_type; + }, else => { #trap(); @@ -9469,7 +9666,50 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi } else { - #trap(); + if (values.length == 0) + { + report_error(); + } + + >expected_type: &Type = zero; + >is_constant: u1 = 1; + + for (value: values) + { + analyze_type(module, value, expected_type, { .must_be_constant = analysis.must_be_constant, zero }); + + is_constant = is_constant and value_is_constant(value); + + >value_type = value.type; + if (expected_type) + { + if (expected_type != value_type) + { + report_error(); + } + } + else + { + assert(value_type != zero); + expected_type = value_type; + } + } + + if (!expected_type) + { + report_error(); + } + + >element_type = expected_type; + >element_count = values.length; + + >array_type = get_array_type(module, element_type, element_count); + value_type = array_type; + + if (value.kind == .left) + { + value_type = get_pointer_type(module, array_type); + } } }, .array_expression => @@ -9607,7 +9847,30 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi }, .union => { - #trap(); + >fields = resolved_aggregate_type.content.union.fields; + >union_field: &UnionField = zero; + + for (&field: fields) + { + if (string_equal(field_name, field.name)) + { + union_field = field; + break; + } + } + + if (!union_field) + { + report_error(); + } + + >field_type = union_field.type; + value_type = field_type; + + if (value.kind == .left) + { + value_type = get_pointer_type(module, value_type); + } }, .bits => { @@ -9965,7 +10228,32 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi }, .union => { - #trap(); + if (elements.length != 1) + { + report_error(); + } + + >initialization_value = elements[0].value; + >initialization_name = elements[0].name; + + >result_field: &UnionField = zero; + >fields = aggregate_type.content.union.fields; + + for (&field: fields) + { + if (string_equal(initialization_name, field.name)) + { + result_field = field; + break; + } + } + + if (!result_field) + { + report_error(); + } + + analyze_type(module, initialization_value, result_field.type, { .must_be_constant = analysis.must_be_constant, zero }); }, .enum_array => { @@ -10345,6 +10633,20 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi analyze_type(module, enum_string_value, string_type, { .must_be_constant = analysis.must_be_constant, zero }); value_type = struct_type; }, + .undefined => + { + if (!expected_type) + { + report_error(); + } + + if (expected_type.id == .void or expected_type.id == .noreturn) + { + report_error(); + } + + value_type = expected_type; + }, else => { #trap(); @@ -11575,7 +11877,29 @@ emit_field_access = fn (module: &Module, value: &Value, left_llvm: &LLVMValue, l }, .union => { - #trap(); + >fields = resolved_aggregate_type.content.union.fields; + >union_field: &UnionField = zero; + + for (&field: fields) + { + if (string_equal(field_name, field.name)) + { + union_field = field; + break; + } + } + + assert(union_field != zero); + >field_type = union_field.type; + resolve_type_in_place(module, field_type); + + >struct_type = LLVMStructTypeInContext(module.llvm.context, &field_type.llvm.memory, 1, 0); + + field_access = { + .type = field_type, + .field_index = 0, + .struct_type = struct_type, + }; }, else => { unreachable; }, } @@ -12487,8 +12811,8 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con report_error(); } - // TODO: field parent pointer - #trap(); + >global: &Global = #field_parent_pointer(variable, "variable"); + llvm_value = global.initial_value.llvm; } else { @@ -12569,7 +12893,51 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con } else { - #trap(); + switch (value.kind) + { + .right => + { + #trap(); + }, + .left => + { + assert(resolved_value_type.id == .pointer); + >array_type = resolved_value_type.content.pointer.element_type; + assert(array_type.id == .array); + + >alloca = create_alloca(module, { + .type = array_type, + .name = "array.init", + zero, + }); + + >element_type = array_type.content.array.element_type; + >pointer_to_element_type = get_pointer_type(module, element_type); + + >u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + + >llvm_u64_type = u64_type.llvm.abi; + >u64_zero = LLVMConstNull(llvm_u64_type); + + >llvm_array_type = array_type.llvm.memory; + + for (i: 0..values.length) + { + >alloca_gep = create_gep(module, { + .type = llvm_array_type, + .pointer = alloca, + .indices = [ u64_zero, LLVMConstInt(llvm_u64_type, i, 0) ][..], + zero, + }); + + >value = values[i]; + emit_assignment(module, alloca_gep, pointer_to_element_type, value); + } + + llvm_value = alloca; + }, + } } }, .array_expression => @@ -13216,7 +13584,45 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, }, .union => { - #trap(); + assert(elements.length == 1); + + >fields = resolved_value_type.content.union.fields; + >biggest_field_index = resolved_value_type.content.union.biggest_field; + >biggest_field = &fields[biggest_field_index]; + >biggest_field_type = biggest_field.type; + >value = elements[0].value; + >field_value_type = value.type; + >field_type_size = get_byte_size(field_value_type); + >union_size = get_byte_size(resolved_value_type); + + if (field_type_size < union_size) + { + >u8_type = uint8(module); + resolve_type_in_place(module, u8_type); + LLVMBuildMemSet(module.llvm.builder, left_llvm, LLVMConstNull(u8_type.llvm.memory), LLVMConstInt(u64_type.llvm.memory, union_size, 0), alignment); + } + else if (field_type_size > union_size) + { + unreachable; + } + + >struct_type: &LLVMType = zero; + + if (type_is_abi_equal(module, field_value_type, biggest_field_type)) + { + struct_type = resolved_value_type.llvm.memory; + } + else + { + struct_type = LLVMStructTypeInContext(module.llvm.context, &field_value_type.llvm.memory, 1, 0); + } + + assert(struct_type != zero); + + >destination_pointer = LLVMBuildStructGEP2(module.llvm.builder, struct_type, left_llvm, 0, ""); + >field_pointer_type = get_pointer_type(module, field_value_type); + + emit_assignment(module, destination_pointer, field_pointer_type, value); }, else => { @@ -13297,6 +13703,26 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, zero, }); }, + .undefined => + { + // TODO: something + }, + .select => + { + if (!type_is_slice(resolved_value_type)) + { + report_error(); + } + + emit_value(module, right, .memory, 0); + + create_store(module, { + .source = right.llvm, + .destination = left_llvm, + .type = resolved_value_type, + zero, + }); + }, else => { #trap(); @@ -14077,7 +14503,33 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v }, .struct => { - #trap(); + assert(type_is_slice(aggregate_type)); + + if (iteratable_kind == .left) + { + iteratable_llvm = create_load(module, { + .type = aggregate_type, + .pointer = iteratable_llvm, + zero, + }); + } + + >extract_pointer = LLVMBuildExtractValue(module.llvm.builder, iteratable_llvm, 0, ""); + + >fields = aggregate_type.content.struct.fields; + >pointer_type = fields[0].type; + assert(pointer_type.id == .pointer); + >gep_type = pointer_type.content.pointer.element_type; + resolve_type_in_place(module, gep_type); + + >gep = create_gep(module, { + .type = gep_type.llvm.memory, + .pointer = extract_pointer, + .indices = [ body_index_load ][..], + zero, + }); + + element_pointer_value = gep; }, else => { unreachable; }, } @@ -14173,7 +14625,31 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v if (value_is_constant(start)) { - #trap(); + switch (start.id) + { + .constant_integer => + { + switch (end.id) + { + .constant_integer => + { + >start_signed = start.content.constant_integer.signed; + >end_signed = end.content.constant_integer.signed; + >is_signed = !(!start_signed and !end_signed); + local_type = integer_type(module, { .bit_count = 64, .signed = is_signed }); + }, + else => + { + analyze_type(module, end, zero, zero); + >end_type = end.type; + assert(end_type != zero); + start.type = end_type; + local_type = end_type; + }, + } + }, + else => { unreachable; }, + } } else { @@ -14261,6 +14737,28 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v module.llvm.continue_block = previous_continue_block; module.llvm.exit_block = previous_exit_block; }, + .break => + { + >exit_block = module.llvm.exit_block; + if (!exit_block) + { + report_error(); + } + + LLVMBuildBr(module.llvm.builder, exit_block); + LLVMClearInsertionPosition(module.llvm.builder); + }, + .continue => + { + >continue_block = module.llvm.continue_block; + if (!continue_block) + { + report_error(); + } + + LLVMBuildBr(module.llvm.builder, continue_block); + LLVMClearInsertionPosition(module.llvm.builder); + }, else => { #trap(); @@ -15712,6 +16210,19 @@ names: [_][]u8 = "for_each", "pointer_decay", "enum_name", + "slice_of_slices", + "type_alias", + "integer_formats", + "for_each_int", + "bool_array", + "basic_union", + "break_continue", + "constant_global_reference", + "concat_logical_or", + "strict_array_type", + "pointer_struct_initialization", + "slice_array_literal", + "slice_only_start", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/emitter.cpp b/src/emitter.cpp index 547a7ef..afc64b7 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -6591,7 +6591,7 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, auto fields = resolved_value_type->union_type.fields; auto biggest_field_index = resolved_value_type->union_type.biggest_field; auto& biggest_field = fields[biggest_field_index]; - auto biggest_field_type = fields[biggest_field_index].type; + auto biggest_field_type = biggest_field.type; auto value = elements[0].value; auto field_value_type = value->type; auto field_type_size = get_byte_size(field_value_type); @@ -6621,7 +6621,7 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, auto destination_pointer = LLVMBuildStructGEP2(module->llvm.builder, struct_type, left_llvm, 0, ""); auto field_pointer_type = get_pointer_type(module, field_value_type); - unused(biggest_field); + emit_assignment(module, destination_pointer, field_pointer_type, value); } break; default: unreachable(); @@ -7587,6 +7587,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect assert(array_type->id == TypeId::array); auto alloca = create_alloca(module, { .type = array_type, + .name = string_literal("array.init"), }); auto pointer_to_element_type = get_pointer_type(module, array_type->array.element_type);