llvm: compute data layout without help like a grownup compiler

This commit is contained in:
Jacob Young 2023-07-07 09:22:27 -04:00
parent d195173ba2
commit 3314fd83af
5 changed files with 511 additions and 107 deletions

View File

@ -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,

View File

@ -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{};

View File

@ -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 },
},
};
}

View File

@ -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');

View File

@ -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 {};