Compile the self-hosted compiler foundations
All checks were successful
CI / ci (ReleaseFast, ubuntu-latest) (push) Successful in 1m31s
CI / ci (ReleaseSmall, ubuntu-latest) (push) Successful in 1m30s
CI / ci (ReleaseSafe, ubuntu-latest) (push) Successful in 1m38s
CI / ci (Debug, ubuntu-latest) (push) Successful in 2m39s

This commit is contained in:
David Gonzalez Martin 2025-04-14 12:11:21 -06:00
parent eaeaa0f3b2
commit d45414cf1d
2 changed files with 185 additions and 94 deletions

View File

@ -528,6 +528,7 @@ pub const Type = struct {
.bits => |bits| bits.backing_type.get_bit_size(),
.array => |array| array.element_type.get_bit_size() * array.element_count,
.structure => |structure| structure.bit_size,
.enumerator => |enumerator| enumerator.backing_type.get_bit_size(),
else => @trap(),
};
return bit_size;
@ -699,6 +700,7 @@ pub const Value = struct {
zero,
slice_expression: SliceExpression,
@"unreachable",
undefined,
},
type: ?*Type = null,
llvm: ?*llvm.Value = null,
@ -729,8 +731,6 @@ pub const Value = struct {
const Intrinsic = union(Id) {
byte_size: *Type,
cast,
cast_to,
extend: *Value,
integer_max: *Type,
int_from_enum: *Value,
@ -746,8 +746,6 @@ pub const Value = struct {
const Id = enum {
byte_size,
cast,
cast_to,
extend,
integer_max,
int_from_enum,
@ -780,7 +778,16 @@ pub const Value = struct {
.variable_reference => false,
.aggregate_initialization => |aggregate_initialization| aggregate_initialization.is_constant,
.field_access => false,
.binary => false,
.array_initialization => |array_initialization| array_initialization.is_constant,
.intrinsic => |intrinsic| switch (intrinsic) {
.byte_size,
.integer_max,
=> true,
else => false,
},
.undefined => true,
.call => false,
else => @trap(),
};
}
@ -2534,7 +2541,9 @@ pub const Module = struct {
.@"unreachable" => .{
.bb = .@"unreachable",
},
else => @trap(),
.undefined => .{
.bb = .undefined,
},
};
value.* = new_value;
return value;
@ -2766,6 +2775,10 @@ pub const Module = struct {
.@">>" => .@">>",
.@"==" => .@"==",
.@"!=" => .@"!=",
.@">=" => .@">=",
.@"<=" => .@"<=",
.@">" => .@">",
.@"<" => .@"<",
else => @trap(),
};
@ -3701,7 +3714,7 @@ pub const Module = struct {
module.llvm.builder.position_at_end(block);
}
pub fn emit_va_arg(module: *Module, function: *Global, value: *Value, left: ?*Value) *llvm.Value {
pub fn emit_va_arg(module: *Module, function: *Global, value: *Value, left_llvm: ?*llvm.Value, left_type: ?*Type) *llvm.Value {
switch (value.bb) {
.intrinsic => |intrinsic| switch (intrinsic) {
.va_arg => |va_arg| {
@ -3796,9 +3809,9 @@ pub const Module = struct {
};
const result = switch (va_arg.type.get_evaluation_kind()) {
.scalar => module.create_load(.{ .type = va_arg.type, .value = llvm_address }),
.aggregate => if (left) |l| b: {
_ = module.llvm.builder.create_memcpy(l.llvm.?, l.type.?.bb.pointer.alignment, llvm_address, va_arg.type.get_byte_alignment(), module.integer_type(64, false).resolve(module).handle.to_integer().get_constant(va_arg.type.get_byte_size(), 0).to_value());
break :b l.llvm.?;
.aggregate => if (left_llvm) |l| b: {
_ = module.llvm.builder.create_memcpy(l, left_type.?.bb.pointer.alignment, llvm_address, va_arg.type.get_byte_alignment(), module.integer_type(64, false).resolve(module).handle.to_integer().get_constant(va_arg.type.get_byte_size(), 0).to_value());
break :b l;
} else llvm_address,
.complex => @trap(),
};
@ -3810,7 +3823,7 @@ pub const Module = struct {
}
}
pub fn emit_call(module: *Module, function: *Global, value: *Value, left: ?*Value) *llvm.Value {
pub fn emit_call(module: *Module, function: *Global, value: *Value, left_llvm: ?*llvm.Value, left_type: ?*Type) *llvm.Value {
switch (value.bb) {
.call => |call| {
const raw_function_type = call.function_type;
@ -3850,9 +3863,9 @@ pub const Module = struct {
// - virtual function pointer thunk
// - return alloca already exists
const semantic_return_type = function_type.return_abi.semantic_type;
const pointer = if (left) |l| b: {
assert(l.type.?.bb.pointer.type == semantic_return_type);
break :b l.llvm.?;
const pointer = if (left_llvm) |l| b: {
assert(left_type.?.bb.pointer.type == semantic_return_type);
break :b l;
} else b: {
const temporal_alloca = module.create_alloca(.{ .type = semantic_return_type, .name = "tmp" });
break :b temporal_alloca;
@ -4081,8 +4094,7 @@ pub const Module = struct {
if (abi_argument_type == semantic_argument_value.type) {
@trap();
} else if (abi_argument_type.bb == .pointer and abi_argument_type.bb.pointer.type == semantic_argument_value.type) {
switch (semantic_argument_value.is_constant()) {
} else if (abi_argument_type.bb == .pointer and abi_argument_type.bb.pointer.type == semantic_argument_value.type) switch (semantic_argument_value.is_constant()) {
true => {
module.emit_value(function, semantic_argument_value);
const global_variable = module.llvm.module.create_global_variable(.{
@ -4098,19 +4110,28 @@ pub const Module = struct {
abi_argument_count += 1;
break :indirect;
},
false => switch (semantic_argument_value.kind) {
.left => module.report_error(),
.right => {
false => switch (semantic_argument_value.bb) {
.variable_reference => {
const pointer_type = module.get_pointer_type(.{ .type = semantic_argument_value.type.? });
semantic_argument_value.type = null;
semantic_argument_value.kind = .left;
semantic_argument_value.type = module.get_pointer_type(.{ .type = semantic_argument_value.type.? });
module.emit_value(function, semantic_argument_value);
assert(abi_argument_type == semantic_argument_value.type);
module.analyze(function, semantic_argument_value, .{ .type = pointer_type });
llvm_abi_argument_value_buffer[abi_argument_count] = semantic_argument_value.llvm.?;
abi_argument_count += 1;
break :indirect;
},
else => {
assert(abi_argument_type.bb.pointer.type == semantic_argument_value.type);
const alloca = module.create_alloca(.{
.type = semantic_argument_value.type.?,
});
const pointer_type = module.get_pointer_type(.{ .type = semantic_argument_value.type.? });
module.emit_assignment(function, alloca, pointer_type, semantic_argument_value);
llvm_abi_argument_value_buffer[abi_argument_count] = alloca;
abi_argument_count += 1;
break :indirect;
},
},
}
} else {
@trap();
}
@ -4223,9 +4244,9 @@ pub const Module = struct {
@trap();
}
const coerce_alloca = if (left) |l| b: {
assert(l.type.?.bb.pointer.type == return_type_abi.semantic_type);
break :b l.llvm.?;
const coerce_alloca = if (left_llvm) |l| b: {
assert(left_type.?.bb.pointer.type == return_type_abi.semantic_type);
break :b l;
} else module.create_alloca(.{ .type = return_type_abi.semantic_type, .name = "coerce" });
var destination_pointer = switch (return_type_abi.attributes.direct.offset == 0) {
true => coerce_alloca,
@ -4717,6 +4738,15 @@ pub const Module = struct {
},
.global => {
module.analyze(null, global.variable.initial_value, .{ .type = global.variable.type });
if (global.variable.type == null) {
global.variable.type = global.variable.initial_value.type;
}
if (global.variable.type != global.variable.initial_value.type) {
module.report_error();
}
const global_variable = module.llvm.module.create_global_variable(.{
.linkage = switch (global.linkage) {
.internal => .InternalLinkage,
@ -5250,6 +5280,13 @@ pub const Module = struct {
module.report_error();
}
},
.undefined => {},
.string_literal => {
const slice_type = module.get_slice_type(.{ .type = module.integer_type(8, false) });
if (expected_type != slice_type) {
module.report_error();
}
},
else => @trap(),
};
@ -5505,7 +5542,7 @@ pub const Module = struct {
const slice_pointer = module.llvm.builder.create_gep(.{
.type = pointer.type.llvm.handle.?,
.aggregate = slice_expression.array_like.llvm.?,
.indices = &.{ start.llvm.? },
.indices = &.{start.llvm.?},
});
const slice_length = module.llvm.builder.create_sub(end.llvm.?, start.llvm.?);
return .{ slice_pointer, slice_length };
@ -5532,7 +5569,7 @@ pub const Module = struct {
const slice_pointer = module.llvm.builder.create_gep(.{
.type = slice_element_type.llvm.handle.?,
.aggregate = old_slice_pointer,
.indices = &.{ slice_expression.start.llvm.? },
.indices = &.{slice_expression.start.llvm.?},
});
const slice_length = module.llvm.builder.create_sub(old_slice_length, slice_expression.start.llvm.?);
return .{ slice_pointer, slice_length };
@ -5761,7 +5798,7 @@ pub const Module = struct {
const truncate = module.llvm.builder.create_truncate(llvm_value, value_type.llvm.handle.?);
break :blk truncate;
},
.va_arg => module.emit_va_arg(function.?, value, null),
.va_arg => module.emit_va_arg(function.?, value, null, null),
.va_end => |va_list| blk: {
module.emit_value(function, va_list);
@ -5787,7 +5824,7 @@ pub const Module = struct {
};
break :blk result;
},
.call => module.emit_call(function.?, value, null),
.call => module.emit_call(function.?, value, null, null),
.array_initialization => |array_initialization| switch (array_initialization.is_constant) {
true => blk: {
var llvm_value_buffer: [64]*llvm.Constant = undefined;
@ -5911,7 +5948,7 @@ pub const Module = struct {
break field.value;
}
} else module.report_error();
const llvm_value = value_type.llvm.handle.?.to_integer().get_constant(enum_int_value, @intFromBool(false));
const llvm_value = value_type.resolve(module).handle.to_integer().get_constant(enum_int_value, @intFromBool(false));
break :blk llvm_value.to_value();
},
.field_access => |field_access| blk: {
@ -5969,7 +6006,6 @@ pub const Module = struct {
}
} else unreachable;
const field = &bits.fields[declaration_index];
_ = field.bit_offset;
const fv = switch (field_value.bb) {
.constant_integer => |ci| ci.value,
@ -5981,15 +6017,30 @@ pub const Module = struct {
const llvm_value = bits.backing_type.resolve(module).handle.to_integer().get_constant(bits_value, @intFromBool(false));
break :blk llvm_value.to_value();
},
false => {
@trap();
false => blk: {
const llvm_type = bits.backing_type.resolve(module).handle;
const zero_value = llvm_type.get_zero();
var result = zero_value.to_value();
for (aggregate_initialization.names, aggregate_initialization.values) |field_name, field_value| {
const declaration_index = for (bits.fields, 0..) |field, declaration_index| {
if (lib.string.equal(field_name, field.name)) {
break declaration_index;
}
} else unreachable;
const field = &bits.fields[declaration_index];
module.emit_value(function, field_value);
const extended = module.llvm.builder.create_zero_extend(field_value.llvm.?, llvm_type);
const shl = module.llvm.builder.create_shl(extended, llvm_type.to_integer().get_constant(field.bit_offset, 0).to_value());
const or_value = module.llvm.builder.create_or(result, shl);
result = or_value;
}
break :blk result;
},
},
.structure => |structure| switch (aggregate_initialization.is_constant) {
true => blk: {
var constant_buffer: [64]*llvm.Constant = undefined;
const constants = constant_buffer[0..structure.fields.len];
_ = &constant_buffer;
for (aggregate_initialization.values, constants[0..aggregate_initialization.values.len]) |field_value, *constant| {
module.emit_value(function, field_value);
constant.* = field_value.llvm.?.to_constant();
@ -6011,6 +6062,7 @@ pub const Module = struct {
@trap();
},
},
.pointer => module.report_error(),
else => @trap(),
},
.zero => value_type.resolve(module).handle.get_zero().to_value(),
@ -6028,6 +6080,30 @@ pub const Module = struct {
const slice_value = slice_length;
break :blk slice_value;
},
.undefined => value_type.resolve(module).handle.get_poison(),
.string_literal => |string_literal| blk: {
const null_terminate = true;
const constant_string = module.llvm.context.get_constant_string(string_literal, null_terminate);
const u8_type = module.integer_type(8, false);
const global_variable = module.llvm.module.create_global_variable(.{
.linkage = .InternalLinkage,
.name = "conststring",
.initial_value = constant_string,
.type = u8_type.llvm.handle.?.get_array_type(string_literal.len + @intFromBool(null_terminate)).to_type(),
});
global_variable.set_unnamed_address(.global);
const slice_type = module.get_slice_type(.{
.type = u8_type,
});
assert(value_type == slice_type);
const slice_poison = slice_type.resolve(module).handle.get_poison();
const slice_pointer = module.llvm.builder.create_insert_value(slice_poison, global_variable.to_value(), 0);
const slice_length = module.llvm.builder.create_insert_value(slice_pointer, module.integer_type(64, false).resolve(module).handle.to_integer().get_constant(string_literal.len, 0).to_value(), 1);
const slice_value = slice_length;
break :blk slice_value;
},
else => @trap(),
};
@ -6176,14 +6252,14 @@ pub const Module = struct {
if (expected_type) |lvt| assert(lvt == local.variable.type);
module.emit_local_storage(local, last_statement_debug_location);
module.emit_assignment(function, local.variable.storage.?, local.variable.initial_value);
module.emit_assignment(function, local.variable.storage.?.llvm.?, local.variable.storage.?.type.?, local.variable.initial_value);
},
.assignment => |assignment| {
module.analyze(function, assignment.left, .{});
switch (assignment.kind) {
.@"=" => {
module.analyze_value_type(function, assignment.right, .{ .type = assignment.left.type.?.bb.pointer.type });
module.emit_assignment(function, assignment.left, assignment.right);
module.emit_assignment(function, assignment.left.llvm.?, assignment.left.type.?, assignment.right);
},
else => |kind| {
const pointer_type = assignment.left.type.?.bb.pointer;
@ -6376,10 +6452,9 @@ pub const Module = struct {
}
}
fn emit_assignment(module: *Module, function: *Global, left: *Value, right: *Value) void {
assert(left.llvm != null);
fn emit_assignment(module: *Module, function: *Global, left_llvm: *llvm.Value, left_type: *Type, right: *Value) void {
assert(right.llvm == null);
const pointer_type = left.type.?;
const pointer_type = left_type;
const value_type = right.type.?;
assert(pointer_type.bb == .pointer);
assert(pointer_type.bb.pointer.type == value_type);
@ -6389,7 +6464,7 @@ pub const Module = struct {
module.emit_value(function, right);
_ = module.create_store(.{
.source_value = right.llvm.?,
.destination_value = left.llvm.?,
.destination_value = left_llvm,
.source_type = value_type,
.destination_type = value_type,
.alignment = pointer_type.bb.pointer.alignment,
@ -6409,7 +6484,7 @@ pub const Module = struct {
const element_type = value_type.bb.array.element_type;
const alignment = element_type.get_byte_alignment();
global_variable.to_value().set_alignment(alignment);
_ = module.llvm.builder.create_memcpy(left.llvm.?, pointer_type.bb.pointer.alignment, global_variable.to_value(), alignment, module.integer_type(64, false).resolve(module).handle.to_integer().get_constant(array_initialization.values.len * pointer_type.bb.pointer.type.bb.array.element_type.get_byte_size(), @intFromBool(false)).to_value());
_ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment, global_variable.to_value(), alignment, module.integer_type(64, false).resolve(module).handle.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 => @trap(),
},
@ -6425,31 +6500,47 @@ pub const Module = struct {
global_variable.set_unnamed_address(.global);
const alignment = value_type.get_byte_alignment();
global_variable.to_value().set_alignment(alignment);
_ = module.llvm.builder.create_memcpy(left.llvm.?, pointer_type.bb.pointer.alignment, global_variable.to_value(), alignment, module.integer_type(64, false).resolve(module).handle.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, global_variable.to_value(), alignment, module.integer_type(64, false).resolve(module).handle.to_integer().get_constant(value_type.get_byte_size(), @intFromBool(false)).to_value());
},
false => {
var max_field_index: u64 = 0;
var field_mask: u64 = 0;
const fields = value_type.bb.structure.fields;
assert(fields.len <= 64);
for (aggregate_initialization.values, aggregate_initialization.names) |initialization_value, initialization_name| {
const field_index = for (value_type.bb.structure.fields, 0..) |*field, field_index| {
const field_index = for (fields, 0..) |*field, field_index| {
if (lib.string.equal(field.name, initialization_name)) {
break field_index;
}
} else module.report_error();
const field = &value_type.bb.structure.fields[field_index];
const destination_pointer = module.llvm.builder.create_struct_gep(value_type.llvm.handle.?.to_struct(), left.llvm.?, @intCast(field_index));
// Create an auxiliary value
const destination_value = module.values.add();
destination_value.* = .{
.bb = .local, // This does not matter
.llvm = destination_pointer,
.type = module.get_pointer_type(.{ .type = field.type }),
.kind = .left,
};
module.emit_assignment(function, destination_value, initialization_value);
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.handle.?.to_struct(), left_llvm, @intCast(field_index));
module.emit_assignment(function, destination_pointer, module.get_pointer_type(.{ .type = field.type }), initialization_value);
}
if (aggregate_initialization.zero) {
const buffer_field_count: u64 = @bitSizeOf(@TypeOf(field_mask));
const raw_end_uninitialized_field_count = @clz(field_mask);
const unused_buffer_field_count = buffer_field_count - fields.len;
const end_uninitialized_field_count = raw_end_uninitialized_field_count - unused_buffer_field_count;
const initialized_field_count = @popCount(field_mask);
const uninitialized_field_count = fields.len - initialized_field_count;
if (uninitialized_field_count != end_uninitialized_field_count) {
@trap();
}
if (end_uninitialized_field_count == 0) {
module.report_error();
}
const field_index_offset = fields.len - end_uninitialized_field_count;
const destination_pointer = module.llvm.builder.create_struct_gep(value_type.llvm.handle.?.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;
_ = module.llvm.builder.create_memset(destination_pointer, module.integer_type(8, false).resolve(module).handle.get_zero().to_value(), module.integer_type(64, false).resolve(module).handle.to_integer().get_constant(memset_size, 0).to_value(), pointer_type.bb.pointer.alignment);
}
},
},
.string_literal => |string_literal| {
@ -6459,7 +6550,7 @@ pub const Module = struct {
const u8_type = module.integer_type(8, false);
const global_variable = module.llvm.module.create_global_variable(.{
.linkage = .InternalLinkage,
.name = module.arena.join_string(&.{"conststring"}),
.name = "conststring",
.initial_value = constant_string,
.type = u8_type.llvm.handle.?.get_array_type(string_literal.len + @intFromBool(null_terminate)).to_type(),
});
@ -6473,7 +6564,7 @@ pub const Module = struct {
.structure => |structure| switch (structure.is_slice) {
true => switch (slice_type == value_type) {
true => {
const pointer_to_pointer = module.llvm.builder.create_struct_gep(slice_type.llvm.handle.?.to_struct(), left.llvm.?, 0);
const pointer_to_pointer = module.llvm.builder.create_struct_gep(slice_type.llvm.handle.?.to_struct(), left_llvm, 0);
const slice_pointer_type = slice_type.bb.structure.fields[0].type;
_ = module.create_store(.{
.destination_value = pointer_to_pointer,
@ -6481,7 +6572,7 @@ pub const Module = struct {
.source_type = slice_pointer_type,
.destination_type = slice_pointer_type,
});
const pointer_to_length = module.llvm.builder.create_struct_gep(slice_type.llvm.handle.?.to_struct(), left.llvm.?, 1);
const pointer_to_length = module.llvm.builder.create_struct_gep(slice_type.llvm.handle.?.to_struct(), left_llvm, 1);
const slice_length_type = slice_type.bb.structure.fields[1].type;
const slice_length_value = slice_length_type.llvm.handle.?.to_integer().get_constant(string_literal.len, @intFromBool(false)).to_value();
_ = module.create_store(.{
@ -6506,12 +6597,12 @@ pub const Module = struct {
const argument_types: []const *llvm.Type = &.{module.llvm.pointer_type};
const intrinsic_function = module.llvm.module.get_intrinsic_declaration(intrinsic_id, argument_types);
const intrinsic_function_type = module.llvm.context.get_intrinsic_type(intrinsic_id, argument_types);
const argument_values: []const *llvm.Value = &.{left.llvm.?};
const argument_values: []const *llvm.Value = &.{left_llvm};
_ = module.llvm.builder.create_call(intrinsic_function_type, intrinsic_function, argument_values);
},
.va_arg => {
const result = module.emit_va_arg(function, right, left);
switch (result == left.llvm.?) {
const result = module.emit_va_arg(function, right, left_llvm, left_type);
switch (result == left_llvm) {
true => {},
false => switch (value_type.get_evaluation_kind()) {
.scalar => {
@ -6525,8 +6616,8 @@ pub const Module = struct {
else => @trap(),
},
.call => {
const result = module.emit_call(function, right, left);
assert(result == left.llvm);
const result = module.emit_call(function, right, left_llvm, left_type);
assert(result == left_llvm);
// if (result != left.llvm) {
// const call_ret_type_ev_kind = right.type.?.get_evaluation_kind();
// switch (call_ret_type_ev_kind) {
@ -6544,12 +6635,12 @@ pub const Module = struct {
const slice_pointer_type = value_type.bb.structure.fields[0].type;
_ = module.create_store(.{
.source_value = slice_values[0],
.destination_value = left.llvm.?,
.destination_value = left_llvm,
.source_type = slice_pointer_type,
.destination_type = slice_pointer_type,
.alignment = pointer_type.bb.pointer.alignment,
});
const slice_length_destination = module.llvm.builder.create_struct_gep(value_type.llvm.handle.?.to_struct(), left.llvm.?, 1);
const slice_length_destination = module.llvm.builder.create_struct_gep(value_type.llvm.handle.?.to_struct(), left_llvm, 1);
_ = module.create_store(.{
.source_value = slice_values[1],
.destination_value = slice_length_destination,
@ -6558,7 +6649,7 @@ pub const Module = struct {
});
},
.zero => {
_ = module.llvm.builder.create_memset(left.llvm.?, module.integer_type(8, false).resolve(module).handle.get_zero().to_value(), module.integer_type(64, false).resolve(module).handle.to_integer().get_constant(value_type.get_byte_size(), 0).to_value(), pointer_type.bb.pointer.alignment);
_ = module.llvm.builder.create_memset(left_llvm, module.integer_type(8, false).resolve(module).handle.get_zero().to_value(), module.integer_type(64, false).resolve(module).handle.to_integer().get_constant(value_type.get_byte_size(), 0).to_value(), pointer_type.bb.pointer.alignment);
},
else => @trap(),
},

View File

@ -22,7 +22,7 @@ c_string_to_slice = fn (c_string: &u8) []u8
string_equal = fn(a: []u8, b: []u8) u1
{
>result: #ReturnType() = 0;
>result: #ReturnType = 0;
if (a.length == b.length)
{