From 272b99be2602bd056d3cddf888e022fa60af00df Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Thu, 12 Jun 2025 18:37:46 -0600 Subject: [PATCH] Pass 'bits' --- src/compiler.bbb | 338 ++++++++++++++++++++++++++++++++++++++++++++++- src/parser.cpp | 3 +- 2 files changed, 338 insertions(+), 3 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index b9e2c35..278c803 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1392,6 +1392,14 @@ TypeStruct = struct next: &Type, } +TypeBits = struct +{ + fields: []Field, + backing_type: &Type, + line: u32, + is_implicit_backing_type: u1, +} + TypeContent = union { integer: TypeInteger, @@ -1401,6 +1409,7 @@ TypeContent = union enum_array: TypeEnumArray, enum: TypeEnum, struct: TypeStruct, + bits: TypeBits, } TypeLLVM = struct @@ -1926,6 +1935,22 @@ ValueVaArg = struct type: &Type, } +AggregateInitializationElement = struct +{ + name: []u8, + value: &Value, + line: u32, + column: u32, +} + +ValueAggregateInitialization = struct +{ + elements: []AggregateInitializationElement, + scope: &Scope, + is_constant: u1, + is_zero: u1, +} + ValueContent = union { constant_integer: ValueConstantInteger, @@ -1942,6 +1967,7 @@ ValueContent = union field_access: ValueFieldAccess, string_literal: []u8, va_arg: ValueVaArg, + aggregate_initialization: ValueAggregateInitialization, } ValueKind = enum @@ -4769,6 +4795,94 @@ reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: [ } } +parse_aggregate_initialization = fn (module: &Module, scope: &Scope, builder: ValueBuilder, end_ch: u8) &Value +{ + skip_space(module); + + >element_buffer: [64]AggregateInitializationElement = undefined; + >field_count: u64 = 0; + >is_zero: u1 = 0; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, end_ch)) + { + break; + } + + >field_index = field_count; + >checkpoint = get_checkpoint(module); + + if (consume_character_if_match(module, '.')) + { + >name = parse_identifier(module); + skip_space(module); + expect_character(module, '='); + skip_space(module); + + >line = get_line(module); + >column = get_column(module); + + >value = parse_value(module, scope, zero); + skip_space(module); + consume_character_if_match(module, ','); + + element_buffer[field_index] = { + .name = name, + .value = value, + .line = line, + .column = column, + }; + } + else + { + >token = tokenize(module); + is_zero = token.id == .value_keyword and token.content.value_keyword == .zero; + + if (is_zero) + { + skip_space(module); + + if (consume_character_if_match(module, ',')) + { + skip_space(module); + } + + expect_character(module, right_brace); + break; + } + else + { + report_error(); + } + } + + field_count += 1; + } + + >elements = arena_allocate_slice[AggregateInitializationElement](module.arena, field_count); + memcpy(#pointer_cast(elements.pointer), #pointer_cast(&element_buffer), field_count * #byte_size(AggregateInitializationElement)); + + >result = new_value(module); + result.& = { + .content = { + .aggregate_initialization = { + .elements = elements, + .scope = scope, + .is_constant = 0, + .is_zero = is_zero, + }, + }, + .id = .aggregate_initialization, + .kind = builder.kind, + zero, + }; + + return result; +} + parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value { >token = builder.token; @@ -5090,7 +5204,7 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .left_brace => { - #trap(); + result = parse_aggregate_initialization(module, scope, builder, right_brace); }, .value_keyword => { @@ -5994,7 +6108,100 @@ parse = fn (module: &Module) void { .bits => { - #trap(); + >is_implicit_type = module.content[module.offset] == left_brace; + + >backing_type: &Type = zero; + + if (!is_implicit_type) + { + backing_type = parse_type(module, scope); + } + + skip_space(module); + expect_character(module, left_brace); + + >field_buffer: [64]Field = undefined; + >field_bit_offset: u64 = 0; + >field_count: u64 = 0; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_brace)) + { + break; + } + + >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_bit_count = get_bit_size(field_type); + + skip_space(module); + + consume_character_if_match(module, ','); + + field_buffer[field_count] = { + .name = field_name, + .type = field_type, + .offset = field_bit_offset, + .line = field_line, + }; + + field_bit_offset += field_bit_count; + field_count += 1; + } + + consume_character_if_match(module, ';'); + + >fields = arena_allocate_slice[Field](module.arena, field_count); + memcpy(#pointer_cast(fields.pointer), #pointer_cast(&field_buffer), field_count * #byte_size(Field)); + + >needed_bit_count = #max(next_power_of_two(field_bit_offset), 8); + if (needed_bit_count > #integer_max(u32)) + { + report_error(); + } + + >bit_count = needed_bit_count; + + if (!backing_type) + { + backing_type = integer_type(module, { .bit_count = bit_count, .signed = 0 }); + } + + if (backing_type.id != .integer) + { + report_error(); + } + + >backing_type_bit_size = get_bit_size(backing_type); + if (backing_type_bit_size > 64) + { + report_error(); + } + + new_type(module, { + .content = { + .bits = { + .fields = fields, + .backing_type = backing_type, + .line = global_line, + .is_implicit_backing_type = is_implicit_type, + }, + }, + .id = .bits, + .name = global_name, + .scope = &module.scope, + zero, + }); }, .enum => { @@ -6331,6 +6538,10 @@ resolve_type_in_place_abi = fn (module: &Module, type: &Type) void >size = get_byte_size(type); assert(llvm_size == size); }, + .bits => + { + #trap(); + }, else => { #trap(); @@ -8494,6 +8705,128 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi value_type = value.content.va_arg.type; typecheck(module, expected_type, value_type); }, + .aggregate_initialization => + { + if (!expected_type) + { + report_error(); + } + + >resolved_type = resolve_alias(module, expected_type); + value_type = resolved_type; + + assert(!value.content.aggregate_initialization.is_constant); + >is_constant: u1 = 1; + >elements = value.content.aggregate_initialization.elements; + >is_zero = value.content.aggregate_initialization.is_zero; + >field_mask: u64 = 0; + + // TODO: make consecutive initialization with `zero` constant + // ie: + // 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) + + >aggregate_type: &Type = zero; + + switch (value.kind) + { + .left => + { + if (resolved_type.id != .pointer) + { + report_error(); + } + + aggregate_type = resolved_type.content.pointer.element_type; + }, + .right => + { + aggregate_type = resolved_type; + }, + } + + switch (aggregate_type.id) + { + .struct => + { + #trap(); + }, + .bits => + { + >fields = aggregate_type.content.bits.fields; + assert(fields.length <= 64); + + >same_values_as_field = fields.length == elements.length; + >is_properly_initialized = same_values_as_field or is_zero; + + if (is_zero and same_values_as_field) + { + report_error(); + } + + if (!is_properly_initialized) + { + report_error(); + } + + assert(elements.length <= fields.length); + + for (&initialization_element: elements) + { + >value = initialization_element.value; + >name = initialization_element.name; + + >declaration_index: u64 = 0; + + while (declaration_index < fields.length) + { + >field = &fields[declaration_index]; + + if (string_equal(name, field.name)) + { + break; + } + + declaration_index += 1; + } + + if (declaration_index == fields.length) + { + report_error(); + } + + >mask = 1 << declaration_index; + >current_mask = field_mask; + + if (current_mask & mask) + { + // Repeated field + report_error(); + } + + field_mask = current_mask | mask; + + >field = &fields[declaration_index]; + >declaration_type = field.type; + + analyze_type(module, value, declaration_type, { .must_be_constant = analysis.must_be_constant, zero }); + + is_constant = is_constant and value_is_constant(value); + } + + value.content.aggregate_initialization.is_constant = is_constant; + }, + .union => + { + #trap(); + }, + .enum_array => + { + #trap(); + }, + else => { report_error(); }, + } + }, else => { #trap(); @@ -11869,6 +12202,7 @@ names: [_][]u8 = [ "argv", "assignment_operators", "not_pointer", + "bits", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/parser.cpp b/src/parser.cpp index 13a51e3..e42590e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3365,7 +3365,8 @@ void parse(Module* module) { skip_space(module); - if (consume_character_if_match(module, right_brace)) { + if (consume_character_if_match(module, right_brace)) + { break; }