From c8468686a6a1774645035c965a2a25fca0172982 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Sun, 27 Apr 2025 21:21:31 -0600 Subject: [PATCH] Constant global reference and proper type solving --- src/bootstrap.zig | 158 ++++++++++++++++++---------- src/compiler.bbb | 119 ++++++++++++++++++++- tests/constant_global_reference.bbb | 7 ++ 3 files changed, 223 insertions(+), 61 deletions(-) create mode 100644 tests/constant_global_reference.bbb diff --git a/src/bootstrap.zig b/src/bootstrap.zig index fadf12b..c3b671f 100644 --- a/src/bootstrap.zig +++ b/src/bootstrap.zig @@ -338,7 +338,7 @@ pub const Type = struct { }; pub const Union = struct { - fields: []const Union.Field, + fields: []Union.Field, byte_size: u64, byte_alignment: u32, line: u32, @@ -719,6 +719,7 @@ pub const Type = struct { .bits => |bits| bits.backing_type.get_byte_size(), .enumerator => |enumerator| enumerator.backing_type.get_byte_size(), .alias => |alias| alias.type.get_byte_size(), + .@"union" => |union_type| union_type.byte_size, else => @trap(), }; return byte_size; @@ -4015,6 +4016,7 @@ pub const Module = struct { var global_keyword = false; if (is_identifier_start_ch(module.content[module.offset])) { + const identifier_offset = module.offset; const global_string = module.parse_identifier(); module.skip_space(); @@ -4692,7 +4694,7 @@ pub const Module = struct { }, } } else { - module.offset -= global_string.len; + module.offset = identifier_offset; } } @@ -6016,13 +6018,19 @@ pub const Module = struct { module.emit_value(value, type_kind); } - pub fn analyze_binary(module: *Module, left: *Value, right: *Value, is_boolean: bool, analysis: ValueAnalysis) void { + pub fn analyze_binary(module: *Module, left: *Value, right: *Value, is_boolean: bool, a: ValueAnalysis) void { + var analysis = a; const is_left_constant = left.is_constant(); const is_right_constant = right.is_constant(); if (analysis.type == null) { if (is_left_constant and is_right_constant) { if (left.type == null and right.type == null) { - module.report_error(); + const are_string_literal = left.bb == .string_literal and right.bb == .string_literal; + if (are_string_literal) { + analysis.type = module.get_slice_type(.{ .type = module.integer_type(8, false) }); + } else { + module.report_error(); + } } } } @@ -6066,12 +6074,18 @@ pub const Module = struct { fn fully_resolve_alias(module: *Module, ty: *Type) *Type { const result_type = switch (ty.bb) { - .integer => ty, + .bits, + .structure, + .@"union", + .integer, + .enumerator, + .array, + .noreturn, + .void, + .function, + => ty, .alias => |alias| alias.type, .pointer => |pointer| module.get_pointer_type(.{ .type = module.fully_resolve_alias(pointer.type) }), - .bits => ty, - .structure => ty, - .@"union" => ty, else => @trap(), }; @@ -7429,9 +7443,12 @@ pub const Module = struct { pub fn emit_value(module: *Module, value: *Value, type_kind: Type.Kind) void { const value_type = value.type orelse unreachable; + const resolved_type = module.fully_resolve_alias(value_type); assert(value.llvm == null); value_type.resolve(module); + const must_be_constant = module.current_function == null and module.current_macro_instantiation == null; + const llvm_value: *llvm.Value = switch (value.bb) { .constant_integer => |constant_integer| value_type.get_llvm(type_kind).to_integer().get_constant(constant_integer.value, @intFromBool(constant_integer.signed)).to_value(), .unary => |unary| switch (unary.id) { @@ -7539,11 +7556,7 @@ pub const Module = struct { module.emit_value(binary.right, .abi); break :b binary.right.llvm orelse unreachable; }; - var it = value_type; - while (it.bb == .alias) { - it = it.bb.alias.type; - } - const result = switch (it.bb) { + const result = switch (resolved_type.bb) { .integer => |integer| switch (binary.id) { .@"+" => module.llvm.builder.create_add(left, right), .@"-" => module.llvm.builder.create_sub(left, right), @@ -7584,7 +7597,10 @@ pub const Module = struct { }, else => module.report_error(), }, - .pointer => |pointer| switch (binary.id) { + .pointer => |pointer| switch (b: { + pointer.type.resolve(module); + break :b binary.id; + }) { .@"+" => module.llvm.builder.create_gep(.{ .type = pointer.type.llvm.abi.?, .aggregate = left, @@ -7629,19 +7645,27 @@ pub const Module = struct { }, }, .right => switch (variable.type == value_type) { - true => switch (value_type.get_evaluation_kind()) { - .scalar => module.create_load(.{ - .type = value_type, - .value = variable.storage.?.llvm.?, - .alignment = variable.storage.?.type.?.bb.pointer.alignment, - }), - // TODO: this might be wrong - .aggregate => module.create_load(.{ - .type = value_type, - .value = variable.storage.?.llvm.?, - .alignment = variable.storage.?.type.?.bb.pointer.alignment, - }), - .complex => @trap(), + true => switch (must_be_constant) { + true => b: { + if (variable.scope.kind != .global) { + module.report_error(); + } + break :b variable.initial_value.llvm.?; + }, + false => switch (value_type.get_evaluation_kind()) { + .scalar => module.create_load(.{ + .type = value_type, + .value = variable.storage.?.llvm.?, + .alignment = variable.storage.?.type.?.bb.pointer.alignment, + }), + // TODO: this might be wrong + .aggregate => module.create_load(.{ + .type = value_type, + .value = variable.storage.?.llvm.?, + .alignment = variable.storage.?.type.?.bb.pointer.alignment, + }), + .complex => @trap(), + }, }, false => module.report_error(), }, @@ -7941,6 +7965,7 @@ pub const Module = struct { else => base_type, }; const ty = pointer_type.bb.pointer.type; + ty.resolve(module); const v = switch (pointer_type == base_type) { false => module.create_load(.{ .type = pointer_type, .value = field_access.aggregate.llvm.? }), true => field_access.aggregate.llvm.?, @@ -8782,19 +8807,20 @@ pub const Module = struct { fn emit_assignment(module: *Module, left_llvm: *llvm.Value, left_type: *Type, right: *Value) void { assert(right.llvm == null); const pointer_type = left_type; - const value_type = right.type.?; - value_type.resolve(module); + const v_type = right.type.?; + v_type.resolve(module); pointer_type.resolve(module); + const resolved_type = module.fully_resolve_alias(v_type); assert(pointer_type.bb == .pointer); - assert(pointer_type.bb.pointer.type == value_type); + assert(pointer_type.bb.pointer.type == v_type); - switch (value_type.get_evaluation_kind()) { + switch (resolved_type.get_evaluation_kind()) { .scalar => { module.emit_value(right, .memory); _ = module.create_store(.{ .source_value = right.llvm.?, .destination_value = left_llvm, - .type = value_type, + .type = v_type, .alignment = pointer_type.bb.pointer.alignment, }); }, @@ -8806,10 +8832,10 @@ pub const Module = struct { .linkage = .InternalLinkage, .name = "constarray", // TODO: format properly .initial_value = right.llvm.?.to_constant(), - .type = value_type.llvm.memory.?, + .type = v_type.llvm.memory.?, }); global_variable.set_unnamed_address(.global); - const element_type = value_type.bb.array.element_type; + const element_type = v_type.bb.array.element_type; const alignment = element_type.get_byte_alignment(); global_variable.to_value().set_alignment(alignment); const uint64 = module.integer_type(64, false); @@ -8817,14 +8843,14 @@ pub const Module = struct { _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment.?, global_variable.to_value(), alignment, uint64.llvm.abi.?.to_integer().get_constant(array_initialization.values.len * pointer_type.bb.pointer.type.bb.array.element_type.get_byte_size(), @intFromBool(false)).to_value()); }, false => { - assert(value_type.bb == .array); + assert(v_type.bb == .array); const uint64 = module.integer_type(64, false); uint64.resolve(module); const u64_zero = uint64.llvm.abi.?.to_integer().get_constant(0, 0).to_value(); - const pointer_to_element_type = module.get_pointer_type(.{ .type = value_type.bb.array.element_type }); + const pointer_to_element_type = module.get_pointer_type(.{ .type = v_type.bb.array.element_type }); for (array_initialization.values, 0..) |v, i| { const alloca_gep = module.llvm.builder.create_gep(.{ - .type = value_type.llvm.memory.?, + .type = v_type.llvm.memory.?, .aggregate = left_llvm, .indices = &.{ u64_zero, uint64.llvm.abi.?.to_integer().get_constant(i, 0).to_value() }, }); @@ -8839,20 +8865,20 @@ pub const Module = struct { .linkage = .InternalLinkage, .name = "conststruct", // TODO: format properly .initial_value = right.llvm.?.to_constant(), - .type = value_type.llvm.abi.?, + .type = v_type.llvm.abi.?, }); global_variable.set_unnamed_address(.global); - const alignment = value_type.get_byte_alignment(); + const alignment = v_type.get_byte_alignment(); global_variable.to_value().set_alignment(alignment); const uint64 = module.integer_type(64, false); uint64.resolve(module); - _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.get_alignment(), global_variable.to_value(), alignment, uint64.llvm.abi.?.to_integer().get_constant(value_type.get_byte_size(), @intFromBool(false)).to_value()); + _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.get_alignment(), global_variable.to_value(), alignment, uint64.llvm.abi.?.to_integer().get_constant(v_type.get_byte_size(), @intFromBool(false)).to_value()); }, - false => switch (value_type.bb) { + false => switch (resolved_type.bb) { .structure => { var max_field_index: u64 = 0; var field_mask: u64 = 0; - const fields = value_type.bb.structure.fields; + const fields = resolved_type.bb.structure.fields; assert(fields.len <= 64); for (aggregate_initialization.values, aggregate_initialization.names) |initialization_value, initialization_name| { @@ -8864,7 +8890,7 @@ pub const Module = struct { field_mask |= @as(@TypeOf(field_mask), 1) << @intCast(field_index); max_field_index = @max(field_index, max_field_index); const field = &fields[field_index]; - const destination_pointer = module.llvm.builder.create_struct_gep(value_type.llvm.abi.?.to_struct(), left_llvm, @intCast(field_index)); + const destination_pointer = module.llvm.builder.create_struct_gep(v_type.llvm.abi.?.to_struct(), left_llvm, @intCast(field_index)); module.emit_assignment(destination_pointer, module.get_pointer_type(.{ .type = field.type }), initialization_value); } @@ -8884,9 +8910,9 @@ pub const Module = struct { } const field_index_offset = fields.len - end_uninitialized_field_count; - const destination_pointer = module.llvm.builder.create_struct_gep(value_type.llvm.abi.?.to_struct(), left_llvm, @intCast(field_index_offset)); + const destination_pointer = module.llvm.builder.create_struct_gep(v_type.llvm.abi.?.to_struct(), left_llvm, @intCast(field_index_offset)); const start_field = &fields[field_index_offset]; - const memset_size = value_type.get_byte_size() - start_field.byte_offset; + const memset_size = v_type.get_byte_size() - start_field.byte_offset; const uint8 = module.integer_type(8, false); uint8.resolve(module); const uint64 = module.integer_type(64, false); @@ -8902,7 +8928,7 @@ pub const Module = struct { const field_value_type = value.type.?; const field_type_size = field_value_type.get_byte_size(); - const struct_type = if (field_value_type.is_abi_equal(biggest_field_type, module)) value_type.llvm.memory.?.to_struct() else module.llvm.context.get_struct_type(&.{field_value_type.llvm.memory.?}); + const struct_type = if (field_value_type.is_abi_equal(biggest_field_type, module)) v_type.llvm.memory.?.to_struct() else module.llvm.context.get_struct_type(&.{field_value_type.llvm.memory.?}); const destination_pointer = module.llvm.builder.create_struct_gep(struct_type, left_llvm, 0); const field_pointer_type = module.get_pointer_type(.{ .type = field_value_type }); @@ -8934,9 +8960,9 @@ pub const Module = struct { .type = u8_type, }); - switch (value_type.bb) { + switch (resolved_type.bb) { .structure => |structure| switch (structure.is_slice) { - true => switch (slice_type == value_type) { + true => switch (slice_type == resolved_type) { true => { const pointer_to_pointer = module.llvm.builder.create_struct_gep(slice_type.llvm.abi.?.to_struct(), left_llvm, 0); const slice_pointer_type = slice_type.bb.structure.fields[0].type; @@ -8997,7 +9023,7 @@ pub const Module = struct { }); }, .va_start => { - assert(value_type == module.get_va_list_type()); + assert(resolved_type == module.get_va_list_type()); assert(pointer_type.bb.pointer.type == module.get_va_list_type()); const intrinsic_id = module.llvm.intrinsic_table.va_start; const argument_types: []const *llvm.Type = &.{module.llvm.pointer_type}; @@ -9010,7 +9036,7 @@ pub const Module = struct { const result = module.emit_va_arg(right, left_llvm, left_type); switch (result == left_llvm) { true => {}, - false => switch (value_type.get_evaluation_kind()) { + false => switch (resolved_type.get_evaluation_kind()) { .scalar => { @trap(); }, @@ -9038,14 +9064,14 @@ pub const Module = struct { }, .slice_expression => { const slice_values = module.emit_slice_expression(right); - const slice_pointer_type = value_type.bb.structure.fields[0].type; + const slice_pointer_type = resolved_type.bb.structure.fields[0].type; _ = module.create_store(.{ .source_value = slice_values[0], .destination_value = left_llvm, .type = slice_pointer_type, .alignment = pointer_type.bb.pointer.alignment, }); - const slice_length_destination = module.llvm.builder.create_struct_gep(value_type.llvm.abi.?.to_struct(), left_llvm, 1); + const slice_length_destination = module.llvm.builder.create_struct_gep(resolved_type.llvm.abi.?.to_struct(), left_llvm, 1); _ = module.create_store(.{ .source_value = slice_values[1], .destination_value = slice_length_destination, @@ -9057,13 +9083,13 @@ pub const Module = struct { u8_type.resolve(module); const u64_type = module.integer_type(64, false); u64_type.resolve(module); - _ = module.llvm.builder.create_memset(left_llvm, u8_type.llvm.abi.?.get_zero().to_value(), u64_type.llvm.abi.?.to_integer().get_constant(value_type.get_byte_size(), 0).to_value(), pointer_type.bb.pointer.alignment.?); + _ = module.llvm.builder.create_memset(left_llvm, u8_type.llvm.abi.?.get_zero().to_value(), u64_type.llvm.abi.?.to_integer().get_constant(resolved_type.get_byte_size(), 0).to_value(), pointer_type.bb.pointer.alignment.?); }, .variable_reference => |variable| switch (right.kind) { .left => @trap(), .right => { const uint64 = module.integer_type(64, false); - _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment.?, variable.storage.?.llvm.?, variable.storage.?.type.?.bb.pointer.alignment.?, uint64.llvm.abi.?.to_integer().get_constant(value_type.get_byte_size(), @intFromBool(false)).to_value()); + _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment.?, variable.storage.?.llvm.?, variable.storage.?.type.?.bb.pointer.alignment.?, uint64.llvm.abi.?.to_integer().get_constant(resolved_type.get_byte_size(), @intFromBool(false)).to_value()); }, }, .field_access => |field_access| { @@ -9077,7 +9103,7 @@ pub const Module = struct { module.emit_value(field_access.aggregate, .memory); const gep = module.llvm.builder.create_struct_gep(struct_type.llvm.abi.?.to_struct(), field_access.aggregate.llvm.?, field_index); const uint64 = module.integer_type(64, false); - _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment.?, gep, value_type.get_byte_alignment(), uint64.llvm.abi.?.to_integer().get_constant(value_type.get_byte_size(), @intFromBool(false)).to_value()); + _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment.?, gep, resolved_type.get_byte_alignment(), uint64.llvm.abi.?.to_integer().get_constant(resolved_type.get_byte_size(), @intFromBool(false)).to_value()); }, .undefined => {}, else => @trap(), @@ -9277,7 +9303,13 @@ pub const Module = struct { } // TODO: is this valid for pointers too? } else if (source_type.is_integer_backing()) { - @trap(); + const destination_integer_type = module.integer_type(@intCast(destination_size * 8), false); + const value = module.coerce_int_or_pointer_to_int_or_pointer(source_value, source_type, destination_integer_type); + _ = module.create_store(.{ + .type = destination_integer_type, + .source_value = value, + .destination_value = destination_pointer, + }); } else { // Coercion through memory const original_destination_alignment = destination_type.get_byte_alignment(); @@ -9410,6 +9442,20 @@ pub const Module = struct { array.element_type = module.resolve_type(array.element_type); break :blk ty; }, + .alias => |*alias| blk: { + alias.type = module.resolve_type(alias.type); + break :blk ty; + }, + .@"union" => |*union_type| blk: { + for (union_type.fields) |*field| { + field.type = module.resolve_type(field.type); + } + break :blk ty; + }, + .enumerator => |*enumerator| blk: { + enumerator.backing_type = module.resolve_type(enumerator.backing_type); + break :blk ty; + }, else => @trap(), }; assert(result_type.bb != .unresolved); diff --git a/src/compiler.bbb b/src/compiler.bbb index 3f1438d..93e5b51 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -586,22 +586,81 @@ CompileOptions = struct silent: u1, } +TypeId = enum +{ + void, + noreturn, + integer, +} + +TypeInteger = struct +{ + bit_count: u32, + signed: u1, +} + +TypeContent = union +{ + integer: TypeInteger, +} + Type = struct { - foo: u32, + content: TypeContent, + id: TypeId, + name: []u8, +} + +ValueId = enum +{ + infer_or_ignore, +} + +Value = struct +{ + type: &Type, + id: ValueId, +} + +i128_offset: u64 = 64 * 2; +void_offset: u64 = i128_offset + 2; + +Module = struct +{ + arena: &Arena, + base_type_allocation: &Type, + void_value: &Value, +} + +module_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)); + return module.base_type_allocation + index; +} + +module_void_type = fn (module: &Module) &Type +{ + return module.base_type_allocation + void_offset; +} + +module_noreturn_type = fn (module: &Module) &Type +{ + return module_void_type(module) + 1; } compile = fn (arena: &Arena, options: CompileOptions) void { >signs: [2]u1 = [0, 1]; - >type_allocation = arena_allocate[Type](arena, - 64 * 2 // Basic integer types + >base_type_allocation = arena_allocate[Type](arena, + i128_offset // Basic integer types + 2 // u128, s128 + 2 // void, noreturn ); - >type_it = type_allocation; + >type_it = base_type_allocation; for (sign: signs) { @@ -616,11 +675,61 @@ compile = fn (arena: &Arena, options: CompileOptions) void >name = arena_duplicate_string(arena, name_buffer[..name_length]); type_it.& = { - zero, + .content = { + .integer = { + .bit_count = #truncate(bit_count), + .signed = sign, + }, + }, + .id = .integer, + .name = name, }; type_it += 1; } } + + for (sign: signs) + { + >name = #select(sign, "s128", "u128"); + type_it.& = { + .content = { + .integer = { + .bit_count = 128, + .signed = sign, + }, + }, + .id = .integer, + .name = name, + }; + type_it += 1; + } + + >void_type = type_it; + type_it += 1; + >noreturn_type = type_it; + type_it += 1; + + void_type.& = { + .id = .void, + .name = "void", + }; + + noreturn_type.& = { + .id = .noreturn, + .name = "noreturn", + }; + + >void_value = arena_allocate[Value](arena, 1); + void_value.& = { + .type = void_type, + .id = .infer_or_ignore, + }; + + >module: Module = { + .arena = arena, + .base_type_allocation = base_type_allocation, + .void_value = void_value, + }; } compile_file = fn (arena: &Arena, compile_options: CompileFile) void diff --git a/tests/constant_global_reference.bbb b/tests/constant_global_reference.bbb new file mode 100644 index 0000000..d0059f6 --- /dev/null +++ b/tests/constant_global_reference.bbb @@ -0,0 +1,7 @@ +i2315_abc: s32 = 5; +asjdkj = i2315_abc - i2315_abc; + +[export] main = fn [cc(c)] () s32 +{ + return asjdkj; +}