mirror of
https://github.com/ziglang/zig.git
synced 2024-11-26 23:22:44 +00:00
llvm: compute data layout without help like a grownup compiler
This commit is contained in:
parent
d195173ba2
commit
3314fd83af
@ -1357,8 +1357,6 @@ pub const Target = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const stack_align = 16;
|
||||
|
||||
pub fn zigTriple(self: Target, allocator: mem.Allocator) ![]u8 {
|
||||
return std.zig.CrossTarget.fromTarget(self).zigTriple(allocator);
|
||||
}
|
||||
@ -1833,7 +1831,7 @@ pub const Target = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn ptrBitWidth(target: std.Target) u16 {
|
||||
pub fn ptrBitWidth(target: Target) u16 {
|
||||
switch (target.abi) {
|
||||
.gnux32, .muslx32, .gnuabin32, .gnuilp32 => return 32,
|
||||
.gnuabi64 => return 64,
|
||||
@ -1910,6 +1908,18 @@ pub const Target = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stackAlignment(target: Target) u16 {
|
||||
return switch (target.cpu.arch) {
|
||||
.x86 => switch (target.os.tag) {
|
||||
.windows => 4,
|
||||
else => 16,
|
||||
},
|
||||
.arm, .armeb, .mips, .mipsel => 8,
|
||||
.aarch64, .aarch64_be, .powerpc64, .powerpc64le, .riscv64, .x86_64, .wasm32, .wasm64 => 16,
|
||||
else => @divExact(target.ptrBitWidth(), 8),
|
||||
};
|
||||
}
|
||||
|
||||
/// Default signedness of `char` for the native C compiler for this target
|
||||
/// Note that char signedness is implementation-defined and many compilers provide
|
||||
/// an option to override the default signedness e.g. GCC's -funsigned-char / -fsigned-char
|
||||
@ -2428,7 +2438,7 @@ pub const Target = struct {
|
||||
else => {},
|
||||
},
|
||||
.avr => switch (c_type) {
|
||||
.int, .uint, .long, .ulong, .float, .longdouble => return 1,
|
||||
.char, .int, .uint, .long, .ulong, .float, .longdouble => return 1,
|
||||
.short, .ushort => return 2,
|
||||
.double => return 4,
|
||||
.longlong, .ulonglong => return 8,
|
||||
|
@ -136,7 +136,7 @@ fn mainTerminal() void {
|
||||
const have_tty = progress.terminal != null and
|
||||
(progress.supports_ansi_escape_codes or progress.is_windows_terminal);
|
||||
|
||||
var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined;
|
||||
var async_frame_buffer: []align(builtin.target.stackAlignment()) u8 = undefined;
|
||||
// TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly
|
||||
// ignores the alignment of the slice.
|
||||
async_frame_buffer = &[_]u8{};
|
||||
|
@ -328,6 +328,212 @@ pub fn supportsTailCall(target: std.Target) bool {
|
||||
}
|
||||
}
|
||||
|
||||
const DataLayoutBuilder = struct {
|
||||
target: std.Target,
|
||||
|
||||
pub fn format(
|
||||
self: DataLayoutBuilder,
|
||||
comptime _: []const u8,
|
||||
_: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
const is_aarch64_windows = self.target.cpu.arch == .aarch64 and self.target.os.tag == .windows;
|
||||
try writer.print("{c}-m:{c}", .{
|
||||
@as(u8, switch (self.target.cpu.arch.endian()) {
|
||||
.Little => 'e',
|
||||
.Big => 'E',
|
||||
}),
|
||||
@as(u8, if (self.target.cpu.arch.isMIPS())
|
||||
'm' // Mips mangling: Private symbols get a $ prefix.
|
||||
else switch (self.target.ofmt) {
|
||||
.elf => 'e', // ELF mangling: Private symbols get a `.L` prefix.
|
||||
//.goff => 'l', // GOFF mangling: Private symbols get a `@` prefix.
|
||||
.macho => 'o', // Mach-O mangling: Private symbols get `L` prefix.
|
||||
// Other symbols get a `_` prefix.
|
||||
.coff => switch (self.target.os.tag) {
|
||||
.windows => switch (self.target.cpu.arch) {
|
||||
.x86 => 'x', // Windows x86 COFF mangling: Private symbols get the usual prefix.
|
||||
// Regular C symbols get a `_` prefix. Functions with `__stdcall`, `__fastcall`,
|
||||
// and `__vectorcall` have custom mangling that appends `@N` where N is the
|
||||
// number of bytes used to pass parameters. C++ symbols starting with `?` are
|
||||
// not mangled in any way.
|
||||
else => 'w', // Windows COFF mangling: Similar to x, except that normal C
|
||||
// symbols do not receive a `_` prefix.
|
||||
},
|
||||
else => 'e',
|
||||
},
|
||||
//.xcoff => 'a', // XCOFF mangling: Private symbols get a `L..` prefix.
|
||||
else => 'e',
|
||||
}),
|
||||
});
|
||||
var any_non_integral = false;
|
||||
const ptr_bit_width = self.target.ptrBitWidth();
|
||||
var default_info = struct { size: u16, abi: u16, pref: u16, idx: u16 }{
|
||||
.size = 64,
|
||||
.abi = 64,
|
||||
.pref = 64,
|
||||
.idx = 64,
|
||||
};
|
||||
const address_space_info = llvmAddressSpaceInfo(self.target);
|
||||
assert(address_space_info[0].llvm == llvm.address_space.default);
|
||||
for (address_space_info) |info| {
|
||||
const is_default = info.llvm == llvm.address_space.default;
|
||||
if (info.non_integral) {
|
||||
assert(!is_default);
|
||||
any_non_integral = true;
|
||||
}
|
||||
const size = info.size orelse ptr_bit_width;
|
||||
const abi = info.abi orelse ptr_bit_width;
|
||||
const pref = info.pref orelse abi;
|
||||
const idx = info.idx orelse size;
|
||||
const matches_default =
|
||||
size == default_info.size and
|
||||
abi == default_info.abi and
|
||||
pref == default_info.pref and
|
||||
idx == default_info.idx;
|
||||
if (is_default) default_info = .{
|
||||
.size = size,
|
||||
.abi = abi,
|
||||
.pref = pref,
|
||||
.idx = idx,
|
||||
};
|
||||
if (!info.force_in_data_layout and matches_default and
|
||||
self.target.cpu.arch != .riscv64 and !is_aarch64_windows) continue;
|
||||
try writer.writeAll("-p");
|
||||
if (!is_default) try writer.print("{d}", .{info.llvm});
|
||||
try writer.print(":{d}:{d}", .{ size, abi });
|
||||
if (pref != abi or idx != size) {
|
||||
try writer.print(":{d}", .{pref});
|
||||
if (idx != size) try writer.print(":{d}", .{idx});
|
||||
}
|
||||
}
|
||||
if (self.target.cpu.arch.isARM() or self.target.cpu.arch.isThumb())
|
||||
try writer.writeAll("-Fi8"); // for thumb interwork
|
||||
try self.typeAlignment(.integer, 8, 8, 8, false, writer);
|
||||
try self.typeAlignment(.integer, 16, 16, 16, false, writer);
|
||||
try self.typeAlignment(.integer, 32, if (is_aarch64_windows) 0 else 32, 32, false, writer);
|
||||
try self.typeAlignment(.integer, 64, 32, 64, false, writer);
|
||||
try self.typeAlignment(.integer, 128, 32, 64, false, writer);
|
||||
if (backendSupportsF16(self.target)) try self.typeAlignment(.float, 16, 16, 16, false, writer);
|
||||
try self.typeAlignment(.float, 32, 32, 32, false, writer);
|
||||
try self.typeAlignment(.float, 64, 64, 64, false, writer);
|
||||
if (backendSupportsF80(self.target)) try self.typeAlignment(.float, 80, 0, 0, false, writer);
|
||||
try self.typeAlignment(.float, 128, 128, 128, false, writer);
|
||||
try self.typeAlignment(.vector, 64, 64, 64, false, writer);
|
||||
try self.typeAlignment(.vector, 128, 128, 128, false, writer);
|
||||
if (self.target.os.tag != .windows) try self.typeAlignment(.aggregate, 0, 0, 64, false, writer);
|
||||
for (@as([]const u24, switch (self.target.cpu.arch) {
|
||||
.aarch64_32,
|
||||
.arm,
|
||||
.armeb,
|
||||
.mips,
|
||||
.mipsel,
|
||||
.powerpc,
|
||||
.powerpcle,
|
||||
.thumb,
|
||||
.thumbeb,
|
||||
.riscv32,
|
||||
=> &.{32},
|
||||
.aarch64,
|
||||
.aarch64_be,
|
||||
.mips64,
|
||||
.mips64el,
|
||||
.powerpc64,
|
||||
.powerpc64le,
|
||||
.riscv64,
|
||||
.wasm32,
|
||||
.wasm64,
|
||||
=> &.{ 32, 64 },
|
||||
.x86 => &.{ 8, 16, 32 },
|
||||
.x86_64 => &.{ 8, 16, 32, 64 },
|
||||
else => &.{},
|
||||
}), 0..) |natural, index| switch (index) {
|
||||
0 => try writer.print("-n{d}", .{natural}),
|
||||
else => try writer.print(":{d}", .{natural}),
|
||||
};
|
||||
if (self.target.os.tag == .windows) try self.typeAlignment(.aggregate, 0, 0, 64, false, writer);
|
||||
const stack_abi = self.target.stackAlignment() * 8;
|
||||
if (self.target.os.tag == .windows or stack_abi != ptr_bit_width)
|
||||
try writer.print("-S{d}", .{stack_abi});
|
||||
try self.typeAlignment(.vector, 256, 128, 128, true, writer);
|
||||
try self.typeAlignment(.vector, 512, 128, 128, true, writer);
|
||||
if (any_non_integral) {
|
||||
try writer.writeAll("-ni");
|
||||
for (address_space_info) |info| if (info.non_integral)
|
||||
try writer.print(":{d}", .{info.llvm});
|
||||
}
|
||||
}
|
||||
|
||||
fn typeAlignment(
|
||||
self: DataLayoutBuilder,
|
||||
kind: enum { integer, vector, float, aggregate },
|
||||
size: u24,
|
||||
default_abi: u24,
|
||||
default_pref: u24,
|
||||
force_pref: bool,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
var abi = default_abi;
|
||||
var pref = default_pref;
|
||||
if (kind == .float and size == 80) {
|
||||
abi = 128;
|
||||
pref = 128;
|
||||
}
|
||||
for (@as([]const std.Target.CType, switch (kind) {
|
||||
.integer => &.{ .char, .short, .int, .long, .longlong },
|
||||
.float => &.{ .float, .double, .longdouble },
|
||||
.vector, .aggregate => &.{},
|
||||
})) |cty| {
|
||||
if (self.target.c_type_bit_size(cty) != size) continue;
|
||||
abi = self.target.c_type_alignment(cty) * 8;
|
||||
pref = self.target.c_type_preferred_alignment(cty) * 8;
|
||||
break;
|
||||
}
|
||||
switch (kind) {
|
||||
.integer => {
|
||||
abi = @min(abi, self.target.maxIntAlignment() * 8);
|
||||
switch (self.target.os.tag) {
|
||||
.linux => switch (self.target.cpu.arch) {
|
||||
.aarch64, .aarch64_be, .mips, .mipsel => pref = @max(pref, 32),
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
switch (self.target.cpu.arch) {
|
||||
.aarch64, .aarch64_be, .riscv64 => switch (size) {
|
||||
128 => {
|
||||
abi = size;
|
||||
pref = size;
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
.vector => if (self.target.cpu.arch.isARM() or self.target.cpu.arch.isThumb()) {
|
||||
switch (size) {
|
||||
128 => abi = 64,
|
||||
else => {},
|
||||
}
|
||||
} else if (self.target.cpu.arch.isPPC64()) {
|
||||
abi = size;
|
||||
pref = size;
|
||||
},
|
||||
.float => {},
|
||||
.aggregate => if (self.target.os.tag == .windows or
|
||||
self.target.cpu.arch.isARM() or self.target.cpu.arch.isThumb())
|
||||
{
|
||||
pref = @min(pref, self.target.ptrBitWidth());
|
||||
},
|
||||
}
|
||||
if (abi == default_abi and pref == default_pref) return;
|
||||
try writer.print("-{c}", .{@tagName(kind)[0]});
|
||||
if (size != 0) try writer.print("{d}", .{size});
|
||||
try writer.print(":{d}", .{abi});
|
||||
if (pref != abi or force_pref) try writer.print(":{d}", .{pref});
|
||||
}
|
||||
};
|
||||
|
||||
/// TODO can this be done with simpler logic / different API binding?
|
||||
fn deleteLlvmGlobal(llvm_global: *llvm.Value) void {
|
||||
if (llvm_global.globalGetValueType().getTypeKind() == .Function) {
|
||||
@ -530,12 +736,17 @@ pub const Object = struct {
|
||||
try builder.init();
|
||||
errdefer builder.deinit();
|
||||
builder.source_filename = try builder.string(options.root_name);
|
||||
builder.data_layout = rep: {
|
||||
builder.data_layout = try builder.fmt("{}", .{DataLayoutBuilder{ .target = options.target }});
|
||||
builder.target_triple = try builder.string(llvm_target_triple);
|
||||
|
||||
if (std.debug.runtime_safety) {
|
||||
const rep = target_data.stringRep();
|
||||
defer llvm.disposeMessage(rep);
|
||||
break :rep try builder.string(std.mem.span(rep));
|
||||
};
|
||||
builder.target_triple = try builder.string(llvm_target_triple);
|
||||
std.testing.expectEqualStrings(
|
||||
std.mem.span(rep),
|
||||
builder.data_layout.toSlice(&builder).?,
|
||||
) catch unreachable;
|
||||
}
|
||||
|
||||
return Object{
|
||||
.gpa = gpa,
|
||||
@ -768,6 +979,10 @@ pub const Object = struct {
|
||||
if (comp.verbose_llvm_ir) |path| {
|
||||
if (std.mem.eql(u8, path, "-")) {
|
||||
self.llvm_module.dump();
|
||||
|
||||
const writer = std.io.getStdErr().writer();
|
||||
try writer.writeAll("\n" ++ "-" ** 200 ++ "\n\n");
|
||||
try self.builder.dump(writer);
|
||||
} else {
|
||||
const path_z = try comp.gpa.dupeZ(u8, path);
|
||||
defer comp.gpa.free(path_z);
|
||||
@ -836,12 +1051,6 @@ pub const Object = struct {
|
||||
emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg,
|
||||
});
|
||||
|
||||
{
|
||||
const writer = std.io.getStdErr().writer();
|
||||
try writer.writeAll("\n" ++ "-" ** 200 ++ "\n\n");
|
||||
try self.builder.dump(writer);
|
||||
}
|
||||
|
||||
// Unfortunately, LLVM shits the bed when we ask for both binary and assembly.
|
||||
// So we call the entire pipeline multiple times if this is requested.
|
||||
var error_message: [*:0]const u8 = undefined;
|
||||
@ -1311,7 +1520,7 @@ pub const Object = struct {
|
||||
try global_index.rename(&self.builder, decl_name);
|
||||
const decl_name_slice = decl_name.toSlice(&self.builder).?;
|
||||
const global = global_index.ptr(&self.builder);
|
||||
global.unnamed_addr = .none;
|
||||
global.unnamed_addr = .default;
|
||||
llvm_global.setUnnamedAddr(.False);
|
||||
global.linkage = .external;
|
||||
llvm_global.setLinkage(.External);
|
||||
@ -2674,11 +2883,15 @@ pub const Object = struct {
|
||||
|
||||
const target = mod.getTarget();
|
||||
|
||||
const llvm_type = try o.lowerLlvmType(decl.ty);
|
||||
const ty = try o.lowerType(decl.ty);
|
||||
const llvm_type = if (ty != .none)
|
||||
o.builder.llvm_types.items[@intFromEnum(ty)]
|
||||
else
|
||||
try o.lowerLlvmType(decl.ty);
|
||||
const llvm_actual_addrspace = toLlvmGlobalAddressSpace(decl.@"addrspace", target);
|
||||
|
||||
var global = Builder.Global{
|
||||
.type = .void,
|
||||
.type = if (ty != .none) ty else .void,
|
||||
.kind = .{ .object = @enumFromInt(o.builder.objects.items.len) },
|
||||
};
|
||||
var object = Builder.Object{
|
||||
@ -2698,7 +2911,7 @@ pub const Object = struct {
|
||||
|
||||
// This is needed for declarations created by `@extern`.
|
||||
if (is_extern) {
|
||||
global.unnamed_addr = .none;
|
||||
global.unnamed_addr = .default;
|
||||
llvm_global.setUnnamedAddr(.False);
|
||||
global.linkage = .external;
|
||||
llvm_global.setLinkage(.External);
|
||||
@ -2708,7 +2921,7 @@ pub const Object = struct {
|
||||
object.thread_local = .generaldynamic;
|
||||
llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
|
||||
} else {
|
||||
object.thread_local = .none;
|
||||
object.thread_local = .default;
|
||||
llvm_global.setThreadLocalMode(.NotThreadLocal);
|
||||
}
|
||||
if (variable.is_weak_linkage) {
|
||||
@ -2962,9 +3175,9 @@ pub const Object = struct {
|
||||
const name = try o.builder.string(mod.intern_pool.stringToSlice(
|
||||
try struct_obj.getFullyQualifiedName(mod),
|
||||
));
|
||||
_ = try o.builder.opaqueType(name);
|
||||
const ty = try o.builder.opaqueType(name);
|
||||
|
||||
const llvm_struct_ty = o.context.structCreateNamed(name.toSlice(&o.builder).?);
|
||||
const llvm_struct_ty = o.builder.llvm_types.items[@intFromEnum(ty)];
|
||||
gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
|
||||
|
||||
assert(struct_obj.haveFieldTypes());
|
||||
@ -3101,16 +3314,6 @@ pub const Object = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn lowerType(o: *Object, t: Type) Allocator.Error!Builder.Type {
|
||||
const mod = o.module;
|
||||
switch (t.toIntern()) {
|
||||
.void_type, .noreturn_type => return .void,
|
||||
else => switch (mod.intern_pool.indexToKey(t.toIntern())) {
|
||||
else => return .none,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn lowerLlvmTypeFn(o: *Object, fn_ty: Type) Allocator.Error!*llvm.Type {
|
||||
const mod = o.module;
|
||||
const ip = &mod.intern_pool;
|
||||
@ -3206,6 +3409,149 @@ pub const Object = struct {
|
||||
return llvm_elem_ty;
|
||||
}
|
||||
|
||||
fn lowerType(o: *Object, t: Type) Allocator.Error!Builder.Type {
|
||||
const mod = o.module;
|
||||
const target = mod.getTarget();
|
||||
return switch (t.toIntern()) {
|
||||
.u0_type, .i0_type => unreachable,
|
||||
inline .u1_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
.i16_type,
|
||||
.u29_type,
|
||||
.u32_type,
|
||||
.i32_type,
|
||||
.u64_type,
|
||||
.i64_type,
|
||||
.u80_type,
|
||||
.u128_type,
|
||||
.i128_type,
|
||||
=> |tag| @field(Builder.Type, "i" ++ @tagName(tag)[1 .. @tagName(tag).len - "_type".len]),
|
||||
.usize_type, .isize_type => try o.builder.intType(target.ptrBitWidth()),
|
||||
inline .c_char_type,
|
||||
.c_short_type,
|
||||
.c_ushort_type,
|
||||
.c_int_type,
|
||||
.c_uint_type,
|
||||
.c_long_type,
|
||||
.c_ulong_type,
|
||||
.c_longlong_type,
|
||||
.c_ulonglong_type,
|
||||
=> |tag| try o.builder.intType(target.c_type_bit_size(
|
||||
@field(std.Target.CType, @tagName(tag)["c_".len .. @tagName(tag).len - "_type".len]),
|
||||
)),
|
||||
.c_longdouble_type,
|
||||
.f16_type,
|
||||
.f32_type,
|
||||
.f64_type,
|
||||
.f80_type,
|
||||
.f128_type,
|
||||
=> switch (t.floatBits(target)) {
|
||||
16 => if (backendSupportsF16(target)) .half else .i16,
|
||||
32 => .float,
|
||||
64 => .double,
|
||||
80 => if (backendSupportsF80(target)) .x86_fp80 else .i80,
|
||||
128 => .fp128,
|
||||
else => unreachable,
|
||||
},
|
||||
.anyopaque_type => unreachable,
|
||||
.bool_type => .i1,
|
||||
.void_type => .void,
|
||||
.type_type => unreachable,
|
||||
.anyerror_type => .i16,
|
||||
.comptime_int_type, .comptime_float_type, .noreturn_type => unreachable,
|
||||
.anyframe_type => @panic("TODO implement lowerType for AnyFrame types"),
|
||||
.null_type,
|
||||
.undefined_type,
|
||||
.enum_literal_type,
|
||||
.atomic_order_type,
|
||||
.atomic_rmw_op_type,
|
||||
.calling_convention_type,
|
||||
.address_space_type,
|
||||
.float_mode_type,
|
||||
.reduce_op_type,
|
||||
.call_modifier_type,
|
||||
.prefetch_options_type,
|
||||
.export_options_type,
|
||||
.extern_options_type,
|
||||
.type_info_type,
|
||||
=> unreachable,
|
||||
.manyptr_u8_type,
|
||||
.manyptr_const_u8_type,
|
||||
.manyptr_const_u8_sentinel_0_type,
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
=> .ptr,
|
||||
.slice_const_u8_type, .slice_const_u8_sentinel_0_type => .none,
|
||||
.optional_noreturn_type => unreachable,
|
||||
.anyerror_void_error_union_type => .i16,
|
||||
.generic_poison_type, .empty_struct_type => unreachable,
|
||||
// values, not types
|
||||
.undef,
|
||||
.zero,
|
||||
.zero_usize,
|
||||
.zero_u8,
|
||||
.one,
|
||||
.one_usize,
|
||||
.one_u8,
|
||||
.four_u8,
|
||||
.negative_one,
|
||||
.calling_convention_c,
|
||||
.calling_convention_inline,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.null_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.empty_struct,
|
||||
.generic_poison,
|
||||
.var_args_param_type,
|
||||
.none,
|
||||
=> unreachable,
|
||||
else => switch (mod.intern_pool.indexToKey(t.toIntern())) {
|
||||
.int_type => |int_type| try o.builder.intType(int_type.bits),
|
||||
.ptr_type => |ptr_type| switch (ptr_type.flags.size) {
|
||||
.One, .Many, .C => try o.builder.pointerType(@enumFromInt(
|
||||
toLlvmAddressSpace(ptr_type.flags.address_space, target),
|
||||
)),
|
||||
.Slice => .none,
|
||||
},
|
||||
.array_type, .vector_type, .opt_type => .none,
|
||||
.anyframe_type => @panic("TODO implement lowerType for AnyFrame types"),
|
||||
.error_union_type => .none,
|
||||
.simple_type => unreachable,
|
||||
.struct_type,
|
||||
.anon_struct_type,
|
||||
.union_type,
|
||||
.opaque_type,
|
||||
=> .none,
|
||||
.enum_type => |enum_type| try o.lowerType(enum_type.tag_ty.toType()),
|
||||
.func_type, .error_set_type, .inferred_error_set_type => .none,
|
||||
// values, not types
|
||||
.undef,
|
||||
.runtime_value,
|
||||
.simple_value,
|
||||
.variable,
|
||||
.extern_func,
|
||||
.func,
|
||||
.int,
|
||||
.err,
|
||||
.error_union,
|
||||
.enum_literal,
|
||||
.enum_tag,
|
||||
.empty_enum_value,
|
||||
.float,
|
||||
.ptr,
|
||||
.opt,
|
||||
.aggregate,
|
||||
.un,
|
||||
// memoization, not types
|
||||
.memoized_call,
|
||||
=> unreachable,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn lowerValue(o: *Object, arg_tv: TypedValue) Error!*llvm.Value {
|
||||
const mod = o.module;
|
||||
const gpa = o.gpa;
|
||||
@ -10606,44 +10952,63 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca
|
||||
|
||||
/// Convert a zig-address space to an llvm address space.
|
||||
fn toLlvmAddressSpace(address_space: std.builtin.AddressSpace, target: std.Target) c_uint {
|
||||
for (llvmAddressSpaceInfo(target)) |info| if (info.zig == address_space) return info.llvm;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
const AddressSpaceInfo = struct {
|
||||
zig: ?std.builtin.AddressSpace,
|
||||
llvm: c_uint,
|
||||
non_integral: bool = false,
|
||||
size: ?u16 = null,
|
||||
abi: ?u16 = null,
|
||||
pref: ?u16 = null,
|
||||
idx: ?u16 = null,
|
||||
force_in_data_layout: bool = false,
|
||||
};
|
||||
fn llvmAddressSpaceInfo(target: std.Target) []const AddressSpaceInfo {
|
||||
return switch (target.cpu.arch) {
|
||||
.x86, .x86_64 => switch (address_space) {
|
||||
.generic => llvm.address_space.default,
|
||||
.gs => llvm.address_space.x86.gs,
|
||||
.fs => llvm.address_space.x86.fs,
|
||||
.ss => llvm.address_space.x86.ss,
|
||||
else => unreachable,
|
||||
.x86, .x86_64 => &.{
|
||||
.{ .zig = .generic, .llvm = llvm.address_space.default },
|
||||
.{ .zig = .gs, .llvm = llvm.address_space.x86.gs },
|
||||
.{ .zig = .fs, .llvm = llvm.address_space.x86.fs },
|
||||
.{ .zig = .ss, .llvm = llvm.address_space.x86.ss },
|
||||
.{ .zig = null, .llvm = llvm.address_space.x86.ptr32_sptr, .size = 32, .abi = 32, .force_in_data_layout = true },
|
||||
.{ .zig = null, .llvm = llvm.address_space.x86.ptr32_uptr, .size = 32, .abi = 32, .force_in_data_layout = true },
|
||||
.{ .zig = null, .llvm = llvm.address_space.x86.ptr64, .size = 64, .abi = 64, .force_in_data_layout = true },
|
||||
},
|
||||
.nvptx, .nvptx64 => switch (address_space) {
|
||||
.generic => llvm.address_space.default,
|
||||
.global => llvm.address_space.nvptx.global,
|
||||
.constant => llvm.address_space.nvptx.constant,
|
||||
.param => llvm.address_space.nvptx.param,
|
||||
.shared => llvm.address_space.nvptx.shared,
|
||||
.local => llvm.address_space.nvptx.local,
|
||||
else => unreachable,
|
||||
.nvptx, .nvptx64 => &.{
|
||||
.{ .zig = .generic, .llvm = llvm.address_space.default },
|
||||
.{ .zig = .global, .llvm = llvm.address_space.nvptx.global },
|
||||
.{ .zig = .constant, .llvm = llvm.address_space.nvptx.constant },
|
||||
.{ .zig = .param, .llvm = llvm.address_space.nvptx.param },
|
||||
.{ .zig = .shared, .llvm = llvm.address_space.nvptx.shared },
|
||||
.{ .zig = .local, .llvm = llvm.address_space.nvptx.local },
|
||||
},
|
||||
.amdgcn => switch (address_space) {
|
||||
.generic => llvm.address_space.amdgpu.flat,
|
||||
.global => llvm.address_space.amdgpu.global,
|
||||
.constant => llvm.address_space.amdgpu.constant,
|
||||
.shared => llvm.address_space.amdgpu.local,
|
||||
.local => llvm.address_space.amdgpu.private,
|
||||
else => unreachable,
|
||||
.amdgcn => &.{
|
||||
.{ .zig = .generic, .llvm = llvm.address_space.amdgpu.flat },
|
||||
.{ .zig = .global, .llvm = llvm.address_space.amdgpu.global },
|
||||
.{ .zig = .constant, .llvm = llvm.address_space.amdgpu.constant },
|
||||
.{ .zig = .shared, .llvm = llvm.address_space.amdgpu.local },
|
||||
.{ .zig = .local, .llvm = llvm.address_space.amdgpu.private },
|
||||
},
|
||||
.avr => switch (address_space) {
|
||||
.generic => llvm.address_space.default,
|
||||
.flash => llvm.address_space.avr.flash,
|
||||
.flash1 => llvm.address_space.avr.flash1,
|
||||
.flash2 => llvm.address_space.avr.flash2,
|
||||
.flash3 => llvm.address_space.avr.flash3,
|
||||
.flash4 => llvm.address_space.avr.flash4,
|
||||
.flash5 => llvm.address_space.avr.flash5,
|
||||
else => unreachable,
|
||||
.avr => &.{
|
||||
.{ .zig = .generic, .llvm = llvm.address_space.default },
|
||||
.{ .zig = .flash, .llvm = llvm.address_space.avr.flash },
|
||||
.{ .zig = .flash1, .llvm = llvm.address_space.avr.flash1 },
|
||||
.{ .zig = .flash2, .llvm = llvm.address_space.avr.flash2 },
|
||||
.{ .zig = .flash3, .llvm = llvm.address_space.avr.flash3 },
|
||||
.{ .zig = .flash4, .llvm = llvm.address_space.avr.flash4 },
|
||||
.{ .zig = .flash5, .llvm = llvm.address_space.avr.flash5 },
|
||||
},
|
||||
else => switch (address_space) {
|
||||
.generic => llvm.address_space.default,
|
||||
else => unreachable,
|
||||
.wasm32, .wasm64 => &.{
|
||||
.{ .zig = .generic, .llvm = llvm.address_space.default },
|
||||
.{ .zig = null, .llvm = llvm.address_space.wasm.variable, .non_integral = true },
|
||||
.{ .zig = null, .llvm = llvm.address_space.wasm.externref, .non_integral = true, .size = 8, .abi = 8 },
|
||||
.{ .zig = null, .llvm = llvm.address_space.wasm.funcref, .non_integral = true, .size = 8, .abi = 8 },
|
||||
},
|
||||
else => &.{
|
||||
.{ .zig = .generic, .llvm = llvm.address_space.default },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ string_indices: std.ArrayListUnmanaged(u32) = .{},
|
||||
|
||||
types: std.AutoArrayHashMapUnmanaged(String, Type) = .{},
|
||||
next_unnamed_type: String = @enumFromInt(0),
|
||||
next_unique_type_id: std.AutoHashMapUnmanaged(String, u32) = .{},
|
||||
type_map: std.AutoArrayHashMapUnmanaged(void, void) = .{},
|
||||
type_data: std.ArrayListUnmanaged(Type.Data) = .{},
|
||||
type_extra: std.ArrayListUnmanaged(u32) = .{},
|
||||
@ -109,8 +110,10 @@ pub const Type = enum(u32) {
|
||||
i1,
|
||||
i8,
|
||||
i16,
|
||||
i29,
|
||||
i32,
|
||||
i64,
|
||||
i80,
|
||||
i128,
|
||||
ptr,
|
||||
|
||||
@ -173,17 +176,18 @@ pub const Type = enum(u32) {
|
||||
if (std.enums.tagName(Type, data.type)) |name| return writer.writeAll(name);
|
||||
const type_data = data.builder.type_data.items[@intFromEnum(data.type)];
|
||||
switch (type_data.tag) {
|
||||
.integer => try writer.print("i{d}", .{type_data.data}),
|
||||
.named_structure => {
|
||||
const extra = data.builder.typeExtraData(NamedStructure, type_data.data);
|
||||
if (comptime std.mem.eql(u8, fmt_str, "")) try writer.print("%{}", .{
|
||||
extra.id.fmt(data.builder),
|
||||
}) else if (comptime std.mem.eql(u8, fmt_str, "+")) switch (extra.child) {
|
||||
if (comptime std.mem.eql(u8, fmt_str, "")) switch (extra.child) {
|
||||
.none => try writer.writeAll("opaque"),
|
||||
else => try format(.{
|
||||
.type = extra.child,
|
||||
.builder = data.builder,
|
||||
}, fmt_str, fmt_opts, writer),
|
||||
} else @compileError("invalid format string: '" ++ fmt_str ++ "'");
|
||||
} else if (comptime std.mem.eql(u8, fmt_str, "%")) try writer.print("%{}", .{
|
||||
extra.id.fmt(data.builder),
|
||||
}) else @compileError("invalid format string: '" ++ fmt_str ++ "'");
|
||||
},
|
||||
else => try writer.print("<type 0x{X}>", .{@intFromEnum(data.type)}),
|
||||
}
|
||||
@ -220,7 +224,7 @@ pub const Linkage = enum {
|
||||
};
|
||||
|
||||
pub const Preemption = enum {
|
||||
none,
|
||||
default,
|
||||
dso_preemptable,
|
||||
dso_local,
|
||||
|
||||
@ -230,7 +234,7 @@ pub const Preemption = enum {
|
||||
_: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
if (self == .none) return;
|
||||
if (self == .default) return;
|
||||
try writer.writeAll(@tagName(self));
|
||||
try writer.writeByte(' ');
|
||||
}
|
||||
@ -271,7 +275,7 @@ pub const DllStorageClass = enum {
|
||||
};
|
||||
|
||||
pub const ThreadLocal = enum {
|
||||
none,
|
||||
default,
|
||||
generaldynamic,
|
||||
localdynamic,
|
||||
initialexec,
|
||||
@ -283,7 +287,7 @@ pub const ThreadLocal = enum {
|
||||
_: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
if (self == .none) return;
|
||||
if (self == .default) return;
|
||||
try writer.writeAll("thread_local");
|
||||
if (self != .generaldynamic) {
|
||||
try writer.writeByte('(');
|
||||
@ -295,7 +299,7 @@ pub const ThreadLocal = enum {
|
||||
};
|
||||
|
||||
pub const UnnamedAddr = enum {
|
||||
none,
|
||||
default,
|
||||
unnamed_addr,
|
||||
local_unnamed_addr,
|
||||
|
||||
@ -305,14 +309,14 @@ pub const UnnamedAddr = enum {
|
||||
_: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
if (self == .none) return;
|
||||
if (self == .default) return;
|
||||
try writer.writeAll(@tagName(self));
|
||||
try writer.writeByte(' ');
|
||||
}
|
||||
};
|
||||
|
||||
pub const AddrSpace = enum(u24) {
|
||||
none,
|
||||
default,
|
||||
_,
|
||||
|
||||
pub fn format(
|
||||
@ -321,13 +325,13 @@ pub const AddrSpace = enum(u24) {
|
||||
_: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
if (self == .none) return;
|
||||
if (self == .default) return;
|
||||
try writer.print("addrspace({d}) ", .{@intFromEnum(self)});
|
||||
}
|
||||
};
|
||||
|
||||
pub const ExternallyInitialized = enum {
|
||||
none,
|
||||
default,
|
||||
externally_initialized,
|
||||
|
||||
pub fn format(
|
||||
@ -336,7 +340,7 @@ pub const ExternallyInitialized = enum {
|
||||
_: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
if (self == .none) return;
|
||||
if (self == .default) return;
|
||||
try writer.writeAll(@tagName(self));
|
||||
try writer.writeByte(' ');
|
||||
}
|
||||
@ -369,12 +373,12 @@ pub const Alignment = enum(u6) {
|
||||
|
||||
pub const Global = struct {
|
||||
linkage: Linkage = .default,
|
||||
preemption: Preemption = .none,
|
||||
preemption: Preemption = .default,
|
||||
visibility: Visibility = .default,
|
||||
dll_storage_class: DllStorageClass = .default,
|
||||
unnamed_addr: UnnamedAddr = .none,
|
||||
addr_space: AddrSpace = .none,
|
||||
externally_initialized: ExternallyInitialized = .none,
|
||||
unnamed_addr: UnnamedAddr = .default,
|
||||
addr_space: AddrSpace = .default,
|
||||
externally_initialized: ExternallyInitialized = .default,
|
||||
type: Type,
|
||||
alignment: Alignment = .default,
|
||||
kind: union(enum) {
|
||||
@ -459,7 +463,7 @@ pub const Alias = struct {
|
||||
|
||||
pub const Object = struct {
|
||||
global: Global.Index,
|
||||
thread_local: ThreadLocal = .none,
|
||||
thread_local: ThreadLocal = .default,
|
||||
mutability: enum { global, constant } = .global,
|
||||
init: void = {},
|
||||
|
||||
@ -525,7 +529,7 @@ pub fn init(self: *Builder) Allocator.Error!void {
|
||||
@field(llvm.Context, simple_field.name ++ "Type")(self.llvm_context),
|
||||
);
|
||||
}
|
||||
inline for (.{ 1, 8, 16, 32, 64, 128 }) |bits| assert(self.intTypeAssumeCapacity(bits) ==
|
||||
inline for (.{ 1, 8, 16, 29, 32, 64, 80, 128 }) |bits| assert(self.intTypeAssumeCapacity(bits) ==
|
||||
@field(Type, std.fmt.comptimePrint("i{d}", .{bits})));
|
||||
inline for (.{0}) |addr_space|
|
||||
assert(self.pointerTypeAssumeCapacity(@enumFromInt(addr_space)) == .ptr);
|
||||
@ -541,6 +545,7 @@ pub fn deinit(self: *Builder) void {
|
||||
self.string_indices.deinit(self.gpa);
|
||||
|
||||
self.types.deinit(self.gpa);
|
||||
self.next_unique_type_id.deinit(self.gpa);
|
||||
self.type_map.deinit(self.gpa);
|
||||
self.type_data.deinit(self.gpa);
|
||||
self.type_extra.deinit(self.gpa);
|
||||
@ -596,13 +601,18 @@ pub fn fmtAssumeCapacity(self: *Builder, comptime fmt_str: []const u8, fmt_args:
|
||||
}
|
||||
|
||||
pub fn opaqueType(self: *Builder, name: String) Allocator.Error!Type {
|
||||
try self.string_map.ensureUnusedCapacity(self.gpa, 1);
|
||||
try self.string_bytes.ensureUnusedCapacity(self.gpa, name.toSlice(self).?.len +
|
||||
comptime std.fmt.count("{d}" ++ .{0}, .{std.math.maxInt(u32)}));
|
||||
try self.string_indices.ensureUnusedCapacity(self.gpa, 1);
|
||||
try self.types.ensureUnusedCapacity(self.gpa, 1);
|
||||
try self.next_unique_type_id.ensureUnusedCapacity(self.gpa, 1);
|
||||
try self.ensureUnusedCapacityTypes(1, Type.NamedStructure);
|
||||
return self.opaqueTypeAssumeCapacity(name);
|
||||
}
|
||||
|
||||
pub fn intType(self: *Builder, bits: u24) Allocator.Error!Type {
|
||||
try self.ensureUnusedCapacityTypes(1);
|
||||
try self.ensureUnusedCapacityTypes(1, null);
|
||||
return self.intTypeAssumeCapacity(bits);
|
||||
}
|
||||
|
||||
@ -695,27 +705,39 @@ fn opaqueTypeAssumeCapacity(self: *Builder, name: String) Type {
|
||||
lhs == ctx.builder.typeExtraData(Type.NamedStructure, rhs_data.data).id;
|
||||
}
|
||||
};
|
||||
const id = if (name == .none) name: {
|
||||
const next_name = self.next_unnamed_type;
|
||||
assert(next_name != .none);
|
||||
self.next_unnamed_type = @enumFromInt(@intFromEnum(next_name) + 1);
|
||||
break :name next_name;
|
||||
} else name: {
|
||||
assert(name.toIndex() != null);
|
||||
break :name name;
|
||||
};
|
||||
const gop = self.type_map.getOrPutAssumeCapacityAdapted(id, Adapter{ .builder = self });
|
||||
if (!gop.found_existing) {
|
||||
gop.key_ptr.* = {};
|
||||
gop.value_ptr.* = {};
|
||||
self.type_data.appendAssumeCapacity(.{
|
||||
.tag = .named_structure,
|
||||
.data = self.addTypeExtraAssumeCapacity(Type.NamedStructure{ .id = id, .child = .none }),
|
||||
});
|
||||
var id = name;
|
||||
if (name == .none) {
|
||||
id = self.next_unnamed_type;
|
||||
assert(id != .none);
|
||||
self.next_unnamed_type = @enumFromInt(@intFromEnum(id) + 1);
|
||||
} else assert(name.toIndex() != null);
|
||||
while (true) {
|
||||
const type_gop = self.types.getOrPutAssumeCapacity(id);
|
||||
if (!type_gop.found_existing) {
|
||||
const gop = self.type_map.getOrPutAssumeCapacityAdapted(id, Adapter{ .builder = self });
|
||||
assert(!gop.found_existing);
|
||||
gop.key_ptr.* = {};
|
||||
gop.value_ptr.* = {};
|
||||
self.type_data.appendAssumeCapacity(.{
|
||||
.tag = .named_structure,
|
||||
.data = self.addTypeExtraAssumeCapacity(Type.NamedStructure{
|
||||
.id = id,
|
||||
.child = .none,
|
||||
}),
|
||||
});
|
||||
const result: Type = @enumFromInt(gop.index);
|
||||
type_gop.value_ptr.* = result;
|
||||
if (self.useLibLlvm()) self.llvm_types.appendAssumeCapacity(
|
||||
self.llvm_context.structCreateNamed(id.toSlice(self) orelse ""),
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
const unique_gop = self.next_unique_type_id.getOrPutAssumeCapacity(name);
|
||||
if (!unique_gop.found_existing) unique_gop.value_ptr.* = 2;
|
||||
id = self.fmtAssumeCapacity("{s}.{d}", .{ name.toSlice(self).?, unique_gop.value_ptr.* });
|
||||
unique_gop.value_ptr.* += 1;
|
||||
}
|
||||
const result: Type = @enumFromInt(gop.index);
|
||||
self.types.putAssumeCapacityNoClobber(id, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn intTypeAssumeCapacity(self: *Builder, bits: u24) Type {
|
||||
@ -786,7 +808,7 @@ pub fn dump(self: *Builder, writer: anytype) @TypeOf(writer).Error!void {
|
||||
, .{self.target_triple.fmt(self)});
|
||||
try writer.writeByte('\n');
|
||||
for (self.types.keys(), self.types.values()) |id, ty| try writer.print(
|
||||
\\%{} = type {+}
|
||||
\\%{} = type {}
|
||||
\\
|
||||
, .{ id.fmt(self), ty.fmt(self) });
|
||||
try writer.writeByte('\n');
|
||||
|
@ -1612,6 +1612,13 @@ pub const address_space = struct {
|
||||
pub const constant_buffer_14: c_uint = 22;
|
||||
pub const constant_buffer_15: c_uint = 23;
|
||||
};
|
||||
|
||||
// See llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h
|
||||
pub const wasm = struct {
|
||||
pub const variable = 1;
|
||||
pub const externref = 10;
|
||||
pub const funcref = 20;
|
||||
};
|
||||
};
|
||||
|
||||
pub const DIEnumerator = opaque {};
|
||||
|
Loading…
Reference in New Issue
Block a user