diff --git a/CMakeLists.txt b/CMakeLists.txt index 0601c269d9c6f454dcafa3ac74b22f3ee4e6e613..700b724b0018797b6d3c43805ceb235f5bc44203 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -500,6 +500,11 @@ set(ZIG_STAGE2_SOURCES lib/std/zig/system/NativePaths.zig lib/std/zig/system/x86.zig lib/std/zig/tokenizer.zig + lib/std/zig/llvm.zig + lib/std/zig/llvm/BitcodeReader.zig + lib/std/zig/llvm/Builder.zig + lib/std/zig/llvm/bitcode_writer.zig + lib/std/zig/llvm/ir.zig src/Air.zig src/Builtin.zig src/Compilation.zig @@ -567,11 +572,7 @@ set(ZIG_STAGE2_SOURCES src/codegen/c.zig src/codegen/c/Type.zig src/codegen/llvm.zig - src/codegen/llvm/BitcodeReader.zig - src/codegen/llvm/Builder.zig src/codegen/llvm/bindings.zig - src/codegen/llvm/bitcode_writer.zig - src/codegen/llvm/ir.zig src/codegen/spirv.zig src/codegen/spirv/Assembler.zig src/codegen/spirv/Module.zig diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 25e0c37597ae82a231816dd415f264d9744583f1..23001554744df403883aba81d69b04954b5c40ee 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -24,6 +24,7 @@ pub const LibCInstallation = @import("zig/LibCInstallation.zig"); pub const WindowsSdk = @import("zig/WindowsSdk.zig"); pub const LibCDirs = @import("zig/LibCDirs.zig"); pub const target = @import("zig/target.zig"); +pub const llvm = @import("zig/llvm.zig"); // Character literal parsing pub const ParsedCharLiteral = string_literal.ParsedCharLiteral; diff --git a/lib/std/zig/llvm.zig b/lib/std/zig/llvm.zig new file mode 100644 index 0000000000000000000000000000000000000000..c2e1ed9c56bfd5a93b8aaaded80023e635dbe568 --- /dev/null +++ b/lib/std/zig/llvm.zig @@ -0,0 +1,3 @@ +pub const BitcodeReader = @import("llvm/BitcodeReader.zig"); +pub const bitcode_writer = @import("llvm/bitcode_writer.zig"); +pub const Builder = @import("llvm/Builder.zig"); diff --git a/lib/std/zig/llvm/BitcodeReader.zig b/lib/std/zig/llvm/BitcodeReader.zig new file mode 100644 index 0000000000000000000000000000000000000000..ea18b7978eb6aeff00bc45a7e82758ea4df16e7a --- /dev/null +++ b/lib/std/zig/llvm/BitcodeReader.zig @@ -0,0 +1,515 @@ +allocator: std.mem.Allocator, +record_arena: std.heap.ArenaAllocator.State, +reader: std.io.AnyReader, +keep_names: bool, +bit_buffer: u32, +bit_offset: u5, +stack: std.ArrayListUnmanaged(State), +block_info: std.AutoHashMapUnmanaged(u32, Block.Info), + +pub const Item = union(enum) { + start_block: Block, + record: Record, + end_block: Block, +}; + +pub const Block = struct { + name: []const u8, + id: u32, + len: u32, + + const block_info: u32 = 0; + const first_reserved: u32 = 1; + const last_standard: u32 = 7; + + const Info = struct { + block_name: []const u8, + record_names: std.AutoHashMapUnmanaged(u32, []const u8), + abbrevs: Abbrev.Store, + + const default: Info = .{ + .block_name = &.{}, + .record_names = .{}, + .abbrevs = .{ .abbrevs = .{} }, + }; + + const set_bid_id: u32 = 1; + const block_name_id: u32 = 2; + const set_record_name_id: u32 = 3; + + fn deinit(info: *Info, allocator: std.mem.Allocator) void { + allocator.free(info.block_name); + var record_names_it = info.record_names.valueIterator(); + while (record_names_it.next()) |record_name| allocator.free(record_name.*); + info.record_names.deinit(allocator); + info.abbrevs.deinit(allocator); + info.* = undefined; + } + }; +}; + +pub const Record = struct { + name: []const u8, + id: u32, + operands: []const u64, + blob: []const u8, + + fn toOwnedAbbrev(record: Record, allocator: std.mem.Allocator) !Abbrev { + var operands = std.ArrayList(Abbrev.Operand).init(allocator); + defer operands.deinit(); + + assert(record.id == Abbrev.Builtin.define_abbrev.toRecordId()); + var i: usize = 0; + while (i < record.operands.len) switch (record.operands[i]) { + Abbrev.Operand.literal_id => { + try operands.append(.{ .literal = record.operands[i + 1] }); + i += 2; + }, + @intFromEnum(Abbrev.Operand.Encoding.fixed) => { + try operands.append(.{ .encoding = .{ .fixed = @intCast(record.operands[i + 1]) } }); + i += 2; + }, + @intFromEnum(Abbrev.Operand.Encoding.vbr) => { + try operands.append(.{ .encoding = .{ .vbr = @intCast(record.operands[i + 1]) } }); + i += 2; + }, + @intFromEnum(Abbrev.Operand.Encoding.array) => { + try operands.append(.{ .encoding = .{ .array = 6 } }); + i += 1; + }, + @intFromEnum(Abbrev.Operand.Encoding.char6) => { + try operands.append(.{ .encoding = .char6 }); + i += 1; + }, + @intFromEnum(Abbrev.Operand.Encoding.blob) => { + try operands.append(.{ .encoding = .{ .blob = 6 } }); + i += 1; + }, + else => unreachable, + }; + + return .{ .operands = try operands.toOwnedSlice() }; + } +}; + +pub const InitOptions = struct { + reader: std.io.AnyReader, + keep_names: bool = false, +}; +pub fn init(allocator: std.mem.Allocator, options: InitOptions) BitcodeReader { + return .{ + .allocator = allocator, + .record_arena = .{}, + .reader = options.reader, + .keep_names = options.keep_names, + .bit_buffer = 0, + .bit_offset = 0, + .stack = .{}, + .block_info = .{}, + }; +} + +pub fn deinit(bc: *BitcodeReader) void { + var block_info_it = bc.block_info.valueIterator(); + while (block_info_it.next()) |block_info| block_info.deinit(bc.allocator); + bc.block_info.deinit(bc.allocator); + for (bc.stack.items) |*state| state.deinit(bc.allocator); + bc.stack.deinit(bc.allocator); + bc.record_arena.promote(bc.allocator).deinit(); + bc.* = undefined; +} + +pub fn checkMagic(bc: *BitcodeReader, magic: *const [4]u8) !void { + var buffer: [4]u8 = undefined; + try bc.readBytes(&buffer); + if (!std.mem.eql(u8, &buffer, magic)) return error.InvalidMagic; + + try bc.startBlock(null, 2); + try bc.block_info.put(bc.allocator, Block.block_info, Block.Info.default); +} + +pub fn next(bc: *BitcodeReader) !?Item { + while (true) { + const record = (try bc.nextRecord()) orelse + return if (bc.stack.items.len > 1) error.EndOfStream else null; + switch (record.id) { + else => return .{ .record = record }, + Abbrev.Builtin.end_block.toRecordId() => { + const block_id = bc.stack.items[bc.stack.items.len - 1].block_id.?; + try bc.endBlock(); + return .{ .end_block = .{ + .name = if (bc.block_info.get(block_id)) |block_info| + block_info.block_name + else + &.{}, + .id = block_id, + .len = 0, + } }; + }, + Abbrev.Builtin.enter_subblock.toRecordId() => { + const block_id: u32 = @intCast(record.operands[0]); + switch (block_id) { + Block.block_info => try bc.parseBlockInfoBlock(), + Block.first_reserved...Block.last_standard => return error.UnsupportedBlockId, + else => { + try bc.startBlock(block_id, @intCast(record.operands[1])); + return .{ .start_block = .{ + .name = if (bc.block_info.get(block_id)) |block_info| + block_info.block_name + else + &.{}, + .id = block_id, + .len = @intCast(record.operands[2]), + } }; + }, + } + }, + Abbrev.Builtin.define_abbrev.toRecordId() => try bc.stack.items[bc.stack.items.len - 1] + .abbrevs.addOwnedAbbrev(bc.allocator, try record.toOwnedAbbrev(bc.allocator)), + } + } +} + +pub fn skipBlock(bc: *BitcodeReader, block: Block) !void { + assert(bc.bit_offset == 0); + try bc.reader.skipBytes(@as(u34, block.len) * 4, .{}); + try bc.endBlock(); +} + +fn nextRecord(bc: *BitcodeReader) !?Record { + const state = &bc.stack.items[bc.stack.items.len - 1]; + const abbrev_id = bc.readFixed(u32, state.abbrev_id_width) catch |err| switch (err) { + error.EndOfStream => return null, + else => |e| return e, + }; + if (abbrev_id >= state.abbrevs.abbrevs.items.len) return error.InvalidAbbrevId; + const abbrev = state.abbrevs.abbrevs.items[abbrev_id]; + + var record_arena = bc.record_arena.promote(bc.allocator); + defer bc.record_arena = record_arena.state; + _ = record_arena.reset(.retain_capacity); + + var operands = try std.ArrayList(u64).initCapacity(record_arena.allocator(), abbrev.operands.len); + var blob = std.ArrayList(u8).init(record_arena.allocator()); + for (abbrev.operands, 0..) |abbrev_operand, abbrev_operand_i| switch (abbrev_operand) { + .literal => |value| operands.appendAssumeCapacity(value), + .encoding => |abbrev_encoding| switch (abbrev_encoding) { + .fixed => |width| operands.appendAssumeCapacity(try bc.readFixed(u64, width)), + .vbr => |width| operands.appendAssumeCapacity(try bc.readVbr(u64, width)), + .array => |len_width| { + assert(abbrev_operand_i + 2 == abbrev.operands.len); + const len: usize = @intCast(try bc.readVbr(u32, len_width)); + try operands.ensureUnusedCapacity(len); + for (0..len) |_| switch (abbrev.operands[abbrev.operands.len - 1]) { + .literal => |elem_value| operands.appendAssumeCapacity(elem_value), + .encoding => |elem_encoding| switch (elem_encoding) { + .fixed => |elem_width| operands.appendAssumeCapacity(try bc.readFixed(u64, elem_width)), + .vbr => |elem_width| operands.appendAssumeCapacity(try bc.readVbr(u64, elem_width)), + .array, .blob => return error.InvalidArrayElement, + .char6 => operands.appendAssumeCapacity(try bc.readChar6()), + }, + .align_32_bits, .block_len => return error.UnsupportedArrayElement, + .abbrev_op => switch (try bc.readFixed(u1, 1)) { + 1 => try operands.appendSlice(&.{ + Abbrev.Operand.literal_id, + try bc.readVbr(u64, 8), + }), + 0 => { + const encoding: Abbrev.Operand.Encoding = + @enumFromInt(try bc.readFixed(u3, 3)); + try operands.append(@intFromEnum(encoding)); + switch (encoding) { + .fixed, .vbr => try operands.append(try bc.readVbr(u7, 5)), + .array, .char6, .blob => {}, + _ => return error.UnsuportedAbbrevEncoding, + } + }, + }, + }; + break; + }, + .char6 => operands.appendAssumeCapacity(try bc.readChar6()), + .blob => |len_width| { + assert(abbrev_operand_i + 1 == abbrev.operands.len); + const len = std.math.cast(usize, try bc.readVbr(u32, len_width)) orelse + return error.Overflow; + bc.align32Bits(); + try bc.readBytes(try blob.addManyAsSlice(len)); + bc.align32Bits(); + }, + }, + .align_32_bits => bc.align32Bits(), + .block_len => operands.appendAssumeCapacity(try bc.read32Bits()), + .abbrev_op => unreachable, + }; + return .{ + .name = name: { + if (operands.items.len < 1) break :name &.{}; + const record_id = std.math.cast(u32, operands.items[0]) orelse break :name &.{}; + if (state.block_id) |block_id| { + if (bc.block_info.get(block_id)) |block_info| { + break :name block_info.record_names.get(record_id) orelse break :name &.{}; + } + } + break :name &.{}; + }, + .id = std.math.cast(u32, operands.items[0]) orelse return error.InvalidRecordId, + .operands = operands.items[1..], + .blob = blob.items, + }; +} + +fn startBlock(bc: *BitcodeReader, block_id: ?u32, new_abbrev_len: u6) !void { + const abbrevs = if (block_id) |id| + if (bc.block_info.get(id)) |block_info| block_info.abbrevs.abbrevs.items else &.{} + else + &.{}; + + const state = try bc.stack.addOne(bc.allocator); + state.* = .{ + .block_id = block_id, + .abbrev_id_width = new_abbrev_len, + .abbrevs = .{ .abbrevs = .{} }, + }; + try state.abbrevs.abbrevs.ensureTotalCapacity( + bc.allocator, + @typeInfo(Abbrev.Builtin).@"enum".fields.len + abbrevs.len, + ); + + assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.end_block)); + try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ + .operands = &.{ + .{ .literal = Abbrev.Builtin.end_block.toRecordId() }, + .align_32_bits, + }, + }); + assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.enter_subblock)); + try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ + .operands = &.{ + .{ .literal = Abbrev.Builtin.enter_subblock.toRecordId() }, + .{ .encoding = .{ .vbr = 8 } }, // blockid + .{ .encoding = .{ .vbr = 4 } }, // newabbrevlen + .align_32_bits, + .block_len, + }, + }); + assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.define_abbrev)); + try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ + .operands = &.{ + .{ .literal = Abbrev.Builtin.define_abbrev.toRecordId() }, + .{ .encoding = .{ .array = 5 } }, // numabbrevops + .abbrev_op, + }, + }); + assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.unabbrev_record)); + try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ + .operands = &.{ + .{ .encoding = .{ .vbr = 6 } }, // code + .{ .encoding = .{ .array = 6 } }, // numops + .{ .encoding = .{ .vbr = 6 } }, // ops + }, + }); + assert(state.abbrevs.abbrevs.items.len == @typeInfo(Abbrev.Builtin).@"enum".fields.len); + for (abbrevs) |abbrev| try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, abbrev); +} + +fn endBlock(bc: *BitcodeReader) !void { + if (bc.stack.items.len == 0) return error.InvalidEndBlock; + bc.stack.items[bc.stack.items.len - 1].deinit(bc.allocator); + bc.stack.items.len -= 1; +} + +fn parseBlockInfoBlock(bc: *BitcodeReader) !void { + var block_id: ?u32 = null; + while (true) { + const record = (try bc.nextRecord()) orelse return error.EndOfStream; + switch (record.id) { + Abbrev.Builtin.end_block.toRecordId() => break, + Abbrev.Builtin.define_abbrev.toRecordId() => { + const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse + return error.UnspecifiedBlockId); + if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; + try gop.value_ptr.abbrevs.addOwnedAbbrev( + bc.allocator, + try record.toOwnedAbbrev(bc.allocator), + ); + }, + Block.Info.set_bid_id => block_id = std.math.cast(u32, record.operands[0]) orelse + return error.Overflow, + Block.Info.block_name_id => if (bc.keep_names) { + const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse + return error.UnspecifiedBlockId); + if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; + const name = try bc.allocator.alloc(u8, record.operands.len); + errdefer bc.allocator.free(name); + for (name, record.operands) |*byte, operand| + byte.* = std.math.cast(u8, operand) orelse return error.InvalidName; + gop.value_ptr.block_name = name; + }, + Block.Info.set_record_name_id => if (bc.keep_names) { + const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse + return error.UnspecifiedBlockId); + if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; + const name = try bc.allocator.alloc(u8, record.operands.len - 1); + errdefer bc.allocator.free(name); + for (name, record.operands[1..]) |*byte, operand| + byte.* = std.math.cast(u8, operand) orelse return error.InvalidName; + try gop.value_ptr.record_names.put( + bc.allocator, + std.math.cast(u32, record.operands[0]) orelse return error.Overflow, + name, + ); + }, + else => return error.UnsupportedBlockInfoRecord, + } + } +} + +fn align32Bits(bc: *BitcodeReader) void { + bc.bit_offset = 0; +} + +fn read32Bits(bc: *BitcodeReader) !u32 { + assert(bc.bit_offset == 0); + return bc.reader.readInt(u32, .little); +} + +fn readBytes(bc: *BitcodeReader, bytes: []u8) !void { + assert(bc.bit_offset == 0); + try bc.reader.readNoEof(bytes); + + const trailing_bytes = bytes.len % 4; + if (trailing_bytes > 0) { + var bit_buffer = [1]u8{0} ** 4; + try bc.reader.readNoEof(bit_buffer[trailing_bytes..]); + bc.bit_buffer = std.mem.readInt(u32, &bit_buffer, .little); + bc.bit_offset = @intCast(trailing_bytes * 8); + } +} + +fn readFixed(bc: *BitcodeReader, comptime T: type, bits: u7) !T { + var result: T = 0; + var shift: std.math.Log2IntCeil(T) = 0; + var remaining = bits; + while (remaining > 0) { + if (bc.bit_offset == 0) bc.bit_buffer = try bc.read32Bits(); + const chunk_len = @min(@as(u6, 32) - bc.bit_offset, remaining); + const chunk_mask = @as(u32, std.math.maxInt(u32)) >> @intCast(32 - chunk_len); + result |= @as(T, @intCast(bc.bit_buffer >> bc.bit_offset & chunk_mask)) << @intCast(shift); + shift += @intCast(chunk_len); + remaining -= chunk_len; + bc.bit_offset = @truncate(bc.bit_offset + chunk_len); + } + return result; +} + +fn readVbr(bc: *BitcodeReader, comptime T: type, bits: u7) !T { + const chunk_bits: u6 = @intCast(bits - 1); + const chunk_msb = @as(u64, 1) << chunk_bits; + + var result: u64 = 0; + var shift: u6 = 0; + while (true) { + const chunk = try bc.readFixed(u64, bits); + result |= (chunk & (chunk_msb - 1)) << shift; + if (chunk & chunk_msb == 0) break; + shift += chunk_bits; + } + return @intCast(result); +} + +fn readChar6(bc: *BitcodeReader) !u8 { + return switch (try bc.readFixed(u6, 6)) { + 0...25 => |c| @as(u8, c - 0) + 'a', + 26...51 => |c| @as(u8, c - 26) + 'A', + 52...61 => |c| @as(u8, c - 52) + '0', + 62 => '.', + 63 => '_', + }; +} + +const State = struct { + block_id: ?u32, + abbrev_id_width: u6, + abbrevs: Abbrev.Store, + + fn deinit(state: *State, allocator: std.mem.Allocator) void { + state.abbrevs.deinit(allocator); + state.* = undefined; + } +}; + +const Abbrev = struct { + operands: []const Operand, + + const Builtin = enum(u2) { + end_block, + enter_subblock, + define_abbrev, + unabbrev_record, + + const first_record_id: u32 = std.math.maxInt(u32) - @typeInfo(Builtin).@"enum".fields.len + 1; + fn toRecordId(builtin: Builtin) u32 { + return first_record_id + @intFromEnum(builtin); + } + }; + + const Operand = union(enum) { + literal: u64, + encoding: union(Encoding) { + fixed: u7, + vbr: u6, + array: u3, + char6, + blob: u3, + }, + align_32_bits, + block_len, + abbrev_op, + + const literal_id = std.math.maxInt(u64); + const Encoding = enum(u3) { + fixed = 1, + vbr = 2, + array = 3, + char6 = 4, + blob = 5, + _, + }; + }; + + const Store = struct { + abbrevs: std.ArrayListUnmanaged(Abbrev), + + fn deinit(store: *Store, allocator: std.mem.Allocator) void { + for (store.abbrevs.items) |abbrev| allocator.free(abbrev.operands); + store.abbrevs.deinit(allocator); + store.* = undefined; + } + + fn addAbbrev(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void { + try store.ensureUnusedCapacity(allocator, 1); + store.addAbbrevAssumeCapacity(abbrev); + } + + fn addAbbrevAssumeCapacity(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void { + store.abbrevs.appendAssumeCapacity(.{ + .operands = try allocator.dupe(Abbrev.Operand, abbrev.operands), + }); + } + + fn addOwnedAbbrev(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void { + try store.abbrevs.ensureUnusedCapacity(allocator, 1); + store.addOwnedAbbrevAssumeCapacity(abbrev); + } + + fn addOwnedAbbrevAssumeCapacity(store: *Store, abbrev: Abbrev) void { + store.abbrevs.appendAssumeCapacity(abbrev); + } + }; +}; + +const assert = std.debug.assert; +const std = @import("../../std.zig"); + +const BitcodeReader = @This(); diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig new file mode 100644 index 0000000000000000000000000000000000000000..6d16a3360200f4fefe45c29aa6040498275e6282 --- /dev/null +++ b/lib/std/zig/llvm/Builder.zig @@ -0,0 +1,15230 @@ +gpa: Allocator, +strip: bool, + +source_filename: String, +data_layout: String, +target_triple: String, +module_asm: std.ArrayListUnmanaged(u8), + +string_map: std.AutoArrayHashMapUnmanaged(void, void), +string_indices: std.ArrayListUnmanaged(u32), +string_bytes: std.ArrayListUnmanaged(u8), + +types: std.AutoArrayHashMapUnmanaged(String, Type), +next_unnamed_type: String, +next_unique_type_id: std.AutoHashMapUnmanaged(String, u32), +type_map: std.AutoArrayHashMapUnmanaged(void, void), +type_items: std.ArrayListUnmanaged(Type.Item), +type_extra: std.ArrayListUnmanaged(u32), + +attributes: std.AutoArrayHashMapUnmanaged(Attribute.Storage, void), +attributes_map: std.AutoArrayHashMapUnmanaged(void, void), +attributes_indices: std.ArrayListUnmanaged(u32), +attributes_extra: std.ArrayListUnmanaged(u32), + +function_attributes_set: std.AutoArrayHashMapUnmanaged(FunctionAttributes, void), + +globals: std.AutoArrayHashMapUnmanaged(StrtabString, Global), +next_unnamed_global: StrtabString, +next_replaced_global: StrtabString, +next_unique_global_id: std.AutoHashMapUnmanaged(StrtabString, u32), +aliases: std.ArrayListUnmanaged(Alias), +variables: std.ArrayListUnmanaged(Variable), +functions: std.ArrayListUnmanaged(Function), + +strtab_string_map: std.AutoArrayHashMapUnmanaged(void, void), +strtab_string_indices: std.ArrayListUnmanaged(u32), +strtab_string_bytes: std.ArrayListUnmanaged(u8), + +constant_map: std.AutoArrayHashMapUnmanaged(void, void), +constant_items: std.MultiArrayList(Constant.Item), +constant_extra: std.ArrayListUnmanaged(u32), +constant_limbs: std.ArrayListUnmanaged(std.math.big.Limb), + +metadata_map: std.AutoArrayHashMapUnmanaged(void, void), +metadata_items: std.MultiArrayList(Metadata.Item), +metadata_extra: std.ArrayListUnmanaged(u32), +metadata_limbs: std.ArrayListUnmanaged(std.math.big.Limb), +metadata_forward_references: std.ArrayListUnmanaged(Metadata), +metadata_named: std.AutoArrayHashMapUnmanaged(MetadataString, struct { + len: u32, + index: Metadata.Item.ExtraIndex, +}), + +metadata_string_map: std.AutoArrayHashMapUnmanaged(void, void), +metadata_string_indices: std.ArrayListUnmanaged(u32), +metadata_string_bytes: std.ArrayListUnmanaged(u8), + +pub const expected_args_len = 16; +pub const expected_attrs_len = 16; +pub const expected_fields_len = 32; +pub const expected_gep_indices_len = 8; +pub const expected_cases_len = 8; +pub const expected_incoming_len = 8; + +pub const Options = struct { + allocator: Allocator, + strip: bool = true, + name: []const u8 = &.{}, + target: std.Target = builtin.target, + triple: []const u8 = &.{}, +}; + +pub const String = enum(u32) { + none = std.math.maxInt(u31), + empty, + _, + + pub fn isAnon(self: String) bool { + assert(self != .none); + return self.toIndex() == null; + } + + pub fn slice(self: String, builder: *const Builder) ?[]const u8 { + const index = self.toIndex() orelse return null; + const start = builder.string_indices.items[index]; + const end = builder.string_indices.items[index + 1]; + return builder.string_bytes.items[start..end]; + } + + const FormatData = struct { + string: String, + builder: *const Builder, + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_| + @compileError("invalid format string: '" ++ fmt_str ++ "'"); + assert(data.string != .none); + const string_slice = data.string.slice(data.builder) orelse + return writer.print("{d}", .{@intFromEnum(data.string)}); + if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_| + return writer.writeAll(string_slice); + try printEscapedString( + string_slice, + if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_| + .always_quote + else + .quote_unless_valid_identifier, + writer, + ); + } + pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .string = self, .builder = builder } }; + } + + fn fromIndex(index: ?usize) String { + return @enumFromInt(@as(u32, @intCast((index orelse return .none) + + @intFromEnum(String.empty)))); + } + + fn toIndex(self: String) ?usize { + return std.math.sub(u32, @intFromEnum(self), @intFromEnum(String.empty)) catch null; + } + + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: Adapter, key: []const u8) u32 { + return @truncate(std.hash.Wyhash.hash(0, key)); + } + pub fn eql(ctx: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool { + return std.mem.eql(u8, lhs_key, String.fromIndex(rhs_index).slice(ctx.builder).?); + } + }; +}; + +pub const BinaryOpcode = enum(u4) { + add = 0, + sub = 1, + mul = 2, + udiv = 3, + sdiv = 4, + urem = 5, + srem = 6, + shl = 7, + lshr = 8, + ashr = 9, + @"and" = 10, + @"or" = 11, + xor = 12, +}; + +pub const CastOpcode = enum(u4) { + trunc = 0, + zext = 1, + sext = 2, + fptoui = 3, + fptosi = 4, + uitofp = 5, + sitofp = 6, + fptrunc = 7, + fpext = 8, + ptrtoint = 9, + inttoptr = 10, + bitcast = 11, + addrspacecast = 12, +}; + +pub const CmpPredicate = enum(u6) { + fcmp_false = 0, + fcmp_oeq = 1, + fcmp_ogt = 2, + fcmp_oge = 3, + fcmp_olt = 4, + fcmp_ole = 5, + fcmp_one = 6, + fcmp_ord = 7, + fcmp_uno = 8, + fcmp_ueq = 9, + fcmp_ugt = 10, + fcmp_uge = 11, + fcmp_ult = 12, + fcmp_ule = 13, + fcmp_une = 14, + fcmp_true = 15, + icmp_eq = 32, + icmp_ne = 33, + icmp_ugt = 34, + icmp_uge = 35, + icmp_ult = 36, + icmp_ule = 37, + icmp_sgt = 38, + icmp_sge = 39, + icmp_slt = 40, + icmp_sle = 41, +}; + +pub const Type = enum(u32) { + void, + half, + bfloat, + float, + double, + fp128, + x86_fp80, + ppc_fp128, + x86_amx, + x86_mmx, + label, + token, + metadata, + + i1, + i8, + i16, + i29, + i32, + i64, + i80, + i128, + ptr, + @"ptr addrspace(4)", + + none = std.math.maxInt(u32), + _, + + pub const ptr_amdgpu_constant = + @field(Type, std.fmt.comptimePrint("ptr{ }", .{AddrSpace.amdgpu.constant})); + + pub const Tag = enum(u4) { + simple, + function, + vararg_function, + integer, + pointer, + target, + vector, + scalable_vector, + small_array, + array, + structure, + packed_structure, + named_structure, + }; + + pub const Simple = enum(u5) { + void = 2, + half = 10, + bfloat = 23, + float = 3, + double = 4, + fp128 = 14, + x86_fp80 = 13, + ppc_fp128 = 15, + x86_amx = 24, + x86_mmx = 17, + label = 5, + token = 22, + metadata = 16, + }; + + pub const Function = struct { + ret: Type, + params_len: u32, + //params: [params_len]Value, + + pub const Kind = enum { normal, vararg }; + }; + + pub const Target = extern struct { + name: String, + types_len: u32, + ints_len: u32, + //types: [types_len]Type, + //ints: [ints_len]u32, + }; + + pub const Vector = extern struct { + len: u32, + child: Type, + + fn length(self: Vector) u32 { + return self.len; + } + + pub const Kind = enum { normal, scalable }; + }; + + pub const Array = extern struct { + len_lo: u32, + len_hi: u32, + child: Type, + + fn length(self: Array) u64 { + return @as(u64, self.len_hi) << 32 | self.len_lo; + } + }; + + pub const Structure = struct { + fields_len: u32, + //fields: [fields_len]Type, + + pub const Kind = enum { normal, @"packed" }; + }; + + pub const NamedStructure = struct { + id: String, + body: Type, + }; + + pub const Item = packed struct(u32) { + tag: Tag, + data: ExtraIndex, + + pub const ExtraIndex = u28; + }; + + pub fn tag(self: Type, builder: *const Builder) Tag { + return builder.type_items.items[@intFromEnum(self)].tag; + } + + pub fn unnamedTag(self: Type, builder: *const Builder) Tag { + const item = builder.type_items.items[@intFromEnum(self)]; + return switch (item.tag) { + .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body + .unnamedTag(builder), + else => item.tag, + }; + } + + pub fn scalarTag(self: Type, builder: *const Builder) Tag { + const item = builder.type_items.items[@intFromEnum(self)]; + return switch (item.tag) { + .vector, .scalable_vector => builder.typeExtraData(Type.Vector, item.data) + .child.tag(builder), + else => item.tag, + }; + } + + pub fn isFloatingPoint(self: Type) bool { + return switch (self) { + .half, .bfloat, .float, .double, .fp128, .x86_fp80, .ppc_fp128 => true, + else => false, + }; + } + + pub fn isInteger(self: Type, builder: *const Builder) bool { + return switch (self) { + .i1, .i8, .i16, .i29, .i32, .i64, .i80, .i128 => true, + else => switch (self.tag(builder)) { + .integer => true, + else => false, + }, + }; + } + + pub fn isPointer(self: Type, builder: *const Builder) bool { + return switch (self) { + .ptr => true, + else => switch (self.tag(builder)) { + .pointer => true, + else => false, + }, + }; + } + + pub fn pointerAddrSpace(self: Type, builder: *const Builder) AddrSpace { + switch (self) { + .ptr => return .default, + else => { + const item = builder.type_items.items[@intFromEnum(self)]; + assert(item.tag == .pointer); + return @enumFromInt(item.data); + }, + } + } + + pub fn isFunction(self: Type, builder: *const Builder) bool { + return switch (self.tag(builder)) { + .function, .vararg_function => true, + else => false, + }; + } + + pub fn functionKind(self: Type, builder: *const Builder) Type.Function.Kind { + return switch (self.tag(builder)) { + .function => .normal, + .vararg_function => .vararg, + else => unreachable, + }; + } + + pub fn functionParameters(self: Type, builder: *const Builder) []const Type { + const item = builder.type_items.items[@intFromEnum(self)]; + switch (item.tag) { + .function, + .vararg_function, + => { + var extra = builder.typeExtraDataTrail(Type.Function, item.data); + return extra.trail.next(extra.data.params_len, Type, builder); + }, + else => unreachable, + } + } + + pub fn functionReturn(self: Type, builder: *const Builder) Type { + const item = builder.type_items.items[@intFromEnum(self)]; + switch (item.tag) { + .function, + .vararg_function, + => return builder.typeExtraData(Type.Function, item.data).ret, + else => unreachable, + } + } + + pub fn isVector(self: Type, builder: *const Builder) bool { + return switch (self.tag(builder)) { + .vector, .scalable_vector => true, + else => false, + }; + } + + pub fn vectorKind(self: Type, builder: *const Builder) Type.Vector.Kind { + return switch (self.tag(builder)) { + .vector => .normal, + .scalable_vector => .scalable, + else => unreachable, + }; + } + + pub fn isStruct(self: Type, builder: *const Builder) bool { + return switch (self.tag(builder)) { + .structure, .packed_structure, .named_structure => true, + else => false, + }; + } + + pub fn structKind(self: Type, builder: *const Builder) Type.Structure.Kind { + return switch (self.unnamedTag(builder)) { + .structure => .normal, + .packed_structure => .@"packed", + else => unreachable, + }; + } + + pub fn isAggregate(self: Type, builder: *const Builder) bool { + return switch (self.tag(builder)) { + .small_array, .array, .structure, .packed_structure, .named_structure => true, + else => false, + }; + } + + pub fn scalarBits(self: Type, builder: *const Builder) u24 { + return switch (self) { + .void, .label, .token, .metadata, .none, .x86_amx => unreachable, + .i1 => 1, + .i8 => 8, + .half, .bfloat, .i16 => 16, + .i29 => 29, + .float, .i32 => 32, + .double, .i64, .x86_mmx => 64, + .x86_fp80, .i80 => 80, + .fp128, .ppc_fp128, .i128 => 128, + .ptr, .@"ptr addrspace(4)" => @panic("TODO: query data layout"), + _ => { + const item = builder.type_items.items[@intFromEnum(self)]; + return switch (item.tag) { + .simple, + .function, + .vararg_function, + => unreachable, + .integer => @intCast(item.data), + .pointer => @panic("TODO: query data layout"), + .target => unreachable, + .vector, + .scalable_vector, + => builder.typeExtraData(Type.Vector, item.data).child.scalarBits(builder), + .small_array, + .array, + .structure, + .packed_structure, + .named_structure, + => unreachable, + }; + }, + }; + } + + pub fn childType(self: Type, builder: *const Builder) Type { + const item = builder.type_items.items[@intFromEnum(self)]; + return switch (item.tag) { + .vector, + .scalable_vector, + .small_array, + => builder.typeExtraData(Type.Vector, item.data).child, + .array => builder.typeExtraData(Type.Array, item.data).child, + .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body, + else => unreachable, + }; + } + + pub fn scalarType(self: Type, builder: *const Builder) Type { + if (self.isFloatingPoint()) return self; + const item = builder.type_items.items[@intFromEnum(self)]; + return switch (item.tag) { + .integer, + .pointer, + => self, + .vector, + .scalable_vector, + => builder.typeExtraData(Type.Vector, item.data).child, + else => unreachable, + }; + } + + pub fn changeScalar(self: Type, scalar: Type, builder: *Builder) Allocator.Error!Type { + try builder.ensureUnusedTypeCapacity(1, Type.Vector, 0); + return self.changeScalarAssumeCapacity(scalar, builder); + } + + pub fn changeScalarAssumeCapacity(self: Type, scalar: Type, builder: *Builder) Type { + if (self.isFloatingPoint()) return scalar; + const item = builder.type_items.items[@intFromEnum(self)]; + return switch (item.tag) { + .integer, + .pointer, + => scalar, + inline .vector, + .scalable_vector, + => |kind| builder.vectorTypeAssumeCapacity( + switch (kind) { + .vector => .normal, + .scalable_vector => .scalable, + else => unreachable, + }, + builder.typeExtraData(Type.Vector, item.data).len, + scalar, + ), + else => unreachable, + }; + } + + pub fn vectorLen(self: Type, builder: *const Builder) u32 { + const item = builder.type_items.items[@intFromEnum(self)]; + return switch (item.tag) { + .vector, + .scalable_vector, + => builder.typeExtraData(Type.Vector, item.data).len, + else => unreachable, + }; + } + + pub fn changeLength(self: Type, len: u32, builder: *Builder) Allocator.Error!Type { + try builder.ensureUnusedTypeCapacity(1, Type.Array, 0); + return self.changeLengthAssumeCapacity(len, builder); + } + + pub fn changeLengthAssumeCapacity(self: Type, len: u32, builder: *Builder) Type { + const item = builder.type_items.items[@intFromEnum(self)]; + return switch (item.tag) { + inline .vector, + .scalable_vector, + => |kind| builder.vectorTypeAssumeCapacity( + switch (kind) { + .vector => .normal, + .scalable_vector => .scalable, + else => unreachable, + }, + len, + builder.typeExtraData(Type.Vector, item.data).child, + ), + .small_array => builder.arrayTypeAssumeCapacity( + len, + builder.typeExtraData(Type.Vector, item.data).child, + ), + .array => builder.arrayTypeAssumeCapacity( + len, + builder.typeExtraData(Type.Array, item.data).child, + ), + else => unreachable, + }; + } + + pub fn aggregateLen(self: Type, builder: *const Builder) usize { + const item = builder.type_items.items[@intFromEnum(self)]; + return switch (item.tag) { + .vector, + .scalable_vector, + .small_array, + => builder.typeExtraData(Type.Vector, item.data).len, + .array => @intCast(builder.typeExtraData(Type.Array, item.data).length()), + .structure, + .packed_structure, + => builder.typeExtraData(Type.Structure, item.data).fields_len, + .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body + .aggregateLen(builder), + else => unreachable, + }; + } + + pub fn structFields(self: Type, builder: *const Builder) []const Type { + const item = builder.type_items.items[@intFromEnum(self)]; + switch (item.tag) { + .structure, + .packed_structure, + => { + var extra = builder.typeExtraDataTrail(Type.Structure, item.data); + return extra.trail.next(extra.data.fields_len, Type, builder); + }, + .named_structure => return builder.typeExtraData(Type.NamedStructure, item.data).body + .structFields(builder), + else => unreachable, + } + } + + pub fn childTypeAt(self: Type, indices: []const u32, builder: *const Builder) Type { + if (indices.len == 0) return self; + const item = builder.type_items.items[@intFromEnum(self)]; + return switch (item.tag) { + .small_array => builder.typeExtraData(Type.Vector, item.data).child + .childTypeAt(indices[1..], builder), + .array => builder.typeExtraData(Type.Array, item.data).child + .childTypeAt(indices[1..], builder), + .structure, + .packed_structure, + => { + var extra = builder.typeExtraDataTrail(Type.Structure, item.data); + const fields = extra.trail.next(extra.data.fields_len, Type, builder); + return fields[indices[0]].childTypeAt(indices[1..], builder); + }, + .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body + .childTypeAt(indices, builder), + else => unreachable, + }; + } + + pub fn targetLayoutType(self: Type, builder: *const Builder) Type { + _ = self; + _ = builder; + @panic("TODO: implement targetLayoutType"); + } + + pub fn isSized(self: Type, builder: *const Builder) Allocator.Error!bool { + var visited: IsSizedVisited = .{}; + defer visited.deinit(builder.gpa); + const result = try self.isSizedVisited(&visited, builder); + return result; + } + + const FormatData = struct { + type: Type, + builder: *const Builder, + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + fmt_opts: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + assert(data.type != .none); + if (comptime std.mem.eql(u8, fmt_str, "m")) { + const item = data.builder.type_items.items[@intFromEnum(data.type)]; + switch (item.tag) { + .simple => try writer.writeAll(switch (@as(Simple, @enumFromInt(item.data))) { + .void => "isVoid", + .half => "f16", + .bfloat => "bf16", + .float => "f32", + .double => "f64", + .fp128 => "f128", + .x86_fp80 => "f80", + .ppc_fp128 => "ppcf128", + .x86_amx => "x86amx", + .x86_mmx => "x86mmx", + .label, .token => unreachable, + .metadata => "Metadata", + }), + .function, .vararg_function => |kind| { + var extra = data.builder.typeExtraDataTrail(Type.Function, item.data); + const params = extra.trail.next(extra.data.params_len, Type, data.builder); + try writer.print("f_{m}", .{extra.data.ret.fmt(data.builder)}); + for (params) |param| try writer.print("{m}", .{param.fmt(data.builder)}); + switch (kind) { + .function => {}, + .vararg_function => try writer.writeAll("vararg"), + else => unreachable, + } + try writer.writeByte('f'); + }, + .integer => try writer.print("i{d}", .{item.data}), + .pointer => try writer.print("p{d}", .{item.data}), + .target => { + var extra = data.builder.typeExtraDataTrail(Type.Target, item.data); + const types = extra.trail.next(extra.data.types_len, Type, data.builder); + const ints = extra.trail.next(extra.data.ints_len, u32, data.builder); + try writer.print("t{s}", .{extra.data.name.slice(data.builder).?}); + for (types) |ty| try writer.print("_{m}", .{ty.fmt(data.builder)}); + for (ints) |int| try writer.print("_{d}", .{int}); + try writer.writeByte('t'); + }, + .vector, .scalable_vector => |kind| { + const extra = data.builder.typeExtraData(Type.Vector, item.data); + try writer.print("{s}v{d}{m}", .{ + switch (kind) { + .vector => "", + .scalable_vector => "nx", + else => unreachable, + }, + extra.len, + extra.child.fmt(data.builder), + }); + }, + inline .small_array, .array => |kind| { + const extra = data.builder.typeExtraData(switch (kind) { + .small_array => Type.Vector, + .array => Type.Array, + else => unreachable, + }, item.data); + try writer.print("a{d}{m}", .{ extra.length(), extra.child.fmt(data.builder) }); + }, + .structure, .packed_structure => { + var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data); + const fields = extra.trail.next(extra.data.fields_len, Type, data.builder); + try writer.writeAll("sl_"); + for (fields) |field| try writer.print("{m}", .{field.fmt(data.builder)}); + try writer.writeByte('s'); + }, + .named_structure => { + const extra = data.builder.typeExtraData(Type.NamedStructure, item.data); + try writer.writeAll("s_"); + if (extra.id.slice(data.builder)) |id| try writer.writeAll(id); + }, + } + return; + } + if (std.enums.tagName(Type, data.type)) |name| return writer.writeAll(name); + const item = data.builder.type_items.items[@intFromEnum(data.type)]; + switch (item.tag) { + .simple => unreachable, + .function, .vararg_function => |kind| { + var extra = data.builder.typeExtraDataTrail(Type.Function, item.data); + const params = extra.trail.next(extra.data.params_len, Type, data.builder); + if (!comptime std.mem.eql(u8, fmt_str, ">")) + try writer.print("{%} ", .{extra.data.ret.fmt(data.builder)}); + if (!comptime std.mem.eql(u8, fmt_str, "<")) { + try writer.writeByte('('); + for (params, 0..) |param, index| { + if (index > 0) try writer.writeAll(", "); + try writer.print("{%}", .{param.fmt(data.builder)}); + } + switch (kind) { + .function => {}, + .vararg_function => { + if (params.len > 0) try writer.writeAll(", "); + try writer.writeAll("..."); + }, + else => unreachable, + } + try writer.writeByte(')'); + } + }, + .integer => try writer.print("i{d}", .{item.data}), + .pointer => try writer.print("ptr{ }", .{@as(AddrSpace, @enumFromInt(item.data))}), + .target => { + var extra = data.builder.typeExtraDataTrail(Type.Target, item.data); + const types = extra.trail.next(extra.data.types_len, Type, data.builder); + const ints = extra.trail.next(extra.data.ints_len, u32, data.builder); + try writer.print( + \\target({"} + , .{extra.data.name.fmt(data.builder)}); + for (types) |ty| try writer.print(", {%}", .{ty.fmt(data.builder)}); + for (ints) |int| try writer.print(", {d}", .{int}); + try writer.writeByte(')'); + }, + .vector, .scalable_vector => |kind| { + const extra = data.builder.typeExtraData(Type.Vector, item.data); + try writer.print("<{s}{d} x {%}>", .{ + switch (kind) { + .vector => "", + .scalable_vector => "vscale x ", + else => unreachable, + }, + extra.len, + extra.child.fmt(data.builder), + }); + }, + inline .small_array, .array => |kind| { + const extra = data.builder.typeExtraData(switch (kind) { + .small_array => Type.Vector, + .array => Type.Array, + else => unreachable, + }, item.data); + try writer.print("[{d} x {%}]", .{ extra.length(), extra.child.fmt(data.builder) }); + }, + .structure, .packed_structure => |kind| { + var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data); + const fields = extra.trail.next(extra.data.fields_len, Type, data.builder); + switch (kind) { + .structure => {}, + .packed_structure => try writer.writeByte('<'), + else => unreachable, + } + try writer.writeAll("{ "); + for (fields, 0..) |field, index| { + if (index > 0) try writer.writeAll(", "); + try writer.print("{%}", .{field.fmt(data.builder)}); + } + try writer.writeAll(" }"); + switch (kind) { + .structure => {}, + .packed_structure => try writer.writeByte('>'), + else => unreachable, + } + }, + .named_structure => { + const extra = data.builder.typeExtraData(Type.NamedStructure, item.data); + if (comptime std.mem.eql(u8, fmt_str, "%")) try writer.print("%{}", .{ + extra.id.fmt(data.builder), + }) else switch (extra.body) { + .none => try writer.writeAll("opaque"), + else => try format(.{ + .type = extra.body, + .builder = data.builder, + }, fmt_str, fmt_opts, writer), + } + }, + } + } + pub fn fmt(self: Type, builder: *const Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .type = self, .builder = builder } }; + } + + const IsSizedVisited = std.AutoHashMapUnmanaged(Type, void); + fn isSizedVisited( + self: Type, + visited: *IsSizedVisited, + builder: *const Builder, + ) Allocator.Error!bool { + return switch (self) { + .void, + .label, + .token, + .metadata, + => false, + .half, + .bfloat, + .float, + .double, + .fp128, + .x86_fp80, + .ppc_fp128, + .x86_amx, + .x86_mmx, + .i1, + .i8, + .i16, + .i29, + .i32, + .i64, + .i80, + .i128, + .ptr, + .@"ptr addrspace(4)", + => true, + .none => unreachable, + _ => { + const item = builder.type_items.items[@intFromEnum(self)]; + return switch (item.tag) { + .simple => unreachable, + .function, + .vararg_function, + => false, + .integer, + .pointer, + => true, + .target => self.targetLayoutType(builder).isSizedVisited(visited, builder), + .vector, + .scalable_vector, + .small_array, + => builder.typeExtraData(Type.Vector, item.data) + .child.isSizedVisited(visited, builder), + .array => builder.typeExtraData(Type.Array, item.data) + .child.isSizedVisited(visited, builder), + .structure, + .packed_structure, + => { + if (try visited.fetchPut(builder.gpa, self, {})) |_| return false; + + var extra = builder.typeExtraDataTrail(Type.Structure, item.data); + const fields = extra.trail.next(extra.data.fields_len, Type, builder); + for (fields) |field| { + if (field.isVector(builder) and field.vectorKind(builder) == .scalable) + return false; + if (!try field.isSizedVisited(visited, builder)) + return false; + } + return true; + }, + .named_structure => { + const body = builder.typeExtraData(Type.NamedStructure, item.data).body; + return body != .none and try body.isSizedVisited(visited, builder); + }, + }; + }, + }; + } +}; + +pub const Attribute = union(Kind) { + // Parameter Attributes + zeroext, + signext, + inreg, + byval: Type, + byref: Type, + preallocated: Type, + inalloca: Type, + sret: Type, + elementtype: Type, + @"align": Alignment, + @"noalias", + nocapture, + nofree, + nest, + returned, + nonnull, + dereferenceable: u32, + dereferenceable_or_null: u32, + swiftself, + swiftasync, + swifterror, + immarg, + noundef, + nofpclass: FpClass, + alignstack: Alignment, + allocalign, + allocptr, + readnone, + readonly, + writeonly, + + // Function Attributes + //alignstack: Alignment, + allockind: AllocKind, + allocsize: AllocSize, + alwaysinline, + builtin, + cold, + convergent, + disable_sanitizer_information, + fn_ret_thunk_extern, + hot, + inlinehint, + jumptable, + memory: Memory, + minsize, + naked, + nobuiltin, + nocallback, + noduplicate, + //nofree, + noimplicitfloat, + @"noinline", + nomerge, + nonlazybind, + noprofile, + skipprofile, + noredzone, + noreturn, + norecurse, + willreturn, + nosync, + nounwind, + nosanitize_bounds, + nosanitize_coverage, + null_pointer_is_valid, + optforfuzzing, + optnone, + optsize, + //preallocated: Type, + returns_twice, + safestack, + sanitize_address, + sanitize_memory, + sanitize_thread, + sanitize_hwaddress, + sanitize_memtag, + speculative_load_hardening, + speculatable, + ssp, + sspstrong, + sspreq, + strictfp, + uwtable: UwTable, + nocf_check, + shadowcallstack, + mustprogress, + vscale_range: VScaleRange, + + // Global Attributes + no_sanitize_address, + no_sanitize_hwaddress, + //sanitize_memtag, + sanitize_address_dyninit, + + string: struct { kind: String, value: String }, + none: noreturn, + + pub const Index = enum(u32) { + _, + + pub fn getKind(self: Index, builder: *const Builder) Kind { + return self.toStorage(builder).kind; + } + + pub fn toAttribute(self: Index, builder: *const Builder) Attribute { + @setEvalBranchQuota(2_000); + const storage = self.toStorage(builder); + if (storage.kind.toString()) |kind| return .{ .string = .{ + .kind = kind, + .value = @enumFromInt(storage.value), + } } else return switch (storage.kind) { + inline .zeroext, + .signext, + .inreg, + .byval, + .byref, + .preallocated, + .inalloca, + .sret, + .elementtype, + .@"align", + .@"noalias", + .nocapture, + .nofree, + .nest, + .returned, + .nonnull, + .dereferenceable, + .dereferenceable_or_null, + .swiftself, + .swiftasync, + .swifterror, + .immarg, + .noundef, + .nofpclass, + .alignstack, + .allocalign, + .allocptr, + .readnone, + .readonly, + .writeonly, + //.alignstack, + .allockind, + .allocsize, + .alwaysinline, + .builtin, + .cold, + .convergent, + .disable_sanitizer_information, + .fn_ret_thunk_extern, + .hot, + .inlinehint, + .jumptable, + .memory, + .minsize, + .naked, + .nobuiltin, + .nocallback, + .noduplicate, + //.nofree, + .noimplicitfloat, + .@"noinline", + .nomerge, + .nonlazybind, + .noprofile, + .skipprofile, + .noredzone, + .noreturn, + .norecurse, + .willreturn, + .nosync, + .nounwind, + .nosanitize_bounds, + .nosanitize_coverage, + .null_pointer_is_valid, + .optforfuzzing, + .optnone, + .optsize, + //.preallocated, + .returns_twice, + .safestack, + .sanitize_address, + .sanitize_memory, + .sanitize_thread, + .sanitize_hwaddress, + .sanitize_memtag, + .speculative_load_hardening, + .speculatable, + .ssp, + .sspstrong, + .sspreq, + .strictfp, + .uwtable, + .nocf_check, + .shadowcallstack, + .mustprogress, + .vscale_range, + .no_sanitize_address, + .no_sanitize_hwaddress, + .sanitize_address_dyninit, + => |kind| { + const field = comptime blk: { + @setEvalBranchQuota(10_000); + for (@typeInfo(Attribute).@"union".fields) |field| { + if (std.mem.eql(u8, field.name, @tagName(kind))) break :blk field; + } + unreachable; + }; + comptime assert(std.mem.eql(u8, @tagName(kind), field.name)); + return @unionInit(Attribute, field.name, switch (field.type) { + void => {}, + u32 => storage.value, + Alignment, String, Type, UwTable => @enumFromInt(storage.value), + AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(storage.value), + else => @compileError("bad payload type: " ++ field.name ++ ": " ++ + @typeName(field.type)), + }); + }, + .string, .none => unreachable, + _ => unreachable, + }; + } + + const FormatData = struct { + attribute_index: Index, + builder: *const Builder, + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (comptime std.mem.indexOfNone(u8, fmt_str, "\"#")) |_| + @compileError("invalid format string: '" ++ fmt_str ++ "'"); + const attribute = data.attribute_index.toAttribute(data.builder); + switch (attribute) { + .zeroext, + .signext, + .inreg, + .@"noalias", + .nocapture, + .nofree, + .nest, + .returned, + .nonnull, + .swiftself, + .swiftasync, + .swifterror, + .immarg, + .noundef, + .allocalign, + .allocptr, + .readnone, + .readonly, + .writeonly, + .alwaysinline, + .builtin, + .cold, + .convergent, + .disable_sanitizer_information, + .fn_ret_thunk_extern, + .hot, + .inlinehint, + .jumptable, + .minsize, + .naked, + .nobuiltin, + .nocallback, + .noduplicate, + .noimplicitfloat, + .@"noinline", + .nomerge, + .nonlazybind, + .noprofile, + .skipprofile, + .noredzone, + .noreturn, + .norecurse, + .willreturn, + .nosync, + .nounwind, + .nosanitize_bounds, + .nosanitize_coverage, + .null_pointer_is_valid, + .optforfuzzing, + .optnone, + .optsize, + .returns_twice, + .safestack, + .sanitize_address, + .sanitize_memory, + .sanitize_thread, + .sanitize_hwaddress, + .sanitize_memtag, + .speculative_load_hardening, + .speculatable, + .ssp, + .sspstrong, + .sspreq, + .strictfp, + .nocf_check, + .shadowcallstack, + .mustprogress, + .no_sanitize_address, + .no_sanitize_hwaddress, + .sanitize_address_dyninit, + => try writer.print(" {s}", .{@tagName(attribute)}), + .byval, + .byref, + .preallocated, + .inalloca, + .sret, + .elementtype, + => |ty| try writer.print(" {s}({%})", .{ @tagName(attribute), ty.fmt(data.builder) }), + .@"align" => |alignment| try writer.print("{ }", .{alignment}), + .dereferenceable, + .dereferenceable_or_null, + => |size| try writer.print(" {s}({d})", .{ @tagName(attribute), size }), + .nofpclass => |fpclass| { + const Int = @typeInfo(FpClass).@"struct".backing_integer.?; + try writer.print(" {s}(", .{@tagName(attribute)}); + var any = false; + var remaining: Int = @bitCast(fpclass); + inline for (@typeInfo(FpClass).@"struct".decls) |decl| { + const pattern: Int = @bitCast(@field(FpClass, decl.name)); + if (remaining & pattern == pattern) { + if (!any) { + try writer.writeByte(' '); + any = true; + } + try writer.writeAll(decl.name); + remaining &= ~pattern; + } + } + try writer.writeByte(')'); + }, + .alignstack => |alignment| try writer.print( + if (comptime std.mem.indexOfScalar(u8, fmt_str, '#') != null) + " {s}={d}" + else + " {s}({d})", + .{ @tagName(attribute), alignment.toByteUnits() orelse return }, + ), + .allockind => |allockind| { + try writer.print(" {s}(\"", .{@tagName(attribute)}); + var any = false; + inline for (@typeInfo(AllocKind).@"struct".fields) |field| { + if (comptime std.mem.eql(u8, field.name, "_")) continue; + if (@field(allockind, field.name)) { + if (!any) { + try writer.writeByte(','); + any = true; + } + try writer.writeAll(field.name); + } + } + try writer.writeAll("\")"); + }, + .allocsize => |allocsize| { + try writer.print(" {s}({d}", .{ @tagName(attribute), allocsize.elem_size }); + if (allocsize.num_elems != AllocSize.none) + try writer.print(",{d}", .{allocsize.num_elems}); + try writer.writeByte(')'); + }, + .memory => |memory| { + try writer.print(" {s}(", .{@tagName(attribute)}); + var any = memory.other != .none or + (memory.argmem == .none and memory.inaccessiblemem == .none); + if (any) try writer.writeAll(@tagName(memory.other)); + inline for (.{ "argmem", "inaccessiblemem" }) |kind| { + if (@field(memory, kind) != memory.other) { + if (any) try writer.writeAll(", "); + try writer.print("{s}: {s}", .{ kind, @tagName(@field(memory, kind)) }); + any = true; + } + } + try writer.writeByte(')'); + }, + .uwtable => |uwtable| if (uwtable != .none) { + try writer.print(" {s}", .{@tagName(attribute)}); + if (uwtable != UwTable.default) try writer.print("({s})", .{@tagName(uwtable)}); + }, + .vscale_range => |vscale_range| try writer.print(" {s}({d},{d})", .{ + @tagName(attribute), + vscale_range.min.toByteUnits().?, + vscale_range.max.toByteUnits() orelse 0, + }), + .string => |string_attr| if (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) { + try writer.print(" {\"}", .{string_attr.kind.fmt(data.builder)}); + if (string_attr.value != .empty) + try writer.print("={\"}", .{string_attr.value.fmt(data.builder)}); + }, + .none => unreachable, + } + } + pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .attribute_index = self, .builder = builder } }; + } + + fn toStorage(self: Index, builder: *const Builder) Storage { + return builder.attributes.keys()[@intFromEnum(self)]; + } + }; + + pub const Kind = enum(u32) { + // Parameter Attributes + zeroext = 34, + signext = 24, + inreg = 5, + byval = 3, + byref = 69, + preallocated = 65, + inalloca = 38, + sret = 29, // TODO: ? + elementtype = 77, + @"align" = 1, + @"noalias" = 9, + nocapture = 11, + nofree = 62, + nest = 8, + returned = 22, + nonnull = 39, + dereferenceable = 41, + dereferenceable_or_null = 42, + swiftself = 46, + swiftasync = 75, + swifterror = 47, + immarg = 60, + noundef = 68, + nofpclass = 87, + alignstack = 25, + allocalign = 80, + allocptr = 81, + readnone = 20, + readonly = 21, + writeonly = 52, + + // Function Attributes + //alignstack, + allockind = 82, + allocsize = 51, + alwaysinline = 2, + builtin = 35, + cold = 36, + convergent = 43, + disable_sanitizer_information = 78, + fn_ret_thunk_extern = 84, + hot = 72, + inlinehint = 4, + jumptable = 40, + memory = 86, + minsize = 6, + naked = 7, + nobuiltin = 10, + nocallback = 71, + noduplicate = 12, + //nofree, + noimplicitfloat = 13, + @"noinline" = 14, + nomerge = 66, + nonlazybind = 15, + noprofile = 73, + skipprofile = 85, + noredzone = 16, + noreturn = 17, + norecurse = 48, + willreturn = 61, + nosync = 63, + nounwind = 18, + nosanitize_bounds = 79, + nosanitize_coverage = 76, + null_pointer_is_valid = 67, + optforfuzzing = 57, + optnone = 37, + optsize = 19, + //preallocated, + returns_twice = 23, + safestack = 44, + sanitize_address = 30, + sanitize_memory = 32, + sanitize_thread = 31, + sanitize_hwaddress = 55, + sanitize_memtag = 64, + speculative_load_hardening = 59, + speculatable = 53, + ssp = 26, + sspstrong = 28, + sspreq = 27, + strictfp = 54, + uwtable = 33, + nocf_check = 56, + shadowcallstack = 58, + mustprogress = 70, + vscale_range = 74, + + // Global Attributes + no_sanitize_address = 100, + no_sanitize_hwaddress = 101, + //sanitize_memtag, + sanitize_address_dyninit = 102, + + string = std.math.maxInt(u31), + none = std.math.maxInt(u32), + _, + + pub const len = @typeInfo(Kind).@"enum".fields.len - 2; + + pub fn fromString(str: String) Kind { + assert(!str.isAnon()); + const kind: Kind = @enumFromInt(@intFromEnum(str)); + assert(kind != .none); + return kind; + } + + fn toString(self: Kind) ?String { + assert(self != .none); + const str: String = @enumFromInt(@intFromEnum(self)); + return if (str.isAnon()) null else str; + } + }; + + pub const FpClass = packed struct(u32) { + signaling_nan: bool = false, + quiet_nan: bool = false, + negative_infinity: bool = false, + negative_normal: bool = false, + negative_subnormal: bool = false, + negative_zero: bool = false, + positive_zero: bool = false, + positive_subnormal: bool = false, + positive_normal: bool = false, + positive_infinity: bool = false, + _: u22 = 0, + + pub const all = FpClass{ + .signaling_nan = true, + .quiet_nan = true, + .negative_infinity = true, + .negative_normal = true, + .negative_subnormal = true, + .negative_zero = true, + .positive_zero = true, + .positive_subnormal = true, + .positive_normal = true, + .positive_infinity = true, + }; + + pub const nan = FpClass{ .signaling_nan = true, .quiet_nan = true }; + pub const snan = FpClass{ .signaling_nan = true }; + pub const qnan = FpClass{ .quiet_nan = true }; + + pub const inf = FpClass{ .negative_infinity = true, .positive_infinity = true }; + pub const ninf = FpClass{ .negative_infinity = true }; + pub const pinf = FpClass{ .positive_infinity = true }; + + pub const zero = FpClass{ .positive_zero = true, .negative_zero = true }; + pub const nzero = FpClass{ .negative_zero = true }; + pub const pzero = FpClass{ .positive_zero = true }; + + pub const sub = FpClass{ .positive_subnormal = true, .negative_subnormal = true }; + pub const nsub = FpClass{ .negative_subnormal = true }; + pub const psub = FpClass{ .positive_subnormal = true }; + + pub const norm = FpClass{ .positive_normal = true, .negative_normal = true }; + pub const nnorm = FpClass{ .negative_normal = true }; + pub const pnorm = FpClass{ .positive_normal = true }; + }; + + pub const AllocKind = packed struct(u32) { + alloc: bool, + realloc: bool, + free: bool, + uninitialized: bool, + zeroed: bool, + aligned: bool, + _: u26 = 0, + }; + + pub const AllocSize = packed struct(u32) { + elem_size: u16, + num_elems: u16, + + pub const none = std.math.maxInt(u16); + + fn toLlvm(self: AllocSize) packed struct(u64) { num_elems: u32, elem_size: u32 } { + return .{ .num_elems = switch (self.num_elems) { + else => self.num_elems, + none => std.math.maxInt(u32), + }, .elem_size = self.elem_size }; + } + }; + + pub const Memory = packed struct(u32) { + argmem: Effect = .none, + inaccessiblemem: Effect = .none, + other: Effect = .none, + _: u26 = 0, + + pub const Effect = enum(u2) { none, read, write, readwrite }; + + fn all(effect: Effect) Memory { + return .{ .argmem = effect, .inaccessiblemem = effect, .other = effect }; + } + }; + + pub const UwTable = enum(u32) { + none, + sync, + @"async", + + pub const default = UwTable.@"async"; + }; + + pub const VScaleRange = packed struct(u32) { + min: Alignment, + max: Alignment, + _: u20 = 0, + + fn toLlvm(self: VScaleRange) packed struct(u64) { max: u32, min: u32 } { + return .{ + .max = @intCast(self.max.toByteUnits() orelse 0), + .min = @intCast(self.min.toByteUnits().?), + }; + } + }; + + pub fn getKind(self: Attribute) Kind { + return switch (self) { + else => self, + .string => |string_attr| Kind.fromString(string_attr.kind), + }; + } + + const Storage = extern struct { + kind: Kind, + value: u32, + }; + + fn toStorage(self: Attribute) Storage { + return switch (self) { + inline else => |value, tag| .{ .kind = @as(Kind, self), .value = switch (@TypeOf(value)) { + void => 0, + u32 => value, + Alignment, String, Type, UwTable => @intFromEnum(value), + AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(value), + else => @compileError("bad payload type: " ++ @tagName(tag) ++ @typeName(@TypeOf(value))), + } }, + .string => |string_attr| .{ + .kind = Kind.fromString(string_attr.kind), + .value = @intFromEnum(string_attr.value), + }, + .none => unreachable, + }; + } +}; + +pub const Attributes = enum(u32) { + none, + _, + + pub fn slice(self: Attributes, builder: *const Builder) []const Attribute.Index { + const start = builder.attributes_indices.items[@intFromEnum(self)]; + const end = builder.attributes_indices.items[@intFromEnum(self) + 1]; + return @ptrCast(builder.attributes_extra.items[start..end]); + } + + const FormatData = struct { + attributes: Attributes, + builder: *const Builder, + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + fmt_opts: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{ + .attribute_index = attribute_index, + .builder = data.builder, + }, fmt_str, fmt_opts, writer); + } + pub fn fmt(self: Attributes, builder: *const Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .attributes = self, .builder = builder } }; + } +}; + +pub const FunctionAttributes = enum(u32) { + none, + _, + + const function_index = 0; + const return_index = 1; + const params_index = 2; + + pub const Wip = struct { + maps: Maps = .{}, + + const Map = std.AutoArrayHashMapUnmanaged(Attribute.Kind, Attribute.Index); + const Maps = std.ArrayListUnmanaged(Map); + + pub fn deinit(self: *Wip, builder: *const Builder) void { + for (self.maps.items) |*map| map.deinit(builder.gpa); + self.maps.deinit(builder.gpa); + self.* = undefined; + } + + pub fn addFnAttr(self: *Wip, attribute: Attribute, builder: *Builder) Allocator.Error!void { + try self.addAttr(function_index, attribute, builder); + } + + pub fn addFnAttrIndex( + self: *Wip, + attribute_index: Attribute.Index, + builder: *const Builder, + ) Allocator.Error!void { + try self.addAttrIndex(function_index, attribute_index, builder); + } + + pub fn removeFnAttr(self: *Wip, attribute_kind: Attribute.Kind) Allocator.Error!bool { + return self.removeAttr(function_index, attribute_kind); + } + + pub fn addRetAttr(self: *Wip, attribute: Attribute, builder: *Builder) Allocator.Error!void { + try self.addAttr(return_index, attribute, builder); + } + + pub fn addRetAttrIndex( + self: *Wip, + attribute_index: Attribute.Index, + builder: *const Builder, + ) Allocator.Error!void { + try self.addAttrIndex(return_index, attribute_index, builder); + } + + pub fn removeRetAttr(self: *Wip, attribute_kind: Attribute.Kind) Allocator.Error!bool { + return self.removeAttr(return_index, attribute_kind); + } + + pub fn addParamAttr( + self: *Wip, + param_index: usize, + attribute: Attribute, + builder: *Builder, + ) Allocator.Error!void { + try self.addAttr(params_index + param_index, attribute, builder); + } + + pub fn addParamAttrIndex( + self: *Wip, + param_index: usize, + attribute_index: Attribute.Index, + builder: *const Builder, + ) Allocator.Error!void { + try self.addAttrIndex(params_index + param_index, attribute_index, builder); + } + + pub fn removeParamAttr( + self: *Wip, + param_index: usize, + attribute_kind: Attribute.Kind, + ) Allocator.Error!bool { + return self.removeAttr(params_index + param_index, attribute_kind); + } + + pub fn finish(self: *const Wip, builder: *Builder) Allocator.Error!FunctionAttributes { + const attributes = try builder.gpa.alloc(Attributes, self.maps.items.len); + defer builder.gpa.free(attributes); + for (attributes, self.maps.items) |*attribute, map| + attribute.* = try builder.attrs(map.values()); + return builder.fnAttrs(attributes); + } + + fn addAttr( + self: *Wip, + index: usize, + attribute: Attribute, + builder: *Builder, + ) Allocator.Error!void { + const map = try self.getOrPutMap(builder.gpa, index); + try map.put(builder.gpa, attribute.getKind(), try builder.attr(attribute)); + } + + fn addAttrIndex( + self: *Wip, + index: usize, + attribute_index: Attribute.Index, + builder: *const Builder, + ) Allocator.Error!void { + const map = try self.getOrPutMap(builder.gpa, index); + try map.put(builder.gpa, attribute_index.getKind(builder), attribute_index); + } + + fn removeAttr(self: *Wip, index: usize, attribute_kind: Attribute.Kind) Allocator.Error!bool { + const map = self.getMap(index) orelse return false; + return map.swapRemove(attribute_kind); + } + + fn getOrPutMap(self: *Wip, allocator: Allocator, index: usize) Allocator.Error!*Map { + if (index >= self.maps.items.len) + try self.maps.appendNTimes(allocator, .{}, index + 1 - self.maps.items.len); + return &self.maps.items[index]; + } + + fn getMap(self: *Wip, index: usize) ?*Map { + return if (index >= self.maps.items.len) null else &self.maps.items[index]; + } + + fn ensureTotalLength(self: *Wip, new_len: usize) Allocator.Error!void { + try self.maps.appendNTimes( + .{}, + std.math.sub(usize, new_len, self.maps.items.len) catch return, + ); + } + }; + + pub fn func(self: FunctionAttributes, builder: *const Builder) Attributes { + return self.get(function_index, builder); + } + + pub fn ret(self: FunctionAttributes, builder: *const Builder) Attributes { + return self.get(return_index, builder); + } + + pub fn param(self: FunctionAttributes, param_index: usize, builder: *const Builder) Attributes { + return self.get(params_index + param_index, builder); + } + + pub fn toWip(self: FunctionAttributes, builder: *const Builder) Allocator.Error!Wip { + var wip: Wip = .{}; + errdefer wip.deinit(builder); + const attributes_slice = self.slice(builder); + try wip.maps.ensureTotalCapacityPrecise(builder.gpa, attributes_slice.len); + for (attributes_slice) |attributes| { + const map = wip.maps.addOneAssumeCapacity(); + map.* = .{}; + const attribute_slice = attributes.slice(builder); + try map.ensureTotalCapacity(builder.gpa, attribute_slice.len); + for (attributes.slice(builder)) |attribute| + map.putAssumeCapacityNoClobber(attribute.getKind(builder), attribute); + } + return wip; + } + + fn get(self: FunctionAttributes, index: usize, builder: *const Builder) Attributes { + const attribute_slice = self.slice(builder); + return if (index < attribute_slice.len) attribute_slice[index] else .none; + } + + fn slice(self: FunctionAttributes, builder: *const Builder) []const Attributes { + const start = builder.attributes_indices.items[@intFromEnum(self)]; + const end = builder.attributes_indices.items[@intFromEnum(self) + 1]; + return @ptrCast(builder.attributes_extra.items[start..end]); + } +}; + +pub const Linkage = enum(u4) { + private = 9, + internal = 3, + weak = 1, + weak_odr = 10, + linkonce = 4, + linkonce_odr = 11, + available_externally = 12, + appending = 2, + common = 8, + extern_weak = 7, + external = 0, + + pub fn format( + self: Linkage, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (self != .external) try writer.print(" {s}", .{@tagName(self)}); + } + + fn formatOptional( + data: ?Linkage, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (data) |linkage| try writer.print(" {s}", .{@tagName(linkage)}); + } + pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(formatOptional) { + return .{ .data = self }; + } +}; + +pub const Preemption = enum { + dso_preemptable, + dso_local, + implicit_dso_local, + + pub fn format( + self: Preemption, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (self == .dso_local) try writer.print(" {s}", .{@tagName(self)}); + } +}; + +pub const Visibility = enum(u2) { + default = 0, + hidden = 1, + protected = 2, + + pub fn format( + self: Visibility, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (self != .default) try writer.print(" {s}", .{@tagName(self)}); + } +}; + +pub const DllStorageClass = enum(u2) { + default = 0, + dllimport = 1, + dllexport = 2, + + pub fn format( + self: DllStorageClass, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (self != .default) try writer.print(" {s}", .{@tagName(self)}); + } +}; + +pub const ThreadLocal = enum(u3) { + default = 0, + generaldynamic = 1, + localdynamic = 2, + initialexec = 3, + localexec = 4, + + pub fn format( + self: ThreadLocal, + comptime prefix: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (self == .default) return; + try writer.print("{s}thread_local", .{prefix}); + if (self != .generaldynamic) try writer.print("({s})", .{@tagName(self)}); + } +}; + +pub const Mutability = enum { global, constant }; + +pub const UnnamedAddr = enum(u2) { + default = 0, + unnamed_addr = 1, + local_unnamed_addr = 2, + + pub fn format( + self: UnnamedAddr, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (self != .default) try writer.print(" {s}", .{@tagName(self)}); + } +}; + +pub const AddrSpace = enum(u24) { + default, + _, + + // See llvm/lib/Target/X86/X86.h + pub const x86 = struct { + pub const gs: AddrSpace = @enumFromInt(256); + pub const fs: AddrSpace = @enumFromInt(257); + pub const ss: AddrSpace = @enumFromInt(258); + + pub const ptr32_sptr: AddrSpace = @enumFromInt(270); + pub const ptr32_uptr: AddrSpace = @enumFromInt(271); + pub const ptr64: AddrSpace = @enumFromInt(272); + }; + pub const x86_64 = x86; + + // See llvm/lib/Target/AVR/AVR.h + pub const avr = struct { + pub const data: AddrSpace = @enumFromInt(0); + pub const program: AddrSpace = @enumFromInt(1); + pub const program1: AddrSpace = @enumFromInt(2); + pub const program2: AddrSpace = @enumFromInt(3); + pub const program3: AddrSpace = @enumFromInt(4); + pub const program4: AddrSpace = @enumFromInt(5); + pub const program5: AddrSpace = @enumFromInt(6); + }; + + // See llvm/lib/Target/NVPTX/NVPTX.h + pub const nvptx = struct { + pub const generic: AddrSpace = @enumFromInt(0); + pub const global: AddrSpace = @enumFromInt(1); + pub const constant: AddrSpace = @enumFromInt(2); + pub const shared: AddrSpace = @enumFromInt(3); + pub const param: AddrSpace = @enumFromInt(4); + pub const local: AddrSpace = @enumFromInt(5); + }; + + // See llvm/lib/Target/AMDGPU/AMDGPU.h + pub const amdgpu = struct { + pub const flat: AddrSpace = @enumFromInt(0); + pub const global: AddrSpace = @enumFromInt(1); + pub const region: AddrSpace = @enumFromInt(2); + pub const local: AddrSpace = @enumFromInt(3); + pub const constant: AddrSpace = @enumFromInt(4); + pub const private: AddrSpace = @enumFromInt(5); + pub const constant_32bit: AddrSpace = @enumFromInt(6); + pub const buffer_fat_pointer: AddrSpace = @enumFromInt(7); + pub const buffer_resource: AddrSpace = @enumFromInt(8); + pub const buffer_strided_pointer: AddrSpace = @enumFromInt(9); + pub const param_d: AddrSpace = @enumFromInt(6); + pub const param_i: AddrSpace = @enumFromInt(7); + pub const constant_buffer_0: AddrSpace = @enumFromInt(8); + pub const constant_buffer_1: AddrSpace = @enumFromInt(9); + pub const constant_buffer_2: AddrSpace = @enumFromInt(10); + pub const constant_buffer_3: AddrSpace = @enumFromInt(11); + pub const constant_buffer_4: AddrSpace = @enumFromInt(12); + pub const constant_buffer_5: AddrSpace = @enumFromInt(13); + pub const constant_buffer_6: AddrSpace = @enumFromInt(14); + pub const constant_buffer_7: AddrSpace = @enumFromInt(15); + pub const constant_buffer_8: AddrSpace = @enumFromInt(16); + pub const constant_buffer_9: AddrSpace = @enumFromInt(17); + pub const constant_buffer_10: AddrSpace = @enumFromInt(18); + pub const constant_buffer_11: AddrSpace = @enumFromInt(19); + pub const constant_buffer_12: AddrSpace = @enumFromInt(20); + pub const constant_buffer_13: AddrSpace = @enumFromInt(21); + pub const constant_buffer_14: AddrSpace = @enumFromInt(22); + pub const constant_buffer_15: AddrSpace = @enumFromInt(23); + pub const streamout_register: AddrSpace = @enumFromInt(128); + }; + + pub const spirv = struct { + pub const function: AddrSpace = @enumFromInt(0); + pub const cross_workgroup: AddrSpace = @enumFromInt(1); + pub const uniform_constant: AddrSpace = @enumFromInt(2); + pub const workgroup: AddrSpace = @enumFromInt(3); + pub const generic: AddrSpace = @enumFromInt(4); + pub const device_only_intel: AddrSpace = @enumFromInt(5); + pub const host_only_intel: AddrSpace = @enumFromInt(6); + pub const input: AddrSpace = @enumFromInt(7); + }; + + // See llvm/include/llvm/CodeGen/WasmAddressSpaces.h + pub const wasm = struct { + pub const default: AddrSpace = @enumFromInt(0); + pub const variable: AddrSpace = @enumFromInt(1); + pub const externref: AddrSpace = @enumFromInt(10); + pub const funcref: AddrSpace = @enumFromInt(20); + }; + + pub fn format( + self: AddrSpace, + comptime prefix: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (self != .default) try writer.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) }); + } +}; + +pub const ExternallyInitialized = enum { + default, + externally_initialized, + + pub fn format( + self: ExternallyInitialized, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (self == .default) return; + try writer.writeByte(' '); + try writer.writeAll(@tagName(self)); + } +}; + +pub const Alignment = enum(u6) { + default = std.math.maxInt(u6), + _, + + pub fn fromByteUnits(bytes: u64) Alignment { + if (bytes == 0) return .default; + assert(std.math.isPowerOfTwo(bytes)); + assert(bytes <= 1 << 32); + return @enumFromInt(@ctz(bytes)); + } + + pub fn toByteUnits(self: Alignment) ?u64 { + return if (self == .default) null else @as(u64, 1) << @intFromEnum(self); + } + + pub fn toLlvm(self: Alignment) u6 { + return if (self == .default) 0 else (@intFromEnum(self) + 1); + } + + pub fn format( + self: Alignment, + comptime prefix: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + try writer.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return }); + } +}; + +pub const CallConv = enum(u10) { + ccc, + + fastcc = 8, + coldcc, + ghccc, + + webkit_jscc = 12, + anyregcc, + preserve_mostcc, + preserve_allcc, + swiftcc, + cxx_fast_tlscc, + tailcc, + cfguard_checkcc, + swifttailcc, + + x86_stdcallcc = 64, + x86_fastcallcc, + arm_apcscc, + arm_aapcscc, + arm_aapcs_vfpcc, + msp430_intrcc, + x86_thiscallcc, + ptx_kernel, + ptx_device, + + spir_func = 75, + spir_kernel, + intel_ocl_bicc, + x86_64_sysvcc, + win64cc, + x86_vectorcallcc, + hhvmcc, + hhvm_ccc, + x86_intrcc, + avr_intrcc, + avr_signalcc, + avr_builtincc, + + amdgpu_vs = 87, + amdgpu_gs, + amdgpu_ps, + amdgpu_cs, + amdgpu_kernel, + x86_regcallcc, + amdgpu_hs, + msp430_builtincc, + + amdgpu_ls = 95, + amdgpu_es, + aarch64_vector_pcs, + aarch64_sve_vector_pcs, + + amdgpu_gfx = 100, + + m68k_intrcc, + + aarch64_sme_preservemost_from_x0 = 102, + aarch64_sme_preservemost_from_x2, + + m68k_rtdcc = 106, + + riscv_vectorcallcc = 110, + + _, + + pub const default = CallConv.ccc; + + pub fn format( + self: CallConv, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + switch (self) { + default => {}, + .fastcc, + .coldcc, + .ghccc, + .webkit_jscc, + .anyregcc, + .preserve_mostcc, + .preserve_allcc, + .swiftcc, + .cxx_fast_tlscc, + .tailcc, + .cfguard_checkcc, + .swifttailcc, + .x86_stdcallcc, + .x86_fastcallcc, + .arm_apcscc, + .arm_aapcscc, + .arm_aapcs_vfpcc, + .msp430_intrcc, + .x86_thiscallcc, + .ptx_kernel, + .ptx_device, + .spir_func, + .spir_kernel, + .intel_ocl_bicc, + .x86_64_sysvcc, + .win64cc, + .x86_vectorcallcc, + .hhvmcc, + .hhvm_ccc, + .x86_intrcc, + .avr_intrcc, + .avr_signalcc, + .avr_builtincc, + .amdgpu_vs, + .amdgpu_gs, + .amdgpu_ps, + .amdgpu_cs, + .amdgpu_kernel, + .x86_regcallcc, + .amdgpu_hs, + .msp430_builtincc, + .amdgpu_ls, + .amdgpu_es, + .aarch64_vector_pcs, + .aarch64_sve_vector_pcs, + .amdgpu_gfx, + .m68k_intrcc, + .aarch64_sme_preservemost_from_x0, + .aarch64_sme_preservemost_from_x2, + .m68k_rtdcc, + .riscv_vectorcallcc, + => try writer.print(" {s}", .{@tagName(self)}), + _ => try writer.print(" cc{d}", .{@intFromEnum(self)}), + } + } +}; + +pub const StrtabString = enum(u32) { + none = std.math.maxInt(u31), + empty, + _, + + pub fn isAnon(self: StrtabString) bool { + assert(self != .none); + return self.toIndex() == null; + } + + pub fn slice(self: StrtabString, builder: *const Builder) ?[]const u8 { + const index = self.toIndex() orelse return null; + const start = builder.strtab_string_indices.items[index]; + const end = builder.strtab_string_indices.items[index + 1]; + return builder.strtab_string_bytes.items[start..end]; + } + + const FormatData = struct { + string: StrtabString, + builder: *const Builder, + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_| + @compileError("invalid format string: '" ++ fmt_str ++ "'"); + assert(data.string != .none); + const string_slice = data.string.slice(data.builder) orelse + return writer.print("{d}", .{@intFromEnum(data.string)}); + if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_| + return writer.writeAll(string_slice); + try printEscapedString( + string_slice, + if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_| + .always_quote + else + .quote_unless_valid_identifier, + writer, + ); + } + pub fn fmt(self: StrtabString, builder: *const Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .string = self, .builder = builder } }; + } + + fn fromIndex(index: ?usize) StrtabString { + return @enumFromInt(@as(u32, @intCast((index orelse return .none) + + @intFromEnum(StrtabString.empty)))); + } + + fn toIndex(self: StrtabString) ?usize { + return std.math.sub(u32, @intFromEnum(self), @intFromEnum(StrtabString.empty)) catch null; + } + + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: Adapter, key: []const u8) u32 { + return @truncate(std.hash.Wyhash.hash(0, key)); + } + pub fn eql(ctx: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool { + return std.mem.eql(u8, lhs_key, StrtabString.fromIndex(rhs_index).slice(ctx.builder).?); + } + }; +}; + +pub fn strtabString(self: *Builder, bytes: []const u8) Allocator.Error!StrtabString { + try self.strtab_string_bytes.ensureUnusedCapacity(self.gpa, bytes.len); + try self.strtab_string_indices.ensureUnusedCapacity(self.gpa, 1); + try self.strtab_string_map.ensureUnusedCapacity(self.gpa, 1); + + const gop = self.strtab_string_map.getOrPutAssumeCapacityAdapted(bytes, StrtabString.Adapter{ .builder = self }); + if (!gop.found_existing) { + self.strtab_string_bytes.appendSliceAssumeCapacity(bytes); + self.strtab_string_indices.appendAssumeCapacity(@intCast(self.strtab_string_bytes.items.len)); + } + return StrtabString.fromIndex(gop.index); +} + +pub fn strtabStringIfExists(self: *const Builder, bytes: []const u8) ?StrtabString { + return StrtabString.fromIndex( + self.strtab_string_map.getIndexAdapted(bytes, StrtabString.Adapter{ .builder = self }) orelse return null, + ); +} + +pub fn strtabStringFmt(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) Allocator.Error!StrtabString { + try self.strtab_string_map.ensureUnusedCapacity(self.gpa, 1); + try self.strtab_string_bytes.ensureUnusedCapacity(self.gpa, @intCast(std.fmt.count(fmt_str, fmt_args))); + try self.strtab_string_indices.ensureUnusedCapacity(self.gpa, 1); + return self.strtabStringFmtAssumeCapacity(fmt_str, fmt_args); +} + +pub fn strtabStringFmtAssumeCapacity(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) StrtabString { + self.strtab_string_bytes.writer(undefined).print(fmt_str, fmt_args) catch unreachable; + return self.trailingStrtabStringAssumeCapacity(); +} + +pub fn trailingStrtabString(self: *Builder) Allocator.Error!StrtabString { + try self.strtab_string_indices.ensureUnusedCapacity(self.gpa, 1); + try self.strtab_string_map.ensureUnusedCapacity(self.gpa, 1); + return self.trailingStrtabStringAssumeCapacity(); +} + +pub fn trailingStrtabStringAssumeCapacity(self: *Builder) StrtabString { + const start = self.strtab_string_indices.getLast(); + const bytes: []const u8 = self.strtab_string_bytes.items[start..]; + const gop = self.strtab_string_map.getOrPutAssumeCapacityAdapted(bytes, StrtabString.Adapter{ .builder = self }); + if (gop.found_existing) { + self.strtab_string_bytes.shrinkRetainingCapacity(start); + } else { + self.strtab_string_indices.appendAssumeCapacity(@intCast(self.strtab_string_bytes.items.len)); + } + return StrtabString.fromIndex(gop.index); +} + +pub const Global = struct { + linkage: Linkage = .external, + preemption: Preemption = .dso_preemptable, + visibility: Visibility = .default, + dll_storage_class: DllStorageClass = .default, + unnamed_addr: UnnamedAddr = .default, + addr_space: AddrSpace = .default, + externally_initialized: ExternallyInitialized = .default, + type: Type, + partition: String = .none, + dbg: Metadata = .none, + kind: union(enum) { + alias: Alias.Index, + variable: Variable.Index, + function: Function.Index, + replaced: Global.Index, + }, + + pub const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + + pub fn unwrap(self: Index, builder: *const Builder) Index { + var cur = self; + while (true) { + const replacement = cur.getReplacement(builder); + if (replacement == .none) return cur; + cur = replacement; + } + } + + pub fn eql(self: Index, other: Index, builder: *const Builder) bool { + return self.unwrap(builder) == other.unwrap(builder); + } + + pub fn ptr(self: Index, builder: *Builder) *Global { + return &builder.globals.values()[@intFromEnum(self.unwrap(builder))]; + } + + pub fn ptrConst(self: Index, builder: *const Builder) *const Global { + return &builder.globals.values()[@intFromEnum(self.unwrap(builder))]; + } + + pub fn name(self: Index, builder: *const Builder) StrtabString { + return builder.globals.keys()[@intFromEnum(self.unwrap(builder))]; + } + + pub fn strtab(self: Index, builder: *const Builder) struct { + offset: u32, + size: u32, + } { + const name_index = self.name(builder).toIndex() orelse return .{ + .offset = 0, + .size = 0, + }; + + return .{ + .offset = builder.strtab_string_indices.items[name_index], + .size = builder.strtab_string_indices.items[name_index + 1] - + builder.strtab_string_indices.items[name_index], + }; + } + + pub fn typeOf(self: Index, builder: *const Builder) Type { + return self.ptrConst(builder).type; + } + + pub fn toConst(self: Index) Constant { + return @enumFromInt(@intFromEnum(Constant.first_global) + @intFromEnum(self)); + } + + pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void { + self.ptr(builder).linkage = linkage; + self.updateDsoLocal(builder); + } + + pub fn setVisibility(self: Index, visibility: Visibility, builder: *Builder) void { + self.ptr(builder).visibility = visibility; + self.updateDsoLocal(builder); + } + + pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void { + self.ptr(builder).dll_storage_class = class; + } + + pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void { + self.ptr(builder).unnamed_addr = unnamed_addr; + } + + pub fn setDebugMetadata(self: Index, dbg: Metadata, builder: *Builder) void { + self.ptr(builder).dbg = dbg; + } + + const FormatData = struct { + global: Index, + builder: *const Builder, + }; + fn format( + data: FormatData, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + try writer.print("@{}", .{ + data.global.unwrap(data.builder).name(data.builder).fmt(data.builder), + }); + } + pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .global = self, .builder = builder } }; + } + + pub fn rename(self: Index, new_name: StrtabString, builder: *Builder) Allocator.Error!void { + try builder.ensureUnusedGlobalCapacity(new_name); + self.renameAssumeCapacity(new_name, builder); + } + + pub fn takeName(self: Index, other: Index, builder: *Builder) Allocator.Error!void { + try builder.ensureUnusedGlobalCapacity(.empty); + self.takeNameAssumeCapacity(other, builder); + } + + pub fn replace(self: Index, other: Index, builder: *Builder) Allocator.Error!void { + try builder.ensureUnusedGlobalCapacity(.empty); + self.replaceAssumeCapacity(other, builder); + } + + pub fn delete(self: Index, builder: *Builder) void { + self.ptr(builder).kind = .{ .replaced = .none }; + } + + fn updateDsoLocal(self: Index, builder: *Builder) void { + const self_ptr = self.ptr(builder); + switch (self_ptr.linkage) { + .private, .internal => { + self_ptr.visibility = .default; + self_ptr.dll_storage_class = .default; + self_ptr.preemption = .implicit_dso_local; + }, + .extern_weak => if (self_ptr.preemption == .implicit_dso_local) { + self_ptr.preemption = .dso_local; + }, + else => switch (self_ptr.visibility) { + .default => if (self_ptr.preemption == .implicit_dso_local) { + self_ptr.preemption = .dso_local; + }, + else => self_ptr.preemption = .implicit_dso_local, + }, + } + } + + fn renameAssumeCapacity(self: Index, new_name: StrtabString, builder: *Builder) void { + const old_name = self.name(builder); + if (new_name == old_name) return; + const index = @intFromEnum(self.unwrap(builder)); + _ = builder.addGlobalAssumeCapacity(new_name, builder.globals.values()[index]); + builder.globals.swapRemoveAt(index); + if (!old_name.isAnon()) return; + builder.next_unnamed_global = @enumFromInt(@intFromEnum(builder.next_unnamed_global) - 1); + if (builder.next_unnamed_global == old_name) return; + builder.getGlobal(builder.next_unnamed_global).?.renameAssumeCapacity(old_name, builder); + } + + fn takeNameAssumeCapacity(self: Index, other: Index, builder: *Builder) void { + const other_name = other.name(builder); + other.renameAssumeCapacity(.empty, builder); + self.renameAssumeCapacity(other_name, builder); + } + + fn replaceAssumeCapacity(self: Index, other: Index, builder: *Builder) void { + if (self.eql(other, builder)) return; + builder.next_replaced_global = @enumFromInt(@intFromEnum(builder.next_replaced_global) - 1); + self.renameAssumeCapacity(builder.next_replaced_global, builder); + self.ptr(builder).kind = .{ .replaced = other.unwrap(builder) }; + } + + fn getReplacement(self: Index, builder: *const Builder) Index { + return switch (builder.globals.values()[@intFromEnum(self)].kind) { + .replaced => |replacement| replacement, + else => .none, + }; + } + }; +}; + +pub const Alias = struct { + global: Global.Index, + thread_local: ThreadLocal = .default, + aliasee: Constant = .no_init, + + pub const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + + pub fn ptr(self: Index, builder: *Builder) *Alias { + return &builder.aliases.items[@intFromEnum(self)]; + } + + pub fn ptrConst(self: Index, builder: *const Builder) *const Alias { + return &builder.aliases.items[@intFromEnum(self)]; + } + + pub fn name(self: Index, builder: *const Builder) StrtabString { + return self.ptrConst(builder).global.name(builder); + } + + pub fn rename(self: Index, new_name: StrtabString, builder: *Builder) Allocator.Error!void { + return self.ptrConst(builder).global.rename(new_name, builder); + } + + pub fn typeOf(self: Index, builder: *const Builder) Type { + return self.ptrConst(builder).global.typeOf(builder); + } + + pub fn toConst(self: Index, builder: *const Builder) Constant { + return self.ptrConst(builder).global.toConst(); + } + + pub fn toValue(self: Index, builder: *const Builder) Value { + return self.toConst(builder).toValue(); + } + + pub fn getAliasee(self: Index, builder: *const Builder) Global.Index { + const aliasee = self.ptrConst(builder).aliasee.getBase(builder); + assert(aliasee != .none); + return aliasee; + } + + pub fn setAliasee(self: Index, aliasee: Constant, builder: *Builder) void { + self.ptr(builder).aliasee = aliasee; + } + }; +}; + +pub const Variable = struct { + global: Global.Index, + thread_local: ThreadLocal = .default, + mutability: Mutability = .global, + init: Constant = .no_init, + section: String = .none, + alignment: Alignment = .default, + + pub const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + + pub fn ptr(self: Index, builder: *Builder) *Variable { + return &builder.variables.items[@intFromEnum(self)]; + } + + pub fn ptrConst(self: Index, builder: *const Builder) *const Variable { + return &builder.variables.items[@intFromEnum(self)]; + } + + pub fn name(self: Index, builder: *const Builder) StrtabString { + return self.ptrConst(builder).global.name(builder); + } + + pub fn rename(self: Index, new_name: StrtabString, builder: *Builder) Allocator.Error!void { + return self.ptrConst(builder).global.rename(new_name, builder); + } + + pub fn typeOf(self: Index, builder: *const Builder) Type { + return self.ptrConst(builder).global.typeOf(builder); + } + + pub fn toConst(self: Index, builder: *const Builder) Constant { + return self.ptrConst(builder).global.toConst(); + } + + pub fn toValue(self: Index, builder: *const Builder) Value { + return self.toConst(builder).toValue(); + } + + pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void { + return self.ptrConst(builder).global.setLinkage(linkage, builder); + } + + pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void { + return self.ptrConst(builder).global.setDllStorageClass(class, builder); + } + + pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void { + return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder); + } + + pub fn setThreadLocal(self: Index, thread_local: ThreadLocal, builder: *Builder) void { + self.ptr(builder).thread_local = thread_local; + } + + pub fn setMutability(self: Index, mutability: Mutability, builder: *Builder) void { + self.ptr(builder).mutability = mutability; + } + + pub fn setInitializer( + self: Index, + initializer: Constant, + builder: *Builder, + ) Allocator.Error!void { + if (initializer != .no_init) { + const variable = self.ptrConst(builder); + const global = variable.global.ptr(builder); + const initializer_type = initializer.typeOf(builder); + global.type = initializer_type; + } + self.ptr(builder).init = initializer; + } + + pub fn setSection(self: Index, section: String, builder: *Builder) void { + self.ptr(builder).section = section; + } + + pub fn setAlignment(self: Index, alignment: Alignment, builder: *Builder) void { + self.ptr(builder).alignment = alignment; + } + + pub fn getAlignment(self: Index, builder: *Builder) Alignment { + return self.ptr(builder).alignment; + } + + pub fn setGlobalVariableExpression(self: Index, expression: Metadata, builder: *Builder) void { + self.ptrConst(builder).global.setDebugMetadata(expression, builder); + } + }; +}; + +pub const Intrinsic = enum { + // Variable Argument Handling + va_start, + va_end, + va_copy, + + // Code Generator + returnaddress, + addressofreturnaddress, + sponentry, + frameaddress, + prefetch, + @"thread.pointer", + + // Standard C/C++ Library + abs, + smax, + smin, + umax, + umin, + memcpy, + @"memcpy.inline", + memmove, + memset, + @"memset.inline", + sqrt, + powi, + sin, + cos, + pow, + exp, + exp10, + exp2, + ldexp, + frexp, + log, + log10, + log2, + fma, + fabs, + minnum, + maxnum, + minimum, + maximum, + copysign, + floor, + ceil, + trunc, + rint, + nearbyint, + round, + roundeven, + lround, + llround, + lrint, + llrint, + + // Bit Manipulation + bitreverse, + bswap, + ctpop, + ctlz, + cttz, + fshl, + fshr, + + // Arithmetic with Overflow + @"sadd.with.overflow", + @"uadd.with.overflow", + @"ssub.with.overflow", + @"usub.with.overflow", + @"smul.with.overflow", + @"umul.with.overflow", + + // Saturation Arithmetic + @"sadd.sat", + @"uadd.sat", + @"ssub.sat", + @"usub.sat", + @"sshl.sat", + @"ushl.sat", + + // Fixed Point Arithmetic + @"smul.fix", + @"umul.fix", + @"smul.fix.sat", + @"umul.fix.sat", + @"sdiv.fix", + @"udiv.fix", + @"sdiv.fix.sat", + @"udiv.fix.sat", + + // Specialised Arithmetic + canonicalize, + fmuladd, + + // Vector Reduction + @"vector.reduce.add", + @"vector.reduce.fadd", + @"vector.reduce.mul", + @"vector.reduce.fmul", + @"vector.reduce.and", + @"vector.reduce.or", + @"vector.reduce.xor", + @"vector.reduce.smax", + @"vector.reduce.smin", + @"vector.reduce.umax", + @"vector.reduce.umin", + @"vector.reduce.fmax", + @"vector.reduce.fmin", + @"vector.reduce.fmaximum", + @"vector.reduce.fminimum", + @"vector.insert", + @"vector.extract", + + // Floating-Point Test + @"is.fpclass", + + // General + @"var.annotation", + @"ptr.annotation", + annotation, + @"codeview.annotation", + trap, + debugtrap, + ubsantrap, + stackprotector, + stackguard, + objectsize, + expect, + @"expect.with.probability", + assume, + @"ssa.copy", + @"type.test", + @"type.checked.load", + @"type.checked.load.relative", + @"arithmetic.fence", + donothing, + @"load.relative", + sideeffect, + @"is.constant", + ptrmask, + @"threadlocal.address", + vscale, + + // Debug + @"dbg.declare", + @"dbg.value", + + // AMDGPU + @"amdgcn.workitem.id.x", + @"amdgcn.workitem.id.y", + @"amdgcn.workitem.id.z", + @"amdgcn.workgroup.id.x", + @"amdgcn.workgroup.id.y", + @"amdgcn.workgroup.id.z", + @"amdgcn.dispatch.ptr", + + // NVPTX + @"nvvm.read.ptx.sreg.tid.x", + @"nvvm.read.ptx.sreg.tid.y", + @"nvvm.read.ptx.sreg.tid.z", + @"nvvm.read.ptx.sreg.ntid.x", + @"nvvm.read.ptx.sreg.ntid.y", + @"nvvm.read.ptx.sreg.ntid.z", + @"nvvm.read.ptx.sreg.ctaid.x", + @"nvvm.read.ptx.sreg.ctaid.y", + @"nvvm.read.ptx.sreg.ctaid.z", + + // WebAssembly + @"wasm.memory.size", + @"wasm.memory.grow", + + const Signature = struct { + ret_len: u8, + params: []const Parameter, + attrs: []const Attribute = &.{}, + + const Parameter = struct { + kind: Kind, + attrs: []const Attribute = &.{}, + + const Kind = union(enum) { + type: Type, + overloaded, + matches: u8, + matches_scalar: u8, + matches_changed_scalar: struct { + index: u8, + scalar: Type, + }, + }; + }; + }; + + const signatures = std.enums.EnumArray(Intrinsic, Signature).init(.{ + .va_start = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, + }, + .va_end = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, + }, + .va_copy = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, + }, + + .returnaddress = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .ptr } }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .addressofreturnaddress = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .sponentry = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .frameaddress = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .prefetch = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .overloaded, .attrs = &.{ .nocapture, .readonly } }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.readwrite) } }, + }, + .@"thread.pointer" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .ptr } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .abs = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .smax = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .smin = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .umax = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .umin = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .memcpy = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .overloaded, .attrs = &.{ .@"noalias", .nocapture, .writeonly } }, + .{ .kind = .overloaded, .attrs = &.{ .@"noalias", .nocapture, .readonly } }, + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nounwind, .willreturn, .{ .memory = .{ .argmem = .readwrite } } }, + }, + .@"memcpy.inline" = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .overloaded, .attrs = &.{ .@"noalias", .nocapture, .writeonly } }, + .{ .kind = .overloaded, .attrs = &.{ .@"noalias", .nocapture, .readonly } }, + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nounwind, .willreturn, .{ .memory = .{ .argmem = .readwrite } } }, + }, + .memmove = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .overloaded, .attrs = &.{ .nocapture, .writeonly } }, + .{ .kind = .overloaded, .attrs = &.{ .nocapture, .readonly } }, + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nounwind, .willreturn, .{ .memory = .{ .argmem = .readwrite } } }, + }, + .memset = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .overloaded, .attrs = &.{ .nocapture, .writeonly } }, + .{ .kind = .{ .type = .i8 } }, + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nounwind, .willreturn, .{ .memory = .{ .argmem = .write } } }, + }, + .@"memset.inline" = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .overloaded, .attrs = &.{ .nocapture, .writeonly } }, + .{ .kind = .{ .type = .i8 } }, + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nounwind, .willreturn, .{ .memory = .{ .argmem = .write } } }, + }, + .sqrt = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .powi = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .sin = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .cos = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .pow = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .exp = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .exp2 = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .exp10 = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .ldexp = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .frexp = .{ + .ret_len = 2, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .log = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .log10 = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .log2 = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .fma = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .fabs = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .minnum = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .maxnum = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .minimum = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .maximum = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .copysign = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .floor = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .ceil = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .trunc = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .rint = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .nearbyint = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .round = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .roundeven = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .lround = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .llround = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .lrint = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .llrint = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .bitreverse = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .bswap = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .ctpop = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .ctlz = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .cttz = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .fshl = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .fshr = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .@"sadd.with.overflow" = .{ + .ret_len = 2, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"uadd.with.overflow" = .{ + .ret_len = 2, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"ssub.with.overflow" = .{ + .ret_len = 2, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"usub.with.overflow" = .{ + .ret_len = 2, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"smul.with.overflow" = .{ + .ret_len = 2, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"umul.with.overflow" = .{ + .ret_len = 2, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .@"sadd.sat" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"uadd.sat" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"ssub.sat" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"usub.sat" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"sshl.sat" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"ushl.sat" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .@"smul.fix" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"umul.fix" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"smul.fix.sat" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"umul.fix.sat" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"sdiv.fix" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"udiv.fix" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"sdiv.fix.sat" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"udiv.fix.sat" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .canonicalize = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .fmuladd = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .@"vector.reduce.add" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.fadd" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 2 } }, + .{ .kind = .{ .matches_scalar = 2 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.mul" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.fmul" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 2 } }, + .{ .kind = .{ .matches_scalar = 2 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.and" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.or" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.xor" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.smax" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.smin" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.umax" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.umin" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.fmax" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.fmin" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.fmaximum" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.reduce.fminimum" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_scalar = 1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.insert" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i64 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"vector.extract" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i64 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .@"is.fpclass" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .matches_changed_scalar = .{ .index = 1, .scalar = .i1 } } }, + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .@"var.annotation" = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 1 } }, + .{ .kind = .{ .type = .i32 } }, + .{ .kind = .{ .matches = 1 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .readwrite } } }, + }, + .@"ptr.annotation" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 2 } }, + .{ .kind = .{ .type = .i32 } }, + .{ .kind = .{ .matches = 2 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .readwrite } } }, + }, + .annotation = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 2 } }, + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .readwrite } } }, + }, + .@"codeview.annotation" = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .{ .type = .metadata } }, + }, + .attrs = &.{ .nocallback, .noduplicate, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .readwrite } } }, + }, + .trap = .{ + .ret_len = 0, + .params = &.{}, + .attrs = &.{ .cold, .noreturn, .nounwind, .{ .memory = .{ .inaccessiblemem = .write } } }, + }, + .debugtrap = .{ + .ret_len = 0, + .params = &.{}, + .attrs = &.{.nounwind}, + }, + .ubsantrap = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .{ .type = .i8 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .cold, .noreturn, .nounwind }, + }, + .stackprotector = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .{ .type = .ptr } }, + .{ .kind = .{ .type = .ptr } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, + }, + .stackguard = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .ptr } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, + }, + .objectsize = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, + .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, + .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .expect = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"expect.with.probability" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .{ .type = .double }, .attrs = &.{.immarg} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .assume = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .{ .type = .i1 }, .attrs = &.{.noundef} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .write } } }, + }, + .@"ssa.copy" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 }, .attrs = &.{.returned} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"type.test" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i1 } }, + .{ .kind = .{ .type = .ptr } }, + .{ .kind = .{ .type = .metadata } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"type.checked.load" = .{ + .ret_len = 2, + .params = &.{ + .{ .kind = .{ .type = .ptr } }, + .{ .kind = .{ .type = .i1 } }, + .{ .kind = .{ .type = .ptr } }, + .{ .kind = .{ .type = .i32 } }, + .{ .kind = .{ .type = .metadata } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"type.checked.load.relative" = .{ + .ret_len = 2, + .params = &.{ + .{ .kind = .{ .type = .ptr } }, + .{ .kind = .{ .type = .i1 } }, + .{ .kind = .{ .type = .ptr } }, + .{ .kind = .{ .type = .i32 } }, + .{ .kind = .{ .type = .metadata } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"arithmetic.fence" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .donothing = .{ + .ret_len = 0, + .params = &.{}, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"load.relative" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .ptr } }, + .{ .kind = .{ .type = .ptr } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .argmem = .read } } }, + }, + .sideeffect = .{ + .ret_len = 0, + .params = &.{}, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .readwrite } } }, + }, + .@"is.constant" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i1 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .convergent, .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .ptrmask = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .matches = 0 } }, + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"threadlocal.address" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded, .attrs = &.{.nonnull} }, + .{ .kind = .{ .matches = 0 }, .attrs = &.{.nonnull} }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .vscale = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .@"dbg.declare" = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .{ .type = .metadata } }, + .{ .kind = .{ .type = .metadata } }, + .{ .kind = .{ .type = .metadata } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"dbg.value" = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .{ .type = .metadata } }, + .{ .kind = .{ .type = .metadata } }, + .{ .kind = .{ .type = .metadata } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .@"amdgcn.workitem.id.x" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"amdgcn.workitem.id.y" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"amdgcn.workitem.id.z" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"amdgcn.workgroup.id.x" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"amdgcn.workgroup.id.y" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"amdgcn.workgroup.id.z" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"amdgcn.dispatch.ptr" = .{ + .ret_len = 1, + .params = &.{ + .{ + .kind = .{ .type = Type.ptr_amdgpu_constant }, + .attrs = &.{.{ .@"align" = Builder.Alignment.fromByteUnits(4) }}, + }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + + .@"nvvm.read.ptx.sreg.tid.x" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nounwind, .readnone }, + }, + .@"nvvm.read.ptx.sreg.tid.y" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nounwind, .readnone }, + }, + .@"nvvm.read.ptx.sreg.tid.z" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nounwind, .readnone }, + }, + + .@"nvvm.read.ptx.sreg.ntid.x" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nounwind, .readnone }, + }, + .@"nvvm.read.ptx.sreg.ntid.y" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nounwind, .readnone }, + }, + .@"nvvm.read.ptx.sreg.ntid.z" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nounwind, .readnone }, + }, + + .@"nvvm.read.ptx.sreg.ctaid.x" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nounwind, .readnone }, + }, + .@"nvvm.read.ptx.sreg.ctaid.y" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nounwind, .readnone }, + }, + .@"nvvm.read.ptx.sreg.ctaid.z" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nounwind, .readnone }, + }, + + .@"wasm.memory.size" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i32 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"wasm.memory.grow" = .{ + .ret_len = 1, + .params = &.{ + .{ .kind = .overloaded }, + .{ .kind = .{ .type = .i32 } }, + .{ .kind = .{ .matches = 0 } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, + }, + }); +}; + +pub const Function = struct { + global: Global.Index, + call_conv: CallConv = CallConv.default, + attributes: FunctionAttributes = .none, + section: String = .none, + alignment: Alignment = .default, + blocks: []const Block = &.{}, + instructions: std.MultiArrayList(Instruction) = .{}, + names: [*]const String = &[0]String{}, + value_indices: [*]const u32 = &[0]u32{}, + strip: bool, + debug_locations: std.AutoHashMapUnmanaged(Instruction.Index, DebugLocation) = .empty, + debug_values: []const Instruction.Index = &.{}, + extra: []const u32 = &.{}, + + pub const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + + pub fn ptr(self: Index, builder: *Builder) *Function { + return &builder.functions.items[@intFromEnum(self)]; + } + + pub fn ptrConst(self: Index, builder: *const Builder) *const Function { + return &builder.functions.items[@intFromEnum(self)]; + } + + pub fn name(self: Index, builder: *const Builder) StrtabString { + return self.ptrConst(builder).global.name(builder); + } + + pub fn rename(self: Index, new_name: StrtabString, builder: *Builder) Allocator.Error!void { + return self.ptrConst(builder).global.rename(new_name, builder); + } + + pub fn typeOf(self: Index, builder: *const Builder) Type { + return self.ptrConst(builder).global.typeOf(builder); + } + + pub fn toConst(self: Index, builder: *const Builder) Constant { + return self.ptrConst(builder).global.toConst(); + } + + pub fn toValue(self: Index, builder: *const Builder) Value { + return self.toConst(builder).toValue(); + } + + pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void { + return self.ptrConst(builder).global.setLinkage(linkage, builder); + } + + pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void { + return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder); + } + + pub fn setCallConv(self: Index, call_conv: CallConv, builder: *Builder) void { + self.ptr(builder).call_conv = call_conv; + } + + pub fn setAttributes( + self: Index, + new_function_attributes: FunctionAttributes, + builder: *Builder, + ) void { + self.ptr(builder).attributes = new_function_attributes; + } + + pub fn setSection(self: Index, section: String, builder: *Builder) void { + self.ptr(builder).section = section; + } + + pub fn setAlignment(self: Index, alignment: Alignment, builder: *Builder) void { + self.ptr(builder).alignment = alignment; + } + + pub fn setSubprogram(self: Index, subprogram: Metadata, builder: *Builder) void { + self.ptrConst(builder).global.setDebugMetadata(subprogram, builder); + } + }; + + pub const Block = struct { + instruction: Instruction.Index, + + pub const Index = WipFunction.Block.Index; + }; + + pub const Instruction = struct { + tag: Tag, + data: u32, + + pub const Tag = enum(u8) { + add, + @"add nsw", + @"add nuw", + @"add nuw nsw", + addrspacecast, + alloca, + @"alloca inalloca", + @"and", + arg, + ashr, + @"ashr exact", + atomicrmw, + bitcast, + block, + br, + br_cond, + call, + @"call fast", + cmpxchg, + @"cmpxchg weak", + extractelement, + extractvalue, + fadd, + @"fadd fast", + @"fcmp false", + @"fcmp fast false", + @"fcmp fast oeq", + @"fcmp fast oge", + @"fcmp fast ogt", + @"fcmp fast ole", + @"fcmp fast olt", + @"fcmp fast one", + @"fcmp fast ord", + @"fcmp fast true", + @"fcmp fast ueq", + @"fcmp fast uge", + @"fcmp fast ugt", + @"fcmp fast ule", + @"fcmp fast ult", + @"fcmp fast une", + @"fcmp fast uno", + @"fcmp oeq", + @"fcmp oge", + @"fcmp ogt", + @"fcmp ole", + @"fcmp olt", + @"fcmp one", + @"fcmp ord", + @"fcmp true", + @"fcmp ueq", + @"fcmp uge", + @"fcmp ugt", + @"fcmp ule", + @"fcmp ult", + @"fcmp une", + @"fcmp uno", + fdiv, + @"fdiv fast", + fence, + fmul, + @"fmul fast", + fneg, + @"fneg fast", + fpext, + fptosi, + fptoui, + fptrunc, + frem, + @"frem fast", + fsub, + @"fsub fast", + getelementptr, + @"getelementptr inbounds", + @"icmp eq", + @"icmp ne", + @"icmp sge", + @"icmp sgt", + @"icmp sle", + @"icmp slt", + @"icmp uge", + @"icmp ugt", + @"icmp ule", + @"icmp ult", + indirectbr, + insertelement, + insertvalue, + inttoptr, + load, + @"load atomic", + lshr, + @"lshr exact", + mul, + @"mul nsw", + @"mul nuw", + @"mul nuw nsw", + @"musttail call", + @"musttail call fast", + @"notail call", + @"notail call fast", + @"or", + phi, + @"phi fast", + ptrtoint, + ret, + @"ret void", + sdiv, + @"sdiv exact", + select, + @"select fast", + sext, + shl, + @"shl nsw", + @"shl nuw", + @"shl nuw nsw", + shufflevector, + sitofp, + srem, + store, + @"store atomic", + sub, + @"sub nsw", + @"sub nuw", + @"sub nuw nsw", + @"switch", + @"tail call", + @"tail call fast", + trunc, + udiv, + @"udiv exact", + urem, + uitofp, + @"unreachable", + va_arg, + xor, + zext, + + pub fn toBinaryOpcode(self: Tag) BinaryOpcode { + return switch (self) { + .add, + .@"add nsw", + .@"add nuw", + .@"add nuw nsw", + .fadd, + .@"fadd fast", + => .add, + .sub, + .@"sub nsw", + .@"sub nuw", + .@"sub nuw nsw", + .fsub, + .@"fsub fast", + => .sub, + .sdiv, + .@"sdiv exact", + .fdiv, + .@"fdiv fast", + => .sdiv, + .fmul, + .@"fmul fast", + .mul, + .@"mul nsw", + .@"mul nuw", + .@"mul nuw nsw", + => .mul, + .srem, + .frem, + .@"frem fast", + => .srem, + .udiv, + .@"udiv exact", + => .udiv, + .shl, + .@"shl nsw", + .@"shl nuw", + .@"shl nuw nsw", + => .shl, + .lshr, + .@"lshr exact", + => .lshr, + .ashr, + .@"ashr exact", + => .ashr, + .@"and" => .@"and", + .@"or" => .@"or", + .xor => .xor, + .urem => .urem, + else => unreachable, + }; + } + + pub fn toCastOpcode(self: Tag) CastOpcode { + return switch (self) { + .trunc => .trunc, + .zext => .zext, + .sext => .sext, + .fptoui => .fptoui, + .fptosi => .fptosi, + .uitofp => .uitofp, + .sitofp => .sitofp, + .fptrunc => .fptrunc, + .fpext => .fpext, + .ptrtoint => .ptrtoint, + .inttoptr => .inttoptr, + .bitcast => .bitcast, + .addrspacecast => .addrspacecast, + else => unreachable, + }; + } + + pub fn toCmpPredicate(self: Tag) CmpPredicate { + return switch (self) { + .@"fcmp false", + .@"fcmp fast false", + => .fcmp_false, + .@"fcmp oeq", + .@"fcmp fast oeq", + => .fcmp_oeq, + .@"fcmp oge", + .@"fcmp fast oge", + => .fcmp_oge, + .@"fcmp ogt", + .@"fcmp fast ogt", + => .fcmp_ogt, + .@"fcmp ole", + .@"fcmp fast ole", + => .fcmp_ole, + .@"fcmp olt", + .@"fcmp fast olt", + => .fcmp_olt, + .@"fcmp one", + .@"fcmp fast one", + => .fcmp_one, + .@"fcmp ord", + .@"fcmp fast ord", + => .fcmp_ord, + .@"fcmp true", + .@"fcmp fast true", + => .fcmp_true, + .@"fcmp ueq", + .@"fcmp fast ueq", + => .fcmp_ueq, + .@"fcmp uge", + .@"fcmp fast uge", + => .fcmp_uge, + .@"fcmp ugt", + .@"fcmp fast ugt", + => .fcmp_ugt, + .@"fcmp ule", + .@"fcmp fast ule", + => .fcmp_ule, + .@"fcmp ult", + .@"fcmp fast ult", + => .fcmp_ult, + .@"fcmp une", + .@"fcmp fast une", + => .fcmp_une, + .@"fcmp uno", + .@"fcmp fast uno", + => .fcmp_uno, + .@"icmp eq" => .icmp_eq, + .@"icmp ne" => .icmp_ne, + .@"icmp sge" => .icmp_sge, + .@"icmp sgt" => .icmp_sgt, + .@"icmp sle" => .icmp_sle, + .@"icmp slt" => .icmp_slt, + .@"icmp uge" => .icmp_uge, + .@"icmp ugt" => .icmp_ugt, + .@"icmp ule" => .icmp_ule, + .@"icmp ult" => .icmp_ult, + else => unreachable, + }; + } + }; + + pub const Index = enum(u32) { + none = std.math.maxInt(u31), + _, + + pub fn name(self: Instruction.Index, function: *const Function) String { + return function.names[@intFromEnum(self)]; + } + + pub fn valueIndex(self: Instruction.Index, function: *const Function) u32 { + return function.value_indices[@intFromEnum(self)]; + } + + pub fn toValue(self: Instruction.Index) Value { + return @enumFromInt(@intFromEnum(self)); + } + + pub fn isTerminatorWip(self: Instruction.Index, wip: *const WipFunction) bool { + return switch (wip.instructions.items(.tag)[@intFromEnum(self)]) { + .br, + .br_cond, + .indirectbr, + .ret, + .@"ret void", + .@"switch", + .@"unreachable", + => true, + else => false, + }; + } + + pub fn hasResultWip(self: Instruction.Index, wip: *const WipFunction) bool { + return switch (wip.instructions.items(.tag)[@intFromEnum(self)]) { + .br, + .br_cond, + .fence, + .indirectbr, + .ret, + .@"ret void", + .store, + .@"store atomic", + .@"switch", + .@"unreachable", + .block, + => false, + .call, + .@"call fast", + .@"musttail call", + .@"musttail call fast", + .@"notail call", + .@"notail call fast", + .@"tail call", + .@"tail call fast", + => self.typeOfWip(wip) != .void, + else => true, + }; + } + + pub fn typeOfWip(self: Instruction.Index, wip: *const WipFunction) Type { + const instruction = wip.instructions.get(@intFromEnum(self)); + return switch (instruction.tag) { + .add, + .@"add nsw", + .@"add nuw", + .@"add nuw nsw", + .@"and", + .ashr, + .@"ashr exact", + .fadd, + .@"fadd fast", + .fdiv, + .@"fdiv fast", + .fmul, + .@"fmul fast", + .frem, + .@"frem fast", + .fsub, + .@"fsub fast", + .lshr, + .@"lshr exact", + .mul, + .@"mul nsw", + .@"mul nuw", + .@"mul nuw nsw", + .@"or", + .sdiv, + .@"sdiv exact", + .shl, + .@"shl nsw", + .@"shl nuw", + .@"shl nuw nsw", + .srem, + .sub, + .@"sub nsw", + .@"sub nuw", + .@"sub nuw nsw", + .udiv, + .@"udiv exact", + .urem, + .xor, + => wip.extraData(Binary, instruction.data).lhs.typeOfWip(wip), + .addrspacecast, + .bitcast, + .fpext, + .fptosi, + .fptoui, + .fptrunc, + .inttoptr, + .ptrtoint, + .sext, + .sitofp, + .trunc, + .uitofp, + .zext, + => wip.extraData(Cast, instruction.data).type, + .alloca, + .@"alloca inalloca", + => wip.builder.ptrTypeAssumeCapacity( + wip.extraData(Alloca, instruction.data).info.addr_space, + ), + .arg => wip.function.typeOf(wip.builder) + .functionParameters(wip.builder)[instruction.data], + .atomicrmw => wip.extraData(AtomicRmw, instruction.data).val.typeOfWip(wip), + .block => .label, + .br, + .br_cond, + .fence, + .indirectbr, + .ret, + .@"ret void", + .store, + .@"store atomic", + .@"switch", + .@"unreachable", + => .none, + .call, + .@"call fast", + .@"musttail call", + .@"musttail call fast", + .@"notail call", + .@"notail call fast", + .@"tail call", + .@"tail call fast", + => wip.extraData(Call, instruction.data).ty.functionReturn(wip.builder), + .cmpxchg, + .@"cmpxchg weak", + => wip.builder.structTypeAssumeCapacity(.normal, &.{ + wip.extraData(CmpXchg, instruction.data).cmp.typeOfWip(wip), + .i1, + }), + .extractelement => wip.extraData(ExtractElement, instruction.data) + .val.typeOfWip(wip).childType(wip.builder), + .extractvalue => { + var extra = wip.extraDataTrail(ExtractValue, instruction.data); + const indices = extra.trail.next(extra.data.indices_len, u32, wip); + return extra.data.val.typeOfWip(wip).childTypeAt(indices, wip.builder); + }, + .@"fcmp false", + .@"fcmp fast false", + .@"fcmp fast oeq", + .@"fcmp fast oge", + .@"fcmp fast ogt", + .@"fcmp fast ole", + .@"fcmp fast olt", + .@"fcmp fast one", + .@"fcmp fast ord", + .@"fcmp fast true", + .@"fcmp fast ueq", + .@"fcmp fast uge", + .@"fcmp fast ugt", + .@"fcmp fast ule", + .@"fcmp fast ult", + .@"fcmp fast une", + .@"fcmp fast uno", + .@"fcmp oeq", + .@"fcmp oge", + .@"fcmp ogt", + .@"fcmp ole", + .@"fcmp olt", + .@"fcmp one", + .@"fcmp ord", + .@"fcmp true", + .@"fcmp ueq", + .@"fcmp uge", + .@"fcmp ugt", + .@"fcmp ule", + .@"fcmp ult", + .@"fcmp une", + .@"fcmp uno", + .@"icmp eq", + .@"icmp ne", + .@"icmp sge", + .@"icmp sgt", + .@"icmp sle", + .@"icmp slt", + .@"icmp uge", + .@"icmp ugt", + .@"icmp ule", + .@"icmp ult", + => wip.extraData(Binary, instruction.data).lhs.typeOfWip(wip) + .changeScalarAssumeCapacity(.i1, wip.builder), + .fneg, + .@"fneg fast", + => @as(Value, @enumFromInt(instruction.data)).typeOfWip(wip), + .getelementptr, + .@"getelementptr inbounds", + => { + var extra = wip.extraDataTrail(GetElementPtr, instruction.data); + const indices = extra.trail.next(extra.data.indices_len, Value, wip); + const base_ty = extra.data.base.typeOfWip(wip); + if (!base_ty.isVector(wip.builder)) for (indices) |index| { + const index_ty = index.typeOfWip(wip); + if (!index_ty.isVector(wip.builder)) continue; + return index_ty.changeScalarAssumeCapacity(base_ty, wip.builder); + }; + return base_ty; + }, + .insertelement => wip.extraData(InsertElement, instruction.data).val.typeOfWip(wip), + .insertvalue => wip.extraData(InsertValue, instruction.data).val.typeOfWip(wip), + .load, + .@"load atomic", + => wip.extraData(Load, instruction.data).type, + .phi, + .@"phi fast", + => wip.extraData(Phi, instruction.data).type, + .select, + .@"select fast", + => wip.extraData(Select, instruction.data).lhs.typeOfWip(wip), + .shufflevector => { + const extra = wip.extraData(ShuffleVector, instruction.data); + return extra.lhs.typeOfWip(wip).changeLengthAssumeCapacity( + extra.mask.typeOfWip(wip).vectorLen(wip.builder), + wip.builder, + ); + }, + .va_arg => wip.extraData(VaArg, instruction.data).type, + }; + } + + pub fn typeOf( + self: Instruction.Index, + function_index: Function.Index, + builder: *Builder, + ) Type { + const function = function_index.ptrConst(builder); + const instruction = function.instructions.get(@intFromEnum(self)); + return switch (instruction.tag) { + .add, + .@"add nsw", + .@"add nuw", + .@"add nuw nsw", + .@"and", + .ashr, + .@"ashr exact", + .fadd, + .@"fadd fast", + .fdiv, + .@"fdiv fast", + .fmul, + .@"fmul fast", + .frem, + .@"frem fast", + .fsub, + .@"fsub fast", + .lshr, + .@"lshr exact", + .mul, + .@"mul nsw", + .@"mul nuw", + .@"mul nuw nsw", + .@"or", + .sdiv, + .@"sdiv exact", + .shl, + .@"shl nsw", + .@"shl nuw", + .@"shl nuw nsw", + .srem, + .sub, + .@"sub nsw", + .@"sub nuw", + .@"sub nuw nsw", + .udiv, + .@"udiv exact", + .urem, + .xor, + => function.extraData(Binary, instruction.data).lhs.typeOf(function_index, builder), + .addrspacecast, + .bitcast, + .fpext, + .fptosi, + .fptoui, + .fptrunc, + .inttoptr, + .ptrtoint, + .sext, + .sitofp, + .trunc, + .uitofp, + .zext, + => function.extraData(Cast, instruction.data).type, + .alloca, + .@"alloca inalloca", + => builder.ptrTypeAssumeCapacity( + function.extraData(Alloca, instruction.data).info.addr_space, + ), + .arg => function.global.typeOf(builder) + .functionParameters(builder)[instruction.data], + .atomicrmw => function.extraData(AtomicRmw, instruction.data) + .val.typeOf(function_index, builder), + .block => .label, + .br, + .br_cond, + .fence, + .indirectbr, + .ret, + .@"ret void", + .store, + .@"store atomic", + .@"switch", + .@"unreachable", + => .none, + .call, + .@"call fast", + .@"musttail call", + .@"musttail call fast", + .@"notail call", + .@"notail call fast", + .@"tail call", + .@"tail call fast", + => function.extraData(Call, instruction.data).ty.functionReturn(builder), + .cmpxchg, + .@"cmpxchg weak", + => builder.structTypeAssumeCapacity(.normal, &.{ + function.extraData(CmpXchg, instruction.data) + .cmp.typeOf(function_index, builder), + .i1, + }), + .extractelement => function.extraData(ExtractElement, instruction.data) + .val.typeOf(function_index, builder).childType(builder), + .extractvalue => { + var extra = function.extraDataTrail(ExtractValue, instruction.data); + const indices = extra.trail.next(extra.data.indices_len, u32, function); + return extra.data.val.typeOf(function_index, builder) + .childTypeAt(indices, builder); + }, + .@"fcmp false", + .@"fcmp fast false", + .@"fcmp fast oeq", + .@"fcmp fast oge", + .@"fcmp fast ogt", + .@"fcmp fast ole", + .@"fcmp fast olt", + .@"fcmp fast one", + .@"fcmp fast ord", + .@"fcmp fast true", + .@"fcmp fast ueq", + .@"fcmp fast uge", + .@"fcmp fast ugt", + .@"fcmp fast ule", + .@"fcmp fast ult", + .@"fcmp fast une", + .@"fcmp fast uno", + .@"fcmp oeq", + .@"fcmp oge", + .@"fcmp ogt", + .@"fcmp ole", + .@"fcmp olt", + .@"fcmp one", + .@"fcmp ord", + .@"fcmp true", + .@"fcmp ueq", + .@"fcmp uge", + .@"fcmp ugt", + .@"fcmp ule", + .@"fcmp ult", + .@"fcmp une", + .@"fcmp uno", + .@"icmp eq", + .@"icmp ne", + .@"icmp sge", + .@"icmp sgt", + .@"icmp sle", + .@"icmp slt", + .@"icmp uge", + .@"icmp ugt", + .@"icmp ule", + .@"icmp ult", + => function.extraData(Binary, instruction.data).lhs.typeOf(function_index, builder) + .changeScalarAssumeCapacity(.i1, builder), + .fneg, + .@"fneg fast", + => @as(Value, @enumFromInt(instruction.data)).typeOf(function_index, builder), + .getelementptr, + .@"getelementptr inbounds", + => { + var extra = function.extraDataTrail(GetElementPtr, instruction.data); + const indices = extra.trail.next(extra.data.indices_len, Value, function); + const base_ty = extra.data.base.typeOf(function_index, builder); + if (!base_ty.isVector(builder)) for (indices) |index| { + const index_ty = index.typeOf(function_index, builder); + if (!index_ty.isVector(builder)) continue; + return index_ty.changeScalarAssumeCapacity(base_ty, builder); + }; + return base_ty; + }, + .insertelement => function.extraData(InsertElement, instruction.data) + .val.typeOf(function_index, builder), + .insertvalue => function.extraData(InsertValue, instruction.data) + .val.typeOf(function_index, builder), + .load, + .@"load atomic", + => function.extraData(Load, instruction.data).type, + .phi, + .@"phi fast", + => function.extraData(Phi, instruction.data).type, + .select, + .@"select fast", + => function.extraData(Select, instruction.data).lhs.typeOf(function_index, builder), + .shufflevector => { + const extra = function.extraData(ShuffleVector, instruction.data); + return extra.lhs.typeOf(function_index, builder).changeLengthAssumeCapacity( + extra.mask.typeOf(function_index, builder).vectorLen(builder), + builder, + ); + }, + .va_arg => function.extraData(VaArg, instruction.data).type, + }; + } + + const FormatData = struct { + instruction: Instruction.Index, + function: Function.Index, + builder: *Builder, + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_| + @compileError("invalid format string: '" ++ fmt_str ++ "'"); + if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) { + if (data.instruction == .none) return; + try writer.writeByte(','); + } + if (comptime std.mem.indexOfScalar(u8, fmt_str, ' ') != null) { + if (data.instruction == .none) return; + try writer.writeByte(' '); + } + if (comptime std.mem.indexOfScalar(u8, fmt_str, '%') != null) try writer.print( + "{%} ", + .{data.instruction.typeOf(data.function, data.builder).fmt(data.builder)}, + ); + assert(data.instruction != .none); + try writer.print("%{}", .{ + data.instruction.name(data.function.ptrConst(data.builder)).fmt(data.builder), + }); + } + pub fn fmt( + self: Instruction.Index, + function: Function.Index, + builder: *Builder, + ) std.fmt.Formatter(format) { + return .{ .data = .{ .instruction = self, .function = function, .builder = builder } }; + } + }; + + pub const ExtraIndex = u32; + + pub const BrCond = struct { + cond: Value, + then: Block.Index, + @"else": Block.Index, + weights: Weights, + pub const Weights = enum(u32) { + // We can do this as metadata indices 0 and 1 are reserved. + none = 0, + unpredictable = 1, + /// These values should be converted to `Metadata` to be used + /// in a `prof` annotation providing branch weights. + _, + }; + }; + + pub const Switch = struct { + val: Value, + default: Block.Index, + cases_len: u32, + weights: BrCond.Weights, + //case_vals: [cases_len]Constant, + //case_blocks: [cases_len]Block.Index, + }; + + pub const IndirectBr = struct { + addr: Value, + targets_len: u32, + //targets: [targets_len]Block.Index, + }; + + pub const Binary = struct { + lhs: Value, + rhs: Value, + }; + + pub const ExtractElement = struct { + val: Value, + index: Value, + }; + + pub const InsertElement = struct { + val: Value, + elem: Value, + index: Value, + }; + + pub const ShuffleVector = struct { + lhs: Value, + rhs: Value, + mask: Value, + }; + + pub const ExtractValue = struct { + val: Value, + indices_len: u32, + //indices: [indices_len]u32, + }; + + pub const InsertValue = struct { + val: Value, + elem: Value, + indices_len: u32, + //indices: [indices_len]u32, + }; + + pub const Alloca = struct { + type: Type, + len: Value, + info: Info, + + pub const Kind = enum { normal, inalloca }; + pub const Info = packed struct(u32) { + alignment: Alignment, + addr_space: AddrSpace, + _: u2 = undefined, + }; + }; + + pub const Load = struct { + info: MemoryAccessInfo, + type: Type, + ptr: Value, + }; + + pub const Store = struct { + info: MemoryAccessInfo, + val: Value, + ptr: Value, + }; + + pub const CmpXchg = struct { + info: MemoryAccessInfo, + ptr: Value, + cmp: Value, + new: Value, + + pub const Kind = enum { strong, weak }; + }; + + pub const AtomicRmw = struct { + info: MemoryAccessInfo, + ptr: Value, + val: Value, + + pub const Operation = enum(u5) { + xchg = 0, + add = 1, + sub = 2, + @"and" = 3, + nand = 4, + @"or" = 5, + xor = 6, + max = 7, + min = 8, + umax = 9, + umin = 10, + fadd = 11, + fsub = 12, + fmax = 13, + fmin = 14, + none = std.math.maxInt(u5), + }; + }; + + pub const GetElementPtr = struct { + type: Type, + base: Value, + indices_len: u32, + //indices: [indices_len]Value, + + pub const Kind = Constant.GetElementPtr.Kind; + }; + + pub const Cast = struct { + val: Value, + type: Type, + + pub const Signedness = Constant.Cast.Signedness; + }; + + pub const Phi = struct { + type: Type, + //incoming_vals: [block.incoming]Value, + //incoming_blocks: [block.incoming]Block.Index, + }; + + pub const Select = struct { + cond: Value, + lhs: Value, + rhs: Value, + }; + + pub const Call = struct { + info: Info, + attributes: FunctionAttributes, + ty: Type, + callee: Value, + args_len: u32, + //args: [args_len]Value, + + pub const Kind = enum { + normal, + fast, + musttail, + musttail_fast, + notail, + notail_fast, + tail, + tail_fast, + }; + pub const Info = packed struct(u32) { + call_conv: CallConv, + has_op_bundle_cold: bool, + _: u21 = undefined, + }; + }; + + pub const VaArg = struct { + list: Value, + type: Type, + }; + }; + + pub fn deinit(self: *Function, gpa: Allocator) void { + gpa.free(self.extra); + gpa.free(self.debug_values); + self.debug_locations.deinit(gpa); + gpa.free(self.value_indices[0..self.instructions.len]); + gpa.free(self.names[0..self.instructions.len]); + self.instructions.deinit(gpa); + gpa.free(self.blocks); + self.* = undefined; + } + + pub fn arg(self: *const Function, index: u32) Value { + const argument = self.instructions.get(index); + assert(argument.tag == .arg); + assert(argument.data == index); + + const argument_index: Instruction.Index = @enumFromInt(index); + return argument_index.toValue(); + } + + const ExtraDataTrail = struct { + index: Instruction.ExtraIndex, + + fn nextMut(self: *ExtraDataTrail, len: u32, comptime Item: type, function: *Function) []Item { + const items: []Item = @ptrCast(function.extra[self.index..][0..len]); + self.index += @intCast(len); + return items; + } + + fn next( + self: *ExtraDataTrail, + len: u32, + comptime Item: type, + function: *const Function, + ) []const Item { + const items: []const Item = @ptrCast(function.extra[self.index..][0..len]); + self.index += @intCast(len); + return items; + } + }; + + fn extraDataTrail( + self: *const Function, + comptime T: type, + index: Instruction.ExtraIndex, + ) struct { data: T, trail: ExtraDataTrail } { + var result: T = undefined; + const fields = @typeInfo(T).@"struct".fields; + inline for (fields, self.extra[index..][0..fields.len]) |field, value| + @field(result, field.name) = switch (field.type) { + u32 => value, + Alignment, + AtomicOrdering, + Block.Index, + FunctionAttributes, + Type, + Value, + Instruction.BrCond.Weights, + => @enumFromInt(value), + MemoryAccessInfo, + Instruction.Alloca.Info, + Instruction.Call.Info, + => @bitCast(value), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), + }; + return .{ + .data = result, + .trail = .{ .index = index + @as(Type.Item.ExtraIndex, @intCast(fields.len)) }, + }; + } + + fn extraData(self: *const Function, comptime T: type, index: Instruction.ExtraIndex) T { + return self.extraDataTrail(T, index).data; + } +}; + +pub const DebugLocation = union(enum) { + no_location: void, + location: Location, + + pub const Location = struct { + line: u32, + column: u32, + scope: Builder.Metadata, + inlined_at: Builder.Metadata, + }; + + pub fn toMetadata(self: DebugLocation, builder: *Builder) Allocator.Error!Metadata { + return switch (self) { + .no_location => .none, + .location => |location| try builder.debugLocation( + location.line, + location.column, + location.scope, + location.inlined_at, + ), + }; + } +}; + +pub const WipFunction = struct { + builder: *Builder, + function: Function.Index, + prev_debug_location: DebugLocation, + debug_location: DebugLocation, + cursor: Cursor, + blocks: std.ArrayListUnmanaged(Block), + instructions: std.MultiArrayList(Instruction), + names: std.ArrayListUnmanaged(String), + strip: bool, + debug_locations: std.AutoArrayHashMapUnmanaged(Instruction.Index, DebugLocation), + debug_values: std.AutoArrayHashMapUnmanaged(Instruction.Index, void), + extra: std.ArrayListUnmanaged(u32), + + pub const Cursor = struct { block: Block.Index, instruction: u32 = 0 }; + + pub const Block = struct { + name: String, + incoming: u32, + branches: u32 = 0, + instructions: std.ArrayListUnmanaged(Instruction.Index), + + const Index = enum(u32) { + entry, + _, + + pub fn ptr(self: Index, wip: *WipFunction) *Block { + return &wip.blocks.items[@intFromEnum(self)]; + } + + pub fn ptrConst(self: Index, wip: *const WipFunction) *const Block { + return &wip.blocks.items[@intFromEnum(self)]; + } + + pub fn toInst(self: Index, function: *const Function) Instruction.Index { + return function.blocks[@intFromEnum(self)].instruction; + } + }; + }; + + pub const Instruction = Function.Instruction; + + pub fn init(builder: *Builder, options: struct { + function: Function.Index, + strip: bool, + }) Allocator.Error!WipFunction { + var self: WipFunction = .{ + .builder = builder, + .function = options.function, + .prev_debug_location = .no_location, + .debug_location = .no_location, + .cursor = undefined, + .blocks = .{}, + .instructions = .{}, + .names = .{}, + .strip = options.strip, + .debug_locations = .{}, + .debug_values = .{}, + .extra = .{}, + }; + errdefer self.deinit(); + + const params_len = options.function.typeOf(self.builder).functionParameters(self.builder).len; + try self.ensureUnusedExtraCapacity(params_len, NoExtra, 0); + try self.instructions.ensureUnusedCapacity(self.builder.gpa, params_len); + if (!self.strip) { + try self.names.ensureUnusedCapacity(self.builder.gpa, params_len); + } + for (0..params_len) |param_index| { + self.instructions.appendAssumeCapacity(.{ .tag = .arg, .data = @intCast(param_index) }); + if (!self.strip) { + self.names.appendAssumeCapacity(.empty); // TODO: param names + } + } + + return self; + } + + pub fn arg(self: *const WipFunction, index: u32) Value { + const argument = self.instructions.get(index); + assert(argument.tag == .arg); + assert(argument.data == index); + + const argument_index: Instruction.Index = @enumFromInt(index); + return argument_index.toValue(); + } + + pub fn block(self: *WipFunction, incoming: u32, name: []const u8) Allocator.Error!Block.Index { + try self.blocks.ensureUnusedCapacity(self.builder.gpa, 1); + + const index: Block.Index = @enumFromInt(self.blocks.items.len); + const final_name = if (self.strip) .empty else try self.builder.string(name); + self.blocks.appendAssumeCapacity(.{ + .name = final_name, + .incoming = incoming, + .instructions = .{}, + }); + return index; + } + + pub fn ret(self: *WipFunction, val: Value) Allocator.Error!Instruction.Index { + assert(val.typeOfWip(self) == self.function.typeOf(self.builder).functionReturn(self.builder)); + try self.ensureUnusedExtraCapacity(1, NoExtra, 0); + return try self.addInst(null, .{ .tag = .ret, .data = @intFromEnum(val) }); + } + + pub fn retVoid(self: *WipFunction) Allocator.Error!Instruction.Index { + try self.ensureUnusedExtraCapacity(1, NoExtra, 0); + return try self.addInst(null, .{ .tag = .@"ret void", .data = undefined }); + } + + pub fn br(self: *WipFunction, dest: Block.Index) Allocator.Error!Instruction.Index { + try self.ensureUnusedExtraCapacity(1, NoExtra, 0); + const instruction = try self.addInst(null, .{ .tag = .br, .data = @intFromEnum(dest) }); + dest.ptr(self).branches += 1; + return instruction; + } + + pub fn brCond( + self: *WipFunction, + cond: Value, + then: Block.Index, + @"else": Block.Index, + weights: enum { none, unpredictable, then_likely, else_likely }, + ) Allocator.Error!Instruction.Index { + assert(cond.typeOfWip(self) == .i1); + try self.ensureUnusedExtraCapacity(1, Instruction.BrCond, 0); + const instruction = try self.addInst(null, .{ + .tag = .br_cond, + .data = self.addExtraAssumeCapacity(Instruction.BrCond{ + .cond = cond, + .then = then, + .@"else" = @"else", + .weights = switch (weights) { + .none => .none, + .unpredictable => .unpredictable, + .then_likely, .else_likely => w: { + const branch_weights_str = try self.builder.metadataString("branch_weights"); + const unlikely_const = try self.builder.metadataConstant(try self.builder.intConst(.i32, 1)); + const likely_const = try self.builder.metadataConstant(try self.builder.intConst(.i32, 2000)); + const weight_vals: [2]Metadata = switch (weights) { + .none, .unpredictable => unreachable, + .then_likely => .{ likely_const, unlikely_const }, + .else_likely => .{ unlikely_const, likely_const }, + }; + const tuple = try self.builder.strTuple(branch_weights_str, &weight_vals); + break :w @enumFromInt(@intFromEnum(tuple)); + }, + }, + }), + }); + then.ptr(self).branches += 1; + @"else".ptr(self).branches += 1; + return instruction; + } + + pub const WipSwitch = struct { + index: u32, + instruction: Instruction.Index, + + pub fn addCase( + self: *WipSwitch, + val: Constant, + dest: Block.Index, + wip: *WipFunction, + ) Allocator.Error!void { + const instruction = wip.instructions.get(@intFromEnum(self.instruction)); + var extra = wip.extraDataTrail(Instruction.Switch, instruction.data); + assert(val.typeOf(wip.builder) == extra.data.val.typeOfWip(wip)); + extra.trail.nextMut(extra.data.cases_len, Constant, wip)[self.index] = val; + extra.trail.nextMut(extra.data.cases_len, Block.Index, wip)[self.index] = dest; + self.index += 1; + dest.ptr(wip).branches += 1; + } + + pub fn finish(self: WipSwitch, wip: *WipFunction) void { + const instruction = wip.instructions.get(@intFromEnum(self.instruction)); + const extra = wip.extraData(Instruction.Switch, instruction.data); + assert(self.index == extra.cases_len); + } + }; + + pub fn @"switch"( + self: *WipFunction, + val: Value, + default: Block.Index, + cases_len: u32, + weights: Instruction.BrCond.Weights, + ) Allocator.Error!WipSwitch { + try self.ensureUnusedExtraCapacity(1, Instruction.Switch, cases_len * 2); + const instruction = try self.addInst(null, .{ + .tag = .@"switch", + .data = self.addExtraAssumeCapacity(Instruction.Switch{ + .val = val, + .default = default, + .cases_len = cases_len, + .weights = weights, + }), + }); + _ = self.extra.addManyAsSliceAssumeCapacity(cases_len * 2); + default.ptr(self).branches += 1; + return .{ .index = 0, .instruction = instruction }; + } + + pub fn indirectbr( + self: *WipFunction, + addr: Value, + targets: []const Block.Index, + ) Allocator.Error!Instruction.Index { + try self.ensureUnusedExtraCapacity(1, Instruction.IndirectBr, targets.len); + const instruction = try self.addInst(null, .{ + .tag = .indirectbr, + .data = self.addExtraAssumeCapacity(Instruction.IndirectBr{ + .addr = addr, + .targets_len = @intCast(targets.len), + }), + }); + _ = self.extra.appendSliceAssumeCapacity(@ptrCast(targets)); + for (targets) |target| target.ptr(self).branches += 1; + return instruction; + } + + pub fn @"unreachable"(self: *WipFunction) Allocator.Error!Instruction.Index { + try self.ensureUnusedExtraCapacity(1, NoExtra, 0); + return try self.addInst(null, .{ .tag = .@"unreachable", .data = undefined }); + } + + pub fn un( + self: *WipFunction, + tag: Instruction.Tag, + val: Value, + name: []const u8, + ) Allocator.Error!Value { + switch (tag) { + .fneg, + .@"fneg fast", + => assert(val.typeOfWip(self).scalarType(self.builder).isFloatingPoint()), + else => unreachable, + } + try self.ensureUnusedExtraCapacity(1, NoExtra, 0); + const instruction = try self.addInst(name, .{ .tag = tag, .data = @intFromEnum(val) }); + return instruction.toValue(); + } + + pub fn not(self: *WipFunction, val: Value, name: []const u8) Allocator.Error!Value { + const ty = val.typeOfWip(self); + const all_ones = try self.builder.splatValue( + ty, + try self.builder.intConst(ty.scalarType(self.builder), -1), + ); + return self.bin(.xor, val, all_ones, name); + } + + pub fn neg(self: *WipFunction, val: Value, name: []const u8) Allocator.Error!Value { + return self.bin(.sub, try self.builder.zeroInitValue(val.typeOfWip(self)), val, name); + } + + pub fn bin( + self: *WipFunction, + tag: Instruction.Tag, + lhs: Value, + rhs: Value, + name: []const u8, + ) Allocator.Error!Value { + switch (tag) { + .add, + .@"add nsw", + .@"add nuw", + .@"and", + .ashr, + .@"ashr exact", + .fadd, + .@"fadd fast", + .fdiv, + .@"fdiv fast", + .fmul, + .@"fmul fast", + .frem, + .@"frem fast", + .fsub, + .@"fsub fast", + .lshr, + .@"lshr exact", + .mul, + .@"mul nsw", + .@"mul nuw", + .@"or", + .sdiv, + .@"sdiv exact", + .shl, + .@"shl nsw", + .@"shl nuw", + .srem, + .sub, + .@"sub nsw", + .@"sub nuw", + .udiv, + .@"udiv exact", + .urem, + .xor, + => assert(lhs.typeOfWip(self) == rhs.typeOfWip(self)), + else => unreachable, + } + try self.ensureUnusedExtraCapacity(1, Instruction.Binary, 0); + const instruction = try self.addInst(name, .{ + .tag = tag, + .data = self.addExtraAssumeCapacity(Instruction.Binary{ .lhs = lhs, .rhs = rhs }), + }); + return instruction.toValue(); + } + + pub fn extractElement( + self: *WipFunction, + val: Value, + index: Value, + name: []const u8, + ) Allocator.Error!Value { + assert(val.typeOfWip(self).isVector(self.builder)); + assert(index.typeOfWip(self).isInteger(self.builder)); + try self.ensureUnusedExtraCapacity(1, Instruction.ExtractElement, 0); + const instruction = try self.addInst(name, .{ + .tag = .extractelement, + .data = self.addExtraAssumeCapacity(Instruction.ExtractElement{ + .val = val, + .index = index, + }), + }); + return instruction.toValue(); + } + + pub fn insertElement( + self: *WipFunction, + val: Value, + elem: Value, + index: Value, + name: []const u8, + ) Allocator.Error!Value { + assert(val.typeOfWip(self).scalarType(self.builder) == elem.typeOfWip(self)); + assert(index.typeOfWip(self).isInteger(self.builder)); + try self.ensureUnusedExtraCapacity(1, Instruction.InsertElement, 0); + const instruction = try self.addInst(name, .{ + .tag = .insertelement, + .data = self.addExtraAssumeCapacity(Instruction.InsertElement{ + .val = val, + .elem = elem, + .index = index, + }), + }); + return instruction.toValue(); + } + + pub fn shuffleVector( + self: *WipFunction, + lhs: Value, + rhs: Value, + mask: Value, + name: []const u8, + ) Allocator.Error!Value { + assert(lhs.typeOfWip(self).isVector(self.builder)); + assert(lhs.typeOfWip(self) == rhs.typeOfWip(self)); + assert(mask.typeOfWip(self).scalarType(self.builder).isInteger(self.builder)); + _ = try self.ensureUnusedExtraCapacity(1, Instruction.ShuffleVector, 0); + const instruction = try self.addInst(name, .{ + .tag = .shufflevector, + .data = self.addExtraAssumeCapacity(Instruction.ShuffleVector{ + .lhs = lhs, + .rhs = rhs, + .mask = mask, + }), + }); + return instruction.toValue(); + } + + pub fn splatVector( + self: *WipFunction, + ty: Type, + elem: Value, + name: []const u8, + ) Allocator.Error!Value { + const scalar_ty = try ty.changeLength(1, self.builder); + const mask_ty = try ty.changeScalar(.i32, self.builder); + const poison = try self.builder.poisonValue(scalar_ty); + const mask = try self.builder.splatValue(mask_ty, .@"0"); + const scalar = try self.insertElement(poison, elem, .@"0", name); + return self.shuffleVector(scalar, poison, mask, name); + } + + pub fn extractValue( + self: *WipFunction, + val: Value, + indices: []const u32, + name: []const u8, + ) Allocator.Error!Value { + assert(indices.len > 0); + _ = val.typeOfWip(self).childTypeAt(indices, self.builder); + try self.ensureUnusedExtraCapacity(1, Instruction.ExtractValue, indices.len); + const instruction = try self.addInst(name, .{ + .tag = .extractvalue, + .data = self.addExtraAssumeCapacity(Instruction.ExtractValue{ + .val = val, + .indices_len = @intCast(indices.len), + }), + }); + self.extra.appendSliceAssumeCapacity(indices); + return instruction.toValue(); + } + + pub fn insertValue( + self: *WipFunction, + val: Value, + elem: Value, + indices: []const u32, + name: []const u8, + ) Allocator.Error!Value { + assert(indices.len > 0); + assert(val.typeOfWip(self).childTypeAt(indices, self.builder) == elem.typeOfWip(self)); + try self.ensureUnusedExtraCapacity(1, Instruction.InsertValue, indices.len); + const instruction = try self.addInst(name, .{ + .tag = .insertvalue, + .data = self.addExtraAssumeCapacity(Instruction.InsertValue{ + .val = val, + .elem = elem, + .indices_len = @intCast(indices.len), + }), + }); + self.extra.appendSliceAssumeCapacity(indices); + return instruction.toValue(); + } + + pub fn buildAggregate( + self: *WipFunction, + ty: Type, + elems: []const Value, + name: []const u8, + ) Allocator.Error!Value { + assert(ty.aggregateLen(self.builder) == elems.len); + var cur = try self.builder.poisonValue(ty); + for (elems, 0..) |elem, index| + cur = try self.insertValue(cur, elem, &[_]u32{@intCast(index)}, name); + return cur; + } + + pub fn alloca( + self: *WipFunction, + kind: Instruction.Alloca.Kind, + ty: Type, + len: Value, + alignment: Alignment, + addr_space: AddrSpace, + name: []const u8, + ) Allocator.Error!Value { + assert(len == .none or len.typeOfWip(self).isInteger(self.builder)); + _ = try self.builder.ptrType(addr_space); + try self.ensureUnusedExtraCapacity(1, Instruction.Alloca, 0); + const instruction = try self.addInst(name, .{ + .tag = switch (kind) { + .normal => .alloca, + .inalloca => .@"alloca inalloca", + }, + .data = self.addExtraAssumeCapacity(Instruction.Alloca{ + .type = ty, + .len = switch (len) { + .none => .@"1", + else => len, + }, + .info = .{ .alignment = alignment, .addr_space = addr_space }, + }), + }); + return instruction.toValue(); + } + + pub fn load( + self: *WipFunction, + access_kind: MemoryAccessKind, + ty: Type, + ptr: Value, + alignment: Alignment, + name: []const u8, + ) Allocator.Error!Value { + return self.loadAtomic(access_kind, ty, ptr, .system, .none, alignment, name); + } + + pub fn loadAtomic( + self: *WipFunction, + access_kind: MemoryAccessKind, + ty: Type, + ptr: Value, + sync_scope: SyncScope, + ordering: AtomicOrdering, + alignment: Alignment, + name: []const u8, + ) Allocator.Error!Value { + assert(ptr.typeOfWip(self).isPointer(self.builder)); + try self.ensureUnusedExtraCapacity(1, Instruction.Load, 0); + const instruction = try self.addInst(name, .{ + .tag = switch (ordering) { + .none => .load, + else => .@"load atomic", + }, + .data = self.addExtraAssumeCapacity(Instruction.Load{ + .info = .{ + .access_kind = access_kind, + .sync_scope = switch (ordering) { + .none => .system, + else => sync_scope, + }, + .success_ordering = ordering, + .alignment = alignment, + }, + .type = ty, + .ptr = ptr, + }), + }); + return instruction.toValue(); + } + + pub fn store( + self: *WipFunction, + kind: MemoryAccessKind, + val: Value, + ptr: Value, + alignment: Alignment, + ) Allocator.Error!Instruction.Index { + return self.storeAtomic(kind, val, ptr, .system, .none, alignment); + } + + pub fn storeAtomic( + self: *WipFunction, + access_kind: MemoryAccessKind, + val: Value, + ptr: Value, + sync_scope: SyncScope, + ordering: AtomicOrdering, + alignment: Alignment, + ) Allocator.Error!Instruction.Index { + assert(ptr.typeOfWip(self).isPointer(self.builder)); + try self.ensureUnusedExtraCapacity(1, Instruction.Store, 0); + const instruction = try self.addInst(null, .{ + .tag = switch (ordering) { + .none => .store, + else => .@"store atomic", + }, + .data = self.addExtraAssumeCapacity(Instruction.Store{ + .info = .{ + .access_kind = access_kind, + .sync_scope = switch (ordering) { + .none => .system, + else => sync_scope, + }, + .success_ordering = ordering, + .alignment = alignment, + }, + .val = val, + .ptr = ptr, + }), + }); + return instruction; + } + + pub fn fence( + self: *WipFunction, + sync_scope: SyncScope, + ordering: AtomicOrdering, + ) Allocator.Error!Instruction.Index { + assert(ordering != .none); + try self.ensureUnusedExtraCapacity(1, NoExtra, 0); + const instruction = try self.addInst(null, .{ + .tag = .fence, + .data = @bitCast(MemoryAccessInfo{ + .sync_scope = sync_scope, + .success_ordering = ordering, + }), + }); + return instruction; + } + + pub fn cmpxchg( + self: *WipFunction, + kind: Instruction.CmpXchg.Kind, + access_kind: MemoryAccessKind, + ptr: Value, + cmp: Value, + new: Value, + sync_scope: SyncScope, + success_ordering: AtomicOrdering, + failure_ordering: AtomicOrdering, + alignment: Alignment, + name: []const u8, + ) Allocator.Error!Value { + assert(ptr.typeOfWip(self).isPointer(self.builder)); + const ty = cmp.typeOfWip(self); + assert(ty == new.typeOfWip(self)); + assert(success_ordering != .none); + assert(failure_ordering != .none); + + _ = try self.builder.structType(.normal, &.{ ty, .i1 }); + try self.ensureUnusedExtraCapacity(1, Instruction.CmpXchg, 0); + const instruction = try self.addInst(name, .{ + .tag = switch (kind) { + .strong => .cmpxchg, + .weak => .@"cmpxchg weak", + }, + .data = self.addExtraAssumeCapacity(Instruction.CmpXchg{ + .info = .{ + .access_kind = access_kind, + .sync_scope = sync_scope, + .success_ordering = success_ordering, + .failure_ordering = failure_ordering, + .alignment = alignment, + }, + .ptr = ptr, + .cmp = cmp, + .new = new, + }), + }); + return instruction.toValue(); + } + + pub fn atomicrmw( + self: *WipFunction, + access_kind: MemoryAccessKind, + operation: Instruction.AtomicRmw.Operation, + ptr: Value, + val: Value, + sync_scope: SyncScope, + ordering: AtomicOrdering, + alignment: Alignment, + name: []const u8, + ) Allocator.Error!Value { + assert(ptr.typeOfWip(self).isPointer(self.builder)); + assert(ordering != .none); + + try self.ensureUnusedExtraCapacity(1, Instruction.AtomicRmw, 0); + const instruction = try self.addInst(name, .{ + .tag = .atomicrmw, + .data = self.addExtraAssumeCapacity(Instruction.AtomicRmw{ + .info = .{ + .access_kind = access_kind, + .atomic_rmw_operation = operation, + .sync_scope = sync_scope, + .success_ordering = ordering, + .alignment = alignment, + }, + .ptr = ptr, + .val = val, + }), + }); + return instruction.toValue(); + } + + pub fn gep( + self: *WipFunction, + kind: Instruction.GetElementPtr.Kind, + ty: Type, + base: Value, + indices: []const Value, + name: []const u8, + ) Allocator.Error!Value { + const base_ty = base.typeOfWip(self); + const base_is_vector = base_ty.isVector(self.builder); + + const VectorInfo = struct { + kind: Type.Vector.Kind, + len: u32, + + fn init(vector_ty: Type, builder: *const Builder) @This() { + return .{ .kind = vector_ty.vectorKind(builder), .len = vector_ty.vectorLen(builder) }; + } + }; + var vector_info: ?VectorInfo = + if (base_is_vector) VectorInfo.init(base_ty, self.builder) else null; + for (indices) |index| { + const index_ty = index.typeOfWip(self); + switch (index_ty.tag(self.builder)) { + .integer => {}, + .vector, .scalable_vector => { + const index_info = VectorInfo.init(index_ty, self.builder); + if (vector_info) |info| + assert(std.meta.eql(info, index_info)) + else + vector_info = index_info; + }, + else => unreachable, + } + } + if (!base_is_vector) if (vector_info) |info| switch (info.kind) { + inline else => |vector_kind| _ = try self.builder.vectorType( + vector_kind, + info.len, + base_ty, + ), + }; + + try self.ensureUnusedExtraCapacity(1, Instruction.GetElementPtr, indices.len); + const instruction = try self.addInst(name, .{ + .tag = switch (kind) { + .normal => .getelementptr, + .inbounds => .@"getelementptr inbounds", + }, + .data = self.addExtraAssumeCapacity(Instruction.GetElementPtr{ + .type = ty, + .base = base, + .indices_len = @intCast(indices.len), + }), + }); + self.extra.appendSliceAssumeCapacity(@ptrCast(indices)); + return instruction.toValue(); + } + + pub fn gepStruct( + self: *WipFunction, + ty: Type, + base: Value, + index: usize, + name: []const u8, + ) Allocator.Error!Value { + assert(ty.isStruct(self.builder)); + return self.gep(.inbounds, ty, base, &.{ .@"0", try self.builder.intValue(.i32, index) }, name); + } + + pub fn conv( + self: *WipFunction, + signedness: Instruction.Cast.Signedness, + val: Value, + ty: Type, + name: []const u8, + ) Allocator.Error!Value { + const val_ty = val.typeOfWip(self); + if (val_ty == ty) return val; + return self.cast(self.builder.convTag(signedness, val_ty, ty), val, ty, name); + } + + pub fn cast( + self: *WipFunction, + tag: Instruction.Tag, + val: Value, + ty: Type, + name: []const u8, + ) Allocator.Error!Value { + switch (tag) { + .addrspacecast, + .bitcast, + .fpext, + .fptosi, + .fptoui, + .fptrunc, + .inttoptr, + .ptrtoint, + .sext, + .sitofp, + .trunc, + .uitofp, + .zext, + => {}, + else => unreachable, + } + if (val.typeOfWip(self) == ty) return val; + try self.ensureUnusedExtraCapacity(1, Instruction.Cast, 0); + const instruction = try self.addInst(name, .{ + .tag = tag, + .data = self.addExtraAssumeCapacity(Instruction.Cast{ + .val = val, + .type = ty, + }), + }); + return instruction.toValue(); + } + + pub fn icmp( + self: *WipFunction, + cond: IntegerCondition, + lhs: Value, + rhs: Value, + name: []const u8, + ) Allocator.Error!Value { + return self.cmpTag(switch (cond) { + inline else => |tag| @field(Instruction.Tag, "icmp " ++ @tagName(tag)), + }, lhs, rhs, name); + } + + pub fn fcmp( + self: *WipFunction, + fast: FastMathKind, + cond: FloatCondition, + lhs: Value, + rhs: Value, + name: []const u8, + ) Allocator.Error!Value { + return self.cmpTag(switch (fast) { + inline else => |fast_tag| switch (cond) { + inline else => |cond_tag| @field(Instruction.Tag, "fcmp " ++ switch (fast_tag) { + .normal => "", + .fast => "fast ", + } ++ @tagName(cond_tag)), + }, + }, lhs, rhs, name); + } + + pub const WipPhi = struct { + block: Block.Index, + instruction: Instruction.Index, + + pub fn toValue(self: WipPhi) Value { + return self.instruction.toValue(); + } + + pub fn finish( + self: WipPhi, + vals: []const Value, + blocks: []const Block.Index, + wip: *WipFunction, + ) void { + const incoming_len = self.block.ptrConst(wip).incoming; + assert(vals.len == incoming_len and blocks.len == incoming_len); + const instruction = wip.instructions.get(@intFromEnum(self.instruction)); + var extra = wip.extraDataTrail(Instruction.Phi, instruction.data); + for (vals) |val| assert(val.typeOfWip(wip) == extra.data.type); + @memcpy(extra.trail.nextMut(incoming_len, Value, wip), vals); + @memcpy(extra.trail.nextMut(incoming_len, Block.Index, wip), blocks); + } + }; + + pub fn phi(self: *WipFunction, ty: Type, name: []const u8) Allocator.Error!WipPhi { + return self.phiTag(.phi, ty, name); + } + + pub fn phiFast(self: *WipFunction, ty: Type, name: []const u8) Allocator.Error!WipPhi { + return self.phiTag(.@"phi fast", ty, name); + } + + pub fn select( + self: *WipFunction, + fast: FastMathKind, + cond: Value, + lhs: Value, + rhs: Value, + name: []const u8, + ) Allocator.Error!Value { + return self.selectTag(switch (fast) { + .normal => .select, + .fast => .@"select fast", + }, cond, lhs, rhs, name); + } + + pub fn call( + self: *WipFunction, + kind: Instruction.Call.Kind, + call_conv: CallConv, + function_attributes: FunctionAttributes, + ty: Type, + callee: Value, + args: []const Value, + name: []const u8, + ) Allocator.Error!Value { + return self.callInner(kind, call_conv, function_attributes, ty, callee, args, name, false); + } + + fn callInner( + self: *WipFunction, + kind: Instruction.Call.Kind, + call_conv: CallConv, + function_attributes: FunctionAttributes, + ty: Type, + callee: Value, + args: []const Value, + name: []const u8, + has_op_bundle_cold: bool, + ) Allocator.Error!Value { + const ret_ty = ty.functionReturn(self.builder); + assert(ty.isFunction(self.builder)); + assert(callee.typeOfWip(self).isPointer(self.builder)); + const params = ty.functionParameters(self.builder); + for (params, args[0..params.len]) |param, arg_val| assert(param == arg_val.typeOfWip(self)); + + try self.ensureUnusedExtraCapacity(1, Instruction.Call, args.len); + const instruction = try self.addInst(switch (ret_ty) { + .void => null, + else => name, + }, .{ + .tag = switch (kind) { + .normal => .call, + .fast => .@"call fast", + .musttail => .@"musttail call", + .musttail_fast => .@"musttail call fast", + .notail => .@"notail call", + .notail_fast => .@"notail call fast", + .tail => .@"tail call", + .tail_fast => .@"tail call fast", + }, + .data = self.addExtraAssumeCapacity(Instruction.Call{ + .info = .{ + .call_conv = call_conv, + .has_op_bundle_cold = has_op_bundle_cold, + }, + .attributes = function_attributes, + .ty = ty, + .callee = callee, + .args_len = @intCast(args.len), + }), + }); + self.extra.appendSliceAssumeCapacity(@ptrCast(args)); + return instruction.toValue(); + } + + pub fn callAsm( + self: *WipFunction, + function_attributes: FunctionAttributes, + ty: Type, + kind: Constant.Assembly.Info, + assembly: String, + constraints: String, + args: []const Value, + name: []const u8, + ) Allocator.Error!Value { + const callee = try self.builder.asmValue(ty, kind, assembly, constraints); + return self.call(.normal, CallConv.default, function_attributes, ty, callee, args, name); + } + + pub fn callIntrinsic( + self: *WipFunction, + fast: FastMathKind, + function_attributes: FunctionAttributes, + id: Intrinsic, + overload: []const Type, + args: []const Value, + name: []const u8, + ) Allocator.Error!Value { + const intrinsic = try self.builder.getIntrinsic(id, overload); + return self.call( + fast.toCallKind(), + CallConv.default, + function_attributes, + intrinsic.typeOf(self.builder), + intrinsic.toValue(self.builder), + args, + name, + ); + } + + pub fn callIntrinsicAssumeCold(self: *WipFunction) Allocator.Error!Value { + const intrinsic = try self.builder.getIntrinsic(.assume, &.{}); + return self.callInner( + .normal, + CallConv.default, + .none, + intrinsic.typeOf(self.builder), + intrinsic.toValue(self.builder), + &.{try self.builder.intValue(.i1, 1)}, + "", + true, + ); + } + + pub fn callMemCpy( + self: *WipFunction, + dst: Value, + dst_align: Alignment, + src: Value, + src_align: Alignment, + len: Value, + kind: MemoryAccessKind, + @"inline": bool, + ) Allocator.Error!Instruction.Index { + var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })}; + var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = src_align })}; + const value = try self.callIntrinsic( + .normal, + try self.builder.fnAttrs(&.{ + .none, + .none, + try self.builder.attrs(&dst_attrs), + try self.builder.attrs(&src_attrs), + }), + if (@"inline") .@"memcpy.inline" else .memcpy, + &.{ dst.typeOfWip(self), src.typeOfWip(self), len.typeOfWip(self) }, + &.{ dst, src, len, switch (kind) { + .normal => Value.false, + .@"volatile" => Value.true, + } }, + undefined, + ); + return value.unwrap().instruction; + } + + pub fn callMemSet( + self: *WipFunction, + dst: Value, + dst_align: Alignment, + val: Value, + len: Value, + kind: MemoryAccessKind, + @"inline": bool, + ) Allocator.Error!Instruction.Index { + var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })}; + const value = try self.callIntrinsic( + .normal, + try self.builder.fnAttrs(&.{ .none, .none, try self.builder.attrs(&dst_attrs) }), + if (@"inline") .@"memset.inline" else .memset, + &.{ dst.typeOfWip(self), len.typeOfWip(self) }, + &.{ dst, val, len, switch (kind) { + .normal => Value.false, + .@"volatile" => Value.true, + } }, + undefined, + ); + return value.unwrap().instruction; + } + + pub fn vaArg(self: *WipFunction, list: Value, ty: Type, name: []const u8) Allocator.Error!Value { + try self.ensureUnusedExtraCapacity(1, Instruction.VaArg, 0); + const instruction = try self.addInst(name, .{ + .tag = .va_arg, + .data = self.addExtraAssumeCapacity(Instruction.VaArg{ + .list = list, + .type = ty, + }), + }); + return instruction.toValue(); + } + + pub fn debugValue(self: *WipFunction, value: Value) Allocator.Error!Metadata { + if (self.strip) return .none; + return switch (value.unwrap()) { + .instruction => |instr_index| blk: { + const gop = try self.debug_values.getOrPut(self.builder.gpa, instr_index); + + const metadata: Metadata = @enumFromInt(Metadata.first_local_metadata + gop.index); + if (!gop.found_existing) gop.key_ptr.* = instr_index; + + break :blk metadata; + }, + .constant => |constant| try self.builder.metadataConstant(constant), + .metadata => |metadata| metadata, + }; + } + + pub fn finish(self: *WipFunction) Allocator.Error!void { + const gpa = self.builder.gpa; + const function = self.function.ptr(self.builder); + const params_len = self.function.typeOf(self.builder).functionParameters(self.builder).len; + const final_instructions_len = self.blocks.items.len + self.instructions.len; + + const blocks = try gpa.alloc(Function.Block, self.blocks.items.len); + errdefer gpa.free(blocks); + + const instructions: struct { + items: []Instruction.Index, + + fn map(instructions: @This(), val: Value) Value { + if (val == .none) return .none; + return switch (val.unwrap()) { + .instruction => |instruction| instructions.items[ + @intFromEnum(instruction) + ].toValue(), + .constant => |constant| constant.toValue(), + .metadata => |metadata| metadata.toValue(), + }; + } + } = .{ .items = try gpa.alloc(Instruction.Index, self.instructions.len) }; + defer gpa.free(instructions.items); + + const names = try gpa.alloc(String, final_instructions_len); + errdefer gpa.free(names); + + const value_indices = try gpa.alloc(u32, final_instructions_len); + errdefer gpa.free(value_indices); + + var debug_locations: std.AutoHashMapUnmanaged(Instruction.Index, DebugLocation) = .empty; + errdefer debug_locations.deinit(gpa); + try debug_locations.ensureUnusedCapacity(gpa, @intCast(self.debug_locations.count())); + + const debug_values = try gpa.alloc(Instruction.Index, self.debug_values.count()); + errdefer gpa.free(debug_values); + + var wip_extra: struct { + index: Instruction.ExtraIndex = 0, + items: []u32, + + fn addExtra(wip_extra: *@This(), extra: anytype) Instruction.ExtraIndex { + const result = wip_extra.index; + inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| { + const value = @field(extra, field.name); + wip_extra.items[wip_extra.index] = switch (field.type) { + u32 => value, + Alignment, + AtomicOrdering, + Block.Index, + FunctionAttributes, + Type, + Value, + Instruction.BrCond.Weights, + => @intFromEnum(value), + MemoryAccessInfo, + Instruction.Alloca.Info, + Instruction.Call.Info, + => @bitCast(value), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), + }; + wip_extra.index += 1; + } + return result; + } + + fn appendSlice(wip_extra: *@This(), slice: anytype) void { + if (@typeInfo(@TypeOf(slice)).pointer.child == Value) + @compileError("use appendMappedValues"); + const data: []const u32 = @ptrCast(slice); + @memcpy(wip_extra.items[wip_extra.index..][0..data.len], data); + wip_extra.index += @intCast(data.len); + } + + fn appendMappedValues(wip_extra: *@This(), vals: []const Value, ctx: anytype) void { + for (wip_extra.items[wip_extra.index..][0..vals.len], vals) |*extra, val| + extra.* = @intFromEnum(ctx.map(val)); + wip_extra.index += @intCast(vals.len); + } + + fn finish(wip_extra: *const @This()) []const u32 { + assert(wip_extra.index == wip_extra.items.len); + return wip_extra.items; + } + } = .{ .items = try gpa.alloc(u32, self.extra.items.len) }; + errdefer gpa.free(wip_extra.items); + + gpa.free(function.blocks); + function.blocks = &.{}; + gpa.free(function.names[0..function.instructions.len]); + function.debug_locations.deinit(gpa); + function.debug_locations = .{}; + gpa.free(function.debug_values); + function.debug_values = &.{}; + gpa.free(function.extra); + function.extra = &.{}; + + function.instructions.shrinkRetainingCapacity(0); + try function.instructions.setCapacity(gpa, final_instructions_len); + errdefer function.instructions.shrinkRetainingCapacity(0); + + { + var final_instruction_index: Instruction.Index = @enumFromInt(0); + for (0..params_len) |param_index| { + instructions.items[param_index] = final_instruction_index; + final_instruction_index = @enumFromInt(@intFromEnum(final_instruction_index) + 1); + } + for (blocks, self.blocks.items) |*final_block, current_block| { + assert(current_block.incoming == current_block.branches); + final_block.instruction = final_instruction_index; + final_instruction_index = @enumFromInt(@intFromEnum(final_instruction_index) + 1); + for (current_block.instructions.items) |instruction| { + instructions.items[@intFromEnum(instruction)] = final_instruction_index; + final_instruction_index = @enumFromInt(@intFromEnum(final_instruction_index) + 1); + } + } + } + + var wip_name: struct { + next_name: String = @enumFromInt(0), + next_unique_name: std.AutoHashMap(String, String), + builder: *Builder, + + fn map(wip_name: *@This(), name: String, sep: []const u8) Allocator.Error!String { + switch (name) { + .none => return .none, + .empty => { + assert(wip_name.next_name != .none); + defer wip_name.next_name = @enumFromInt(@intFromEnum(wip_name.next_name) + 1); + return wip_name.next_name; + }, + _ => { + assert(!name.isAnon()); + const gop = try wip_name.next_unique_name.getOrPut(name); + if (!gop.found_existing) { + gop.value_ptr.* = @enumFromInt(0); + return name; + } + + while (true) { + gop.value_ptr.* = @enumFromInt(@intFromEnum(gop.value_ptr.*) + 1); + const unique_name = try wip_name.builder.fmt("{r}{s}{r}", .{ + name.fmt(wip_name.builder), + sep, + gop.value_ptr.fmt(wip_name.builder), + }); + const unique_gop = try wip_name.next_unique_name.getOrPut(unique_name); + if (!unique_gop.found_existing) { + unique_gop.value_ptr.* = @enumFromInt(0); + return unique_name; + } + } + }, + } + } + } = .{ + .next_unique_name = std.AutoHashMap(String, String).init(gpa), + .builder = self.builder, + }; + defer wip_name.next_unique_name.deinit(); + + var value_index: u32 = 0; + for (0..params_len) |param_index| { + const old_argument_index: Instruction.Index = @enumFromInt(param_index); + const new_argument_index: Instruction.Index = @enumFromInt(function.instructions.len); + const argument = self.instructions.get(@intFromEnum(old_argument_index)); + assert(argument.tag == .arg); + assert(argument.data == param_index); + value_indices[function.instructions.len] = value_index; + value_index += 1; + function.instructions.appendAssumeCapacity(argument); + names[@intFromEnum(new_argument_index)] = try wip_name.map( + if (self.strip) .empty else self.names.items[@intFromEnum(old_argument_index)], + ".", + ); + if (self.debug_locations.get(old_argument_index)) |location| { + debug_locations.putAssumeCapacity(new_argument_index, location); + } + if (self.debug_values.getIndex(old_argument_index)) |index| { + debug_values[index] = new_argument_index; + } + } + for (self.blocks.items) |current_block| { + const new_block_index: Instruction.Index = @enumFromInt(function.instructions.len); + value_indices[function.instructions.len] = value_index; + function.instructions.appendAssumeCapacity(.{ + .tag = .block, + .data = current_block.incoming, + }); + names[@intFromEnum(new_block_index)] = try wip_name.map(current_block.name, ""); + for (current_block.instructions.items) |old_instruction_index| { + const new_instruction_index: Instruction.Index = @enumFromInt(function.instructions.len); + var instruction = self.instructions.get(@intFromEnum(old_instruction_index)); + switch (instruction.tag) { + .add, + .@"add nsw", + .@"add nuw", + .@"add nuw nsw", + .@"and", + .ashr, + .@"ashr exact", + .fadd, + .@"fadd fast", + .@"fcmp false", + .@"fcmp fast false", + .@"fcmp fast oeq", + .@"fcmp fast oge", + .@"fcmp fast ogt", + .@"fcmp fast ole", + .@"fcmp fast olt", + .@"fcmp fast one", + .@"fcmp fast ord", + .@"fcmp fast true", + .@"fcmp fast ueq", + .@"fcmp fast uge", + .@"fcmp fast ugt", + .@"fcmp fast ule", + .@"fcmp fast ult", + .@"fcmp fast une", + .@"fcmp fast uno", + .@"fcmp oeq", + .@"fcmp oge", + .@"fcmp ogt", + .@"fcmp ole", + .@"fcmp olt", + .@"fcmp one", + .@"fcmp ord", + .@"fcmp true", + .@"fcmp ueq", + .@"fcmp uge", + .@"fcmp ugt", + .@"fcmp ule", + .@"fcmp ult", + .@"fcmp une", + .@"fcmp uno", + .fdiv, + .@"fdiv fast", + .fmul, + .@"fmul fast", + .frem, + .@"frem fast", + .fsub, + .@"fsub fast", + .@"icmp eq", + .@"icmp ne", + .@"icmp sge", + .@"icmp sgt", + .@"icmp sle", + .@"icmp slt", + .@"icmp uge", + .@"icmp ugt", + .@"icmp ule", + .@"icmp ult", + .lshr, + .@"lshr exact", + .mul, + .@"mul nsw", + .@"mul nuw", + .@"mul nuw nsw", + .@"or", + .sdiv, + .@"sdiv exact", + .shl, + .@"shl nsw", + .@"shl nuw", + .@"shl nuw nsw", + .srem, + .sub, + .@"sub nsw", + .@"sub nuw", + .@"sub nuw nsw", + .udiv, + .@"udiv exact", + .urem, + .xor, + => { + const extra = self.extraData(Instruction.Binary, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.Binary{ + .lhs = instructions.map(extra.lhs), + .rhs = instructions.map(extra.rhs), + }); + }, + .addrspacecast, + .bitcast, + .fpext, + .fptosi, + .fptoui, + .fptrunc, + .inttoptr, + .ptrtoint, + .sext, + .sitofp, + .trunc, + .uitofp, + .zext, + => { + const extra = self.extraData(Instruction.Cast, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.Cast{ + .val = instructions.map(extra.val), + .type = extra.type, + }); + }, + .alloca, + .@"alloca inalloca", + => { + const extra = self.extraData(Instruction.Alloca, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.Alloca{ + .type = extra.type, + .len = instructions.map(extra.len), + .info = extra.info, + }); + }, + .arg, + .block, + => unreachable, + .atomicrmw => { + const extra = self.extraData(Instruction.AtomicRmw, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.AtomicRmw{ + .info = extra.info, + .ptr = instructions.map(extra.ptr), + .val = instructions.map(extra.val), + }); + }, + .br, + .fence, + .@"ret void", + .@"unreachable", + => {}, + .br_cond => { + const extra = self.extraData(Instruction.BrCond, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.BrCond{ + .cond = instructions.map(extra.cond), + .then = extra.then, + .@"else" = extra.@"else", + .weights = extra.weights, + }); + }, + .call, + .@"call fast", + .@"musttail call", + .@"musttail call fast", + .@"notail call", + .@"notail call fast", + .@"tail call", + .@"tail call fast", + => { + var extra = self.extraDataTrail(Instruction.Call, instruction.data); + const args = extra.trail.next(extra.data.args_len, Value, self); + instruction.data = wip_extra.addExtra(Instruction.Call{ + .info = extra.data.info, + .attributes = extra.data.attributes, + .ty = extra.data.ty, + .callee = instructions.map(extra.data.callee), + .args_len = extra.data.args_len, + }); + wip_extra.appendMappedValues(args, instructions); + }, + .cmpxchg, + .@"cmpxchg weak", + => { + const extra = self.extraData(Instruction.CmpXchg, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.CmpXchg{ + .info = extra.info, + .ptr = instructions.map(extra.ptr), + .cmp = instructions.map(extra.cmp), + .new = instructions.map(extra.new), + }); + }, + .extractelement => { + const extra = self.extraData(Instruction.ExtractElement, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.ExtractElement{ + .val = instructions.map(extra.val), + .index = instructions.map(extra.index), + }); + }, + .extractvalue => { + var extra = self.extraDataTrail(Instruction.ExtractValue, instruction.data); + const indices = extra.trail.next(extra.data.indices_len, u32, self); + instruction.data = wip_extra.addExtra(Instruction.ExtractValue{ + .val = instructions.map(extra.data.val), + .indices_len = extra.data.indices_len, + }); + wip_extra.appendSlice(indices); + }, + .fneg, + .@"fneg fast", + .ret, + => instruction.data = @intFromEnum(instructions.map(@enumFromInt(instruction.data))), + .getelementptr, + .@"getelementptr inbounds", + => { + var extra = self.extraDataTrail(Instruction.GetElementPtr, instruction.data); + const indices = extra.trail.next(extra.data.indices_len, Value, self); + instruction.data = wip_extra.addExtra(Instruction.GetElementPtr{ + .type = extra.data.type, + .base = instructions.map(extra.data.base), + .indices_len = extra.data.indices_len, + }); + wip_extra.appendMappedValues(indices, instructions); + }, + .indirectbr => { + var extra = self.extraDataTrail(Instruction.IndirectBr, instruction.data); + const targets = extra.trail.next(extra.data.targets_len, Block.Index, self); + instruction.data = wip_extra.addExtra(Instruction.IndirectBr{ + .addr = instructions.map(extra.data.addr), + .targets_len = extra.data.targets_len, + }); + wip_extra.appendSlice(targets); + }, + .insertelement => { + const extra = self.extraData(Instruction.InsertElement, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.InsertElement{ + .val = instructions.map(extra.val), + .elem = instructions.map(extra.elem), + .index = instructions.map(extra.index), + }); + }, + .insertvalue => { + var extra = self.extraDataTrail(Instruction.InsertValue, instruction.data); + const indices = extra.trail.next(extra.data.indices_len, u32, self); + instruction.data = wip_extra.addExtra(Instruction.InsertValue{ + .val = instructions.map(extra.data.val), + .elem = instructions.map(extra.data.elem), + .indices_len = extra.data.indices_len, + }); + wip_extra.appendSlice(indices); + }, + .load, + .@"load atomic", + => { + const extra = self.extraData(Instruction.Load, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.Load{ + .type = extra.type, + .ptr = instructions.map(extra.ptr), + .info = extra.info, + }); + }, + .phi, + .@"phi fast", + => { + const incoming_len = current_block.incoming; + var extra = self.extraDataTrail(Instruction.Phi, instruction.data); + const incoming_vals = extra.trail.next(incoming_len, Value, self); + const incoming_blocks = extra.trail.next(incoming_len, Block.Index, self); + instruction.data = wip_extra.addExtra(Instruction.Phi{ + .type = extra.data.type, + }); + wip_extra.appendMappedValues(incoming_vals, instructions); + wip_extra.appendSlice(incoming_blocks); + }, + .select, + .@"select fast", + => { + const extra = self.extraData(Instruction.Select, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.Select{ + .cond = instructions.map(extra.cond), + .lhs = instructions.map(extra.lhs), + .rhs = instructions.map(extra.rhs), + }); + }, + .shufflevector => { + const extra = self.extraData(Instruction.ShuffleVector, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.ShuffleVector{ + .lhs = instructions.map(extra.lhs), + .rhs = instructions.map(extra.rhs), + .mask = instructions.map(extra.mask), + }); + }, + .store, + .@"store atomic", + => { + const extra = self.extraData(Instruction.Store, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.Store{ + .val = instructions.map(extra.val), + .ptr = instructions.map(extra.ptr), + .info = extra.info, + }); + }, + .@"switch" => { + var extra = self.extraDataTrail(Instruction.Switch, instruction.data); + const case_vals = extra.trail.next(extra.data.cases_len, Constant, self); + const case_blocks = extra.trail.next(extra.data.cases_len, Block.Index, self); + instruction.data = wip_extra.addExtra(Instruction.Switch{ + .val = instructions.map(extra.data.val), + .default = extra.data.default, + .cases_len = extra.data.cases_len, + .weights = extra.data.weights, + }); + wip_extra.appendSlice(case_vals); + wip_extra.appendSlice(case_blocks); + }, + .va_arg => { + const extra = self.extraData(Instruction.VaArg, instruction.data); + instruction.data = wip_extra.addExtra(Instruction.VaArg{ + .list = instructions.map(extra.list), + .type = extra.type, + }); + }, + } + function.instructions.appendAssumeCapacity(instruction); + names[@intFromEnum(new_instruction_index)] = try wip_name.map(if (self.strip) + if (old_instruction_index.hasResultWip(self)) .empty else .none + else + self.names.items[@intFromEnum(old_instruction_index)], "."); + + if (self.debug_locations.get(old_instruction_index)) |location| { + debug_locations.putAssumeCapacity(new_instruction_index, location); + } + + if (self.debug_values.getIndex(old_instruction_index)) |index| { + debug_values[index] = new_instruction_index; + } + + value_indices[@intFromEnum(new_instruction_index)] = value_index; + if (old_instruction_index.hasResultWip(self)) value_index += 1; + } + } + + assert(function.instructions.len == final_instructions_len); + function.extra = wip_extra.finish(); + function.blocks = blocks; + function.names = names.ptr; + function.value_indices = value_indices.ptr; + function.strip = self.strip; + function.debug_locations = debug_locations; + function.debug_values = debug_values; + } + + pub fn deinit(self: *WipFunction) void { + self.extra.deinit(self.builder.gpa); + self.debug_values.deinit(self.builder.gpa); + self.debug_locations.deinit(self.builder.gpa); + self.names.deinit(self.builder.gpa); + self.instructions.deinit(self.builder.gpa); + for (self.blocks.items) |*b| b.instructions.deinit(self.builder.gpa); + self.blocks.deinit(self.builder.gpa); + self.* = undefined; + } + + fn cmpTag( + self: *WipFunction, + tag: Instruction.Tag, + lhs: Value, + rhs: Value, + name: []const u8, + ) Allocator.Error!Value { + switch (tag) { + .@"fcmp false", + .@"fcmp fast false", + .@"fcmp fast oeq", + .@"fcmp fast oge", + .@"fcmp fast ogt", + .@"fcmp fast ole", + .@"fcmp fast olt", + .@"fcmp fast one", + .@"fcmp fast ord", + .@"fcmp fast true", + .@"fcmp fast ueq", + .@"fcmp fast uge", + .@"fcmp fast ugt", + .@"fcmp fast ule", + .@"fcmp fast ult", + .@"fcmp fast une", + .@"fcmp fast uno", + .@"fcmp oeq", + .@"fcmp oge", + .@"fcmp ogt", + .@"fcmp ole", + .@"fcmp olt", + .@"fcmp one", + .@"fcmp ord", + .@"fcmp true", + .@"fcmp ueq", + .@"fcmp uge", + .@"fcmp ugt", + .@"fcmp ule", + .@"fcmp ult", + .@"fcmp une", + .@"fcmp uno", + .@"icmp eq", + .@"icmp ne", + .@"icmp sge", + .@"icmp sgt", + .@"icmp sle", + .@"icmp slt", + .@"icmp uge", + .@"icmp ugt", + .@"icmp ule", + .@"icmp ult", + => assert(lhs.typeOfWip(self) == rhs.typeOfWip(self)), + else => unreachable, + } + _ = try lhs.typeOfWip(self).changeScalar(.i1, self.builder); + try self.ensureUnusedExtraCapacity(1, Instruction.Binary, 0); + const instruction = try self.addInst(name, .{ + .tag = tag, + .data = self.addExtraAssumeCapacity(Instruction.Binary{ + .lhs = lhs, + .rhs = rhs, + }), + }); + return instruction.toValue(); + } + + fn phiTag( + self: *WipFunction, + tag: Instruction.Tag, + ty: Type, + name: []const u8, + ) Allocator.Error!WipPhi { + switch (tag) { + .phi, .@"phi fast" => assert(try ty.isSized(self.builder)), + else => unreachable, + } + const incoming = self.cursor.block.ptrConst(self).incoming; + assert(incoming > 0); + try self.ensureUnusedExtraCapacity(1, Instruction.Phi, incoming * 2); + const instruction = try self.addInst(name, .{ + .tag = tag, + .data = self.addExtraAssumeCapacity(Instruction.Phi{ .type = ty }), + }); + _ = self.extra.addManyAsSliceAssumeCapacity(incoming * 2); + return .{ .block = self.cursor.block, .instruction = instruction }; + } + + fn selectTag( + self: *WipFunction, + tag: Instruction.Tag, + cond: Value, + lhs: Value, + rhs: Value, + name: []const u8, + ) Allocator.Error!Value { + switch (tag) { + .select, .@"select fast" => { + assert(cond.typeOfWip(self).scalarType(self.builder) == .i1); + assert(lhs.typeOfWip(self) == rhs.typeOfWip(self)); + }, + else => unreachable, + } + try self.ensureUnusedExtraCapacity(1, Instruction.Select, 0); + const instruction = try self.addInst(name, .{ + .tag = tag, + .data = self.addExtraAssumeCapacity(Instruction.Select{ + .cond = cond, + .lhs = lhs, + .rhs = rhs, + }), + }); + return instruction.toValue(); + } + + fn ensureUnusedExtraCapacity( + self: *WipFunction, + count: usize, + comptime Extra: type, + trail_len: usize, + ) Allocator.Error!void { + try self.extra.ensureUnusedCapacity( + self.builder.gpa, + count * (@typeInfo(Extra).@"struct".fields.len + trail_len), + ); + } + + fn addInst( + self: *WipFunction, + name: ?[]const u8, + instruction: Instruction, + ) Allocator.Error!Instruction.Index { + const block_instructions = &self.cursor.block.ptr(self).instructions; + try self.instructions.ensureUnusedCapacity(self.builder.gpa, 1); + if (!self.strip) { + try self.names.ensureUnusedCapacity(self.builder.gpa, 1); + try self.debug_locations.ensureUnusedCapacity(self.builder.gpa, 1); + } + try block_instructions.ensureUnusedCapacity(self.builder.gpa, 1); + const final_name = if (name) |n| + if (self.strip) .empty else try self.builder.string(n) + else + .none; + + const index: Instruction.Index = @enumFromInt(self.instructions.len); + self.instructions.appendAssumeCapacity(instruction); + if (!self.strip) { + self.names.appendAssumeCapacity(final_name); + if (block_instructions.items.len == 0 or + !std.meta.eql(self.debug_location, self.prev_debug_location)) + { + self.debug_locations.putAssumeCapacity(index, self.debug_location); + self.prev_debug_location = self.debug_location; + } + } + block_instructions.insertAssumeCapacity(self.cursor.instruction, index); + self.cursor.instruction += 1; + return index; + } + + fn addExtraAssumeCapacity(self: *WipFunction, extra: anytype) Instruction.ExtraIndex { + const result: Instruction.ExtraIndex = @intCast(self.extra.items.len); + inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| { + const value = @field(extra, field.name); + self.extra.appendAssumeCapacity(switch (field.type) { + u32 => value, + Alignment, + AtomicOrdering, + Block.Index, + FunctionAttributes, + Type, + Value, + Instruction.BrCond.Weights, + => @intFromEnum(value), + MemoryAccessInfo, + Instruction.Alloca.Info, + Instruction.Call.Info, + => @bitCast(value), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), + }); + } + return result; + } + + const ExtraDataTrail = struct { + index: Instruction.ExtraIndex, + + fn nextMut(self: *ExtraDataTrail, len: u32, comptime Item: type, wip: *WipFunction) []Item { + const items: []Item = @ptrCast(wip.extra.items[self.index..][0..len]); + self.index += @intCast(len); + return items; + } + + fn next( + self: *ExtraDataTrail, + len: u32, + comptime Item: type, + wip: *const WipFunction, + ) []const Item { + const items: []const Item = @ptrCast(wip.extra.items[self.index..][0..len]); + self.index += @intCast(len); + return items; + } + }; + + fn extraDataTrail( + self: *const WipFunction, + comptime T: type, + index: Instruction.ExtraIndex, + ) struct { data: T, trail: ExtraDataTrail } { + var result: T = undefined; + const fields = @typeInfo(T).@"struct".fields; + inline for (fields, self.extra.items[index..][0..fields.len]) |field, value| + @field(result, field.name) = switch (field.type) { + u32 => value, + Alignment, + AtomicOrdering, + Block.Index, + FunctionAttributes, + Type, + Value, + Instruction.BrCond.Weights, + => @enumFromInt(value), + MemoryAccessInfo, + Instruction.Alloca.Info, + Instruction.Call.Info, + => @bitCast(value), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), + }; + return .{ + .data = result, + .trail = .{ .index = index + @as(Type.Item.ExtraIndex, @intCast(fields.len)) }, + }; + } + + fn extraData(self: *const WipFunction, comptime T: type, index: Instruction.ExtraIndex) T { + return self.extraDataTrail(T, index).data; + } +}; + +pub const FloatCondition = enum(u4) { + oeq = 1, + ogt = 2, + oge = 3, + olt = 4, + ole = 5, + one = 6, + ord = 7, + uno = 8, + ueq = 9, + ugt = 10, + uge = 11, + ult = 12, + ule = 13, + une = 14, +}; + +pub const IntegerCondition = enum(u6) { + eq = 32, + ne = 33, + ugt = 34, + uge = 35, + ult = 36, + ule = 37, + sgt = 38, + sge = 39, + slt = 40, + sle = 41, +}; + +pub const MemoryAccessKind = enum(u1) { + normal, + @"volatile", + + pub fn format( + self: MemoryAccessKind, + comptime prefix: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (self != .normal) try writer.print("{s}{s}", .{ prefix, @tagName(self) }); + } +}; + +pub const SyncScope = enum(u1) { + singlethread, + system, + + pub fn format( + self: SyncScope, + comptime prefix: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (self != .system) try writer.print( + \\{s}syncscope("{s}") + , .{ prefix, @tagName(self) }); + } +}; + +pub const AtomicOrdering = enum(u3) { + none = 0, + unordered = 1, + monotonic = 2, + acquire = 3, + release = 4, + acq_rel = 5, + seq_cst = 6, + + pub fn format( + self: AtomicOrdering, + comptime prefix: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (self != .none) try writer.print("{s}{s}", .{ prefix, @tagName(self) }); + } +}; + +const MemoryAccessInfo = packed struct(u32) { + access_kind: MemoryAccessKind = .normal, + atomic_rmw_operation: Function.Instruction.AtomicRmw.Operation = .none, + sync_scope: SyncScope, + success_ordering: AtomicOrdering, + failure_ordering: AtomicOrdering = .none, + alignment: Alignment = .default, + _: u13 = undefined, +}; + +pub const FastMath = packed struct(u8) { + unsafe_algebra: bool = false, // Legacy + nnan: bool = false, + ninf: bool = false, + nsz: bool = false, + arcp: bool = false, + contract: bool = false, + afn: bool = false, + reassoc: bool = false, + + pub const fast = FastMath{ + .nnan = true, + .ninf = true, + .nsz = true, + .arcp = true, + .contract = true, + .afn = true, + .reassoc = true, + }; +}; + +pub const FastMathKind = enum { + normal, + fast, + + pub fn toCallKind(self: FastMathKind) Function.Instruction.Call.Kind { + return switch (self) { + .normal => .normal, + .fast => .fast, + }; + } +}; + +pub const Constant = enum(u32) { + false, + true, + @"0", + @"1", + none, + no_init = (1 << 30) - 1, + _, + + const first_global: Constant = @enumFromInt(1 << 29); + + pub const Tag = enum(u7) { + positive_integer, + negative_integer, + half, + bfloat, + float, + double, + fp128, + x86_fp80, + ppc_fp128, + null, + none, + structure, + packed_structure, + array, + string, + vector, + splat, + zeroinitializer, + undef, + poison, + blockaddress, + dso_local_equivalent, + no_cfi, + trunc, + ptrtoint, + inttoptr, + bitcast, + addrspacecast, + getelementptr, + @"getelementptr inbounds", + add, + @"add nsw", + @"add nuw", + sub, + @"sub nsw", + @"sub nuw", + shl, + xor, + @"asm", + @"asm sideeffect", + @"asm alignstack", + @"asm sideeffect alignstack", + @"asm inteldialect", + @"asm sideeffect inteldialect", + @"asm alignstack inteldialect", + @"asm sideeffect alignstack inteldialect", + @"asm unwind", + @"asm sideeffect unwind", + @"asm alignstack unwind", + @"asm sideeffect alignstack unwind", + @"asm inteldialect unwind", + @"asm sideeffect inteldialect unwind", + @"asm alignstack inteldialect unwind", + @"asm sideeffect alignstack inteldialect unwind", + + pub fn toBinaryOpcode(self: Tag) BinaryOpcode { + return switch (self) { + .add, + .@"add nsw", + .@"add nuw", + => .add, + .sub, + .@"sub nsw", + .@"sub nuw", + => .sub, + .shl => .shl, + .xor => .xor, + else => unreachable, + }; + } + + pub fn toCastOpcode(self: Tag) CastOpcode { + return switch (self) { + .trunc => .trunc, + .ptrtoint => .ptrtoint, + .inttoptr => .inttoptr, + .bitcast => .bitcast, + .addrspacecast => .addrspacecast, + else => unreachable, + }; + } + }; + + pub const Item = struct { + tag: Tag, + data: ExtraIndex, + + const ExtraIndex = u32; + }; + + pub const Integer = packed struct(u64) { + type: Type, + limbs_len: u32, + + pub const limbs = @divExact(@bitSizeOf(Integer), @bitSizeOf(std.math.big.Limb)); + }; + + pub const Double = struct { + lo: u32, + hi: u32, + }; + + pub const Fp80 = struct { + lo_lo: u32, + lo_hi: u32, + hi: u32, + }; + + pub const Fp128 = struct { + lo_lo: u32, + lo_hi: u32, + hi_lo: u32, + hi_hi: u32, + }; + + pub const Aggregate = struct { + type: Type, + //fields: [type.aggregateLen(builder)]Constant, + }; + + pub const Splat = extern struct { + type: Type, + value: Constant, + }; + + pub const BlockAddress = extern struct { + function: Function.Index, + block: Function.Block.Index, + }; + + pub const Cast = extern struct { + val: Constant, + type: Type, + + pub const Signedness = enum { unsigned, signed, unneeded }; + }; + + pub const GetElementPtr = struct { + type: Type, + base: Constant, + info: Info, + //indices: [info.indices_len]Constant, + + pub const Kind = enum { normal, inbounds }; + pub const InRangeIndex = enum(u16) { none = std.math.maxInt(u16), _ }; + pub const Info = packed struct(u32) { indices_len: u16, inrange: InRangeIndex }; + }; + + pub const Binary = extern struct { + lhs: Constant, + rhs: Constant, + }; + + pub const Assembly = extern struct { + type: Type, + assembly: String, + constraints: String, + + pub const Info = packed struct { + sideeffect: bool = false, + alignstack: bool = false, + inteldialect: bool = false, + unwind: bool = false, + }; + }; + + pub fn unwrap(self: Constant) union(enum) { + constant: u30, + global: Global.Index, + } { + return if (@intFromEnum(self) < @intFromEnum(first_global)) + .{ .constant = @intCast(@intFromEnum(self)) } + else + .{ .global = @enumFromInt(@intFromEnum(self) - @intFromEnum(first_global)) }; + } + + pub fn toValue(self: Constant) Value { + return @enumFromInt(Value.first_constant + @intFromEnum(self)); + } + + pub fn typeOf(self: Constant, builder: *Builder) Type { + switch (self.unwrap()) { + .constant => |constant| { + const item = builder.constant_items.get(constant); + return switch (item.tag) { + .positive_integer, + .negative_integer, + => @as( + *align(@alignOf(std.math.big.Limb)) Integer, + @ptrCast(builder.constant_limbs.items[item.data..][0..Integer.limbs]), + ).type, + .half => .half, + .bfloat => .bfloat, + .float => .float, + .double => .double, + .fp128 => .fp128, + .x86_fp80 => .x86_fp80, + .ppc_fp128 => .ppc_fp128, + .null, + .none, + .zeroinitializer, + .undef, + .poison, + => @enumFromInt(item.data), + .structure, + .packed_structure, + .array, + .vector, + => builder.constantExtraData(Aggregate, item.data).type, + .splat => builder.constantExtraData(Splat, item.data).type, + .string => builder.arrayTypeAssumeCapacity( + @as(String, @enumFromInt(item.data)).slice(builder).?.len, + .i8, + ), + .blockaddress => builder.ptrTypeAssumeCapacity( + builder.constantExtraData(BlockAddress, item.data) + .function.ptrConst(builder).global.ptrConst(builder).addr_space, + ), + .dso_local_equivalent, + .no_cfi, + => builder.ptrTypeAssumeCapacity(@as(Function.Index, @enumFromInt(item.data)) + .ptrConst(builder).global.ptrConst(builder).addr_space), + .trunc, + .ptrtoint, + .inttoptr, + .bitcast, + .addrspacecast, + => builder.constantExtraData(Cast, item.data).type, + .getelementptr, + .@"getelementptr inbounds", + => { + var extra = builder.constantExtraDataTrail(GetElementPtr, item.data); + const indices = + extra.trail.next(extra.data.info.indices_len, Constant, builder); + const base_ty = extra.data.base.typeOf(builder); + if (!base_ty.isVector(builder)) for (indices) |index| { + const index_ty = index.typeOf(builder); + if (!index_ty.isVector(builder)) continue; + return index_ty.changeScalarAssumeCapacity(base_ty, builder); + }; + return base_ty; + }, + .add, + .@"add nsw", + .@"add nuw", + .sub, + .@"sub nsw", + .@"sub nuw", + .shl, + .xor, + => builder.constantExtraData(Binary, item.data).lhs.typeOf(builder), + .@"asm", + .@"asm sideeffect", + .@"asm alignstack", + .@"asm sideeffect alignstack", + .@"asm inteldialect", + .@"asm sideeffect inteldialect", + .@"asm alignstack inteldialect", + .@"asm sideeffect alignstack inteldialect", + .@"asm unwind", + .@"asm sideeffect unwind", + .@"asm alignstack unwind", + .@"asm sideeffect alignstack unwind", + .@"asm inteldialect unwind", + .@"asm sideeffect inteldialect unwind", + .@"asm alignstack inteldialect unwind", + .@"asm sideeffect alignstack inteldialect unwind", + => .ptr, + }; + }, + .global => |global| return builder.ptrTypeAssumeCapacity( + global.ptrConst(builder).addr_space, + ), + } + } + + pub fn isZeroInit(self: Constant, builder: *const Builder) bool { + switch (self.unwrap()) { + .constant => |constant| { + const item = builder.constant_items.get(constant); + return switch (item.tag) { + .positive_integer => { + const extra: *align(@alignOf(std.math.big.Limb)) Integer = + @ptrCast(builder.constant_limbs.items[item.data..][0..Integer.limbs]); + const limbs = builder.constant_limbs + .items[item.data + Integer.limbs ..][0..extra.limbs_len]; + return std.mem.eql(std.math.big.Limb, limbs, &.{0}); + }, + .half, .bfloat, .float => item.data == 0, + .double => { + const extra = builder.constantExtraData(Constant.Double, item.data); + return extra.lo == 0 and extra.hi == 0; + }, + .fp128, .ppc_fp128 => { + const extra = builder.constantExtraData(Constant.Fp128, item.data); + return extra.lo_lo == 0 and extra.lo_hi == 0 and + extra.hi_lo == 0 and extra.hi_hi == 0; + }, + .x86_fp80 => { + const extra = builder.constantExtraData(Constant.Fp80, item.data); + return extra.lo_lo == 0 and extra.lo_hi == 0 and extra.hi == 0; + }, + .vector => { + var extra = builder.constantExtraDataTrail(Aggregate, item.data); + const len: u32 = @intCast(extra.data.type.aggregateLen(builder)); + const vals = extra.trail.next(len, Constant, builder); + for (vals) |val| if (!val.isZeroInit(builder)) return false; + return true; + }, + .null, .zeroinitializer => true, + else => false, + }; + }, + .global => return false, + } + } + + pub fn getBase(self: Constant, builder: *const Builder) Global.Index { + var cur = self; + while (true) switch (cur.unwrap()) { + .constant => |constant| { + const item = builder.constant_items.get(constant); + switch (item.tag) { + .ptrtoint, + .inttoptr, + .bitcast, + => cur = builder.constantExtraData(Cast, item.data).val, + .getelementptr => cur = builder.constantExtraData(GetElementPtr, item.data).base, + .add => { + const extra = builder.constantExtraData(Binary, item.data); + const lhs_base = extra.lhs.getBase(builder); + const rhs_base = extra.rhs.getBase(builder); + return if (lhs_base != .none and rhs_base != .none) + .none + else if (lhs_base != .none) lhs_base else rhs_base; + }, + .sub => { + const extra = builder.constantExtraData(Binary, item.data); + if (extra.rhs.getBase(builder) != .none) return .none; + cur = extra.lhs; + }, + else => return .none, + } + }, + .global => |global| switch (global.ptrConst(builder).kind) { + .alias => |alias| cur = alias.ptrConst(builder).aliasee, + .variable, .function => return global, + .replaced => unreachable, + }, + }; + } + + const FormatData = struct { + constant: Constant, + builder: *Builder, + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_| + @compileError("invalid format string: '" ++ fmt_str ++ "'"); + if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) { + if (data.constant == .no_init) return; + try writer.writeByte(','); + } + if (comptime std.mem.indexOfScalar(u8, fmt_str, ' ') != null) { + if (data.constant == .no_init) return; + try writer.writeByte(' '); + } + if (comptime std.mem.indexOfScalar(u8, fmt_str, '%') != null) + try writer.print("{%} ", .{data.constant.typeOf(data.builder).fmt(data.builder)}); + assert(data.constant != .no_init); + if (std.enums.tagName(Constant, data.constant)) |name| return writer.writeAll(name); + switch (data.constant.unwrap()) { + .constant => |constant| { + const item = data.builder.constant_items.get(constant); + switch (item.tag) { + .positive_integer, + .negative_integer, + => |tag| { + const extra: *align(@alignOf(std.math.big.Limb)) const Integer = + @ptrCast(data.builder.constant_limbs.items[item.data..][0..Integer.limbs]); + const limbs = data.builder.constant_limbs + .items[item.data + Integer.limbs ..][0..extra.limbs_len]; + const bigint: std.math.big.int.Const = .{ + .limbs = limbs, + .positive = switch (tag) { + .positive_integer => true, + .negative_integer => false, + else => unreachable, + }, + }; + const ExpectedContents = extern struct { + const expected_limbs = @divExact(512, @bitSizeOf(std.math.big.Limb)); + string: [ + (std.math.big.int.Const{ + .limbs = &([1]std.math.big.Limb{ + std.math.maxInt(std.math.big.Limb), + } ** expected_limbs), + .positive = false, + }).sizeInBaseUpperBound(10) + ]u8, + limbs: [ + std.math.big.int.calcToStringLimbsBufferLen(expected_limbs, 10) + ]std.math.big.Limb, + }; + var stack align(@alignOf(ExpectedContents)) = + std.heap.stackFallback(@sizeOf(ExpectedContents), data.builder.gpa); + const allocator = stack.get(); + const str = try bigint.toStringAlloc(allocator, 10, undefined); + defer allocator.free(str); + try writer.writeAll(str); + }, + .half, + .bfloat, + => |tag| try writer.print("0x{c}{X:0>4}", .{ @as(u8, switch (tag) { + .half => 'H', + .bfloat => 'R', + else => unreachable, + }), item.data >> switch (tag) { + .half => 0, + .bfloat => 16, + else => unreachable, + } }), + .float => { + const Float = struct { + fn Repr(comptime T: type) type { + return packed struct(std.meta.Int(.unsigned, @bitSizeOf(T))) { + mantissa: std.meta.Int(.unsigned, std.math.floatMantissaBits(T)), + exponent: std.meta.Int(.unsigned, std.math.floatExponentBits(T)), + sign: u1, + }; + } + }; + const Mantissa64 = std.meta.FieldType(Float.Repr(f64), .mantissa); + const Exponent32 = std.meta.FieldType(Float.Repr(f32), .exponent); + const Exponent64 = std.meta.FieldType(Float.Repr(f64), .exponent); + + const repr: Float.Repr(f32) = @bitCast(item.data); + const denormal_shift = switch (repr.exponent) { + std.math.minInt(Exponent32) => @as( + std.math.Log2Int(Mantissa64), + @clz(repr.mantissa), + ) + 1, + else => 0, + }; + try writer.print("0x{X:0>16}", .{@as(u64, @bitCast(Float.Repr(f64){ + .mantissa = std.math.shl( + Mantissa64, + repr.mantissa, + std.math.floatMantissaBits(f64) - std.math.floatMantissaBits(f32) + + denormal_shift, + ), + .exponent = switch (repr.exponent) { + std.math.minInt(Exponent32) => if (repr.mantissa > 0) + @as(Exponent64, std.math.floatExponentMin(f32) + + std.math.floatExponentMax(f64)) - denormal_shift + else + std.math.minInt(Exponent64), + else => @as(Exponent64, repr.exponent) + + (std.math.floatExponentMax(f64) - std.math.floatExponentMax(f32)), + std.math.maxInt(Exponent32) => std.math.maxInt(Exponent64), + }, + .sign = repr.sign, + }))}); + }, + .double => { + const extra = data.builder.constantExtraData(Double, item.data); + try writer.print("0x{X:0>8}{X:0>8}", .{ extra.hi, extra.lo }); + }, + .fp128, + .ppc_fp128, + => |tag| { + const extra = data.builder.constantExtraData(Fp128, item.data); + try writer.print("0x{c}{X:0>8}{X:0>8}{X:0>8}{X:0>8}", .{ + @as(u8, switch (tag) { + .fp128 => 'L', + .ppc_fp128 => 'M', + else => unreachable, + }), + extra.lo_hi, + extra.lo_lo, + extra.hi_hi, + extra.hi_lo, + }); + }, + .x86_fp80 => { + const extra = data.builder.constantExtraData(Fp80, item.data); + try writer.print("0xK{X:0>4}{X:0>8}{X:0>8}", .{ + extra.hi, extra.lo_hi, extra.lo_lo, + }); + }, + .null, + .none, + .zeroinitializer, + .undef, + .poison, + => |tag| try writer.writeAll(@tagName(tag)), + .structure, + .packed_structure, + .array, + .vector, + => |tag| { + var extra = data.builder.constantExtraDataTrail(Aggregate, item.data); + const len: u32 = @intCast(extra.data.type.aggregateLen(data.builder)); + const vals = extra.trail.next(len, Constant, data.builder); + try writer.writeAll(switch (tag) { + .structure => "{ ", + .packed_structure => "<{ ", + .array => "[", + .vector => "<", + else => unreachable, + }); + for (vals, 0..) |val, index| { + if (index > 0) try writer.writeAll(", "); + try writer.print("{%}", .{val.fmt(data.builder)}); + } + try writer.writeAll(switch (tag) { + .structure => " }", + .packed_structure => " }>", + .array => "]", + .vector => ">", + else => unreachable, + }); + }, + .splat => { + const extra = data.builder.constantExtraData(Splat, item.data); + const len = extra.type.vectorLen(data.builder); + try writer.writeByte('<'); + for (0..len) |index| { + if (index > 0) try writer.writeAll(", "); + try writer.print("{%}", .{extra.value.fmt(data.builder)}); + } + try writer.writeByte('>'); + }, + .string => try writer.print("c{\"}", .{ + @as(String, @enumFromInt(item.data)).fmt(data.builder), + }), + .blockaddress => |tag| { + const extra = data.builder.constantExtraData(BlockAddress, item.data); + const function = extra.function.ptrConst(data.builder); + try writer.print("{s}({}, {})", .{ + @tagName(tag), + function.global.fmt(data.builder), + extra.block.toInst(function).fmt(extra.function, data.builder), + }); + }, + .dso_local_equivalent, + .no_cfi, + => |tag| { + const function: Function.Index = @enumFromInt(item.data); + try writer.print("{s} {}", .{ + @tagName(tag), + function.ptrConst(data.builder).global.fmt(data.builder), + }); + }, + .trunc, + .ptrtoint, + .inttoptr, + .bitcast, + .addrspacecast, + => |tag| { + const extra = data.builder.constantExtraData(Cast, item.data); + try writer.print("{s} ({%} to {%})", .{ + @tagName(tag), + extra.val.fmt(data.builder), + extra.type.fmt(data.builder), + }); + }, + .getelementptr, + .@"getelementptr inbounds", + => |tag| { + var extra = data.builder.constantExtraDataTrail(GetElementPtr, item.data); + const indices = + extra.trail.next(extra.data.info.indices_len, Constant, data.builder); + try writer.print("{s} ({%}, {%}", .{ + @tagName(tag), + extra.data.type.fmt(data.builder), + extra.data.base.fmt(data.builder), + }); + for (indices) |index| try writer.print(", {%}", .{index.fmt(data.builder)}); + try writer.writeByte(')'); + }, + .add, + .@"add nsw", + .@"add nuw", + .sub, + .@"sub nsw", + .@"sub nuw", + .shl, + .xor, + => |tag| { + const extra = data.builder.constantExtraData(Binary, item.data); + try writer.print("{s} ({%}, {%})", .{ + @tagName(tag), + extra.lhs.fmt(data.builder), + extra.rhs.fmt(data.builder), + }); + }, + .@"asm", + .@"asm sideeffect", + .@"asm alignstack", + .@"asm sideeffect alignstack", + .@"asm inteldialect", + .@"asm sideeffect inteldialect", + .@"asm alignstack inteldialect", + .@"asm sideeffect alignstack inteldialect", + .@"asm unwind", + .@"asm sideeffect unwind", + .@"asm alignstack unwind", + .@"asm sideeffect alignstack unwind", + .@"asm inteldialect unwind", + .@"asm sideeffect inteldialect unwind", + .@"asm alignstack inteldialect unwind", + .@"asm sideeffect alignstack inteldialect unwind", + => |tag| { + const extra = data.builder.constantExtraData(Assembly, item.data); + try writer.print("{s} {\"}, {\"}", .{ + @tagName(tag), + extra.assembly.fmt(data.builder), + extra.constraints.fmt(data.builder), + }); + }, + } + }, + .global => |global| try writer.print("{}", .{global.fmt(data.builder)}), + } + } + pub fn fmt(self: Constant, builder: *Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .constant = self, .builder = builder } }; + } +}; + +pub const Value = enum(u32) { + none = std.math.maxInt(u31), + false = first_constant + @intFromEnum(Constant.false), + true = first_constant + @intFromEnum(Constant.true), + @"0" = first_constant + @intFromEnum(Constant.@"0"), + @"1" = first_constant + @intFromEnum(Constant.@"1"), + _, + + const first_constant = 1 << 30; + const first_metadata = 1 << 31; + + pub fn unwrap(self: Value) union(enum) { + instruction: Function.Instruction.Index, + constant: Constant, + metadata: Metadata, + } { + return if (@intFromEnum(self) < first_constant) + .{ .instruction = @enumFromInt(@intFromEnum(self)) } + else if (@intFromEnum(self) < first_metadata) + .{ .constant = @enumFromInt(@intFromEnum(self) - first_constant) } + else + .{ .metadata = @enumFromInt(@intFromEnum(self) - first_metadata) }; + } + + pub fn typeOfWip(self: Value, wip: *const WipFunction) Type { + return switch (self.unwrap()) { + .instruction => |instruction| instruction.typeOfWip(wip), + .constant => |constant| constant.typeOf(wip.builder), + .metadata => .metadata, + }; + } + + pub fn typeOf(self: Value, function: Function.Index, builder: *Builder) Type { + return switch (self.unwrap()) { + .instruction => |instruction| instruction.typeOf(function, builder), + .constant => |constant| constant.typeOf(builder), + .metadata => .metadata, + }; + } + + pub fn toConst(self: Value) ?Constant { + return switch (self.unwrap()) { + .instruction, .metadata => null, + .constant => |constant| constant, + }; + } + + const FormatData = struct { + value: Value, + function: Function.Index, + builder: *Builder, + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + fmt_opts: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + switch (data.value.unwrap()) { + .instruction => |instruction| try Function.Instruction.Index.format(.{ + .instruction = instruction, + .function = data.function, + .builder = data.builder, + }, fmt_str, fmt_opts, writer), + .constant => |constant| try Constant.format(.{ + .constant = constant, + .builder = data.builder, + }, fmt_str, fmt_opts, writer), + .metadata => unreachable, + } + } + pub fn fmt(self: Value, function: Function.Index, builder: *Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .value = self, .function = function, .builder = builder } }; + } +}; + +pub const MetadataString = enum(u32) { + none = 0, + _, + + pub fn slice(self: MetadataString, builder: *const Builder) []const u8 { + const index = @intFromEnum(self); + const start = builder.metadata_string_indices.items[index]; + const end = builder.metadata_string_indices.items[index + 1]; + return builder.metadata_string_bytes.items[start..end]; + } + + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: Adapter, key: []const u8) u32 { + return @truncate(std.hash.Wyhash.hash(0, key)); + } + pub fn eql(ctx: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool { + const rhs_metadata_string: MetadataString = @enumFromInt(rhs_index); + return std.mem.eql(u8, lhs_key, rhs_metadata_string.slice(ctx.builder)); + } + }; + + const FormatData = struct { + metadata_string: MetadataString, + builder: *const Builder, + }; + fn format( + data: FormatData, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, writer); + } + fn fmt(self: MetadataString, builder: *const Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .metadata_string = self, .builder = builder } }; + } +}; + +pub const Metadata = enum(u32) { + none = 0, + empty_tuple = 1, + _, + + const first_forward_reference = 1 << 29; + const first_local_metadata = 1 << 30; + + pub const Tag = enum(u6) { + none, + file, + compile_unit, + @"compile_unit optimized", + subprogram, + @"subprogram local", + @"subprogram definition", + @"subprogram local definition", + @"subprogram optimized", + @"subprogram optimized local", + @"subprogram optimized definition", + @"subprogram optimized local definition", + lexical_block, + location, + basic_bool_type, + basic_unsigned_type, + basic_signed_type, + basic_float_type, + composite_struct_type, + composite_union_type, + composite_enumeration_type, + composite_array_type, + composite_vector_type, + derived_pointer_type, + derived_member_type, + subroutine_type, + enumerator_unsigned, + enumerator_signed_positive, + enumerator_signed_negative, + subrange, + tuple, + str_tuple, + module_flag, + expression, + local_var, + parameter, + global_var, + @"global_var local", + global_var_expression, + constant, + + pub fn isInline(tag: Tag) bool { + return switch (tag) { + .none, + .expression, + .constant, + => true, + .file, + .compile_unit, + .@"compile_unit optimized", + .subprogram, + .@"subprogram local", + .@"subprogram definition", + .@"subprogram local definition", + .@"subprogram optimized", + .@"subprogram optimized local", + .@"subprogram optimized definition", + .@"subprogram optimized local definition", + .lexical_block, + .location, + .basic_bool_type, + .basic_unsigned_type, + .basic_signed_type, + .basic_float_type, + .composite_struct_type, + .composite_union_type, + .composite_enumeration_type, + .composite_array_type, + .composite_vector_type, + .derived_pointer_type, + .derived_member_type, + .subroutine_type, + .enumerator_unsigned, + .enumerator_signed_positive, + .enumerator_signed_negative, + .subrange, + .tuple, + .str_tuple, + .module_flag, + .local_var, + .parameter, + .global_var, + .@"global_var local", + .global_var_expression, + => false, + }; + } + }; + + pub fn isInline(self: Metadata, builder: *const Builder) bool { + return builder.metadata_items.items(.tag)[@intFromEnum(self)].isInline(); + } + + pub fn unwrap(self: Metadata, builder: *const Builder) Metadata { + var metadata = self; + while (@intFromEnum(metadata) >= Metadata.first_forward_reference and + @intFromEnum(metadata) < Metadata.first_local_metadata) + { + const index = @intFromEnum(metadata) - Metadata.first_forward_reference; + metadata = builder.metadata_forward_references.items[index]; + assert(metadata != .none); + } + return metadata; + } + + pub const Item = struct { + tag: Tag, + data: ExtraIndex, + + const ExtraIndex = u32; + }; + + pub const DIFlags = packed struct(u32) { + Visibility: enum(u2) { Zero, Private, Protected, Public } = .Zero, + FwdDecl: bool = false, + AppleBlock: bool = false, + ReservedBit4: u1 = 0, + Virtual: bool = false, + Artificial: bool = false, + Explicit: bool = false, + Prototyped: bool = false, + ObjcClassComplete: bool = false, + ObjectPointer: bool = false, + Vector: bool = false, + StaticMember: bool = false, + LValueReference: bool = false, + RValueReference: bool = false, + ExportSymbols: bool = false, + Inheritance: enum(u2) { + Zero, + SingleInheritance, + MultipleInheritance, + VirtualInheritance, + } = .Zero, + IntroducedVirtual: bool = false, + BitField: bool = false, + NoReturn: bool = false, + ReservedBit21: u1 = 0, + TypePassbyValue: bool = false, + TypePassbyReference: bool = false, + EnumClass: bool = false, + Thunk: bool = false, + NonTrivial: bool = false, + BigEndian: bool = false, + LittleEndian: bool = false, + AllCallsDescribed: bool = false, + Unused: u2 = 0, + + pub fn format( + self: DIFlags, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + var need_pipe = false; + inline for (@typeInfo(DIFlags).@"struct".fields) |field| { + switch (@typeInfo(field.type)) { + .bool => if (@field(self, field.name)) { + if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; + try writer.print("DIFlag{s}", .{field.name}); + }, + .@"enum" => if (@field(self, field.name) != .Zero) { + if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; + try writer.print("DIFlag{s}", .{@tagName(@field(self, field.name))}); + }, + .int => assert(@field(self, field.name) == 0), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ + @typeName(field.type)), + } + } + if (!need_pipe) try writer.writeByte('0'); + } + }; + + pub const File = struct { + filename: MetadataString, + directory: MetadataString, + }; + + pub const CompileUnit = struct { + pub const Options = struct { + optimized: bool, + }; + + file: Metadata, + producer: MetadataString, + enums: Metadata, + globals: Metadata, + }; + + pub const Subprogram = struct { + pub const Options = struct { + di_flags: DIFlags, + sp_flags: DISPFlags, + }; + + pub const DISPFlags = packed struct(u32) { + Virtuality: enum(u2) { Zero, Virtual, PureVirtual } = .Zero, + LocalToUnit: bool = false, + Definition: bool = false, + Optimized: bool = false, + Pure: bool = false, + Elemental: bool = false, + Recursive: bool = false, + MainSubprogram: bool = false, + Deleted: bool = false, + ReservedBit10: u1 = 0, + ObjCDirect: bool = false, + Unused: u20 = 0, + + pub fn format( + self: DISPFlags, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + var need_pipe = false; + inline for (@typeInfo(DISPFlags).@"struct".fields) |field| { + switch (@typeInfo(field.type)) { + .bool => if (@field(self, field.name)) { + if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; + try writer.print("DISPFlag{s}", .{field.name}); + }, + .@"enum" => if (@field(self, field.name) != .Zero) { + if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; + try writer.print("DISPFlag{s}", .{@tagName(@field(self, field.name))}); + }, + .int => assert(@field(self, field.name) == 0), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ + @typeName(field.type)), + } + } + if (!need_pipe) try writer.writeByte('0'); + } + }; + + file: Metadata, + name: MetadataString, + linkage_name: MetadataString, + line: u32, + scope_line: u32, + ty: Metadata, + di_flags: DIFlags, + compile_unit: Metadata, + }; + + pub const LexicalBlock = struct { + scope: Metadata, + file: Metadata, + line: u32, + column: u32, + }; + + pub const Location = struct { + line: u32, + column: u32, + scope: Metadata, + inlined_at: Metadata, + }; + + pub const BasicType = struct { + name: MetadataString, + size_in_bits_lo: u32, + size_in_bits_hi: u32, + + pub fn bitSize(self: BasicType) u64 { + return @as(u64, self.size_in_bits_hi) << 32 | self.size_in_bits_lo; + } + }; + + pub const CompositeType = struct { + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits_lo: u32, + size_in_bits_hi: u32, + align_in_bits_lo: u32, + align_in_bits_hi: u32, + fields_tuple: Metadata, + + pub fn bitSize(self: CompositeType) u64 { + return @as(u64, self.size_in_bits_hi) << 32 | self.size_in_bits_lo; + } + pub fn bitAlign(self: CompositeType) u64 { + return @as(u64, self.align_in_bits_hi) << 32 | self.align_in_bits_lo; + } + }; + + pub const DerivedType = struct { + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits_lo: u32, + size_in_bits_hi: u32, + align_in_bits_lo: u32, + align_in_bits_hi: u32, + offset_in_bits_lo: u32, + offset_in_bits_hi: u32, + + pub fn bitSize(self: DerivedType) u64 { + return @as(u64, self.size_in_bits_hi) << 32 | self.size_in_bits_lo; + } + pub fn bitAlign(self: DerivedType) u64 { + return @as(u64, self.align_in_bits_hi) << 32 | self.align_in_bits_lo; + } + pub fn bitOffset(self: DerivedType) u64 { + return @as(u64, self.offset_in_bits_hi) << 32 | self.offset_in_bits_lo; + } + }; + + pub const SubroutineType = struct { + types_tuple: Metadata, + }; + + pub const Enumerator = struct { + name: MetadataString, + bit_width: u32, + limbs_index: u32, + limbs_len: u32, + }; + + pub const Subrange = struct { + lower_bound: Metadata, + count: Metadata, + }; + + pub const Expression = struct { + elements_len: u32, + + // elements: [elements_len]u32 + }; + + pub const Tuple = struct { + elements_len: u32, + + // elements: [elements_len]Metadata + }; + + pub const StrTuple = struct { + str: MetadataString, + elements_len: u32, + + // elements: [elements_len]Metadata + }; + + pub const ModuleFlag = struct { + behavior: Metadata, + name: MetadataString, + constant: Metadata, + }; + + pub const LocalVar = struct { + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + }; + + pub const Parameter = struct { + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + arg_no: u32, + }; + + pub const GlobalVar = struct { + pub const Options = struct { + local: bool, + }; + + name: MetadataString, + linkage_name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + variable: Variable.Index, + }; + + pub const GlobalVarExpression = struct { + variable: Metadata, + expression: Metadata, + }; + + pub fn toValue(self: Metadata) Value { + return @enumFromInt(Value.first_metadata + @intFromEnum(self)); + } + + const Formatter = struct { + builder: *Builder, + need_comma: bool, + map: std.AutoArrayHashMapUnmanaged(union(enum) { + metadata: Metadata, + debug_location: DebugLocation.Location, + }, void) = .{}, + + const FormatData = struct { + formatter: *Formatter, + prefix: []const u8 = "", + node: Node, + + const Node = union(enum) { + none, + @"inline": Metadata, + index: u32, + + local_value: ValueData, + local_metadata: ValueData, + local_inline: Metadata, + local_index: u32, + + string: MetadataString, + bool: bool, + u32: u32, + u64: u64, + di_flags: DIFlags, + sp_flags: Subprogram.DISPFlags, + raw: []const u8, + + const ValueData = struct { + value: Value, + function: Function.Index, + }; + }; + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + fmt_opts: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (data.node == .none) return; + + const is_specialized = fmt_str.len > 0 and fmt_str[0] == 'S'; + const recurse_fmt_str = if (is_specialized) fmt_str[1..] else fmt_str; + + if (data.formatter.need_comma) try writer.writeAll(", "); + defer data.formatter.need_comma = true; + try writer.writeAll(data.prefix); + + const builder = data.formatter.builder; + switch (data.node) { + .none => unreachable, + .@"inline" => |node| { + const needed_comma = data.formatter.need_comma; + defer data.formatter.need_comma = needed_comma; + data.formatter.need_comma = false; + + const item = builder.metadata_items.get(@intFromEnum(node)); + switch (item.tag) { + .expression => { + var extra = builder.metadataExtraDataTrail(Expression, item.data); + const elements = extra.trail.next(extra.data.elements_len, u32, builder); + try writer.writeAll("!DIExpression("); + for (elements) |element| try format(.{ + .formatter = data.formatter, + .node = .{ .u64 = element }, + }, "%", fmt_opts, writer); + try writer.writeByte(')'); + }, + .constant => try Constant.format(.{ + .constant = @enumFromInt(item.data), + .builder = builder, + }, recurse_fmt_str, fmt_opts, writer), + else => unreachable, + } + }, + .index => |node| try writer.print("!{d}", .{node}), + inline .local_value, .local_metadata => |node, tag| try Value.format(.{ + .value = node.value, + .function = node.function, + .builder = builder, + }, switch (tag) { + .local_value => recurse_fmt_str, + .local_metadata => "%", + else => unreachable, + }, fmt_opts, writer), + inline .local_inline, .local_index => |node, tag| { + if (comptime std.mem.eql(u8, recurse_fmt_str, "%")) + try writer.print("{%} ", .{Type.metadata.fmt(builder)}); + try format(.{ + .formatter = data.formatter, + .node = @unionInit(FormatData.Node, @tagName(tag)["local_".len..], node), + }, "%", fmt_opts, writer); + }, + .string => |node| try writer.print((if (is_specialized) "" else "!") ++ "{}", .{ + node.fmt(builder), + }), + inline .bool, + .u32, + .u64, + .di_flags, + .sp_flags, + => |node| try writer.print("{}", .{node}), + .raw => |node| try writer.writeAll(node), + } + } + inline fn fmt(formatter: *Formatter, prefix: []const u8, node: anytype) switch (@TypeOf(node)) { + Metadata => Allocator.Error, + else => error{}, + }!std.fmt.Formatter(format) { + const Node = @TypeOf(node); + const MaybeNode = switch (@typeInfo(Node)) { + .optional => Node, + .null => ?noreturn, + else => ?Node, + }; + const Some = @typeInfo(MaybeNode).optional.child; + return .{ .data = .{ + .formatter = formatter, + .prefix = prefix, + .node = if (@as(MaybeNode, node)) |some| switch (@typeInfo(Some)) { + .@"enum" => |enum_info| switch (Some) { + Metadata => switch (some) { + .none => .none, + else => try formatter.refUnwrapped(some.unwrap(formatter.builder)), + }, + MetadataString => .{ .string = some }, + else => if (enum_info.is_exhaustive) + .{ .raw = @tagName(some) } + else + @compileError("unknown type to format: " ++ @typeName(Node)), + }, + .enum_literal => .{ .raw = @tagName(some) }, + .bool => .{ .bool = some }, + .@"struct" => switch (Some) { + DIFlags => .{ .di_flags = some }, + Subprogram.DISPFlags => .{ .sp_flags = some }, + else => @compileError("unknown type to format: " ++ @typeName(Node)), + }, + .int, .comptime_int => .{ .u64 = some }, + .pointer => .{ .raw = some }, + else => @compileError("unknown type to format: " ++ @typeName(Node)), + } else switch (@typeInfo(Node)) { + .optional, .null => .none, + else => unreachable, + }, + } }; + } + inline fn fmtLocal( + formatter: *Formatter, + prefix: []const u8, + value: Value, + function: Function.Index, + ) Allocator.Error!std.fmt.Formatter(format) { + return .{ .data = .{ + .formatter = formatter, + .prefix = prefix, + .node = switch (value.unwrap()) { + .instruction, .constant => .{ .local_value = .{ + .value = value, + .function = function, + } }, + .metadata => |metadata| if (value == .none) .none else node: { + const unwrapped = metadata.unwrap(formatter.builder); + break :node if (@intFromEnum(unwrapped) >= first_local_metadata) + .{ .local_metadata = .{ + .value = function.ptrConst(formatter.builder).debug_values[ + @intFromEnum(unwrapped) - first_local_metadata + ].toValue(), + .function = function, + } } + else switch (try formatter.refUnwrapped(unwrapped)) { + .@"inline" => |node| .{ .local_inline = node }, + .index => |node| .{ .local_index = node }, + else => unreachable, + }; + }, + }, + } }; + } + fn refUnwrapped(formatter: *Formatter, node: Metadata) Allocator.Error!FormatData.Node { + assert(node != .none); + assert(@intFromEnum(node) < first_forward_reference); + const builder = formatter.builder; + const unwrapped_metadata = node.unwrap(builder); + const tag = formatter.builder.metadata_items.items(.tag)[@intFromEnum(unwrapped_metadata)]; + switch (tag) { + .none => unreachable, + .expression, .constant => return .{ .@"inline" = unwrapped_metadata }, + else => { + assert(!tag.isInline()); + const gop = try formatter.map.getOrPut(builder.gpa, .{ .metadata = unwrapped_metadata }); + return .{ .index = @intCast(gop.index) }; + }, + } + } + + inline fn specialized( + formatter: *Formatter, + distinct: enum { @"!", @"distinct !" }, + node: enum { + DIFile, + DICompileUnit, + DISubprogram, + DILexicalBlock, + DILocation, + DIBasicType, + DICompositeType, + DIDerivedType, + DISubroutineType, + DIEnumerator, + DISubrange, + DILocalVariable, + DIGlobalVariable, + DIGlobalVariableExpression, + }, + nodes: anytype, + writer: anytype, + ) !void { + comptime var fmt_str: []const u8 = ""; + const names = comptime std.meta.fieldNames(@TypeOf(nodes)); + comptime var fields: [2 + names.len]std.builtin.Type.StructField = undefined; + inline for (fields[0..2], .{ "distinct", "node" }) |*field, name| { + fmt_str = fmt_str ++ "{[" ++ name ++ "]s}"; + field.* = .{ + .name = name, + .type = []const u8, + .default_value_ptr = null, + .is_comptime = false, + .alignment = 0, + }; + } + fmt_str = fmt_str ++ "("; + inline for (fields[2..], names) |*field, name| { + fmt_str = fmt_str ++ "{[" ++ name ++ "]S}"; + field.* = .{ + .name = name, + .type = std.fmt.Formatter(format), + .default_value_ptr = null, + .is_comptime = false, + .alignment = 0, + }; + } + fmt_str = fmt_str ++ ")\n"; + + var fmt_args: @Type(.{ .@"struct" = .{ + .layout = .auto, + .fields = &fields, + .decls = &.{}, + .is_tuple = false, + } }) = undefined; + fmt_args.distinct = @tagName(distinct); + fmt_args.node = @tagName(node); + inline for (names) |name| @field(fmt_args, name) = try formatter.fmt( + name ++ ": ", + @field(nodes, name), + ); + try writer.print(fmt_str, fmt_args); + } + }; +}; + +pub fn init(options: Options) Allocator.Error!Builder { + var self: Builder = .{ + .gpa = options.allocator, + .strip = options.strip, + + .source_filename = .none, + .data_layout = .none, + .target_triple = .none, + .module_asm = .{}, + + .string_map = .{}, + .string_indices = .{}, + .string_bytes = .{}, + + .types = .{}, + .next_unnamed_type = @enumFromInt(0), + .next_unique_type_id = .{}, + .type_map = .{}, + .type_items = .{}, + .type_extra = .{}, + + .attributes = .{}, + .attributes_map = .{}, + .attributes_indices = .{}, + .attributes_extra = .{}, + + .function_attributes_set = .{}, + + .globals = .{}, + .next_unnamed_global = @enumFromInt(0), + .next_replaced_global = .none, + .next_unique_global_id = .{}, + .aliases = .{}, + .variables = .{}, + .functions = .{}, + + .strtab_string_map = .{}, + .strtab_string_indices = .{}, + .strtab_string_bytes = .{}, + + .constant_map = .{}, + .constant_items = .{}, + .constant_extra = .{}, + .constant_limbs = .{}, + + .metadata_map = .{}, + .metadata_items = .{}, + .metadata_extra = .{}, + .metadata_limbs = .{}, + .metadata_forward_references = .{}, + .metadata_named = .{}, + .metadata_string_map = .{}, + .metadata_string_indices = .{}, + .metadata_string_bytes = .{}, + }; + errdefer self.deinit(); + + try self.string_indices.append(self.gpa, 0); + assert(try self.string("") == .empty); + + try self.strtab_string_indices.append(self.gpa, 0); + assert(try self.strtabString("") == .empty); + + if (options.name.len > 0) self.source_filename = try self.string(options.name); + + if (options.triple.len > 0) { + self.target_triple = try self.string(options.triple); + } + + { + const static_len = @typeInfo(Type).@"enum".fields.len - 1; + try self.type_map.ensureTotalCapacity(self.gpa, static_len); + try self.type_items.ensureTotalCapacity(self.gpa, static_len); + inline for (@typeInfo(Type.Simple).@"enum".fields) |simple_field| { + const result = self.getOrPutTypeNoExtraAssumeCapacity( + .{ .tag = .simple, .data = simple_field.value }, + ); + assert(result.new and result.type == @field(Type, simple_field.name)); + } + 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, 4 }) |addr_space_index| { + const addr_space: AddrSpace = @enumFromInt(addr_space_index); + assert(self.ptrTypeAssumeCapacity(addr_space) == + @field(Type, std.fmt.comptimePrint("ptr{ }", .{addr_space}))); + } + } + + { + try self.attributes_indices.append(self.gpa, 0); + assert(try self.attrs(&.{}) == .none); + assert(try self.fnAttrs(&.{}) == .none); + } + + assert(try self.intConst(.i1, 0) == .false); + assert(try self.intConst(.i1, 1) == .true); + assert(try self.intConst(.i32, 0) == .@"0"); + assert(try self.intConst(.i32, 1) == .@"1"); + assert(try self.noneConst(.token) == .none); + + assert(try self.metadataNone() == .none); + assert(try self.metadataTuple(&.{}) == .empty_tuple); + + try self.metadata_string_indices.append(self.gpa, 0); + assert(try self.metadataString("") == .none); + + return self; +} + +pub fn clearAndFree(self: *Builder) void { + self.module_asm.clearAndFree(self.gpa); + + self.string_map.clearAndFree(self.gpa); + self.string_indices.clearAndFree(self.gpa); + self.string_bytes.clearAndFree(self.gpa); + + self.types.clearAndFree(self.gpa); + self.next_unique_type_id.clearAndFree(self.gpa); + self.type_map.clearAndFree(self.gpa); + self.type_items.clearAndFree(self.gpa); + self.type_extra.clearAndFree(self.gpa); + + self.attributes.clearAndFree(self.gpa); + self.attributes_map.clearAndFree(self.gpa); + self.attributes_indices.clearAndFree(self.gpa); + self.attributes_extra.clearAndFree(self.gpa); + + self.function_attributes_set.clearAndFree(self.gpa); + + self.globals.clearAndFree(self.gpa); + self.next_unique_global_id.clearAndFree(self.gpa); + self.aliases.clearAndFree(self.gpa); + self.variables.clearAndFree(self.gpa); + for (self.functions.items) |*function| function.deinit(self.gpa); + self.functions.clearAndFree(self.gpa); + + self.strtab_string_map.clearAndFree(self.gpa); + self.strtab_string_indices.clearAndFree(self.gpa); + self.strtab_string_bytes.clearAndFree(self.gpa); + + self.constant_map.clearAndFree(self.gpa); + self.constant_items.shrinkAndFree(self.gpa, 0); + self.constant_extra.clearAndFree(self.gpa); + self.constant_limbs.clearAndFree(self.gpa); + + self.metadata_map.clearAndFree(self.gpa); + self.metadata_items.shrinkAndFree(self.gpa, 0); + self.metadata_extra.clearAndFree(self.gpa); + self.metadata_limbs.clearAndFree(self.gpa); + self.metadata_forward_references.clearAndFree(self.gpa); + self.metadata_named.clearAndFree(self.gpa); + + self.metadata_string_map.clearAndFree(self.gpa); + self.metadata_string_indices.clearAndFree(self.gpa); + self.metadata_string_bytes.clearAndFree(self.gpa); +} + +pub fn deinit(self: *Builder) void { + self.module_asm.deinit(self.gpa); + + self.string_map.deinit(self.gpa); + self.string_indices.deinit(self.gpa); + self.string_bytes.deinit(self.gpa); + + self.types.deinit(self.gpa); + self.next_unique_type_id.deinit(self.gpa); + self.type_map.deinit(self.gpa); + self.type_items.deinit(self.gpa); + self.type_extra.deinit(self.gpa); + + self.attributes.deinit(self.gpa); + self.attributes_map.deinit(self.gpa); + self.attributes_indices.deinit(self.gpa); + self.attributes_extra.deinit(self.gpa); + + self.function_attributes_set.deinit(self.gpa); + + self.globals.deinit(self.gpa); + self.next_unique_global_id.deinit(self.gpa); + self.aliases.deinit(self.gpa); + self.variables.deinit(self.gpa); + for (self.functions.items) |*function| function.deinit(self.gpa); + self.functions.deinit(self.gpa); + + self.strtab_string_map.deinit(self.gpa); + self.strtab_string_indices.deinit(self.gpa); + self.strtab_string_bytes.deinit(self.gpa); + + self.constant_map.deinit(self.gpa); + self.constant_items.deinit(self.gpa); + self.constant_extra.deinit(self.gpa); + self.constant_limbs.deinit(self.gpa); + + self.metadata_map.deinit(self.gpa); + self.metadata_items.deinit(self.gpa); + self.metadata_extra.deinit(self.gpa); + self.metadata_limbs.deinit(self.gpa); + self.metadata_forward_references.deinit(self.gpa); + self.metadata_named.deinit(self.gpa); + + self.metadata_string_map.deinit(self.gpa); + self.metadata_string_indices.deinit(self.gpa); + self.metadata_string_bytes.deinit(self.gpa); + + self.* = undefined; +} + +pub fn setModuleAsm(self: *Builder) std.ArrayListUnmanaged(u8).Writer { + self.module_asm.clearRetainingCapacity(); + return self.appendModuleAsm(); +} + +pub fn appendModuleAsm(self: *Builder) std.ArrayListUnmanaged(u8).Writer { + return self.module_asm.writer(self.gpa); +} + +pub fn finishModuleAsm(self: *Builder) Allocator.Error!void { + if (self.module_asm.getLastOrNull()) |last| if (last != '\n') + try self.module_asm.append(self.gpa, '\n'); +} + +pub fn string(self: *Builder, bytes: []const u8) Allocator.Error!String { + try self.string_bytes.ensureUnusedCapacity(self.gpa, bytes.len); + try self.string_indices.ensureUnusedCapacity(self.gpa, 1); + try self.string_map.ensureUnusedCapacity(self.gpa, 1); + + const gop = self.string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self }); + if (!gop.found_existing) { + self.string_bytes.appendSliceAssumeCapacity(bytes); + self.string_indices.appendAssumeCapacity(@intCast(self.string_bytes.items.len)); + } + return String.fromIndex(gop.index); +} + +pub fn stringNull(self: *Builder, bytes: [:0]const u8) Allocator.Error!String { + return self.string(bytes[0 .. bytes.len + 1]); +} + +pub fn stringIfExists(self: *const Builder, bytes: []const u8) ?String { + return String.fromIndex( + self.string_map.getIndexAdapted(bytes, String.Adapter{ .builder = self }) orelse return null, + ); +} + +pub fn fmt(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) Allocator.Error!String { + try self.string_map.ensureUnusedCapacity(self.gpa, 1); + try self.string_bytes.ensureUnusedCapacity(self.gpa, @intCast(std.fmt.count(fmt_str, fmt_args))); + try self.string_indices.ensureUnusedCapacity(self.gpa, 1); + return self.fmtAssumeCapacity(fmt_str, fmt_args); +} + +pub fn fmtAssumeCapacity(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) String { + self.string_bytes.writer(undefined).print(fmt_str, fmt_args) catch unreachable; + return self.trailingStringAssumeCapacity(); +} + +pub fn trailingString(self: *Builder) Allocator.Error!String { + try self.string_indices.ensureUnusedCapacity(self.gpa, 1); + try self.string_map.ensureUnusedCapacity(self.gpa, 1); + return self.trailingStringAssumeCapacity(); +} + +pub fn trailingStringAssumeCapacity(self: *Builder) String { + const start = self.string_indices.getLast(); + const bytes: []const u8 = self.string_bytes.items[start..]; + const gop = self.string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self }); + if (gop.found_existing) { + self.string_bytes.shrinkRetainingCapacity(start); + } else { + self.string_indices.appendAssumeCapacity(@intCast(self.string_bytes.items.len)); + } + return String.fromIndex(gop.index); +} + +pub fn fnType( + self: *Builder, + ret: Type, + params: []const Type, + kind: Type.Function.Kind, +) Allocator.Error!Type { + try self.ensureUnusedTypeCapacity(1, Type.Function, params.len); + switch (kind) { + inline else => |comptime_kind| return self.fnTypeAssumeCapacity(ret, params, comptime_kind), + } +} + +pub fn intType(self: *Builder, bits: u24) Allocator.Error!Type { + try self.ensureUnusedTypeCapacity(1, NoExtra, 0); + return self.intTypeAssumeCapacity(bits); +} + +pub fn ptrType(self: *Builder, addr_space: AddrSpace) Allocator.Error!Type { + try self.ensureUnusedTypeCapacity(1, NoExtra, 0); + return self.ptrTypeAssumeCapacity(addr_space); +} + +pub fn vectorType( + self: *Builder, + kind: Type.Vector.Kind, + len: u32, + child: Type, +) Allocator.Error!Type { + try self.ensureUnusedTypeCapacity(1, Type.Vector, 0); + switch (kind) { + inline else => |comptime_kind| return self.vectorTypeAssumeCapacity(comptime_kind, len, child), + } +} + +pub fn arrayType(self: *Builder, len: u64, child: Type) Allocator.Error!Type { + comptime assert(@sizeOf(Type.Array) >= @sizeOf(Type.Vector)); + try self.ensureUnusedTypeCapacity(1, Type.Array, 0); + return self.arrayTypeAssumeCapacity(len, child); +} + +pub fn structType( + self: *Builder, + kind: Type.Structure.Kind, + fields: []const Type, +) Allocator.Error!Type { + try self.ensureUnusedTypeCapacity(1, Type.Structure, fields.len); + switch (kind) { + inline else => |comptime_kind| return self.structTypeAssumeCapacity(comptime_kind, fields), + } +} + +pub fn opaqueType(self: *Builder, name: String) Allocator.Error!Type { + try self.string_map.ensureUnusedCapacity(self.gpa, 1); + if (name.slice(self)) |id| { + const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)}); + try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); + } + 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.ensureUnusedTypeCapacity(1, Type.NamedStructure, 0); + return self.opaqueTypeAssumeCapacity(name); +} + +pub fn namedTypeSetBody( + self: *Builder, + named_type: Type, + body_type: Type, +) void { + const named_item = self.type_items.items[@intFromEnum(named_type)]; + self.type_extra.items[named_item.data + std.meta.fieldIndex(Type.NamedStructure, "body").?] = + @intFromEnum(body_type); +} + +pub fn attr(self: *Builder, attribute: Attribute) Allocator.Error!Attribute.Index { + try self.attributes.ensureUnusedCapacity(self.gpa, 1); + + const gop = self.attributes.getOrPutAssumeCapacity(attribute.toStorage()); + if (!gop.found_existing) gop.value_ptr.* = {}; + return @enumFromInt(gop.index); +} + +pub fn attrs(self: *Builder, attributes: []Attribute.Index) Allocator.Error!Attributes { + std.sort.heap(Attribute.Index, attributes, self, struct { + pub fn lessThan(builder: *const Builder, lhs: Attribute.Index, rhs: Attribute.Index) bool { + const lhs_kind = lhs.getKind(builder); + const rhs_kind = rhs.getKind(builder); + assert(lhs_kind != rhs_kind); + return @intFromEnum(lhs_kind) < @intFromEnum(rhs_kind); + } + }.lessThan); + return @enumFromInt(try self.attrGeneric(@ptrCast(attributes))); +} + +pub fn fnAttrs(self: *Builder, fn_attributes: []const Attributes) Allocator.Error!FunctionAttributes { + try self.function_attributes_set.ensureUnusedCapacity(self.gpa, 1); + const function_attributes: FunctionAttributes = @enumFromInt(try self.attrGeneric(@ptrCast( + fn_attributes[0..if (std.mem.lastIndexOfNone(Attributes, fn_attributes, &.{.none})) |last| + last + 1 + else + 0], + ))); + + _ = self.function_attributes_set.getOrPutAssumeCapacity(function_attributes); + return function_attributes; +} + +pub fn addGlobal(self: *Builder, name: StrtabString, global: Global) Allocator.Error!Global.Index { + assert(!name.isAnon()); + try self.ensureUnusedTypeCapacity(1, NoExtra, 0); + try self.ensureUnusedGlobalCapacity(name); + return self.addGlobalAssumeCapacity(name, global); +} + +pub fn addGlobalAssumeCapacity(self: *Builder, name: StrtabString, global: Global) Global.Index { + _ = self.ptrTypeAssumeCapacity(global.addr_space); + var id = name; + if (name == .empty) { + id = self.next_unnamed_global; + assert(id != self.next_replaced_global); + self.next_unnamed_global = @enumFromInt(@intFromEnum(id) + 1); + } + while (true) { + const global_gop = self.globals.getOrPutAssumeCapacity(id); + if (!global_gop.found_existing) { + global_gop.value_ptr.* = global; + const global_index: Global.Index = @enumFromInt(global_gop.index); + global_index.updateDsoLocal(self); + return global_index; + } + + const unique_gop = self.next_unique_global_id.getOrPutAssumeCapacity(name); + if (!unique_gop.found_existing) unique_gop.value_ptr.* = 2; + id = self.strtabStringFmtAssumeCapacity("{s}.{d}", .{ name.slice(self).?, unique_gop.value_ptr.* }); + unique_gop.value_ptr.* += 1; + } +} + +pub fn getGlobal(self: *const Builder, name: StrtabString) ?Global.Index { + return @enumFromInt(self.globals.getIndex(name) orelse return null); +} + +pub fn addAlias( + self: *Builder, + name: StrtabString, + ty: Type, + addr_space: AddrSpace, + aliasee: Constant, +) Allocator.Error!Alias.Index { + assert(!name.isAnon()); + try self.ensureUnusedTypeCapacity(1, NoExtra, 0); + try self.ensureUnusedGlobalCapacity(name); + try self.aliases.ensureUnusedCapacity(self.gpa, 1); + return self.addAliasAssumeCapacity(name, ty, addr_space, aliasee); +} + +pub fn addAliasAssumeCapacity( + self: *Builder, + name: StrtabString, + ty: Type, + addr_space: AddrSpace, + aliasee: Constant, +) Alias.Index { + const alias_index: Alias.Index = @enumFromInt(self.aliases.items.len); + self.aliases.appendAssumeCapacity(.{ .global = self.addGlobalAssumeCapacity(name, .{ + .addr_space = addr_space, + .type = ty, + .kind = .{ .alias = alias_index }, + }), .aliasee = aliasee }); + return alias_index; +} + +pub fn addVariable( + self: *Builder, + name: StrtabString, + ty: Type, + addr_space: AddrSpace, +) Allocator.Error!Variable.Index { + assert(!name.isAnon()); + try self.ensureUnusedTypeCapacity(1, NoExtra, 0); + try self.ensureUnusedGlobalCapacity(name); + try self.variables.ensureUnusedCapacity(self.gpa, 1); + return self.addVariableAssumeCapacity(ty, name, addr_space); +} + +pub fn addVariableAssumeCapacity( + self: *Builder, + ty: Type, + name: StrtabString, + addr_space: AddrSpace, +) Variable.Index { + const variable_index: Variable.Index = @enumFromInt(self.variables.items.len); + self.variables.appendAssumeCapacity(.{ .global = self.addGlobalAssumeCapacity(name, .{ + .addr_space = addr_space, + .type = ty, + .kind = .{ .variable = variable_index }, + }) }); + return variable_index; +} + +pub fn addFunction( + self: *Builder, + ty: Type, + name: StrtabString, + addr_space: AddrSpace, +) Allocator.Error!Function.Index { + assert(!name.isAnon()); + try self.ensureUnusedTypeCapacity(1, NoExtra, 0); + try self.ensureUnusedGlobalCapacity(name); + try self.functions.ensureUnusedCapacity(self.gpa, 1); + return self.addFunctionAssumeCapacity(ty, name, addr_space); +} + +pub fn addFunctionAssumeCapacity( + self: *Builder, + ty: Type, + name: StrtabString, + addr_space: AddrSpace, +) Function.Index { + assert(ty.isFunction(self)); + const function_index: Function.Index = @enumFromInt(self.functions.items.len); + self.functions.appendAssumeCapacity(.{ + .global = self.addGlobalAssumeCapacity(name, .{ + .addr_space = addr_space, + .type = ty, + .kind = .{ .function = function_index }, + }), + .strip = undefined, + }); + return function_index; +} + +pub fn getIntrinsic( + self: *Builder, + id: Intrinsic, + overload: []const Type, +) Allocator.Error!Function.Index { + const ExpectedContents = extern union { + attrs: extern struct { + params: [expected_args_len]Type, + fn_attrs: [FunctionAttributes.params_index + expected_args_len]Attributes, + attrs: [expected_attrs_len]Attribute.Index, + fields: [expected_fields_len]Type, + }, + }; + var stack align(@max(@alignOf(std.heap.StackFallbackAllocator(0)), @alignOf(ExpectedContents))) = + std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); + const allocator = stack.get(); + + const name = name: { + const writer = self.strtab_string_bytes.writer(self.gpa); + try writer.print("llvm.{s}", .{@tagName(id)}); + for (overload) |ty| try writer.print(".{m}", .{ty.fmt(self)}); + break :name try self.trailingStrtabString(); + }; + if (self.getGlobal(name)) |global| return global.ptrConst(self).kind.function; + + const signature = Intrinsic.signatures.get(id); + const param_types = try allocator.alloc(Type, signature.params.len); + defer allocator.free(param_types); + const function_attributes = try allocator.alloc( + Attributes, + FunctionAttributes.params_index + (signature.params.len - signature.ret_len), + ); + defer allocator.free(function_attributes); + + var attributes: struct { + builder: *Builder, + list: std.ArrayList(Attribute.Index), + + fn deinit(state: *@This()) void { + state.list.deinit(); + state.* = undefined; + } + + fn get(state: *@This(), attributes: []const Attribute) Allocator.Error!Attributes { + try state.list.resize(attributes.len); + for (state.list.items, attributes) |*item, attribute| + item.* = try state.builder.attr(attribute); + return state.builder.attrs(state.list.items); + } + } = .{ .builder = self, .list = std.ArrayList(Attribute.Index).init(allocator) }; + defer attributes.deinit(); + + var overload_index: usize = 0; + function_attributes[FunctionAttributes.function_index] = try attributes.get(signature.attrs); + function_attributes[FunctionAttributes.return_index] = .none; // needed for void return + for (0.., param_types, signature.params) |param_index, *param_type, signature_param| { + switch (signature_param.kind) { + .type => |ty| param_type.* = ty, + .overloaded => { + param_type.* = overload[overload_index]; + overload_index += 1; + }, + .matches, .matches_scalar, .matches_changed_scalar => {}, + } + function_attributes[ + if (param_index < signature.ret_len) + FunctionAttributes.return_index + else + FunctionAttributes.params_index + (param_index - signature.ret_len) + ] = try attributes.get(signature_param.attrs); + } + assert(overload_index == overload.len); + for (param_types, signature.params) |*param_type, signature_param| { + param_type.* = switch (signature_param.kind) { + .type, .overloaded => continue, + .matches => |param_index| param_types[param_index], + .matches_scalar => |param_index| param_types[param_index].scalarType(self), + .matches_changed_scalar => |info| try param_types[info.index] + .changeScalar(info.scalar, self), + }; + } + + const function_index = try self.addFunction(try self.fnType(switch (signature.ret_len) { + 0 => .void, + 1 => param_types[0], + else => try self.structType(.normal, param_types[0..signature.ret_len]), + }, param_types[signature.ret_len..], .normal), name, .default); + function_index.ptr(self).attributes = try self.fnAttrs(function_attributes); + return function_index; +} + +pub fn intConst(self: *Builder, ty: Type, value: anytype) Allocator.Error!Constant { + const int_value = switch (@typeInfo(@TypeOf(value))) { + .int, .comptime_int => value, + .@"enum" => @intFromEnum(value), + else => @compileError("intConst expected an integral value, got " ++ @typeName(@TypeOf(value))), + }; + var limbs: [ + switch (@typeInfo(@TypeOf(int_value))) { + .int => |info| std.math.big.int.calcTwosCompLimbCount(info.bits), + .comptime_int => std.math.big.int.calcLimbLen(int_value), + else => unreachable, + } + ]std.math.big.Limb = undefined; + return self.bigIntConst(ty, std.math.big.int.Mutable.init(&limbs, int_value).toConst()); +} + +pub fn intValue(self: *Builder, ty: Type, value: anytype) Allocator.Error!Value { + return (try self.intConst(ty, value)).toValue(); +} + +pub fn bigIntConst(self: *Builder, ty: Type, value: std.math.big.int.Const) Allocator.Error!Constant { + try self.constant_map.ensureUnusedCapacity(self.gpa, 1); + try self.constant_items.ensureUnusedCapacity(self.gpa, 1); + try self.constant_limbs.ensureUnusedCapacity(self.gpa, Constant.Integer.limbs + value.limbs.len); + return self.bigIntConstAssumeCapacity(ty, value); +} + +pub fn bigIntValue(self: *Builder, ty: Type, value: std.math.big.int.Const) Allocator.Error!Value { + return (try self.bigIntConst(ty, value)).toValue(); +} + +pub fn fpConst(self: *Builder, ty: Type, comptime val: comptime_float) Allocator.Error!Constant { + return switch (ty) { + .half => try self.halfConst(val), + .bfloat => try self.bfloatConst(val), + .float => try self.floatConst(val), + .double => try self.doubleConst(val), + .fp128 => try self.fp128Const(val), + .x86_fp80 => try self.x86_fp80Const(val), + .ppc_fp128 => try self.ppc_fp128Const(.{ val, -0.0 }), + else => unreachable, + }; +} + +pub fn fpValue(self: *Builder, ty: Type, comptime value: comptime_float) Allocator.Error!Value { + return (try self.fpConst(ty, value)).toValue(); +} + +pub fn nanConst(self: *Builder, ty: Type) Allocator.Error!Constant { + return switch (ty) { + .half => try self.halfConst(std.math.nan(f16)), + .bfloat => try self.bfloatConst(std.math.nan(f32)), + .float => try self.floatConst(std.math.nan(f32)), + .double => try self.doubleConst(std.math.nan(f64)), + .fp128 => try self.fp128Const(std.math.nan(f128)), + .x86_fp80 => try self.x86_fp80Const(std.math.nan(f80)), + .ppc_fp128 => try self.ppc_fp128Const(.{std.math.nan(f64)} ** 2), + else => unreachable, + }; +} + +pub fn nanValue(self: *Builder, ty: Type) Allocator.Error!Value { + return (try self.nanConst(ty)).toValue(); +} + +pub fn halfConst(self: *Builder, val: f16) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, NoExtra, 0); + return self.halfConstAssumeCapacity(val); +} + +pub fn halfValue(self: *Builder, ty: Type, value: f16) Allocator.Error!Value { + return (try self.halfConst(ty, value)).toValue(); +} + +pub fn bfloatConst(self: *Builder, val: f32) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, NoExtra, 0); + return self.bfloatConstAssumeCapacity(val); +} + +pub fn bfloatValue(self: *Builder, ty: Type, value: f32) Allocator.Error!Value { + return (try self.bfloatConst(ty, value)).toValue(); +} + +pub fn floatConst(self: *Builder, val: f32) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, NoExtra, 0); + return self.floatConstAssumeCapacity(val); +} + +pub fn floatValue(self: *Builder, ty: Type, value: f32) Allocator.Error!Value { + return (try self.floatConst(ty, value)).toValue(); +} + +pub fn doubleConst(self: *Builder, val: f64) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Double, 0); + return self.doubleConstAssumeCapacity(val); +} + +pub fn doubleValue(self: *Builder, ty: Type, value: f64) Allocator.Error!Value { + return (try self.doubleConst(ty, value)).toValue(); +} + +pub fn fp128Const(self: *Builder, val: f128) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Fp128, 0); + return self.fp128ConstAssumeCapacity(val); +} + +pub fn fp128Value(self: *Builder, ty: Type, value: f128) Allocator.Error!Value { + return (try self.fp128Const(ty, value)).toValue(); +} + +pub fn x86_fp80Const(self: *Builder, val: f80) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Fp80, 0); + return self.x86_fp80ConstAssumeCapacity(val); +} + +pub fn x86_fp80Value(self: *Builder, ty: Type, value: f80) Allocator.Error!Value { + return (try self.x86_fp80Const(ty, value)).toValue(); +} + +pub fn ppc_fp128Const(self: *Builder, val: [2]f64) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Fp128, 0); + return self.ppc_fp128ConstAssumeCapacity(val); +} + +pub fn ppc_fp128Value(self: *Builder, ty: Type, value: [2]f64) Allocator.Error!Value { + return (try self.ppc_fp128Const(ty, value)).toValue(); +} + +pub fn nullConst(self: *Builder, ty: Type) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, NoExtra, 0); + return self.nullConstAssumeCapacity(ty); +} + +pub fn nullValue(self: *Builder, ty: Type) Allocator.Error!Value { + return (try self.nullConst(ty)).toValue(); +} + +pub fn noneConst(self: *Builder, ty: Type) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, NoExtra, 0); + return self.noneConstAssumeCapacity(ty); +} + +pub fn noneValue(self: *Builder, ty: Type) Allocator.Error!Value { + return (try self.noneConst(ty)).toValue(); +} + +pub fn structConst(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Aggregate, vals.len); + return self.structConstAssumeCapacity(ty, vals); +} + +pub fn structValue(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Value { + return (try self.structConst(ty, vals)).toValue(); +} + +pub fn arrayConst(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Aggregate, vals.len); + return self.arrayConstAssumeCapacity(ty, vals); +} + +pub fn arrayValue(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Value { + return (try self.arrayConst(ty, vals)).toValue(); +} + +pub fn stringConst(self: *Builder, val: String) Allocator.Error!Constant { + try self.ensureUnusedTypeCapacity(1, Type.Array, 0); + try self.ensureUnusedConstantCapacity(1, NoExtra, 0); + return self.stringConstAssumeCapacity(val); +} + +pub fn stringValue(self: *Builder, val: String) Allocator.Error!Value { + return (try self.stringConst(val)).toValue(); +} + +pub fn vectorConst(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Aggregate, vals.len); + return self.vectorConstAssumeCapacity(ty, vals); +} + +pub fn vectorValue(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Value { + return (try self.vectorConst(ty, vals)).toValue(); +} + +pub fn splatConst(self: *Builder, ty: Type, val: Constant) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Splat, 0); + return self.splatConstAssumeCapacity(ty, val); +} + +pub fn splatValue(self: *Builder, ty: Type, val: Constant) Allocator.Error!Value { + return (try self.splatConst(ty, val)).toValue(); +} + +pub fn zeroInitConst(self: *Builder, ty: Type) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Fp128, 0); + try self.constant_limbs.ensureUnusedCapacity( + self.gpa, + Constant.Integer.limbs + comptime std.math.big.int.calcLimbLen(0), + ); + return self.zeroInitConstAssumeCapacity(ty); +} + +pub fn zeroInitValue(self: *Builder, ty: Type) Allocator.Error!Value { + return (try self.zeroInitConst(ty)).toValue(); +} + +pub fn undefConst(self: *Builder, ty: Type) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, NoExtra, 0); + return self.undefConstAssumeCapacity(ty); +} + +pub fn undefValue(self: *Builder, ty: Type) Allocator.Error!Value { + return (try self.undefConst(ty)).toValue(); +} + +pub fn poisonConst(self: *Builder, ty: Type) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, NoExtra, 0); + return self.poisonConstAssumeCapacity(ty); +} + +pub fn poisonValue(self: *Builder, ty: Type) Allocator.Error!Value { + return (try self.poisonConst(ty)).toValue(); +} + +pub fn blockAddrConst( + self: *Builder, + function: Function.Index, + block: Function.Block.Index, +) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.BlockAddress, 0); + return self.blockAddrConstAssumeCapacity(function, block); +} + +pub fn blockAddrValue( + self: *Builder, + function: Function.Index, + block: Function.Block.Index, +) Allocator.Error!Value { + return (try self.blockAddrConst(function, block)).toValue(); +} + +pub fn dsoLocalEquivalentConst(self: *Builder, function: Function.Index) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, NoExtra, 0); + return self.dsoLocalEquivalentConstAssumeCapacity(function); +} + +pub fn dsoLocalEquivalentValue(self: *Builder, function: Function.Index) Allocator.Error!Value { + return (try self.dsoLocalEquivalentConst(function)).toValue(); +} + +pub fn noCfiConst(self: *Builder, function: Function.Index) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, NoExtra, 0); + return self.noCfiConstAssumeCapacity(function); +} + +pub fn noCfiValue(self: *Builder, function: Function.Index) Allocator.Error!Value { + return (try self.noCfiConst(function)).toValue(); +} + +pub fn convConst( + self: *Builder, + val: Constant, + ty: Type, +) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Cast, 0); + return self.convConstAssumeCapacity(val, ty); +} + +pub fn convValue( + self: *Builder, + val: Constant, + ty: Type, +) Allocator.Error!Value { + return (try self.convConst(val, ty)).toValue(); +} + +pub fn castConst(self: *Builder, tag: Constant.Tag, val: Constant, ty: Type) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Cast, 0); + return self.castConstAssumeCapacity(tag, val, ty); +} + +pub fn castValue(self: *Builder, tag: Constant.Tag, val: Constant, ty: Type) Allocator.Error!Value { + return (try self.castConst(tag, val, ty)).toValue(); +} + +pub fn gepConst( + self: *Builder, + comptime kind: Constant.GetElementPtr.Kind, + ty: Type, + base: Constant, + inrange: ?u16, + indices: []const Constant, +) Allocator.Error!Constant { + try self.ensureUnusedTypeCapacity(1, Type.Vector, 0); + try self.ensureUnusedConstantCapacity(1, Constant.GetElementPtr, indices.len); + return self.gepConstAssumeCapacity(kind, ty, base, inrange, indices); +} + +pub fn gepValue( + self: *Builder, + comptime kind: Constant.GetElementPtr.Kind, + ty: Type, + base: Constant, + inrange: ?u16, + indices: []const Constant, +) Allocator.Error!Value { + return (try self.gepConst(kind, ty, base, inrange, indices)).toValue(); +} + +pub fn binConst( + self: *Builder, + tag: Constant.Tag, + lhs: Constant, + rhs: Constant, +) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Binary, 0); + return self.binConstAssumeCapacity(tag, lhs, rhs); +} + +pub fn binValue(self: *Builder, tag: Constant.Tag, lhs: Constant, rhs: Constant) Allocator.Error!Value { + return (try self.binConst(tag, lhs, rhs)).toValue(); +} + +pub fn asmConst( + self: *Builder, + ty: Type, + info: Constant.Assembly.Info, + assembly: String, + constraints: String, +) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Assembly, 0); + return self.asmConstAssumeCapacity(ty, info, assembly, constraints); +} + +pub fn asmValue( + self: *Builder, + ty: Type, + info: Constant.Assembly.Info, + assembly: String, + constraints: String, +) Allocator.Error!Value { + return (try self.asmConst(ty, info, assembly, constraints)).toValue(); +} + +pub fn dump(self: *Builder) void { + self.print(std.io.getStdErr().writer()) catch {}; +} + +pub fn printToFile(self: *Builder, path: []const u8) Allocator.Error!bool { + var file = std.fs.cwd().createFile(path, .{}) catch |err| { + log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); + return false; + }; + defer file.close(); + self.print(file.writer()) catch |err| { + log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); + return false; + }; + return true; +} + +pub fn print(self: *Builder, writer: anytype) (@TypeOf(writer).Error || Allocator.Error)!void { + var bw = std.io.bufferedWriter(writer); + try self.printUnbuffered(bw.writer()); + try bw.flush(); +} + +fn WriterWithErrors(comptime BackingWriter: type, comptime ExtraErrors: type) type { + return struct { + backing_writer: BackingWriter, + + pub const Error = BackingWriter.Error || ExtraErrors; + pub const Writer = std.io.Writer(*const Self, Error, write); + + const Self = @This(); + + pub fn writer(self: *const Self) Writer { + return .{ .context = self }; + } + + pub fn write(self: *const Self, bytes: []const u8) Error!usize { + return self.backing_writer.write(bytes); + } + }; +} +fn writerWithErrors( + backing_writer: anytype, + comptime ExtraErrors: type, +) WriterWithErrors(@TypeOf(backing_writer), ExtraErrors) { + return .{ .backing_writer = backing_writer }; +} + +pub fn printUnbuffered( + self: *Builder, + backing_writer: anytype, +) (@TypeOf(backing_writer).Error || Allocator.Error)!void { + const writer_with_errors = writerWithErrors(backing_writer, Allocator.Error); + const writer = writer_with_errors.writer(); + + var need_newline = false; + var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined }; + defer metadata_formatter.map.deinit(self.gpa); + + if (self.source_filename != .none or self.data_layout != .none or self.target_triple != .none) { + if (need_newline) try writer.writeByte('\n') else need_newline = true; + if (self.source_filename != .none) try writer.print( + \\; ModuleID = '{s}' + \\source_filename = {"} + \\ + , .{ self.source_filename.slice(self).?, self.source_filename.fmt(self) }); + if (self.data_layout != .none) try writer.print( + \\target datalayout = {"} + \\ + , .{self.data_layout.fmt(self)}); + if (self.target_triple != .none) try writer.print( + \\target triple = {"} + \\ + , .{self.target_triple.fmt(self)}); + } + + if (self.module_asm.items.len > 0) { + if (need_newline) try writer.writeByte('\n') else need_newline = true; + var line_it = std.mem.tokenizeScalar(u8, self.module_asm.items, '\n'); + while (line_it.next()) |line| { + try writer.writeAll("module asm "); + try printEscapedString(line, .always_quote, writer); + try writer.writeByte('\n'); + } + } + + if (self.types.count() > 0) { + if (need_newline) try writer.writeByte('\n') else need_newline = true; + for (self.types.keys(), self.types.values()) |id, ty| try writer.print( + \\%{} = type {} + \\ + , .{ id.fmt(self), ty.fmt(self) }); + } + + if (self.variables.items.len > 0) { + if (need_newline) try writer.writeByte('\n') else need_newline = true; + for (self.variables.items) |variable| { + if (variable.global.getReplacement(self) != .none) continue; + const global = variable.global.ptrConst(self); + metadata_formatter.need_comma = true; + defer metadata_formatter.need_comma = undefined; + try writer.print( + \\{} ={}{}{}{}{ }{}{ }{} {s} {%}{ }{, }{} + \\ + , .{ + variable.global.fmt(self), + Linkage.fmtOptional(if (global.linkage == .external and + variable.init != .no_init) null else global.linkage), + global.preemption, + global.visibility, + global.dll_storage_class, + variable.thread_local, + global.unnamed_addr, + global.addr_space, + global.externally_initialized, + @tagName(variable.mutability), + global.type.fmt(self), + variable.init.fmt(self), + variable.alignment, + try metadata_formatter.fmt("!dbg ", global.dbg), + }); + } + } + + if (self.aliases.items.len > 0) { + if (need_newline) try writer.writeByte('\n') else need_newline = true; + for (self.aliases.items) |alias| { + if (alias.global.getReplacement(self) != .none) continue; + const global = alias.global.ptrConst(self); + metadata_formatter.need_comma = true; + defer metadata_formatter.need_comma = undefined; + try writer.print( + \\{} ={}{}{}{}{ }{} alias {%}, {%}{} + \\ + , .{ + alias.global.fmt(self), + global.linkage, + global.preemption, + global.visibility, + global.dll_storage_class, + alias.thread_local, + global.unnamed_addr, + global.type.fmt(self), + alias.aliasee.fmt(self), + try metadata_formatter.fmt("!dbg ", global.dbg), + }); + } + } + + var attribute_groups: std.AutoArrayHashMapUnmanaged(Attributes, void) = .empty; + defer attribute_groups.deinit(self.gpa); + + for (0.., self.functions.items) |function_i, function| { + if (function.global.getReplacement(self) != .none) continue; + if (need_newline) try writer.writeByte('\n') else need_newline = true; + const function_index: Function.Index = @enumFromInt(function_i); + const global = function.global.ptrConst(self); + const params_len = global.type.functionParameters(self).len; + const function_attributes = function.attributes.func(self); + if (function_attributes != .none) try writer.print( + \\; Function Attrs:{} + \\ + , .{function_attributes.fmt(self)}); + try writer.print( + \\{s}{}{}{}{}{}{"} {%} {}( + , .{ + if (function.instructions.len > 0) "define" else "declare", + global.linkage, + global.preemption, + global.visibility, + global.dll_storage_class, + function.call_conv, + function.attributes.ret(self).fmt(self), + global.type.functionReturn(self).fmt(self), + function.global.fmt(self), + }); + for (0..params_len) |arg| { + if (arg > 0) try writer.writeAll(", "); + try writer.print( + \\{%}{"} + , .{ + global.type.functionParameters(self)[arg].fmt(self), + function.attributes.param(arg, self).fmt(self), + }); + if (function.instructions.len > 0) + try writer.print(" {}", .{function.arg(@intCast(arg)).fmt(function_index, self)}) + else + try writer.print(" %{d}", .{arg}); + } + switch (global.type.functionKind(self)) { + .normal => {}, + .vararg => { + if (params_len > 0) try writer.writeAll(", "); + try writer.writeAll("..."); + }, + } + try writer.print("){}{ }", .{ global.unnamed_addr, global.addr_space }); + if (function_attributes != .none) try writer.print(" #{d}", .{ + (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index, + }); + { + metadata_formatter.need_comma = false; + defer metadata_formatter.need_comma = undefined; + try writer.print("{ }{}", .{ + function.alignment, + try metadata_formatter.fmt(" !dbg ", global.dbg), + }); + } + if (function.instructions.len > 0) { + var block_incoming_len: u32 = undefined; + try writer.writeAll(" {\n"); + var maybe_dbg_index: ?u32 = null; + for (params_len..function.instructions.len) |instruction_i| { + const instruction_index: Function.Instruction.Index = @enumFromInt(instruction_i); + const instruction = function.instructions.get(@intFromEnum(instruction_index)); + if (function.debug_locations.get(instruction_index)) |debug_location| switch (debug_location) { + .no_location => maybe_dbg_index = null, + .location => |location| { + const gop = try metadata_formatter.map.getOrPut(self.gpa, .{ + .debug_location = location, + }); + maybe_dbg_index = @intCast(gop.index); + }, + }; + switch (instruction.tag) { + .add, + .@"add nsw", + .@"add nuw", + .@"add nuw nsw", + .@"and", + .ashr, + .@"ashr exact", + .fadd, + .@"fadd fast", + .@"fcmp false", + .@"fcmp fast false", + .@"fcmp fast oeq", + .@"fcmp fast oge", + .@"fcmp fast ogt", + .@"fcmp fast ole", + .@"fcmp fast olt", + .@"fcmp fast one", + .@"fcmp fast ord", + .@"fcmp fast true", + .@"fcmp fast ueq", + .@"fcmp fast uge", + .@"fcmp fast ugt", + .@"fcmp fast ule", + .@"fcmp fast ult", + .@"fcmp fast une", + .@"fcmp fast uno", + .@"fcmp oeq", + .@"fcmp oge", + .@"fcmp ogt", + .@"fcmp ole", + .@"fcmp olt", + .@"fcmp one", + .@"fcmp ord", + .@"fcmp true", + .@"fcmp ueq", + .@"fcmp uge", + .@"fcmp ugt", + .@"fcmp ule", + .@"fcmp ult", + .@"fcmp une", + .@"fcmp uno", + .fdiv, + .@"fdiv fast", + .fmul, + .@"fmul fast", + .frem, + .@"frem fast", + .fsub, + .@"fsub fast", + .@"icmp eq", + .@"icmp ne", + .@"icmp sge", + .@"icmp sgt", + .@"icmp sle", + .@"icmp slt", + .@"icmp uge", + .@"icmp ugt", + .@"icmp ule", + .@"icmp ult", + .lshr, + .@"lshr exact", + .mul, + .@"mul nsw", + .@"mul nuw", + .@"mul nuw nsw", + .@"or", + .sdiv, + .@"sdiv exact", + .srem, + .shl, + .@"shl nsw", + .@"shl nuw", + .@"shl nuw nsw", + .sub, + .@"sub nsw", + .@"sub nuw", + .@"sub nuw nsw", + .udiv, + .@"udiv exact", + .urem, + .xor, + => |tag| { + const extra = function.extraData(Function.Instruction.Binary, instruction.data); + try writer.print(" %{} = {s} {%}, {}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.lhs.fmt(function_index, self), + extra.rhs.fmt(function_index, self), + }); + }, + .addrspacecast, + .bitcast, + .fpext, + .fptosi, + .fptoui, + .fptrunc, + .inttoptr, + .ptrtoint, + .sext, + .sitofp, + .trunc, + .uitofp, + .zext, + => |tag| { + const extra = function.extraData(Function.Instruction.Cast, instruction.data); + try writer.print(" %{} = {s} {%} to {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.val.fmt(function_index, self), + extra.type.fmt(self), + }); + }, + .alloca, + .@"alloca inalloca", + => |tag| { + const extra = function.extraData(Function.Instruction.Alloca, instruction.data); + try writer.print(" %{} = {s} {%}{,%}{, }{, }", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.type.fmt(self), + Value.fmt(switch (extra.len) { + .@"1" => .none, + else => extra.len, + }, function_index, self), + extra.info.alignment, + extra.info.addr_space, + }); + }, + .arg => unreachable, + .atomicrmw => |tag| { + const extra = + function.extraData(Function.Instruction.AtomicRmw, instruction.data); + try writer.print(" %{} = {s}{ } {s} {%}, {%}{ }{ }{, }", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.info.access_kind, + @tagName(extra.info.atomic_rmw_operation), + extra.ptr.fmt(function_index, self), + extra.val.fmt(function_index, self), + extra.info.sync_scope, + extra.info.success_ordering, + extra.info.alignment, + }); + }, + .block => { + block_incoming_len = instruction.data; + const name = instruction_index.name(&function); + if (@intFromEnum(instruction_index) > params_len) + try writer.writeByte('\n'); + try writer.print("{}:\n", .{name.fmt(self)}); + continue; + }, + .br => |tag| { + const target: Function.Block.Index = @enumFromInt(instruction.data); + try writer.print(" {s} {%}", .{ + @tagName(tag), target.toInst(&function).fmt(function_index, self), + }); + }, + .br_cond => { + const extra = function.extraData(Function.Instruction.BrCond, instruction.data); + try writer.print(" br {%}, {%}, {%}", .{ + extra.cond.fmt(function_index, self), + extra.then.toInst(&function).fmt(function_index, self), + extra.@"else".toInst(&function).fmt(function_index, self), + }); + metadata_formatter.need_comma = true; + defer metadata_formatter.need_comma = undefined; + switch (extra.weights) { + .none => {}, + .unpredictable => try writer.writeAll("!unpredictable !{}"), + _ => try writer.print("{}", .{ + try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights)))), + }), + } + }, + .call, + .@"call fast", + .@"musttail call", + .@"musttail call fast", + .@"notail call", + .@"notail call fast", + .@"tail call", + .@"tail call fast", + => |tag| { + var extra = + function.extraDataTrail(Function.Instruction.Call, instruction.data); + const args = extra.trail.next(extra.data.args_len, Value, &function); + try writer.writeAll(" "); + const ret_ty = extra.data.ty.functionReturn(self); + switch (ret_ty) { + .void => {}, + else => try writer.print("%{} = ", .{ + instruction_index.name(&function).fmt(self), + }), + .none => unreachable, + } + try writer.print("{s}{}{}{} {%} {}(", .{ + @tagName(tag), + extra.data.info.call_conv, + extra.data.attributes.ret(self).fmt(self), + extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self), + switch (extra.data.ty.functionKind(self)) { + .normal => ret_ty, + .vararg => extra.data.ty, + }.fmt(self), + extra.data.callee.fmt(function_index, self), + }); + for (0.., args) |arg_index, arg| { + if (arg_index > 0) try writer.writeAll(", "); + metadata_formatter.need_comma = false; + defer metadata_formatter.need_comma = undefined; + try writer.print("{%}{}{}", .{ + arg.typeOf(function_index, self).fmt(self), + extra.data.attributes.param(arg_index, self).fmt(self), + try metadata_formatter.fmtLocal(" ", arg, function_index), + }); + } + try writer.writeByte(')'); + if (extra.data.info.has_op_bundle_cold) { + try writer.writeAll(" [ \"cold\"() ]"); + } + const call_function_attributes = extra.data.attributes.func(self); + if (call_function_attributes != .none) try writer.print(" #{d}", .{ + (try attribute_groups.getOrPutValue( + self.gpa, + call_function_attributes, + {}, + )).index, + }); + }, + .cmpxchg, + .@"cmpxchg weak", + => |tag| { + const extra = + function.extraData(Function.Instruction.CmpXchg, instruction.data); + try writer.print(" %{} = {s}{ } {%}, {%}, {%}{ }{ }{ }{, }", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.info.access_kind, + extra.ptr.fmt(function_index, self), + extra.cmp.fmt(function_index, self), + extra.new.fmt(function_index, self), + extra.info.sync_scope, + extra.info.success_ordering, + extra.info.failure_ordering, + extra.info.alignment, + }); + }, + .extractelement => |tag| { + const extra = + function.extraData(Function.Instruction.ExtractElement, instruction.data); + try writer.print(" %{} = {s} {%}, {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.val.fmt(function_index, self), + extra.index.fmt(function_index, self), + }); + }, + .extractvalue => |tag| { + var extra = function.extraDataTrail( + Function.Instruction.ExtractValue, + instruction.data, + ); + const indices = extra.trail.next(extra.data.indices_len, u32, &function); + try writer.print(" %{} = {s} {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.data.val.fmt(function_index, self), + }); + for (indices) |index| try writer.print(", {d}", .{index}); + }, + .fence => |tag| { + const info: MemoryAccessInfo = @bitCast(instruction.data); + try writer.print(" {s}{ }{ }", .{ + @tagName(tag), + info.sync_scope, + info.success_ordering, + }); + }, + .fneg, + .@"fneg fast", + => |tag| { + const val: Value = @enumFromInt(instruction.data); + try writer.print(" %{} = {s} {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + val.fmt(function_index, self), + }); + }, + .getelementptr, + .@"getelementptr inbounds", + => |tag| { + var extra = function.extraDataTrail( + Function.Instruction.GetElementPtr, + instruction.data, + ); + const indices = extra.trail.next(extra.data.indices_len, Value, &function); + try writer.print(" %{} = {s} {%}, {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.data.type.fmt(self), + extra.data.base.fmt(function_index, self), + }); + for (indices) |index| try writer.print(", {%}", .{ + index.fmt(function_index, self), + }); + }, + .indirectbr => |tag| { + var extra = + function.extraDataTrail(Function.Instruction.IndirectBr, instruction.data); + const targets = + extra.trail.next(extra.data.targets_len, Function.Block.Index, &function); + try writer.print(" {s} {%}, [", .{ + @tagName(tag), + extra.data.addr.fmt(function_index, self), + }); + for (0.., targets) |target_index, target| { + if (target_index > 0) try writer.writeAll(", "); + try writer.print("{%}", .{ + target.toInst(&function).fmt(function_index, self), + }); + } + try writer.writeByte(']'); + }, + .insertelement => |tag| { + const extra = + function.extraData(Function.Instruction.InsertElement, instruction.data); + try writer.print(" %{} = {s} {%}, {%}, {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.val.fmt(function_index, self), + extra.elem.fmt(function_index, self), + extra.index.fmt(function_index, self), + }); + }, + .insertvalue => |tag| { + var extra = + function.extraDataTrail(Function.Instruction.InsertValue, instruction.data); + const indices = extra.trail.next(extra.data.indices_len, u32, &function); + try writer.print(" %{} = {s} {%}, {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.data.val.fmt(function_index, self), + extra.data.elem.fmt(function_index, self), + }); + for (indices) |index| try writer.print(", {d}", .{index}); + }, + .load, + .@"load atomic", + => |tag| { + const extra = function.extraData(Function.Instruction.Load, instruction.data); + try writer.print(" %{} = {s}{ } {%}, {%}{ }{ }{, }", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.info.access_kind, + extra.type.fmt(self), + extra.ptr.fmt(function_index, self), + extra.info.sync_scope, + extra.info.success_ordering, + extra.info.alignment, + }); + }, + .phi, + .@"phi fast", + => |tag| { + var extra = function.extraDataTrail(Function.Instruction.Phi, instruction.data); + const vals = extra.trail.next(block_incoming_len, Value, &function); + const blocks = + extra.trail.next(block_incoming_len, Function.Block.Index, &function); + try writer.print(" %{} = {s} {%} ", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + vals[0].typeOf(function_index, self).fmt(self), + }); + for (0.., vals, blocks) |incoming_index, incoming_val, incoming_block| { + if (incoming_index > 0) try writer.writeAll(", "); + try writer.print("[ {}, {} ]", .{ + incoming_val.fmt(function_index, self), + incoming_block.toInst(&function).fmt(function_index, self), + }); + } + }, + .ret => |tag| { + const val: Value = @enumFromInt(instruction.data); + try writer.print(" {s} {%}", .{ + @tagName(tag), + val.fmt(function_index, self), + }); + }, + .@"ret void", + .@"unreachable", + => |tag| try writer.print(" {s}", .{@tagName(tag)}), + .select, + .@"select fast", + => |tag| { + const extra = function.extraData(Function.Instruction.Select, instruction.data); + try writer.print(" %{} = {s} {%}, {%}, {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.cond.fmt(function_index, self), + extra.lhs.fmt(function_index, self), + extra.rhs.fmt(function_index, self), + }); + }, + .shufflevector => |tag| { + const extra = + function.extraData(Function.Instruction.ShuffleVector, instruction.data); + try writer.print(" %{} = {s} {%}, {%}, {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.lhs.fmt(function_index, self), + extra.rhs.fmt(function_index, self), + extra.mask.fmt(function_index, self), + }); + }, + .store, + .@"store atomic", + => |tag| { + const extra = function.extraData(Function.Instruction.Store, instruction.data); + try writer.print(" {s}{ } {%}, {%}{ }{ }{, }", .{ + @tagName(tag), + extra.info.access_kind, + extra.val.fmt(function_index, self), + extra.ptr.fmt(function_index, self), + extra.info.sync_scope, + extra.info.success_ordering, + extra.info.alignment, + }); + }, + .@"switch" => |tag| { + var extra = + function.extraDataTrail(Function.Instruction.Switch, instruction.data); + const vals = extra.trail.next(extra.data.cases_len, Constant, &function); + const blocks = + extra.trail.next(extra.data.cases_len, Function.Block.Index, &function); + try writer.print(" {s} {%}, {%} [\n", .{ + @tagName(tag), + extra.data.val.fmt(function_index, self), + extra.data.default.toInst(&function).fmt(function_index, self), + }); + for (vals, blocks) |case_val, case_block| try writer.print( + " {%}, {%}\n", + .{ + case_val.fmt(self), + case_block.toInst(&function).fmt(function_index, self), + }, + ); + try writer.writeAll(" ]"); + metadata_formatter.need_comma = true; + defer metadata_formatter.need_comma = undefined; + switch (extra.data.weights) { + .none => {}, + .unpredictable => try writer.writeAll("!unpredictable !{}"), + _ => try writer.print("{}", .{ + try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights)))), + }), + } + }, + .va_arg => |tag| { + const extra = function.extraData(Function.Instruction.VaArg, instruction.data); + try writer.print(" %{} = {s} {%}, {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.list.fmt(function_index, self), + extra.type.fmt(self), + }); + }, + } + + if (maybe_dbg_index) |dbg_index| { + try writer.print(", !dbg !{}", .{dbg_index}); + } + try writer.writeByte('\n'); + } + try writer.writeByte('}'); + } + try writer.writeByte('\n'); + } + + if (attribute_groups.count() > 0) { + if (need_newline) try writer.writeByte('\n') else need_newline = true; + for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group| + try writer.print( + \\attributes #{d} = {{{#"} }} + \\ + , .{ attribute_group_index, attribute_group.fmt(self) }); + } + + if (self.metadata_named.count() > 0) { + if (need_newline) try writer.writeByte('\n') else need_newline = true; + for (self.metadata_named.keys(), self.metadata_named.values()) |name, data| { + const elements: []const Metadata = + @ptrCast(self.metadata_extra.items[data.index..][0..data.len]); + try writer.writeByte('!'); + try printEscapedString(name.slice(self), .quote_unless_valid_identifier, writer); + try writer.writeAll(" = !{"); + metadata_formatter.need_comma = false; + defer metadata_formatter.need_comma = undefined; + for (elements) |element| try writer.print("{}", .{try metadata_formatter.fmt("", element)}); + try writer.writeAll("}\n"); + } + } + + if (metadata_formatter.map.count() > 0) { + if (need_newline) try writer.writeByte('\n') else need_newline = true; + var metadata_index: usize = 0; + while (metadata_index < metadata_formatter.map.count()) : (metadata_index += 1) { + @setEvalBranchQuota(10_000); + try writer.print("!{} = ", .{metadata_index}); + metadata_formatter.need_comma = false; + defer metadata_formatter.need_comma = undefined; + + const key = metadata_formatter.map.keys()[metadata_index]; + const metadata_item = switch (key) { + .debug_location => |location| { + try metadata_formatter.specialized(.@"!", .DILocation, .{ + .line = location.line, + .column = location.column, + .scope = location.scope, + .inlinedAt = location.inlined_at, + .isImplicitCode = false, + }, writer); + continue; + }, + .metadata => |metadata| self.metadata_items.get(@intFromEnum(metadata)), + }; + + switch (metadata_item.tag) { + .none, .expression, .constant => unreachable, + .file => { + const extra = self.metadataExtraData(Metadata.File, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DIFile, .{ + .filename = extra.filename, + .directory = extra.directory, + .checksumkind = null, + .checksum = null, + .source = null, + }, writer); + }, + .compile_unit, + .@"compile_unit optimized", + => |kind| { + const extra = self.metadataExtraData(Metadata.CompileUnit, metadata_item.data); + try metadata_formatter.specialized(.@"distinct !", .DICompileUnit, .{ + .language = .DW_LANG_C99, + .file = extra.file, + .producer = extra.producer, + .isOptimized = switch (kind) { + .compile_unit => false, + .@"compile_unit optimized" => true, + else => unreachable, + }, + .flags = null, + .runtimeVersion = 0, + .splitDebugFilename = null, + .emissionKind = .FullDebug, + .enums = extra.enums, + .retainedTypes = null, + .globals = extra.globals, + .imports = null, + .macros = null, + .dwoId = null, + .splitDebugInlining = false, + .debugInfoForProfiling = null, + .nameTableKind = null, + .rangesBaseAddress = null, + .sysroot = null, + .sdk = null, + }, writer); + }, + .subprogram, + .@"subprogram local", + .@"subprogram definition", + .@"subprogram local definition", + .@"subprogram optimized", + .@"subprogram optimized local", + .@"subprogram optimized definition", + .@"subprogram optimized local definition", + => |kind| { + const extra = self.metadataExtraData(Metadata.Subprogram, metadata_item.data); + try metadata_formatter.specialized(.@"distinct !", .DISubprogram, .{ + .name = extra.name, + .linkageName = extra.linkage_name, + .scope = extra.file, + .file = extra.file, + .line = extra.line, + .type = extra.ty, + .scopeLine = extra.scope_line, + .containingType = null, + .virtualIndex = null, + .thisAdjustment = null, + .flags = extra.di_flags, + .spFlags = @as(Metadata.Subprogram.DISPFlags, @bitCast(@as(u32, @as(u3, @intCast( + @intFromEnum(kind) - @intFromEnum(Metadata.Tag.subprogram), + ))) << 2)), + .unit = extra.compile_unit, + .templateParams = null, + .declaration = null, + .retainedNodes = null, + .thrownTypes = null, + .annotations = null, + .targetFuncName = null, + }, writer); + }, + .lexical_block => { + const extra = self.metadataExtraData(Metadata.LexicalBlock, metadata_item.data); + try metadata_formatter.specialized(.@"distinct !", .DILexicalBlock, .{ + .scope = extra.scope, + .file = extra.file, + .line = extra.line, + .column = extra.column, + }, writer); + }, + .location => { + const extra = self.metadataExtraData(Metadata.Location, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DILocation, .{ + .line = extra.line, + .column = extra.column, + .scope = extra.scope, + .inlinedAt = extra.inlined_at, + .isImplicitCode = false, + }, writer); + }, + .basic_bool_type, + .basic_unsigned_type, + .basic_signed_type, + .basic_float_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.BasicType, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DIBasicType, .{ + .tag = null, + .name = switch (extra.name) { + .none => null, + else => extra.name, + }, + .size = extra.bitSize(), + .@"align" = null, + .encoding = @as(enum { + DW_ATE_boolean, + DW_ATE_unsigned, + DW_ATE_signed, + DW_ATE_float, + }, switch (kind) { + .basic_bool_type => .DW_ATE_boolean, + .basic_unsigned_type => .DW_ATE_unsigned, + .basic_signed_type => .DW_ATE_signed, + .basic_float_type => .DW_ATE_float, + else => unreachable, + }), + .flags = null, + }, writer); + }, + .composite_struct_type, + .composite_union_type, + .composite_enumeration_type, + .composite_array_type, + .composite_vector_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.CompositeType, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DICompositeType, .{ + .tag = @as(enum { + DW_TAG_structure_type, + DW_TAG_union_type, + DW_TAG_enumeration_type, + DW_TAG_array_type, + }, switch (kind) { + .composite_struct_type => .DW_TAG_structure_type, + .composite_union_type => .DW_TAG_union_type, + .composite_enumeration_type => .DW_TAG_enumeration_type, + .composite_array_type, .composite_vector_type => .DW_TAG_array_type, + else => unreachable, + }), + .name = switch (extra.name) { + .none => null, + else => extra.name, + }, + .scope = extra.scope, + .file = null, + .line = null, + .baseType = extra.underlying_type, + .size = extra.bitSize(), + .@"align" = extra.bitAlign(), + .offset = null, + .flags = null, + .elements = extra.fields_tuple, + .runtimeLang = null, + .vtableHolder = null, + .templateParams = null, + .identifier = null, + .discriminator = null, + .dataLocation = null, + .associated = null, + .allocated = null, + .rank = null, + .annotations = null, + }, writer); + }, + .derived_pointer_type, + .derived_member_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.DerivedType, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DIDerivedType, .{ + .tag = @as(enum { + DW_TAG_pointer_type, + DW_TAG_member, + }, switch (kind) { + .derived_pointer_type => .DW_TAG_pointer_type, + .derived_member_type => .DW_TAG_member, + else => unreachable, + }), + .name = switch (extra.name) { + .none => null, + else => extra.name, + }, + .scope = extra.scope, + .file = null, + .line = null, + .baseType = extra.underlying_type, + .size = extra.bitSize(), + .@"align" = extra.bitAlign(), + .offset = switch (extra.bitOffset()) { + 0 => null, + else => |bit_offset| bit_offset, + }, + .flags = null, + .extraData = null, + .dwarfAddressSpace = null, + .annotations = null, + }, writer); + }, + .subroutine_type => { + const extra = self.metadataExtraData(Metadata.SubroutineType, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DISubroutineType, .{ + .flags = null, + .cc = null, + .types = extra.types_tuple, + }, writer); + }, + .enumerator_unsigned, + .enumerator_signed_positive, + .enumerator_signed_negative, + => |kind| { + const extra = self.metadataExtraData(Metadata.Enumerator, metadata_item.data); + + const ExpectedContents = extern struct { + const expected_limbs = @divExact(512, @bitSizeOf(std.math.big.Limb)); + string: [ + (std.math.big.int.Const{ + .limbs = &([1]std.math.big.Limb{ + std.math.maxInt(std.math.big.Limb), + } ** expected_limbs), + .positive = false, + }).sizeInBaseUpperBound(10) + ]u8, + limbs: [ + std.math.big.int.calcToStringLimbsBufferLen(expected_limbs, 10) + ]std.math.big.Limb, + }; + var stack align(@alignOf(ExpectedContents)) = + std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); + const allocator = stack.get(); + + const limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len]; + const bigint: std.math.big.int.Const = .{ + .limbs = limbs, + .positive = switch (kind) { + .enumerator_unsigned, + .enumerator_signed_positive, + => true, + .enumerator_signed_negative => false, + else => unreachable, + }, + }; + const str = try bigint.toStringAlloc(allocator, 10, undefined); + defer allocator.free(str); + + try metadata_formatter.specialized(.@"!", .DIEnumerator, .{ + .name = extra.name, + .value = str, + .isUnsigned = switch (kind) { + .enumerator_unsigned => true, + .enumerator_signed_positive, + .enumerator_signed_negative, + => false, + else => unreachable, + }, + }, writer); + }, + .subrange => { + const extra = self.metadataExtraData(Metadata.Subrange, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DISubrange, .{ + .count = extra.count, + .lowerBound = extra.lower_bound, + .upperBound = null, + .stride = null, + }, writer); + }, + .tuple => { + var extra = self.metadataExtraDataTrail(Metadata.Tuple, metadata_item.data); + const elements = extra.trail.next(extra.data.elements_len, Metadata, self); + try writer.writeAll("!{"); + for (elements) |element| try writer.print("{[element]%}", .{ + .element = try metadata_formatter.fmt("", element), + }); + try writer.writeAll("}\n"); + }, + .str_tuple => { + var extra = self.metadataExtraDataTrail(Metadata.StrTuple, metadata_item.data); + const elements = extra.trail.next(extra.data.elements_len, Metadata, self); + try writer.print("!{{{[str]%}", .{ + .str = try metadata_formatter.fmt("", extra.data.str), + }); + for (elements) |element| try writer.print("{[element]%}", .{ + .element = try metadata_formatter.fmt("", element), + }); + try writer.writeAll("}\n"); + }, + .module_flag => { + const extra = self.metadataExtraData(Metadata.ModuleFlag, metadata_item.data); + try writer.print("!{{{[behavior]%}{[name]%}{[constant]%}}}\n", .{ + .behavior = try metadata_formatter.fmt("", extra.behavior), + .name = try metadata_formatter.fmt("", extra.name), + .constant = try metadata_formatter.fmt("", extra.constant), + }); + }, + .local_var => { + const extra = self.metadataExtraData(Metadata.LocalVar, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DILocalVariable, .{ + .name = extra.name, + .arg = null, + .scope = extra.scope, + .file = extra.file, + .line = extra.line, + .type = extra.ty, + .flags = null, + .@"align" = null, + .annotations = null, + }, writer); + }, + .parameter => { + const extra = self.metadataExtraData(Metadata.Parameter, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DILocalVariable, .{ + .name = extra.name, + .arg = extra.arg_no, + .scope = extra.scope, + .file = extra.file, + .line = extra.line, + .type = extra.ty, + .flags = null, + .@"align" = null, + .annotations = null, + }, writer); + }, + .global_var, + .@"global_var local", + => |kind| { + const extra = self.metadataExtraData(Metadata.GlobalVar, metadata_item.data); + try metadata_formatter.specialized(.@"distinct !", .DIGlobalVariable, .{ + .name = extra.name, + .linkageName = extra.linkage_name, + .scope = extra.scope, + .file = extra.file, + .line = extra.line, + .type = extra.ty, + .isLocal = switch (kind) { + .global_var => false, + .@"global_var local" => true, + else => unreachable, + }, + .isDefinition = true, + .declaration = null, + .templateParams = null, + .@"align" = null, + .annotations = null, + }, writer); + }, + .global_var_expression => { + const extra = + self.metadataExtraData(Metadata.GlobalVarExpression, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DIGlobalVariableExpression, .{ + .@"var" = extra.variable, + .expr = extra.expression, + }, writer); + }, + } + } + } +} + +const NoExtra = struct {}; + +fn isValidIdentifier(id: []const u8) bool { + for (id, 0..) |byte, index| switch (byte) { + '$', '-', '.', 'A'...'Z', '_', 'a'...'z' => {}, + '0'...'9' => if (index == 0) return false, + else => return false, + }; + return true; +} + +const QuoteBehavior = enum { always_quote, quote_unless_valid_identifier }; +fn printEscapedString( + slice: []const u8, + quotes: QuoteBehavior, + writer: anytype, +) @TypeOf(writer).Error!void { + const need_quotes = switch (quotes) { + .always_quote => true, + .quote_unless_valid_identifier => !isValidIdentifier(slice), + }; + if (need_quotes) try writer.writeByte('"'); + for (slice) |byte| switch (byte) { + '\\' => try writer.writeAll("\\\\"), + ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try writer.writeByte(byte), + else => try writer.print("\\{X:0>2}", .{byte}), + }; + if (need_quotes) try writer.writeByte('"'); +} + +fn ensureUnusedGlobalCapacity(self: *Builder, name: StrtabString) Allocator.Error!void { + try self.strtab_string_map.ensureUnusedCapacity(self.gpa, 1); + if (name.slice(self)) |id| { + const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)}); + try self.strtab_string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); + } + try self.strtab_string_indices.ensureUnusedCapacity(self.gpa, 1); + try self.globals.ensureUnusedCapacity(self.gpa, 1); + try self.next_unique_global_id.ensureUnusedCapacity(self.gpa, 1); +} + +fn fnTypeAssumeCapacity( + self: *Builder, + ret: Type, + params: []const Type, + comptime kind: Type.Function.Kind, +) Type { + const tag: Type.Tag = switch (kind) { + .normal => .function, + .vararg => .vararg_function, + }; + const Key = struct { ret: Type, params: []const Type }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = std.hash.Wyhash.init(comptime std.hash.uint32(@intFromEnum(tag))); + hasher.update(std.mem.asBytes(&key.ret)); + hasher.update(std.mem.sliceAsBytes(key.params)); + return @truncate(hasher.final()); + } + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + const rhs_data = ctx.builder.type_items.items[rhs_index]; + if (rhs_data.tag != tag) return false; + var rhs_extra = ctx.builder.typeExtraDataTrail(Type.Function, rhs_data.data); + const rhs_params = rhs_extra.trail.next(rhs_extra.data.params_len, Type, ctx.builder); + return lhs_key.ret == rhs_extra.data.ret and std.mem.eql(Type, lhs_key.params, rhs_params); + } + }; + const gop = self.type_map.getOrPutAssumeCapacityAdapted( + Key{ .ret = ret, .params = params }, + Adapter{ .builder = self }, + ); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.type_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addTypeExtraAssumeCapacity(Type.Function{ + .ret = ret, + .params_len = @intCast(params.len), + }), + }); + self.type_extra.appendSliceAssumeCapacity(@ptrCast(params)); + } + return @enumFromInt(gop.index); +} + +fn intTypeAssumeCapacity(self: *Builder, bits: u24) Type { + assert(bits > 0); + const result = self.getOrPutTypeNoExtraAssumeCapacity(.{ .tag = .integer, .data = bits }); + return result.type; +} + +fn ptrTypeAssumeCapacity(self: *Builder, addr_space: AddrSpace) Type { + const result = self.getOrPutTypeNoExtraAssumeCapacity( + .{ .tag = .pointer, .data = @intFromEnum(addr_space) }, + ); + return result.type; +} + +fn vectorTypeAssumeCapacity( + self: *Builder, + comptime kind: Type.Vector.Kind, + len: u32, + child: Type, +) Type { + assert(child.isFloatingPoint() or child.isInteger(self) or child.isPointer(self)); + const tag: Type.Tag = switch (kind) { + .normal => .vector, + .scalable => .scalable_vector, + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Type.Vector) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(tag)), + std.mem.asBytes(&key), + )); + } + pub fn eql(ctx: @This(), lhs_key: Type.Vector, _: void, rhs_index: usize) bool { + const rhs_data = ctx.builder.type_items.items[rhs_index]; + return rhs_data.tag == tag and + std.meta.eql(lhs_key, ctx.builder.typeExtraData(Type.Vector, rhs_data.data)); + } + }; + const data = Type.Vector{ .len = len, .child = child }; + const gop = self.type_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.type_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addTypeExtraAssumeCapacity(data), + }); + } + return @enumFromInt(gop.index); +} + +fn arrayTypeAssumeCapacity(self: *Builder, len: u64, child: Type) Type { + if (std.math.cast(u32, len)) |small_len| { + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Type.Vector) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(Type.Tag.small_array)), + std.mem.asBytes(&key), + )); + } + pub fn eql(ctx: @This(), lhs_key: Type.Vector, _: void, rhs_index: usize) bool { + const rhs_data = ctx.builder.type_items.items[rhs_index]; + return rhs_data.tag == .small_array and + std.meta.eql(lhs_key, ctx.builder.typeExtraData(Type.Vector, rhs_data.data)); + } + }; + const data = Type.Vector{ .len = small_len, .child = child }; + const gop = self.type_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.type_items.appendAssumeCapacity(.{ + .tag = .small_array, + .data = self.addTypeExtraAssumeCapacity(data), + }); + } + return @enumFromInt(gop.index); + } else { + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Type.Array) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(Type.Tag.array)), + std.mem.asBytes(&key), + )); + } + pub fn eql(ctx: @This(), lhs_key: Type.Array, _: void, rhs_index: usize) bool { + const rhs_data = ctx.builder.type_items.items[rhs_index]; + return rhs_data.tag == .array and + std.meta.eql(lhs_key, ctx.builder.typeExtraData(Type.Array, rhs_data.data)); + } + }; + const data = Type.Array{ + .len_lo = @truncate(len), + .len_hi = @intCast(len >> 32), + .child = child, + }; + const gop = self.type_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.type_items.appendAssumeCapacity(.{ + .tag = .array, + .data = self.addTypeExtraAssumeCapacity(data), + }); + } + return @enumFromInt(gop.index); + } +} + +fn structTypeAssumeCapacity( + self: *Builder, + comptime kind: Type.Structure.Kind, + fields: []const Type, +) Type { + const tag: Type.Tag = switch (kind) { + .normal => .structure, + .@"packed" => .packed_structure, + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: []const Type) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(tag)), + std.mem.sliceAsBytes(key), + )); + } + pub fn eql(ctx: @This(), lhs_key: []const Type, _: void, rhs_index: usize) bool { + const rhs_data = ctx.builder.type_items.items[rhs_index]; + if (rhs_data.tag != tag) return false; + var rhs_extra = ctx.builder.typeExtraDataTrail(Type.Structure, rhs_data.data); + const rhs_fields = rhs_extra.trail.next(rhs_extra.data.fields_len, Type, ctx.builder); + return std.mem.eql(Type, lhs_key, rhs_fields); + } + }; + const gop = self.type_map.getOrPutAssumeCapacityAdapted(fields, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.type_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addTypeExtraAssumeCapacity(Type.Structure{ + .fields_len = @intCast(fields.len), + }), + }); + self.type_extra.appendSliceAssumeCapacity(@ptrCast(fields)); + } + return @enumFromInt(gop.index); +} + +fn opaqueTypeAssumeCapacity(self: *Builder, name: String) Type { + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: String) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(Type.Tag.named_structure)), + std.mem.asBytes(&key), + )); + } + pub fn eql(ctx: @This(), lhs_key: String, _: void, rhs_index: usize) bool { + const rhs_data = ctx.builder.type_items.items[rhs_index]; + return rhs_data.tag == .named_structure and + lhs_key == ctx.builder.typeExtraData(Type.NamedStructure, rhs_data.data).id; + } + }; + var id = name; + if (name == .empty) { + id = self.next_unnamed_type; + assert(id != .none); + self.next_unnamed_type = @enumFromInt(@intFromEnum(id) + 1); + } else assert(!name.isAnon()); + 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_items.appendAssumeCapacity(.{ + .tag = .named_structure, + .data = self.addTypeExtraAssumeCapacity(Type.NamedStructure{ + .id = id, + .body = .none, + }), + }); + const result: Type = @enumFromInt(gop.index); + type_gop.value_ptr.* = result; + 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.slice(self).?, unique_gop.value_ptr.* }); + unique_gop.value_ptr.* += 1; + } +} + +fn ensureUnusedTypeCapacity( + self: *Builder, + count: usize, + comptime Extra: type, + trail_len: usize, +) Allocator.Error!void { + try self.type_map.ensureUnusedCapacity(self.gpa, count); + try self.type_items.ensureUnusedCapacity(self.gpa, count); + try self.type_extra.ensureUnusedCapacity( + self.gpa, + count * (@typeInfo(Extra).@"struct".fields.len + trail_len), + ); +} + +fn getOrPutTypeNoExtraAssumeCapacity(self: *Builder, item: Type.Item) struct { new: bool, type: Type } { + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Type.Item) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(Type.Tag.simple)), + std.mem.asBytes(&key), + )); + } + pub fn eql(ctx: @This(), lhs_key: Type.Item, _: void, rhs_index: usize) bool { + const lhs_bits: u32 = @bitCast(lhs_key); + const rhs_bits: u32 = @bitCast(ctx.builder.type_items.items[rhs_index]); + return lhs_bits == rhs_bits; + } + }; + const gop = self.type_map.getOrPutAssumeCapacityAdapted(item, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.type_items.appendAssumeCapacity(item); + } + return .{ .new = !gop.found_existing, .type = @enumFromInt(gop.index) }; +} + +fn addTypeExtraAssumeCapacity(self: *Builder, extra: anytype) Type.Item.ExtraIndex { + const result: Type.Item.ExtraIndex = @intCast(self.type_extra.items.len); + inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| { + const value = @field(extra, field.name); + self.type_extra.appendAssumeCapacity(switch (field.type) { + u32 => value, + String, Type => @intFromEnum(value), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), + }); + } + return result; +} + +const TypeExtraDataTrail = struct { + index: Type.Item.ExtraIndex, + + fn nextMut(self: *TypeExtraDataTrail, len: u32, comptime Item: type, builder: *Builder) []Item { + const items: []Item = @ptrCast(builder.type_extra.items[self.index..][0..len]); + self.index += @intCast(len); + return items; + } + + fn next( + self: *TypeExtraDataTrail, + len: u32, + comptime Item: type, + builder: *const Builder, + ) []const Item { + const items: []const Item = @ptrCast(builder.type_extra.items[self.index..][0..len]); + self.index += @intCast(len); + return items; + } +}; + +fn typeExtraDataTrail( + self: *const Builder, + comptime T: type, + index: Type.Item.ExtraIndex, +) struct { data: T, trail: TypeExtraDataTrail } { + var result: T = undefined; + const fields = @typeInfo(T).@"struct".fields; + inline for (fields, self.type_extra.items[index..][0..fields.len]) |field, value| + @field(result, field.name) = switch (field.type) { + u32 => value, + String, Type => @enumFromInt(value), + else => @compileError("bad field type: " ++ @typeName(field.type)), + }; + return .{ + .data = result, + .trail = .{ .index = index + @as(Type.Item.ExtraIndex, @intCast(fields.len)) }, + }; +} + +fn typeExtraData(self: *const Builder, comptime T: type, index: Type.Item.ExtraIndex) T { + return self.typeExtraDataTrail(T, index).data; +} + +fn attrGeneric(self: *Builder, data: []const u32) Allocator.Error!u32 { + try self.attributes_map.ensureUnusedCapacity(self.gpa, 1); + try self.attributes_indices.ensureUnusedCapacity(self.gpa, 1); + try self.attributes_extra.ensureUnusedCapacity(self.gpa, data.len); + + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: []const u32) u32 { + return @truncate(std.hash.Wyhash.hash(1, std.mem.sliceAsBytes(key))); + } + pub fn eql(ctx: @This(), lhs_key: []const u32, _: void, rhs_index: usize) bool { + const start = ctx.builder.attributes_indices.items[rhs_index]; + const end = ctx.builder.attributes_indices.items[rhs_index + 1]; + return std.mem.eql(u32, lhs_key, ctx.builder.attributes_extra.items[start..end]); + } + }; + const gop = self.attributes_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + self.attributes_extra.appendSliceAssumeCapacity(data); + self.attributes_indices.appendAssumeCapacity(@intCast(self.attributes_extra.items.len)); + } + return @intCast(gop.index); +} + +fn bigIntConstAssumeCapacity( + self: *Builder, + ty: Type, + value: std.math.big.int.Const, +) Allocator.Error!Constant { + const type_item = self.type_items.items[@intFromEnum(ty)]; + assert(type_item.tag == .integer); + const bits = type_item.data; + + const ExpectedContents = [64 / @sizeOf(std.math.big.Limb)]std.math.big.Limb; + var stack align(@alignOf(ExpectedContents)) = + std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); + const allocator = stack.get(); + + var limbs: []std.math.big.Limb = &.{}; + defer allocator.free(limbs); + const canonical_value = if (value.fitsInTwosComp(.signed, bits)) value else canon: { + assert(value.fitsInTwosComp(.unsigned, bits)); + limbs = try allocator.alloc(std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(bits)); + var temp_value = std.math.big.int.Mutable.init(limbs, 0); + temp_value.truncate(value, .signed, bits); + break :canon temp_value.toConst(); + }; + assert(canonical_value.fitsInTwosComp(.signed, bits)); + + const ExtraPtr = *align(@alignOf(std.math.big.Limb)) Constant.Integer; + const Key = struct { tag: Constant.Tag, type: Type, limbs: []const std.math.big.Limb }; + const tag: Constant.Tag = switch (canonical_value.positive) { + true => .positive_integer, + false => .negative_integer, + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(key.tag))); + hasher.update(std.mem.asBytes(&key.type)); + hasher.update(std.mem.sliceAsBytes(key.limbs)); + return @truncate(hasher.final()); + } + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra: ExtraPtr = + @ptrCast(ctx.builder.constant_limbs.items[rhs_data..][0..Constant.Integer.limbs]); + const rhs_limbs = ctx.builder.constant_limbs + .items[rhs_data + Constant.Integer.limbs ..][0..rhs_extra.limbs_len]; + return lhs_key.type == rhs_extra.type and + std.mem.eql(std.math.big.Limb, lhs_key.limbs, rhs_limbs); + } + }; + + const gop = self.constant_map.getOrPutAssumeCapacityAdapted( + Key{ .tag = tag, .type = ty, .limbs = canonical_value.limbs }, + Adapter{ .builder = self }, + ); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = tag, + .data = @intCast(self.constant_limbs.items.len), + }); + const extra: ExtraPtr = + @ptrCast(self.constant_limbs.addManyAsArrayAssumeCapacity(Constant.Integer.limbs)); + extra.* = .{ .type = ty, .limbs_len = @intCast(canonical_value.limbs.len) }; + self.constant_limbs.appendSliceAssumeCapacity(canonical_value.limbs); + } + return @enumFromInt(gop.index); +} + +fn halfConstAssumeCapacity(self: *Builder, val: f16) Constant { + const result = self.getOrPutConstantNoExtraAssumeCapacity( + .{ .tag = .half, .data = @as(u16, @bitCast(val)) }, + ); + return result.constant; +} + +fn bfloatConstAssumeCapacity(self: *Builder, val: f32) Constant { + assert(@as(u16, @truncate(@as(u32, @bitCast(val)))) == 0); + const result = self.getOrPutConstantNoExtraAssumeCapacity( + .{ .tag = .bfloat, .data = @bitCast(val) }, + ); + return result.constant; +} + +fn floatConstAssumeCapacity(self: *Builder, val: f32) Constant { + const result = self.getOrPutConstantNoExtraAssumeCapacity( + .{ .tag = .float, .data = @bitCast(val) }, + ); + return result.constant; +} + +fn doubleConstAssumeCapacity(self: *Builder, val: f64) Constant { + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: f64) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(Constant.Tag.double)), + std.mem.asBytes(&key), + )); + } + pub fn eql(ctx: @This(), lhs_key: f64, _: void, rhs_index: usize) bool { + if (ctx.builder.constant_items.items(.tag)[rhs_index] != .double) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.constantExtraData(Constant.Double, rhs_data); + return @as(u64, @bitCast(lhs_key)) == @as(u64, rhs_extra.hi) << 32 | rhs_extra.lo; + } + }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(val, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = .double, + .data = self.addConstantExtraAssumeCapacity(Constant.Double{ + .lo = @truncate(@as(u64, @bitCast(val))), + .hi = @intCast(@as(u64, @bitCast(val)) >> 32), + }), + }); + } + return @enumFromInt(gop.index); +} + +fn fp128ConstAssumeCapacity(self: *Builder, val: f128) Constant { + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: f128) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(Constant.Tag.fp128)), + std.mem.asBytes(&key), + )); + } + pub fn eql(ctx: @This(), lhs_key: f128, _: void, rhs_index: usize) bool { + if (ctx.builder.constant_items.items(.tag)[rhs_index] != .fp128) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.constantExtraData(Constant.Fp128, rhs_data); + return @as(u128, @bitCast(lhs_key)) == @as(u128, rhs_extra.hi_hi) << 96 | + @as(u128, rhs_extra.hi_lo) << 64 | @as(u128, rhs_extra.lo_hi) << 32 | rhs_extra.lo_lo; + } + }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(val, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = .fp128, + .data = self.addConstantExtraAssumeCapacity(Constant.Fp128{ + .lo_lo = @truncate(@as(u128, @bitCast(val))), + .lo_hi = @truncate(@as(u128, @bitCast(val)) >> 32), + .hi_lo = @truncate(@as(u128, @bitCast(val)) >> 64), + .hi_hi = @intCast(@as(u128, @bitCast(val)) >> 96), + }), + }); + } + return @enumFromInt(gop.index); +} + +fn x86_fp80ConstAssumeCapacity(self: *Builder, val: f80) Constant { + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: f80) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(Constant.Tag.x86_fp80)), + std.mem.asBytes(&key)[0..10], + )); + } + pub fn eql(ctx: @This(), lhs_key: f80, _: void, rhs_index: usize) bool { + if (ctx.builder.constant_items.items(.tag)[rhs_index] != .x86_fp80) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.constantExtraData(Constant.Fp80, rhs_data); + return @as(u80, @bitCast(lhs_key)) == @as(u80, rhs_extra.hi) << 64 | + @as(u80, rhs_extra.lo_hi) << 32 | rhs_extra.lo_lo; + } + }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(val, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = .x86_fp80, + .data = self.addConstantExtraAssumeCapacity(Constant.Fp80{ + .lo_lo = @truncate(@as(u80, @bitCast(val))), + .lo_hi = @truncate(@as(u80, @bitCast(val)) >> 32), + .hi = @intCast(@as(u80, @bitCast(val)) >> 64), + }), + }); + } + return @enumFromInt(gop.index); +} + +fn ppc_fp128ConstAssumeCapacity(self: *Builder, val: [2]f64) Constant { + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: [2]f64) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(Constant.Tag.ppc_fp128)), + std.mem.asBytes(&key), + )); + } + pub fn eql(ctx: @This(), lhs_key: [2]f64, _: void, rhs_index: usize) bool { + if (ctx.builder.constant_items.items(.tag)[rhs_index] != .ppc_fp128) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.constantExtraData(Constant.Fp128, rhs_data); + return @as(u64, @bitCast(lhs_key[0])) == @as(u64, rhs_extra.lo_hi) << 32 | rhs_extra.lo_lo and + @as(u64, @bitCast(lhs_key[1])) == @as(u64, rhs_extra.hi_hi) << 32 | rhs_extra.hi_lo; + } + }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(val, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = .ppc_fp128, + .data = self.addConstantExtraAssumeCapacity(Constant.Fp128{ + .lo_lo = @truncate(@as(u64, @bitCast(val[0]))), + .lo_hi = @intCast(@as(u64, @bitCast(val[0])) >> 32), + .hi_lo = @truncate(@as(u64, @bitCast(val[1]))), + .hi_hi = @intCast(@as(u64, @bitCast(val[1])) >> 32), + }), + }); + } + return @enumFromInt(gop.index); +} + +fn nullConstAssumeCapacity(self: *Builder, ty: Type) Constant { + assert(self.type_items.items[@intFromEnum(ty)].tag == .pointer); + const result = self.getOrPutConstantNoExtraAssumeCapacity( + .{ .tag = .null, .data = @intFromEnum(ty) }, + ); + return result.constant; +} + +fn noneConstAssumeCapacity(self: *Builder, ty: Type) Constant { + assert(ty == .token); + const result = self.getOrPutConstantNoExtraAssumeCapacity( + .{ .tag = .none, .data = @intFromEnum(ty) }, + ); + return result.constant; +} + +fn structConstAssumeCapacity(self: *Builder, ty: Type, vals: []const Constant) Constant { + const type_item = self.type_items.items[@intFromEnum(ty)]; + var extra = self.typeExtraDataTrail(Type.Structure, switch (type_item.tag) { + .structure, .packed_structure => type_item.data, + .named_structure => data: { + const body_ty = self.typeExtraData(Type.NamedStructure, type_item.data).body; + const body_item = self.type_items.items[@intFromEnum(body_ty)]; + switch (body_item.tag) { + .structure, .packed_structure => break :data body_item.data, + else => unreachable, + } + }, + else => unreachable, + }); + const fields = extra.trail.next(extra.data.fields_len, Type, self); + for (fields, vals) |field, val| assert(field == val.typeOf(self)); + + for (vals) |val| { + if (!val.isZeroInit(self)) break; + } else return self.zeroInitConstAssumeCapacity(ty); + + const tag: Constant.Tag = switch (ty.unnamedTag(self)) { + .structure => .structure, + .packed_structure => .packed_structure, + else => unreachable, + }; + const result = self.getOrPutConstantAggregateAssumeCapacity(tag, ty, vals); + return result.constant; +} + +fn arrayConstAssumeCapacity(self: *Builder, ty: Type, vals: []const Constant) Constant { + const type_item = self.type_items.items[@intFromEnum(ty)]; + const type_extra: struct { len: u64, child: Type } = switch (type_item.tag) { + inline .small_array, .array => |kind| extra: { + const extra = self.typeExtraData(switch (kind) { + .small_array => Type.Vector, + .array => Type.Array, + else => unreachable, + }, type_item.data); + break :extra .{ .len = extra.length(), .child = extra.child }; + }, + else => unreachable, + }; + assert(type_extra.len == vals.len); + for (vals) |val| assert(type_extra.child == val.typeOf(self)); + + for (vals) |val| { + if (!val.isZeroInit(self)) break; + } else return self.zeroInitConstAssumeCapacity(ty); + + const result = self.getOrPutConstantAggregateAssumeCapacity(.array, ty, vals); + return result.constant; +} + +fn stringConstAssumeCapacity(self: *Builder, val: String) Constant { + const slice = val.slice(self).?; + const ty = self.arrayTypeAssumeCapacity(slice.len, .i8); + if (std.mem.allEqual(u8, slice, 0)) return self.zeroInitConstAssumeCapacity(ty); + const result = self.getOrPutConstantNoExtraAssumeCapacity( + .{ .tag = .string, .data = @intFromEnum(val) }, + ); + return result.constant; +} + +fn vectorConstAssumeCapacity(self: *Builder, ty: Type, vals: []const Constant) Constant { + assert(ty.isVector(self)); + assert(ty.vectorLen(self) == vals.len); + for (vals) |val| assert(ty.childType(self) == val.typeOf(self)); + + for (vals[1..]) |val| { + if (vals[0] != val) break; + } else return self.splatConstAssumeCapacity(ty, vals[0]); + for (vals) |val| { + if (!val.isZeroInit(self)) break; + } else return self.zeroInitConstAssumeCapacity(ty); + + const result = self.getOrPutConstantAggregateAssumeCapacity(.vector, ty, vals); + return result.constant; +} + +fn splatConstAssumeCapacity(self: *Builder, ty: Type, val: Constant) Constant { + assert(ty.scalarType(self) == val.typeOf(self)); + + if (!ty.isVector(self)) return val; + if (val.isZeroInit(self)) return self.zeroInitConstAssumeCapacity(ty); + + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Constant.Splat) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(Constant.Tag.splat)), + std.mem.asBytes(&key), + )); + } + pub fn eql(ctx: @This(), lhs_key: Constant.Splat, _: void, rhs_index: usize) bool { + if (ctx.builder.constant_items.items(.tag)[rhs_index] != .splat) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.constantExtraData(Constant.Splat, rhs_data); + return std.meta.eql(lhs_key, rhs_extra); + } + }; + const data = Constant.Splat{ .type = ty, .value = val }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = .splat, + .data = self.addConstantExtraAssumeCapacity(data), + }); + } + return @enumFromInt(gop.index); +} + +fn zeroInitConstAssumeCapacity(self: *Builder, ty: Type) Constant { + switch (ty) { + inline .half, + .bfloat, + .float, + .double, + .fp128, + .x86_fp80, + => |tag| return @field(Builder, @tagName(tag) ++ "ConstAssumeCapacity")(self, 0.0), + .ppc_fp128 => return self.ppc_fp128ConstAssumeCapacity(.{ 0.0, 0.0 }), + .token => return .none, + .i1 => return .false, + else => switch (self.type_items.items[@intFromEnum(ty)].tag) { + .simple, + .function, + .vararg_function, + => unreachable, + .integer => { + var limbs: [std.math.big.int.calcLimbLen(0)]std.math.big.Limb = undefined; + const bigint = std.math.big.int.Mutable.init(&limbs, 0); + return self.bigIntConstAssumeCapacity(ty, bigint.toConst()) catch unreachable; + }, + .pointer => return self.nullConstAssumeCapacity(ty), + .target, + .vector, + .scalable_vector, + .small_array, + .array, + .structure, + .packed_structure, + .named_structure, + => {}, + }, + } + const result = self.getOrPutConstantNoExtraAssumeCapacity( + .{ .tag = .zeroinitializer, .data = @intFromEnum(ty) }, + ); + return result.constant; +} + +fn undefConstAssumeCapacity(self: *Builder, ty: Type) Constant { + switch (self.type_items.items[@intFromEnum(ty)].tag) { + .simple => switch (ty) { + .void, .label => unreachable, + else => {}, + }, + .function, .vararg_function => unreachable, + else => {}, + } + const result = self.getOrPutConstantNoExtraAssumeCapacity( + .{ .tag = .undef, .data = @intFromEnum(ty) }, + ); + return result.constant; +} + +fn poisonConstAssumeCapacity(self: *Builder, ty: Type) Constant { + switch (self.type_items.items[@intFromEnum(ty)].tag) { + .simple => switch (ty) { + .void, .label => unreachable, + else => {}, + }, + .function, .vararg_function => unreachable, + else => {}, + } + const result = self.getOrPutConstantNoExtraAssumeCapacity( + .{ .tag = .poison, .data = @intFromEnum(ty) }, + ); + return result.constant; +} + +fn blockAddrConstAssumeCapacity( + self: *Builder, + function: Function.Index, + block: Function.Block.Index, +) Constant { + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Constant.BlockAddress) u32 { + return @truncate(std.hash.Wyhash.hash( + comptime std.hash.uint32(@intFromEnum(Constant.Tag.blockaddress)), + std.mem.asBytes(&key), + )); + } + pub fn eql(ctx: @This(), lhs_key: Constant.BlockAddress, _: void, rhs_index: usize) bool { + if (ctx.builder.constant_items.items(.tag)[rhs_index] != .blockaddress) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.constantExtraData(Constant.BlockAddress, rhs_data); + return std.meta.eql(lhs_key, rhs_extra); + } + }; + const data = Constant.BlockAddress{ .function = function, .block = block }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = .blockaddress, + .data = self.addConstantExtraAssumeCapacity(data), + }); + } + return @enumFromInt(gop.index); +} + +fn dsoLocalEquivalentConstAssumeCapacity(self: *Builder, function: Function.Index) Constant { + const result = self.getOrPutConstantNoExtraAssumeCapacity( + .{ .tag = .dso_local_equivalent, .data = @intFromEnum(function) }, + ); + return result.constant; +} + +fn noCfiConstAssumeCapacity(self: *Builder, function: Function.Index) Constant { + const result = self.getOrPutConstantNoExtraAssumeCapacity( + .{ .tag = .no_cfi, .data = @intFromEnum(function) }, + ); + return result.constant; +} + +fn convTag( + self: *Builder, + signedness: Constant.Cast.Signedness, + val_ty: Type, + ty: Type, +) Function.Instruction.Tag { + assert(val_ty != ty); + return switch (val_ty.scalarTag(self)) { + .simple => switch (ty.scalarTag(self)) { + .simple => switch (std.math.order(val_ty.scalarBits(self), ty.scalarBits(self))) { + .lt => .fpext, + .eq => unreachable, + .gt => .fptrunc, + }, + .integer => switch (signedness) { + .unsigned => .fptoui, + .signed => .fptosi, + .unneeded => unreachable, + }, + else => unreachable, + }, + .integer => switch (ty.scalarTag(self)) { + .simple => switch (signedness) { + .unsigned => .uitofp, + .signed => .sitofp, + .unneeded => unreachable, + }, + .integer => switch (std.math.order(val_ty.scalarBits(self), ty.scalarBits(self))) { + .lt => switch (signedness) { + .unsigned => .zext, + .signed => .sext, + .unneeded => unreachable, + }, + .eq => unreachable, + .gt => .trunc, + }, + .pointer => .inttoptr, + else => unreachable, + }, + .pointer => switch (ty.scalarTag(self)) { + .integer => .ptrtoint, + .pointer => .addrspacecast, + else => unreachable, + }, + else => unreachable, + }; +} + +fn convConstTag( + self: *Builder, + val_ty: Type, + ty: Type, +) Constant.Tag { + assert(val_ty != ty); + return switch (val_ty.scalarTag(self)) { + .integer => switch (ty.scalarTag(self)) { + .integer => switch (std.math.order(val_ty.scalarBits(self), ty.scalarBits(self))) { + .gt => .trunc, + else => unreachable, + }, + .pointer => .inttoptr, + else => unreachable, + }, + .pointer => switch (ty.scalarTag(self)) { + .integer => .ptrtoint, + .pointer => .addrspacecast, + else => unreachable, + }, + else => unreachable, + }; +} + +fn convConstAssumeCapacity( + self: *Builder, + val: Constant, + ty: Type, +) Constant { + const val_ty = val.typeOf(self); + if (val_ty == ty) return val; + return self.castConstAssumeCapacity(self.convConstTag(val_ty, ty), val, ty); +} + +fn castConstAssumeCapacity(self: *Builder, tag: Constant.Tag, val: Constant, ty: Type) Constant { + const Key = struct { tag: Constant.Tag, cast: Constant.Cast }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + return @truncate(std.hash.Wyhash.hash( + std.hash.uint32(@intFromEnum(key.tag)), + std.mem.asBytes(&key.cast), + )); + } + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.constantExtraData(Constant.Cast, rhs_data); + return std.meta.eql(lhs_key.cast, rhs_extra); + } + }; + const data = Key{ .tag = tag, .cast = .{ .val = val, .type = ty } }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addConstantExtraAssumeCapacity(data.cast), + }); + } + return @enumFromInt(gop.index); +} + +fn gepConstAssumeCapacity( + self: *Builder, + comptime kind: Constant.GetElementPtr.Kind, + ty: Type, + base: Constant, + inrange: ?u16, + indices: []const Constant, +) Constant { + const tag: Constant.Tag = switch (kind) { + .normal => .getelementptr, + .inbounds => .@"getelementptr inbounds", + }; + const base_ty = base.typeOf(self); + const base_is_vector = base_ty.isVector(self); + + const VectorInfo = struct { + kind: Type.Vector.Kind, + len: u32, + + fn init(vector_ty: Type, builder: *const Builder) @This() { + return .{ .kind = vector_ty.vectorKind(builder), .len = vector_ty.vectorLen(builder) }; + } + }; + var vector_info: ?VectorInfo = if (base_is_vector) VectorInfo.init(base_ty, self) else null; + for (indices) |index| { + const index_ty = index.typeOf(self); + switch (index_ty.tag(self)) { + .integer => {}, + .vector, .scalable_vector => { + const index_info = VectorInfo.init(index_ty, self); + if (vector_info) |info| + assert(std.meta.eql(info, index_info)) + else + vector_info = index_info; + }, + else => unreachable, + } + } + if (!base_is_vector) if (vector_info) |info| switch (info.kind) { + inline else => |vector_kind| _ = self.vectorTypeAssumeCapacity(vector_kind, info.len, base_ty), + }; + + const Key = struct { + type: Type, + base: Constant, + inrange: Constant.GetElementPtr.InRangeIndex, + indices: []const Constant, + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = std.hash.Wyhash.init(comptime std.hash.uint32(@intFromEnum(tag))); + hasher.update(std.mem.asBytes(&key.type)); + hasher.update(std.mem.asBytes(&key.base)); + hasher.update(std.mem.asBytes(&key.inrange)); + hasher.update(std.mem.sliceAsBytes(key.indices)); + return @truncate(hasher.final()); + } + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (ctx.builder.constant_items.items(.tag)[rhs_index] != tag) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + var rhs_extra = ctx.builder.constantExtraDataTrail(Constant.GetElementPtr, rhs_data); + const rhs_indices = + rhs_extra.trail.next(rhs_extra.data.info.indices_len, Constant, ctx.builder); + return lhs_key.type == rhs_extra.data.type and lhs_key.base == rhs_extra.data.base and + lhs_key.inrange == rhs_extra.data.info.inrange and + std.mem.eql(Constant, lhs_key.indices, rhs_indices); + } + }; + const data = Key{ + .type = ty, + .base = base, + .inrange = if (inrange) |index| @enumFromInt(index) else .none, + .indices = indices, + }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addConstantExtraAssumeCapacity(Constant.GetElementPtr{ + .type = ty, + .base = base, + .info = .{ .indices_len = @intCast(indices.len), .inrange = data.inrange }, + }), + }); + self.constant_extra.appendSliceAssumeCapacity(@ptrCast(indices)); + } + return @enumFromInt(gop.index); +} + +fn binConstAssumeCapacity( + self: *Builder, + tag: Constant.Tag, + lhs: Constant, + rhs: Constant, +) Constant { + switch (tag) { + .add, + .@"add nsw", + .@"add nuw", + .sub, + .@"sub nsw", + .@"sub nuw", + .shl, + .xor, + => {}, + else => unreachable, + } + const Key = struct { tag: Constant.Tag, extra: Constant.Binary }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + return @truncate(std.hash.Wyhash.hash( + std.hash.uint32(@intFromEnum(key.tag)), + std.mem.asBytes(&key.extra), + )); + } + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.constantExtraData(Constant.Binary, rhs_data); + return std.meta.eql(lhs_key.extra, rhs_extra); + } + }; + const data = Key{ .tag = tag, .extra = .{ .lhs = lhs, .rhs = rhs } }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addConstantExtraAssumeCapacity(data.extra), + }); + } + return @enumFromInt(gop.index); +} + +fn asmConstAssumeCapacity( + self: *Builder, + ty: Type, + info: Constant.Assembly.Info, + assembly: String, + constraints: String, +) Constant { + assert(ty.functionKind(self) == .normal); + + const Key = struct { tag: Constant.Tag, extra: Constant.Assembly }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + return @truncate(std.hash.Wyhash.hash( + std.hash.uint32(@intFromEnum(key.tag)), + std.mem.asBytes(&key.extra), + )); + } + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.constantExtraData(Constant.Assembly, rhs_data); + return std.meta.eql(lhs_key.extra, rhs_extra); + } + }; + + const data = Key{ + .tag = @enumFromInt(@intFromEnum(Constant.Tag.@"asm") + @as(u4, @bitCast(info))), + .extra = .{ .type = ty, .assembly = assembly, .constraints = constraints }, + }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = data.tag, + .data = self.addConstantExtraAssumeCapacity(data.extra), + }); + } + return @enumFromInt(gop.index); +} + +fn ensureUnusedConstantCapacity( + self: *Builder, + count: usize, + comptime Extra: type, + trail_len: usize, +) Allocator.Error!void { + try self.constant_map.ensureUnusedCapacity(self.gpa, count); + try self.constant_items.ensureUnusedCapacity(self.gpa, count); + try self.constant_extra.ensureUnusedCapacity( + self.gpa, + count * (@typeInfo(Extra).@"struct".fields.len + trail_len), + ); +} + +fn getOrPutConstantNoExtraAssumeCapacity( + self: *Builder, + item: Constant.Item, +) struct { new: bool, constant: Constant } { + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Constant.Item) u32 { + return @truncate(std.hash.Wyhash.hash( + std.hash.uint32(@intFromEnum(key.tag)), + std.mem.asBytes(&key.data), + )); + } + pub fn eql(ctx: @This(), lhs_key: Constant.Item, _: void, rhs_index: usize) bool { + return std.meta.eql(lhs_key, ctx.builder.constant_items.get(rhs_index)); + } + }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(item, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(item); + } + return .{ .new = !gop.found_existing, .constant = @enumFromInt(gop.index) }; +} + +fn getOrPutConstantAggregateAssumeCapacity( + self: *Builder, + tag: Constant.Tag, + ty: Type, + vals: []const Constant, +) struct { new: bool, constant: Constant } { + switch (tag) { + .structure, .packed_structure, .array, .vector => {}, + else => unreachable, + } + const Key = struct { tag: Constant.Tag, type: Type, vals: []const Constant }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(key.tag))); + hasher.update(std.mem.asBytes(&key.type)); + hasher.update(std.mem.sliceAsBytes(key.vals)); + return @truncate(hasher.final()); + } + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + var rhs_extra = ctx.builder.constantExtraDataTrail(Constant.Aggregate, rhs_data); + if (lhs_key.type != rhs_extra.data.type) return false; + const rhs_vals = rhs_extra.trail.next(@intCast(lhs_key.vals.len), Constant, ctx.builder); + return std.mem.eql(Constant, lhs_key.vals, rhs_vals); + } + }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted( + Key{ .tag = tag, .type = ty, .vals = vals }, + Adapter{ .builder = self }, + ); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addConstantExtraAssumeCapacity(Constant.Aggregate{ .type = ty }), + }); + self.constant_extra.appendSliceAssumeCapacity(@ptrCast(vals)); + } + return .{ .new = !gop.found_existing, .constant = @enumFromInt(gop.index) }; +} + +fn addConstantExtraAssumeCapacity(self: *Builder, extra: anytype) Constant.Item.ExtraIndex { + const result: Constant.Item.ExtraIndex = @intCast(self.constant_extra.items.len); + inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| { + const value = @field(extra, field.name); + self.constant_extra.appendAssumeCapacity(switch (field.type) { + u32 => value, + String, Type, Constant, Function.Index, Function.Block.Index => @intFromEnum(value), + Constant.GetElementPtr.Info => @bitCast(value), + else => @compileError("bad field type: " ++ @typeName(field.type)), + }); + } + return result; +} + +const ConstantExtraDataTrail = struct { + index: Constant.Item.ExtraIndex, + + fn nextMut(self: *ConstantExtraDataTrail, len: u32, comptime Item: type, builder: *Builder) []Item { + const items: []Item = @ptrCast(builder.constant_extra.items[self.index..][0..len]); + self.index += @intCast(len); + return items; + } + + fn next( + self: *ConstantExtraDataTrail, + len: u32, + comptime Item: type, + builder: *const Builder, + ) []const Item { + const items: []const Item = @ptrCast(builder.constant_extra.items[self.index..][0..len]); + self.index += @intCast(len); + return items; + } +}; + +fn constantExtraDataTrail( + self: *const Builder, + comptime T: type, + index: Constant.Item.ExtraIndex, +) struct { data: T, trail: ConstantExtraDataTrail } { + var result: T = undefined; + const fields = @typeInfo(T).@"struct".fields; + inline for (fields, self.constant_extra.items[index..][0..fields.len]) |field, value| + @field(result, field.name) = switch (field.type) { + u32 => value, + String, Type, Constant, Function.Index, Function.Block.Index => @enumFromInt(value), + Constant.GetElementPtr.Info => @bitCast(value), + else => @compileError("bad field type: " ++ @typeName(field.type)), + }; + return .{ + .data = result, + .trail = .{ .index = index + @as(Constant.Item.ExtraIndex, @intCast(fields.len)) }, + }; +} + +fn constantExtraData(self: *const Builder, comptime T: type, index: Constant.Item.ExtraIndex) T { + return self.constantExtraDataTrail(T, index).data; +} + +fn ensureUnusedMetadataCapacity( + self: *Builder, + count: usize, + comptime Extra: type, + trail_len: usize, +) Allocator.Error!void { + try self.metadata_map.ensureUnusedCapacity(self.gpa, count); + try self.metadata_items.ensureUnusedCapacity(self.gpa, count); + try self.metadata_extra.ensureUnusedCapacity( + self.gpa, + count * (@typeInfo(Extra).@"struct".fields.len + trail_len), + ); +} + +fn addMetadataExtraAssumeCapacity(self: *Builder, extra: anytype) Metadata.Item.ExtraIndex { + const result: Metadata.Item.ExtraIndex = @intCast(self.metadata_extra.items.len); + inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| { + const value = @field(extra, field.name); + self.metadata_extra.appendAssumeCapacity(switch (field.type) { + u32 => value, + MetadataString, Metadata, Variable.Index, Value => @intFromEnum(value), + Metadata.DIFlags => @bitCast(value), + else => @compileError("bad field type: " ++ @typeName(field.type)), + }); + } + return result; +} + +const MetadataExtraDataTrail = struct { + index: Metadata.Item.ExtraIndex, + + fn nextMut(self: *MetadataExtraDataTrail, len: u32, comptime Item: type, builder: *Builder) []Item { + const items: []Item = @ptrCast(builder.metadata_extra.items[self.index..][0..len]); + self.index += @intCast(len); + return items; + } + + fn next( + self: *MetadataExtraDataTrail, + len: u32, + comptime Item: type, + builder: *const Builder, + ) []const Item { + const items: []const Item = @ptrCast(builder.metadata_extra.items[self.index..][0..len]); + self.index += @intCast(len); + return items; + } +}; + +fn metadataExtraDataTrail( + self: *const Builder, + comptime T: type, + index: Metadata.Item.ExtraIndex, +) struct { data: T, trail: MetadataExtraDataTrail } { + var result: T = undefined; + const fields = @typeInfo(T).@"struct".fields; + inline for (fields, self.metadata_extra.items[index..][0..fields.len]) |field, value| + @field(result, field.name) = switch (field.type) { + u32 => value, + MetadataString, Metadata, Variable.Index, Value => @enumFromInt(value), + Metadata.DIFlags => @bitCast(value), + else => @compileError("bad field type: " ++ @typeName(field.type)), + }; + return .{ + .data = result, + .trail = .{ .index = index + @as(Metadata.Item.ExtraIndex, @intCast(fields.len)) }, + }; +} + +fn metadataExtraData(self: *const Builder, comptime T: type, index: Metadata.Item.ExtraIndex) T { + return self.metadataExtraDataTrail(T, index).data; +} + +pub fn metadataString(self: *Builder, bytes: []const u8) Allocator.Error!MetadataString { + try self.metadata_string_bytes.ensureUnusedCapacity(self.gpa, bytes.len); + try self.metadata_string_indices.ensureUnusedCapacity(self.gpa, 1); + try self.metadata_string_map.ensureUnusedCapacity(self.gpa, 1); + + const gop = self.metadata_string_map.getOrPutAssumeCapacityAdapted( + bytes, + MetadataString.Adapter{ .builder = self }, + ); + if (!gop.found_existing) { + self.metadata_string_bytes.appendSliceAssumeCapacity(bytes); + self.metadata_string_indices.appendAssumeCapacity(@intCast(self.metadata_string_bytes.items.len)); + } + return @enumFromInt(gop.index); +} + +pub fn metadataStringFromStrtabString(self: *Builder, str: StrtabString) Allocator.Error!MetadataString { + if (str == .none or str == .empty) return MetadataString.none; + return try self.metadataString(str.slice(self).?); +} + +pub fn metadataStringFmt(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) Allocator.Error!MetadataString { + try self.metadata_string_map.ensureUnusedCapacity(self.gpa, 1); + try self.metadata_string_bytes.ensureUnusedCapacity(self.gpa, @intCast(std.fmt.count(fmt_str, fmt_args))); + try self.metadata_string_indices.ensureUnusedCapacity(self.gpa, 1); + return self.metadataStringFmtAssumeCapacity(fmt_str, fmt_args); +} + +pub fn metadataStringFmtAssumeCapacity(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) MetadataString { + self.metadata_string_bytes.writer(undefined).print(fmt_str, fmt_args) catch unreachable; + return self.trailingMetadataStringAssumeCapacity(); +} + +pub fn trailingMetadataString(self: *Builder) Allocator.Error!MetadataString { + try self.metadata_string_indices.ensureUnusedCapacity(self.gpa, 1); + try self.metadata_string_map.ensureUnusedCapacity(self.gpa, 1); + return self.trailingMetadataStringAssumeCapacity(); +} + +pub fn trailingMetadataStringAssumeCapacity(self: *Builder) MetadataString { + const start = self.metadata_string_indices.getLast(); + const bytes: []const u8 = self.metadata_string_bytes.items[start..]; + const gop = self.metadata_string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self }); + if (gop.found_existing) { + self.metadata_string_bytes.shrinkRetainingCapacity(start); + } else { + self.metadata_string_indices.appendAssumeCapacity(@intCast(self.metadata_string_bytes.items.len)); + } + return @enumFromInt(gop.index); +} + +pub fn metadataNamed(self: *Builder, name: MetadataString, operands: []const Metadata) Allocator.Error!void { + try self.metadata_extra.ensureUnusedCapacity(self.gpa, operands.len); + try self.metadata_named.ensureUnusedCapacity(self.gpa, 1); + self.metadataNamedAssumeCapacity(name, operands); +} + +fn metadataNone(self: *Builder) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, NoExtra, 0); + return self.metadataNoneAssumeCapacity(); +} + +pub fn debugFile( + self: *Builder, + filename: MetadataString, + directory: MetadataString, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.File, 0); + return self.debugFileAssumeCapacity(filename, directory); +} + +pub fn debugCompileUnit( + self: *Builder, + file: Metadata, + producer: MetadataString, + enums: Metadata, + globals: Metadata, + options: Metadata.CompileUnit.Options, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompileUnit, 0); + return self.debugCompileUnitAssumeCapacity(file, producer, enums, globals, options); +} + +pub fn debugSubprogram( + self: *Builder, + file: Metadata, + name: MetadataString, + linkage_name: MetadataString, + line: u32, + scope_line: u32, + ty: Metadata, + options: Metadata.Subprogram.Options, + compile_unit: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Subprogram, 0); + return self.debugSubprogramAssumeCapacity( + file, + name, + linkage_name, + line, + scope_line, + ty, + options, + compile_unit, + ); +} + +pub fn debugLexicalBlock(self: *Builder, scope: Metadata, file: Metadata, line: u32, column: u32) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.LexicalBlock, 0); + return self.debugLexicalBlockAssumeCapacity(scope, file, line, column); +} + +pub fn debugLocation(self: *Builder, line: u32, column: u32, scope: Metadata, inlined_at: Metadata) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Location, 0); + return self.debugLocationAssumeCapacity(line, column, scope, inlined_at); +} + +pub fn debugBoolType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); + return self.debugBoolTypeAssumeCapacity(name, size_in_bits); +} + +pub fn debugUnsignedType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); + return self.debugUnsignedTypeAssumeCapacity(name, size_in_bits); +} + +pub fn debugSignedType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); + return self.debugSignedTypeAssumeCapacity(name, size_in_bits); +} + +pub fn debugFloatType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); + return self.debugFloatTypeAssumeCapacity(name, size_in_bits); +} + +pub fn debugForwardReference(self: *Builder) Allocator.Error!Metadata { + try self.metadata_forward_references.ensureUnusedCapacity(self.gpa, 1); + return self.debugForwardReferenceAssumeCapacity(); +} + +pub fn debugStructType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); + return self.debugStructTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +pub fn debugUnionType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); + return self.debugUnionTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +pub fn debugEnumerationType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); + return self.debugEnumerationTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +pub fn debugArrayType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); + return self.debugArrayTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +pub fn debugVectorType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); + return self.debugVectorTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +pub fn debugPointerType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.DerivedType, 0); + return self.debugPointerTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + offset_in_bits, + ); +} + +pub fn debugMemberType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.DerivedType, 0); + return self.debugMemberTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + offset_in_bits, + ); +} + +pub fn debugSubroutineType( + self: *Builder, + types_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.SubroutineType, 0); + return self.debugSubroutineTypeAssumeCapacity(types_tuple); +} + +pub fn debugEnumerator( + self: *Builder, + name: MetadataString, + unsigned: bool, + bit_width: u32, + value: std.math.big.int.Const, +) Allocator.Error!Metadata { + assert(!(unsigned and !value.positive)); + try self.ensureUnusedMetadataCapacity(1, Metadata.Enumerator, 0); + try self.metadata_limbs.ensureUnusedCapacity(self.gpa, value.limbs.len); + return self.debugEnumeratorAssumeCapacity(name, unsigned, bit_width, value); +} + +pub fn debugSubrange( + self: *Builder, + lower_bound: Metadata, + count: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Subrange, 0); + return self.debugSubrangeAssumeCapacity(lower_bound, count); +} + +pub fn debugExpression( + self: *Builder, + elements: []const u32, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Expression, elements.len); + return self.debugExpressionAssumeCapacity(elements); +} + +pub fn metadataTuple( + self: *Builder, + elements: []const Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Tuple, elements.len); + return self.metadataTupleAssumeCapacity(elements); +} + +pub fn strTuple( + self: *Builder, + str: MetadataString, + elements: []const Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.StrTuple, elements.len); + return self.strTupleAssumeCapacity(str, elements); +} + +pub fn metadataModuleFlag( + self: *Builder, + behavior: Metadata, + name: MetadataString, + constant: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.ModuleFlag, 0); + return self.metadataModuleFlagAssumeCapacity(behavior, name, constant); +} + +pub fn debugLocalVar( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.LocalVar, 0); + return self.debugLocalVarAssumeCapacity(name, file, scope, line, ty); +} + +pub fn debugParameter( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + arg_no: u32, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Parameter, 0); + return self.debugParameterAssumeCapacity(name, file, scope, line, ty, arg_no); +} + +pub fn debugGlobalVar( + self: *Builder, + name: MetadataString, + linkage_name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + variable: Variable.Index, + options: Metadata.GlobalVar.Options, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.GlobalVar, 0); + return self.debugGlobalVarAssumeCapacity( + name, + linkage_name, + file, + scope, + line, + ty, + variable, + options, + ); +} + +pub fn debugGlobalVarExpression( + self: *Builder, + variable: Metadata, + expression: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.GlobalVarExpression, 0); + return self.debugGlobalVarExpressionAssumeCapacity(variable, expression); +} + +pub fn metadataConstant(self: *Builder, value: Constant) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, NoExtra, 0); + return self.metadataConstantAssumeCapacity(value); +} + +pub fn debugForwardReferenceSetType(self: *Builder, fwd_ref: Metadata, ty: Metadata) void { + assert( + @intFromEnum(fwd_ref) >= Metadata.first_forward_reference and + @intFromEnum(fwd_ref) <= Metadata.first_local_metadata, + ); + const index = @intFromEnum(fwd_ref) - Metadata.first_forward_reference; + self.metadata_forward_references.items[index] = ty; +} + +fn metadataSimpleAssumeCapacity(self: *Builder, tag: Metadata.Tag, value: anytype) Metadata { + const Key = struct { + tag: Metadata.Tag, + value: @TypeOf(value), + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(key.tag))); + inline for (std.meta.fields(@TypeOf(value))) |field| { + hasher.update(std.mem.asBytes(&@field(key.value, field.name))); + } + return @truncate(hasher.final()); + } + + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (lhs_key.tag != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.metadataExtraData(@TypeOf(value), rhs_data); + return std.meta.eql(lhs_key.value, rhs_extra); + } + }; + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + Key{ .tag = tag, .value = value }, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addMetadataExtraAssumeCapacity(value), + }); + } + return @enumFromInt(gop.index); +} + +fn metadataDistinctAssumeCapacity(self: *Builder, tag: Metadata.Tag, value: anytype) Metadata { + const Key = struct { tag: Metadata.Tag, index: Metadata }; + const Adapter = struct { + pub fn hash(_: @This(), key: Key) u32 { + return @truncate(std.hash.Wyhash.hash( + std.hash.uint32(@intFromEnum(key.tag)), + std.mem.asBytes(&key.index), + )); + } + + pub fn eql(_: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + return @intFromEnum(lhs_key.index) == rhs_index; + } + }; + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + Key{ .tag = tag, .index = @enumFromInt(self.metadata_map.count()) }, + Adapter{}, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addMetadataExtraAssumeCapacity(value), + }); + } + return @enumFromInt(gop.index); +} + +fn metadataNamedAssumeCapacity(self: *Builder, name: MetadataString, operands: []const Metadata) void { + assert(name != .none); + const extra_index: u32 = @intCast(self.metadata_extra.items.len); + self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(operands)); + + const gop = self.metadata_named.getOrPutAssumeCapacity(name); + gop.value_ptr.* = .{ + .index = extra_index, + .len = @intCast(operands.len), + }; +} + +pub fn metadataNoneAssumeCapacity(self: *Builder) Metadata { + return self.metadataSimpleAssumeCapacity(.none, .{}); +} + +fn debugFileAssumeCapacity( + self: *Builder, + filename: MetadataString, + directory: MetadataString, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.file, Metadata.File{ + .filename = filename, + .directory = directory, + }); +} + +pub fn debugCompileUnitAssumeCapacity( + self: *Builder, + file: Metadata, + producer: MetadataString, + enums: Metadata, + globals: Metadata, + options: Metadata.CompileUnit.Options, +) Metadata { + assert(!self.strip); + return self.metadataDistinctAssumeCapacity( + if (options.optimized) .@"compile_unit optimized" else .compile_unit, + Metadata.CompileUnit{ + .file = file, + .producer = producer, + .enums = enums, + .globals = globals, + }, + ); +} + +fn debugSubprogramAssumeCapacity( + self: *Builder, + file: Metadata, + name: MetadataString, + linkage_name: MetadataString, + line: u32, + scope_line: u32, + ty: Metadata, + options: Metadata.Subprogram.Options, + compile_unit: Metadata, +) Metadata { + assert(!self.strip); + const tag: Metadata.Tag = @enumFromInt(@intFromEnum(Metadata.Tag.subprogram) + + @as(u3, @truncate(@as(u32, @bitCast(options.sp_flags)) >> 2))); + return self.metadataDistinctAssumeCapacity(tag, Metadata.Subprogram{ + .file = file, + .name = name, + .linkage_name = linkage_name, + .line = line, + .scope_line = scope_line, + .ty = ty, + .di_flags = options.di_flags, + .compile_unit = compile_unit, + }); +} + +fn debugLexicalBlockAssumeCapacity(self: *Builder, scope: Metadata, file: Metadata, line: u32, column: u32) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.lexical_block, Metadata.LexicalBlock{ + .scope = scope, + .file = file, + .line = line, + .column = column, + }); +} + +fn debugLocationAssumeCapacity(self: *Builder, line: u32, column: u32, scope: Metadata, inlined_at: Metadata) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.location, Metadata.Location{ + .line = line, + .column = column, + .scope = scope, + .inlined_at = inlined_at, + }); +} + +fn debugBoolTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.basic_bool_type, Metadata.BasicType{ + .name = name, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + }); +} + +fn debugUnsignedTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.basic_unsigned_type, Metadata.BasicType{ + .name = name, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + }); +} + +fn debugSignedTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.basic_signed_type, Metadata.BasicType{ + .name = name, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + }); +} + +fn debugFloatTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.basic_float_type, Metadata.BasicType{ + .name = name, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + }); +} + +fn debugForwardReferenceAssumeCapacity(self: *Builder) Metadata { + assert(!self.strip); + const index = Metadata.first_forward_reference + self.metadata_forward_references.items.len; + self.metadata_forward_references.appendAssumeCapacity(.none); + return @enumFromInt(index); +} + +fn debugStructTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.debugCompositeTypeAssumeCapacity( + .composite_struct_type, + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +fn debugUnionTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.debugCompositeTypeAssumeCapacity( + .composite_union_type, + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +fn debugEnumerationTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.debugCompositeTypeAssumeCapacity( + .composite_enumeration_type, + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +fn debugArrayTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.debugCompositeTypeAssumeCapacity( + .composite_array_type, + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +fn debugVectorTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.debugCompositeTypeAssumeCapacity( + .composite_vector_type, + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +fn debugCompositeTypeAssumeCapacity( + self: *Builder, + tag: Metadata.Tag, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(tag, Metadata.CompositeType{ + .name = name, + .file = file, + .scope = scope, + .line = line, + .underlying_type = underlying_type, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + .align_in_bits_lo = @truncate(align_in_bits), + .align_in_bits_hi = @truncate(align_in_bits >> 32), + .fields_tuple = fields_tuple, + }); +} + +fn debugPointerTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.derived_pointer_type, Metadata.DerivedType{ + .name = name, + .file = file, + .scope = scope, + .line = line, + .underlying_type = underlying_type, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + .align_in_bits_lo = @truncate(align_in_bits), + .align_in_bits_hi = @truncate(align_in_bits >> 32), + .offset_in_bits_lo = @truncate(offset_in_bits), + .offset_in_bits_hi = @truncate(offset_in_bits >> 32), + }); +} + +fn debugMemberTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.derived_member_type, Metadata.DerivedType{ + .name = name, + .file = file, + .scope = scope, + .line = line, + .underlying_type = underlying_type, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + .align_in_bits_lo = @truncate(align_in_bits), + .align_in_bits_hi = @truncate(align_in_bits >> 32), + .offset_in_bits_lo = @truncate(offset_in_bits), + .offset_in_bits_hi = @truncate(offset_in_bits >> 32), + }); +} + +fn debugSubroutineTypeAssumeCapacity( + self: *Builder, + types_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.subroutine_type, Metadata.SubroutineType{ + .types_tuple = types_tuple, + }); +} + +fn debugEnumeratorAssumeCapacity( + self: *Builder, + name: MetadataString, + unsigned: bool, + bit_width: u32, + value: std.math.big.int.Const, +) Metadata { + assert(!self.strip); + const Key = struct { + tag: Metadata.Tag, + name: MetadataString, + bit_width: u32, + value: std.math.big.int.Const, + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(key.tag))); + hasher.update(std.mem.asBytes(&key.name)); + hasher.update(std.mem.asBytes(&key.bit_width)); + hasher.update(std.mem.sliceAsBytes(key.value.limbs)); + return @truncate(hasher.final()); + } + + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (lhs_key.tag != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.metadataExtraData(Metadata.Enumerator, rhs_data); + const limbs = ctx.builder.metadata_limbs + .items[rhs_extra.limbs_index..][0..rhs_extra.limbs_len]; + const rhs_value = std.math.big.int.Const{ + .limbs = limbs, + .positive = lhs_key.value.positive, + }; + return lhs_key.name == rhs_extra.name and + lhs_key.bit_width == rhs_extra.bit_width and + lhs_key.value.eql(rhs_value); + } + }; + + const tag: Metadata.Tag = if (unsigned) + .enumerator_unsigned + else if (value.positive) + .enumerator_signed_positive + else + .enumerator_signed_negative; + + assert(!(tag == .enumerator_unsigned and !value.positive)); + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + Key{ + .tag = tag, + .name = name, + .bit_width = bit_width, + .value = value, + }, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addMetadataExtraAssumeCapacity(Metadata.Enumerator{ + .name = name, + .bit_width = bit_width, + .limbs_index = @intCast(self.metadata_limbs.items.len), + .limbs_len = @intCast(value.limbs.len), + }), + }); + self.metadata_limbs.appendSliceAssumeCapacity(value.limbs); + } + return @enumFromInt(gop.index); +} + +fn debugSubrangeAssumeCapacity( + self: *Builder, + lower_bound: Metadata, + count: Metadata, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.subrange, Metadata.Subrange{ + .lower_bound = lower_bound, + .count = count, + }); +} + +fn debugExpressionAssumeCapacity( + self: *Builder, + elements: []const u32, +) Metadata { + assert(!self.strip); + const Key = struct { + elements: []const u32, + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = comptime std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(Metadata.Tag.expression))); + hasher.update(std.mem.sliceAsBytes(key.elements)); + return @truncate(hasher.final()); + } + + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (Metadata.Tag.expression != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; + var rhs_extra = ctx.builder.metadataExtraDataTrail(Metadata.Expression, rhs_data); + return std.mem.eql( + u32, + lhs_key.elements, + rhs_extra.trail.next(rhs_extra.data.elements_len, u32, ctx.builder), + ); + } + }; + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + Key{ .elements = elements }, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = .expression, + .data = self.addMetadataExtraAssumeCapacity(Metadata.Expression{ + .elements_len = @intCast(elements.len), + }), + }); + self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(elements)); + } + return @enumFromInt(gop.index); +} + +fn metadataTupleAssumeCapacity( + self: *Builder, + elements: []const Metadata, +) Metadata { + const Key = struct { + elements: []const Metadata, + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = comptime std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(Metadata.Tag.tuple))); + hasher.update(std.mem.sliceAsBytes(key.elements)); + return @truncate(hasher.final()); + } + + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (Metadata.Tag.tuple != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; + var rhs_extra = ctx.builder.metadataExtraDataTrail(Metadata.Tuple, rhs_data); + return std.mem.eql( + Metadata, + lhs_key.elements, + rhs_extra.trail.next(rhs_extra.data.elements_len, Metadata, ctx.builder), + ); + } + }; + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + Key{ .elements = elements }, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = .tuple, + .data = self.addMetadataExtraAssumeCapacity(Metadata.Tuple{ + .elements_len = @intCast(elements.len), + }), + }); + self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(elements)); + } + return @enumFromInt(gop.index); +} + +fn strTupleAssumeCapacity( + self: *Builder, + str: MetadataString, + elements: []const Metadata, +) Metadata { + const Key = struct { + str: MetadataString, + elements: []const Metadata, + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = comptime std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(Metadata.Tag.tuple))); + hasher.update(std.mem.sliceAsBytes(key.elements)); + return @truncate(hasher.final()); + } + + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (.str_tuple != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; + var rhs_extra = ctx.builder.metadataExtraDataTrail(Metadata.StrTuple, rhs_data); + return rhs_extra.data.str == lhs_key.str and std.mem.eql( + Metadata, + lhs_key.elements, + rhs_extra.trail.next(rhs_extra.data.elements_len, Metadata, ctx.builder), + ); + } + }; + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + Key{ .str = str, .elements = elements }, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = .str_tuple, + .data = self.addMetadataExtraAssumeCapacity(Metadata.StrTuple{ + .str = str, + .elements_len = @intCast(elements.len), + }), + }); + self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(elements)); + } + return @enumFromInt(gop.index); +} + +fn metadataModuleFlagAssumeCapacity( + self: *Builder, + behavior: Metadata, + name: MetadataString, + constant: Metadata, +) Metadata { + return self.metadataSimpleAssumeCapacity(.module_flag, Metadata.ModuleFlag{ + .behavior = behavior, + .name = name, + .constant = constant, + }); +} + +fn debugLocalVarAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.local_var, Metadata.LocalVar{ + .name = name, + .file = file, + .scope = scope, + .line = line, + .ty = ty, + }); +} + +fn debugParameterAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + arg_no: u32, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.parameter, Metadata.Parameter{ + .name = name, + .file = file, + .scope = scope, + .line = line, + .ty = ty, + .arg_no = arg_no, + }); +} + +fn debugGlobalVarAssumeCapacity( + self: *Builder, + name: MetadataString, + linkage_name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + variable: Variable.Index, + options: Metadata.GlobalVar.Options, +) Metadata { + assert(!self.strip); + return self.metadataDistinctAssumeCapacity( + if (options.local) .@"global_var local" else .global_var, + Metadata.GlobalVar{ + .name = name, + .linkage_name = linkage_name, + .file = file, + .scope = scope, + .line = line, + .ty = ty, + .variable = variable, + }, + ); +} + +fn debugGlobalVarExpressionAssumeCapacity( + self: *Builder, + variable: Metadata, + expression: Metadata, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.global_var_expression, Metadata.GlobalVarExpression{ + .variable = variable, + .expression = expression, + }); +} + +fn metadataConstantAssumeCapacity(self: *Builder, constant: Constant) Metadata { + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Constant) u32 { + var hasher = comptime std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(Metadata.Tag.constant))); + hasher.update(std.mem.asBytes(&key)); + return @truncate(hasher.final()); + } + + pub fn eql(ctx: @This(), lhs_key: Constant, _: void, rhs_index: usize) bool { + if (Metadata.Tag.constant != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; + const rhs_data: Constant = @enumFromInt(ctx.builder.metadata_items.items(.data)[rhs_index]); + return rhs_data == lhs_key; + } + }; + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + constant, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = .constant, + .data = @intFromEnum(constant), + }); + } + return @enumFromInt(gop.index); +} + +pub const Producer = struct { + name: []const u8, + version: std.SemanticVersion, +}; + +pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitcode_writer.Error![]const u32 { + const BitcodeWriter = bitcode_writer.BitcodeWriter(&.{ Type, FunctionAttributes }); + var bitcode = BitcodeWriter.init(allocator, .{ + std.math.log2_int_ceil(usize, self.type_items.items.len), + std.math.log2_int_ceil(usize, 1 + self.function_attributes_set.count()), + }); + errdefer bitcode.deinit(); + + // Write LLVM IR magic + try bitcode.writeBits(ir.MAGIC, 32); + + var record: std.ArrayListUnmanaged(u64) = .empty; + defer record.deinit(self.gpa); + + // IDENTIFICATION_BLOCK + { + const Identification = ir.Identification; + var identification_block = try bitcode.enterTopBlock(Identification); + + const producer_str = try std.fmt.allocPrint(self.gpa, "{s} {d}.{d}.{d}", .{ + producer.name, + producer.version.major, + producer.version.minor, + producer.version.patch, + }); + defer self.gpa.free(producer_str); + + try identification_block.writeAbbrev(Identification.Version{ .string = producer_str }); + try identification_block.writeAbbrev(Identification.Epoch{ .epoch = 0 }); + + try identification_block.end(); + } + + // MODULE_BLOCK + { + const Module = ir.Module; + var module_block = try bitcode.enterTopBlock(Module); + + try module_block.writeAbbrev(Module.Version{}); + + if (self.target_triple.slice(self)) |triple| { + try module_block.writeAbbrev(Module.String{ + .code = 2, + .string = triple, + }); + } + + if (self.data_layout.slice(self)) |data_layout| { + try module_block.writeAbbrev(Module.String{ + .code = 3, + .string = data_layout, + }); + } + + if (self.source_filename.slice(self)) |source_filename| { + try module_block.writeAbbrev(Module.String{ + .code = 16, + .string = source_filename, + }); + } + + if (self.module_asm.items.len != 0) { + try module_block.writeAbbrev(Module.String{ + .code = 4, + .string = self.module_asm.items, + }); + } + + // TYPE_BLOCK + { + var type_block = try module_block.enterSubBlock(ir.Type, true); + + try type_block.writeAbbrev(ir.Type.NumEntry{ .num = @intCast(self.type_items.items.len) }); + + for (self.type_items.items, 0..) |item, i| { + const ty: Type = @enumFromInt(i); + + switch (item.tag) { + .simple => try type_block.writeAbbrev(ir.Type.Simple{ .code = @truncate(item.data) }), + .integer => try type_block.writeAbbrev(ir.Type.Integer{ .width = item.data }), + .structure, + .packed_structure, + => |kind| { + const is_packed = switch (kind) { + .structure => false, + .packed_structure => true, + else => unreachable, + }; + var extra = self.typeExtraDataTrail(Type.Structure, item.data); + try type_block.writeAbbrev(ir.Type.StructAnon{ + .is_packed = is_packed, + .types = extra.trail.next(extra.data.fields_len, Type, self), + }); + }, + .named_structure => { + const extra = self.typeExtraData(Type.NamedStructure, item.data); + try type_block.writeAbbrev(ir.Type.StructName{ + .string = extra.id.slice(self).?, + }); + + switch (extra.body) { + .none => try type_block.writeAbbrev(ir.Type.Opaque{}), + else => { + const real_struct = self.type_items.items[@intFromEnum(extra.body)]; + const is_packed: bool = switch (real_struct.tag) { + .structure => false, + .packed_structure => true, + else => unreachable, + }; + + var real_extra = self.typeExtraDataTrail(Type.Structure, real_struct.data); + try type_block.writeAbbrev(ir.Type.StructNamed{ + .is_packed = is_packed, + .types = real_extra.trail.next(real_extra.data.fields_len, Type, self), + }); + }, + } + }, + .array, + .small_array, + => try type_block.writeAbbrev(ir.Type.Array{ + .len = ty.aggregateLen(self), + .child = ty.childType(self), + }), + .vector, + .scalable_vector, + => try type_block.writeAbbrev(ir.Type.Vector{ + .len = ty.aggregateLen(self), + .child = ty.childType(self), + }), + .pointer => try type_block.writeAbbrev(ir.Type.Pointer{ + .addr_space = ty.pointerAddrSpace(self), + }), + .target => { + var extra = self.typeExtraDataTrail(Type.Target, item.data); + try type_block.writeAbbrev(ir.Type.StructName{ + .string = extra.data.name.slice(self).?, + }); + + const types = extra.trail.next(extra.data.types_len, Type, self); + const ints = extra.trail.next(extra.data.ints_len, u32, self); + + try type_block.writeAbbrev(ir.Type.Target{ + .num_types = extra.data.types_len, + .types = types, + .ints = ints, + }); + }, + .function, .vararg_function => |kind| { + const is_vararg = switch (kind) { + .function => false, + .vararg_function => true, + else => unreachable, + }; + var extra = self.typeExtraDataTrail(Type.Function, item.data); + try type_block.writeAbbrev(ir.Type.Function{ + .is_vararg = is_vararg, + .return_type = extra.data.ret, + .param_types = extra.trail.next(extra.data.params_len, Type, self), + }); + }, + } + } + + try type_block.end(); + } + + var attributes_set: std.AutoArrayHashMapUnmanaged(struct { + attributes: Attributes, + index: u32, + }, void) = .{}; + defer attributes_set.deinit(self.gpa); + + // PARAMATTR_GROUP_BLOCK + { + const ParamattrGroup = ir.ParamattrGroup; + + var paramattr_group_block = try module_block.enterSubBlock(ParamattrGroup, true); + + for (self.function_attributes_set.keys()) |func_attributes| { + for (func_attributes.slice(self), 0..) |attributes, i| { + const attributes_slice = attributes.slice(self); + if (attributes_slice.len == 0) continue; + + const attr_gop = try attributes_set.getOrPut(self.gpa, .{ + .attributes = attributes, + .index = @intCast(i), + }); + + if (attr_gop.found_existing) continue; + + record.clearRetainingCapacity(); + try record.ensureUnusedCapacity(self.gpa, 2); + + record.appendAssumeCapacity(attr_gop.index); + record.appendAssumeCapacity(switch (i) { + 0 => 0xffffffff, + else => i - 1, + }); + + for (attributes_slice) |attr_index| { + const kind = attr_index.getKind(self); + switch (attr_index.toAttribute(self)) { + .zeroext, + .signext, + .inreg, + .@"noalias", + .nocapture, + .nofree, + .nest, + .returned, + .nonnull, + .swiftself, + .swiftasync, + .swifterror, + .immarg, + .noundef, + .allocalign, + .allocptr, + .readnone, + .readonly, + .writeonly, + .alwaysinline, + .builtin, + .cold, + .convergent, + .disable_sanitizer_information, + .fn_ret_thunk_extern, + .hot, + .inlinehint, + .jumptable, + .minsize, + .naked, + .nobuiltin, + .nocallback, + .noduplicate, + .noimplicitfloat, + .@"noinline", + .nomerge, + .nonlazybind, + .noprofile, + .skipprofile, + .noredzone, + .noreturn, + .norecurse, + .willreturn, + .nosync, + .nounwind, + .nosanitize_bounds, + .nosanitize_coverage, + .null_pointer_is_valid, + .optforfuzzing, + .optnone, + .optsize, + .returns_twice, + .safestack, + .sanitize_address, + .sanitize_memory, + .sanitize_thread, + .sanitize_hwaddress, + .sanitize_memtag, + .speculative_load_hardening, + .speculatable, + .ssp, + .sspstrong, + .sspreq, + .strictfp, + .nocf_check, + .shadowcallstack, + .mustprogress, + .no_sanitize_address, + .no_sanitize_hwaddress, + .sanitize_address_dyninit, + => { + try record.ensureUnusedCapacity(self.gpa, 2); + record.appendAssumeCapacity(0); + record.appendAssumeCapacity(@intFromEnum(kind)); + }, + .byval, + .byref, + .preallocated, + .inalloca, + .sret, + .elementtype, + => |ty| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(6); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@intFromEnum(ty)); + }, + .@"align", + .alignstack, + => |alignment| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(alignment.toByteUnits() orelse 0); + }, + .dereferenceable, + .dereferenceable_or_null, + => |size| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(size); + }, + .nofpclass => |fpclass| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@as(u32, @bitCast(fpclass))); + }, + .allockind => |allockind| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@as(u32, @bitCast(allockind))); + }, + + .allocsize => |allocsize| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@bitCast(allocsize.toLlvm())); + }, + .memory => |memory| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@as(u32, @bitCast(memory))); + }, + .uwtable => |uwtable| if (uwtable != .none) { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@intFromEnum(uwtable)); + }, + .vscale_range => |vscale_range| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@bitCast(vscale_range.toLlvm())); + }, + .string => |string_attr| { + const string_attr_kind_slice = string_attr.kind.slice(self).?; + const string_attr_value_slice = if (string_attr.value != .none) + string_attr.value.slice(self).? + else + null; + + try record.ensureUnusedCapacity( + self.gpa, + 2 + string_attr_kind_slice.len + if (string_attr_value_slice) |slice| slice.len + 1 else 0, + ); + record.appendAssumeCapacity(if (string_attr.value == .none) 3 else 4); + for (string_attr.kind.slice(self).?) |c| { + record.appendAssumeCapacity(c); + } + record.appendAssumeCapacity(0); + if (string_attr_value_slice) |slice| { + for (slice) |c| { + record.appendAssumeCapacity(c); + } + record.appendAssumeCapacity(0); + } + }, + .none => unreachable, + } + } + + try paramattr_group_block.writeUnabbrev(3, record.items); + } + } + + try paramattr_group_block.end(); + } + + // PARAMATTR_BLOCK + { + const Paramattr = ir.Paramattr; + var paramattr_block = try module_block.enterSubBlock(Paramattr, true); + + for (self.function_attributes_set.keys()) |func_attributes| { + const func_attributes_slice = func_attributes.slice(self); + record.clearRetainingCapacity(); + try record.ensureUnusedCapacity(self.gpa, func_attributes_slice.len); + for (func_attributes_slice, 0..) |attributes, i| { + const attributes_slice = attributes.slice(self); + if (attributes_slice.len == 0) continue; + + const group_index = attributes_set.getIndex(.{ + .attributes = attributes, + .index = @intCast(i), + }).?; + record.appendAssumeCapacity(@intCast(group_index)); + } + + try paramattr_block.writeAbbrev(Paramattr.Entry{ .group_indices = record.items }); + } + + try paramattr_block.end(); + } + + var globals: std.AutoArrayHashMapUnmanaged(Global.Index, void) = .empty; + defer globals.deinit(self.gpa); + try globals.ensureUnusedCapacity( + self.gpa, + self.variables.items.len + + self.functions.items.len + + self.aliases.items.len, + ); + + for (self.variables.items) |variable| { + if (variable.global.getReplacement(self) != .none) continue; + + globals.putAssumeCapacity(variable.global, {}); + } + + for (self.functions.items) |function| { + if (function.global.getReplacement(self) != .none) continue; + + globals.putAssumeCapacity(function.global, {}); + } + + for (self.aliases.items) |alias| { + if (alias.global.getReplacement(self) != .none) continue; + + globals.putAssumeCapacity(alias.global, {}); + } + + const ConstantAdapter = struct { + const ConstantAdapter = @This(); + builder: *const Builder, + globals: *const std.AutoArrayHashMapUnmanaged(Global.Index, void), + + pub fn get(adapter: @This(), param: anytype, comptime field_name: []const u8) @TypeOf(param) { + _ = field_name; + return switch (@TypeOf(param)) { + Constant => @enumFromInt(adapter.getConstantIndex(param)), + else => param, + }; + } + + pub fn getConstantIndex(adapter: ConstantAdapter, constant: Constant) u32 { + return switch (constant.unwrap()) { + .constant => |c| c + adapter.numGlobals(), + .global => |global| @intCast(adapter.globals.getIndex(global.unwrap(adapter.builder)).?), + }; + } + + pub fn numConstants(adapter: ConstantAdapter) u32 { + return @intCast(adapter.globals.count() + adapter.builder.constant_items.len); + } + + pub fn numGlobals(adapter: ConstantAdapter) u32 { + return @intCast(adapter.globals.count()); + } + }; + + const constant_adapter = ConstantAdapter{ + .builder = self, + .globals = &globals, + }; + + // Globals + { + var section_map: std.AutoArrayHashMapUnmanaged(String, void) = .empty; + defer section_map.deinit(self.gpa); + try section_map.ensureUnusedCapacity(self.gpa, globals.count()); + + for (self.variables.items) |variable| { + if (variable.global.getReplacement(self) != .none) continue; + + const section = blk: { + if (variable.section == .none) break :blk 0; + const gop = section_map.getOrPutAssumeCapacity(variable.section); + if (!gop.found_existing) { + try module_block.writeAbbrev(Module.String{ + .code = 5, + .string = variable.section.slice(self).?, + }); + } + break :blk gop.index + 1; + }; + + const initid = if (variable.init == .no_init) + 0 + else + (constant_adapter.getConstantIndex(variable.init) + 1); + + const strtab = variable.global.strtab(self); + + const global = variable.global.ptrConst(self); + try module_block.writeAbbrev(Module.Variable{ + .strtab_offset = strtab.offset, + .strtab_size = strtab.size, + .type_index = global.type, + .is_const = .{ + .is_const = switch (variable.mutability) { + .global => false, + .constant => true, + }, + .addr_space = global.addr_space, + }, + .initid = initid, + .linkage = global.linkage, + .alignment = variable.alignment.toLlvm(), + .section = section, + .visibility = global.visibility, + .thread_local = variable.thread_local, + .unnamed_addr = global.unnamed_addr, + .externally_initialized = global.externally_initialized, + .dllstorageclass = global.dll_storage_class, + .preemption = global.preemption, + }); + } + + for (self.functions.items) |func| { + if (func.global.getReplacement(self) != .none) continue; + + const section = blk: { + if (func.section == .none) break :blk 0; + const gop = section_map.getOrPutAssumeCapacity(func.section); + if (!gop.found_existing) { + try module_block.writeAbbrev(Module.String{ + .code = 5, + .string = func.section.slice(self).?, + }); + } + break :blk gop.index + 1; + }; + + const paramattr_index = if (self.function_attributes_set.getIndex(func.attributes)) |index| + index + 1 + else + 0; + + const strtab = func.global.strtab(self); + + const global = func.global.ptrConst(self); + try module_block.writeAbbrev(Module.Function{ + .strtab_offset = strtab.offset, + .strtab_size = strtab.size, + .type_index = global.type, + .call_conv = func.call_conv, + .is_proto = func.instructions.len == 0, + .linkage = global.linkage, + .paramattr = paramattr_index, + .alignment = func.alignment.toLlvm(), + .section = section, + .visibility = global.visibility, + .unnamed_addr = global.unnamed_addr, + .dllstorageclass = global.dll_storage_class, + .preemption = global.preemption, + .addr_space = global.addr_space, + }); + } + + for (self.aliases.items) |alias| { + if (alias.global.getReplacement(self) != .none) continue; + + const strtab = alias.global.strtab(self); + + const global = alias.global.ptrConst(self); + try module_block.writeAbbrev(Module.Alias{ + .strtab_offset = strtab.offset, + .strtab_size = strtab.size, + .type_index = global.type, + .addr_space = global.addr_space, + .aliasee = constant_adapter.getConstantIndex(alias.aliasee), + .linkage = global.linkage, + .visibility = global.visibility, + .thread_local = alias.thread_local, + .unnamed_addr = global.unnamed_addr, + .dllstorageclass = global.dll_storage_class, + .preemption = global.preemption, + }); + } + } + + // CONSTANTS_BLOCK + { + const Constants = ir.Constants; + var constants_block = try module_block.enterSubBlock(Constants, true); + + var current_type: Type = .none; + const tags = self.constant_items.items(.tag); + const datas = self.constant_items.items(.data); + for (0..self.constant_items.len) |index| { + record.clearRetainingCapacity(); + const constant: Constant = @enumFromInt(index); + const constant_type = constant.typeOf(self); + if (constant_type != current_type) { + try constants_block.writeAbbrev(Constants.SetType{ .type_id = constant_type }); + current_type = constant_type; + } + const data = datas[index]; + switch (tags[index]) { + .null, + .zeroinitializer, + .none, + => try constants_block.writeAbbrev(Constants.Null{}), + .undef => try constants_block.writeAbbrev(Constants.Undef{}), + .poison => try constants_block.writeAbbrev(Constants.Poison{}), + .positive_integer, + .negative_integer, + => |tag| { + const extra: *align(@alignOf(std.math.big.Limb)) Constant.Integer = + @ptrCast(self.constant_limbs.items[data..][0..Constant.Integer.limbs]); + const bigint: std.math.big.int.Const = .{ + .limbs = self.constant_limbs + .items[data + Constant.Integer.limbs ..][0..extra.limbs_len], + .positive = switch (tag) { + .positive_integer => true, + .negative_integer => false, + else => unreachable, + }, + }; + const bit_count = extra.type.scalarBits(self); + const val: i64 = if (bit_count <= 64) + bigint.toInt(i64) catch unreachable + else if (bigint.toInt(u64)) |val| + @bitCast(val) + else |_| { + const limbs = try record.addManyAsSlice( + self.gpa, + std.math.divCeil(u24, bit_count, 64) catch unreachable, + ); + bigint.writeTwosComplement(std.mem.sliceAsBytes(limbs), .little); + for (limbs) |*limb| { + const val = std.mem.littleToNative(i64, @bitCast(limb.*)); + limb.* = @bitCast(if (val >= 0) + val << 1 | 0 + else + -%val << 1 | 1); + } + try constants_block.writeUnabbrev(5, record.items); + continue; + }; + try constants_block.writeAbbrev(Constants.Integer{ + .value = @bitCast(if (val >= 0) + val << 1 | 0 + else + -%val << 1 | 1), + }); + }, + .half, + .bfloat, + => try constants_block.writeAbbrev(Constants.Half{ .value = @truncate(data) }), + .float => try constants_block.writeAbbrev(Constants.Float{ .value = data }), + .double => { + const extra = self.constantExtraData(Constant.Double, data); + try constants_block.writeAbbrev(Constants.Double{ + .value = (@as(u64, extra.hi) << 32) | extra.lo, + }); + }, + .x86_fp80 => { + const extra = self.constantExtraData(Constant.Fp80, data); + try constants_block.writeAbbrev(Constants.Fp80{ + .hi = @as(u64, extra.hi) << 48 | @as(u64, extra.lo_hi) << 16 | + extra.lo_lo >> 16, + .lo = @truncate(extra.lo_lo), + }); + }, + .fp128, + .ppc_fp128, + => { + const extra = self.constantExtraData(Constant.Fp128, data); + try constants_block.writeAbbrev(Constants.Fp128{ + .lo = @as(u64, extra.lo_hi) << 32 | @as(u64, extra.lo_lo), + .hi = @as(u64, extra.hi_hi) << 32 | @as(u64, extra.hi_lo), + }); + }, + .array, + .vector, + .structure, + .packed_structure, + => { + var extra = self.constantExtraDataTrail(Constant.Aggregate, data); + const len: u32 = @intCast(extra.data.type.aggregateLen(self)); + const values = extra.trail.next(len, Constant, self); + + try constants_block.writeAbbrevAdapted( + Constants.Aggregate{ .values = values }, + constant_adapter, + ); + }, + .splat => { + const ConstantsWriter = @TypeOf(constants_block); + const extra = self.constantExtraData(Constant.Splat, data); + const vector_len = extra.type.vectorLen(self); + const c = constant_adapter.getConstantIndex(extra.value); + + try bitcode.writeBits( + ConstantsWriter.abbrevId(Constants.Aggregate), + ConstantsWriter.abbrev_len, + ); + try bitcode.writeVBR(vector_len, 6); + for (0..vector_len) |_| { + try bitcode.writeBits(c, Constants.Aggregate.ops[1].array_fixed); + } + }, + .string => { + const str: String = @enumFromInt(data); + if (str == .none) { + try constants_block.writeAbbrev(Constants.Null{}); + } else { + const slice = str.slice(self).?; + if (slice.len > 0 and slice[slice.len - 1] == 0) + try constants_block.writeAbbrev(Constants.CString{ .string = slice[0 .. slice.len - 1] }) + else + try constants_block.writeAbbrev(Constants.String{ .string = slice }); + } + }, + .bitcast, + .inttoptr, + .ptrtoint, + .addrspacecast, + .trunc, + => |tag| { + const extra = self.constantExtraData(Constant.Cast, data); + try constants_block.writeAbbrevAdapted(Constants.Cast{ + .type_index = extra.type, + .val = extra.val, + .opcode = tag.toCastOpcode(), + }, constant_adapter); + }, + .add, + .@"add nsw", + .@"add nuw", + .sub, + .@"sub nsw", + .@"sub nuw", + .shl, + .xor, + => |tag| { + const extra = self.constantExtraData(Constant.Binary, data); + try constants_block.writeAbbrevAdapted(Constants.Binary{ + .opcode = tag.toBinaryOpcode(), + .lhs = extra.lhs, + .rhs = extra.rhs, + }, constant_adapter); + }, + .getelementptr, + .@"getelementptr inbounds", + => |tag| { + var extra = self.constantExtraDataTrail(Constant.GetElementPtr, data); + const indices = extra.trail.next(extra.data.info.indices_len, Constant, self); + try record.ensureUnusedCapacity(self.gpa, 1 + 2 + 2 * indices.len); + + record.appendAssumeCapacity(@intFromEnum(extra.data.type)); + + record.appendAssumeCapacity(@intFromEnum(extra.data.base.typeOf(self))); + record.appendAssumeCapacity(constant_adapter.getConstantIndex(extra.data.base)); + + for (indices) |i| { + record.appendAssumeCapacity(@intFromEnum(i.typeOf(self))); + record.appendAssumeCapacity(constant_adapter.getConstantIndex(i)); + } + + try constants_block.writeUnabbrev(switch (tag) { + .getelementptr => 12, + .@"getelementptr inbounds" => 20, + else => unreachable, + }, record.items); + }, + .@"asm", + .@"asm sideeffect", + .@"asm alignstack", + .@"asm sideeffect alignstack", + .@"asm inteldialect", + .@"asm sideeffect inteldialect", + .@"asm alignstack inteldialect", + .@"asm sideeffect alignstack inteldialect", + .@"asm unwind", + .@"asm sideeffect unwind", + .@"asm alignstack unwind", + .@"asm sideeffect alignstack unwind", + .@"asm inteldialect unwind", + .@"asm sideeffect inteldialect unwind", + .@"asm alignstack inteldialect unwind", + .@"asm sideeffect alignstack inteldialect unwind", + => |tag| { + const extra = self.constantExtraData(Constant.Assembly, data); + + const assembly_slice = extra.assembly.slice(self).?; + const constraints_slice = extra.constraints.slice(self).?; + + try record.ensureUnusedCapacity(self.gpa, 4 + assembly_slice.len + constraints_slice.len); + + record.appendAssumeCapacity(@intFromEnum(extra.type)); + record.appendAssumeCapacity(switch (tag) { + .@"asm" => 0, + .@"asm sideeffect" => 0b0001, + .@"asm sideeffect alignstack" => 0b0011, + .@"asm sideeffect inteldialect" => 0b0101, + .@"asm sideeffect alignstack inteldialect" => 0b0111, + .@"asm sideeffect unwind" => 0b1001, + .@"asm sideeffect alignstack unwind" => 0b1011, + .@"asm sideeffect inteldialect unwind" => 0b1101, + .@"asm sideeffect alignstack inteldialect unwind" => 0b1111, + .@"asm alignstack" => 0b0010, + .@"asm inteldialect" => 0b0100, + .@"asm alignstack inteldialect" => 0b0110, + .@"asm unwind" => 0b1000, + .@"asm alignstack unwind" => 0b1010, + .@"asm inteldialect unwind" => 0b1100, + .@"asm alignstack inteldialect unwind" => 0b1110, + else => unreachable, + }); + + record.appendAssumeCapacity(assembly_slice.len); + for (assembly_slice) |c| record.appendAssumeCapacity(c); + + record.appendAssumeCapacity(constraints_slice.len); + for (constraints_slice) |c| record.appendAssumeCapacity(c); + + try constants_block.writeUnabbrev(30, record.items); + }, + .blockaddress => { + const extra = self.constantExtraData(Constant.BlockAddress, data); + try constants_block.writeAbbrev(Constants.BlockAddress{ + .type_id = extra.function.typeOf(self), + .function = constant_adapter.getConstantIndex(extra.function.toConst(self)), + .block = @intFromEnum(extra.block), + }); + }, + .dso_local_equivalent, + .no_cfi, + => |tag| { + const function: Function.Index = @enumFromInt(data); + try constants_block.writeAbbrev(Constants.DsoLocalEquivalentOrNoCfi{ + .code = switch (tag) { + .dso_local_equivalent => 27, + .no_cfi => 29, + else => unreachable, + }, + .type_id = function.typeOf(self), + .function = constant_adapter.getConstantIndex(function.toConst(self)), + }); + }, + } + } + + try constants_block.end(); + } + + // METADATA_KIND_BLOCK + { + const MetadataKindBlock = ir.MetadataKindBlock; + var metadata_kind_block = try module_block.enterSubBlock(MetadataKindBlock, true); + + inline for (@typeInfo(ir.FixedMetadataKind).@"enum".fields) |field| { + // don't include `dbg` in stripped functions + if (!(self.strip and std.mem.eql(u8, field.name, "dbg"))) { + try metadata_kind_block.writeAbbrev(MetadataKindBlock.Kind{ + .id = field.value, + .name = field.name, + }); + } + } + + try metadata_kind_block.end(); + } + + const MetadataAdapter = struct { + builder: *const Builder, + constant_adapter: ConstantAdapter, + + pub fn init( + builder: *const Builder, + const_adapter: ConstantAdapter, + ) @This() { + return .{ + .builder = builder, + .constant_adapter = const_adapter, + }; + } + + pub fn get(adapter: @This(), value: anytype, comptime field_name: []const u8) @TypeOf(value) { + _ = field_name; + const Ty = @TypeOf(value); + return switch (Ty) { + Metadata => @enumFromInt(adapter.getMetadataIndex(value)), + MetadataString => @enumFromInt(adapter.getMetadataStringIndex(value)), + Constant => @enumFromInt(adapter.constant_adapter.getConstantIndex(value)), + else => value, + }; + } + + pub fn getMetadataIndex(adapter: @This(), metadata: Metadata) u32 { + if (metadata == .none) return 0; + return @intCast(adapter.builder.metadata_string_map.count() + + @intFromEnum(metadata.unwrap(adapter.builder)) - 1); + } + + pub fn getMetadataStringIndex(_: @This(), metadata_string: MetadataString) u32 { + return @intFromEnum(metadata_string); + } + }; + + const metadata_adapter = MetadataAdapter.init(self, constant_adapter); + + // METADATA_BLOCK + { + const MetadataBlock = ir.MetadataBlock; + var metadata_block = try module_block.enterSubBlock(MetadataBlock, true); + + const MetadataBlockWriter = @TypeOf(metadata_block); + + // Emit all MetadataStrings + if (self.metadata_string_map.count() > 1) { + const strings_offset, const strings_size = blk: { + var strings_offset: u32 = 0; + var strings_size: u32 = 0; + for (1..self.metadata_string_map.count()) |metadata_string_index| { + const metadata_string: MetadataString = @enumFromInt(metadata_string_index); + const slice = metadata_string.slice(self); + strings_offset += bitcode.bitsVBR(@as(u32, @intCast(slice.len)), 6); + strings_size += @intCast(slice.len * 8); + } + break :blk .{ + std.mem.alignForward(u32, strings_offset, 32) / 8, + std.mem.alignForward(u32, strings_size, 32) / 8, + }; + }; + + try bitcode.writeBits( + comptime MetadataBlockWriter.abbrevId(MetadataBlock.Strings), + MetadataBlockWriter.abbrev_len, + ); + + try bitcode.writeVBR(@as(u32, @intCast(self.metadata_string_map.count() - 1)), 6); + try bitcode.writeVBR(strings_offset, 6); + + try bitcode.writeVBR(strings_size + strings_offset, 6); + + try bitcode.alignTo32(); + + for (1..self.metadata_string_map.count()) |metadata_string_index| { + const metadata_string: MetadataString = @enumFromInt(metadata_string_index); + const slice = metadata_string.slice(self); + try bitcode.writeVBR(@as(u32, @intCast(slice.len)), 6); + } + + try bitcode.writeBlob(self.metadata_string_bytes.items); + } + + for ( + self.metadata_items.items(.tag)[1..], + self.metadata_items.items(.data)[1..], + ) |tag, data| { + record.clearRetainingCapacity(); + switch (tag) { + .none => unreachable, + .file => { + const extra = self.metadataExtraData(Metadata.File, data); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.File{ + .filename = extra.filename, + .directory = extra.directory, + }, metadata_adapter); + }, + .compile_unit, + .@"compile_unit optimized", + => |kind| { + const extra = self.metadataExtraData(Metadata.CompileUnit, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.CompileUnit{ + .file = extra.file, + .producer = extra.producer, + .is_optimized = switch (kind) { + .compile_unit => false, + .@"compile_unit optimized" => true, + else => unreachable, + }, + .enums = extra.enums, + .globals = extra.globals, + }, metadata_adapter); + }, + .subprogram, + .@"subprogram local", + .@"subprogram definition", + .@"subprogram local definition", + .@"subprogram optimized", + .@"subprogram optimized local", + .@"subprogram optimized definition", + .@"subprogram optimized local definition", + => |kind| { + const extra = self.metadataExtraData(Metadata.Subprogram, data); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.Subprogram{ + .scope = extra.file, + .name = extra.name, + .linkage_name = extra.linkage_name, + .file = extra.file, + .line = extra.line, + .ty = extra.ty, + .scope_line = extra.scope_line, + .sp_flags = @bitCast(@as(u32, @as(u3, @intCast( + @intFromEnum(kind) - @intFromEnum(Metadata.Tag.subprogram), + ))) << 2), + .flags = extra.di_flags, + .compile_unit = extra.compile_unit, + }, metadata_adapter); + }, + .lexical_block => { + const extra = self.metadataExtraData(Metadata.LexicalBlock, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.LexicalBlock{ + .scope = extra.scope, + .file = extra.file, + .line = extra.line, + .column = extra.column, + }, metadata_adapter); + }, + .location => { + const extra = self.metadataExtraData(Metadata.Location, data); + assert(extra.scope != .none); + try metadata_block.writeAbbrev(MetadataBlock.Location{ + .line = extra.line, + .column = extra.column, + .scope = metadata_adapter.getMetadataIndex(extra.scope) - 1, + .inlined_at = @enumFromInt(metadata_adapter.getMetadataIndex(extra.inlined_at)), + }); + }, + .basic_bool_type, + .basic_unsigned_type, + .basic_signed_type, + .basic_float_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.BasicType, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.BasicType{ + .name = extra.name, + .size_in_bits = extra.bitSize(), + .encoding = switch (kind) { + .basic_bool_type => DW.ATE.boolean, + .basic_unsigned_type => DW.ATE.unsigned, + .basic_signed_type => DW.ATE.signed, + .basic_float_type => DW.ATE.float, + else => unreachable, + }, + }, metadata_adapter); + }, + .composite_struct_type, + .composite_union_type, + .composite_enumeration_type, + .composite_array_type, + .composite_vector_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.CompositeType, data); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.CompositeType{ + .tag = switch (kind) { + .composite_struct_type => DW.TAG.structure_type, + .composite_union_type => DW.TAG.union_type, + .composite_enumeration_type => DW.TAG.enumeration_type, + .composite_array_type, .composite_vector_type => DW.TAG.array_type, + else => unreachable, + }, + .name = extra.name, + .file = extra.file, + .line = extra.line, + .scope = extra.scope, + .underlying_type = extra.underlying_type, + .size_in_bits = extra.bitSize(), + .align_in_bits = extra.bitAlign(), + .flags = if (kind == .composite_vector_type) .{ .Vector = true } else .{}, + .elements = extra.fields_tuple, + }, metadata_adapter); + }, + .derived_pointer_type, + .derived_member_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.DerivedType, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.DerivedType{ + .tag = switch (kind) { + .derived_pointer_type => DW.TAG.pointer_type, + .derived_member_type => DW.TAG.member, + else => unreachable, + }, + .name = extra.name, + .file = extra.file, + .line = extra.line, + .scope = extra.scope, + .underlying_type = extra.underlying_type, + .size_in_bits = extra.bitSize(), + .align_in_bits = extra.bitAlign(), + .offset_in_bits = extra.bitOffset(), + }, metadata_adapter); + }, + .subroutine_type => { + const extra = self.metadataExtraData(Metadata.SubroutineType, data); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.SubroutineType{ + .types = extra.types_tuple, + }, metadata_adapter); + }, + .enumerator_unsigned, + .enumerator_signed_positive, + .enumerator_signed_negative, + => |kind| { + const extra = self.metadataExtraData(Metadata.Enumerator, data); + const bigint: std.math.big.int.Const = .{ + .limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len], + .positive = switch (kind) { + .enumerator_unsigned, + .enumerator_signed_positive, + => true, + .enumerator_signed_negative => false, + else => unreachable, + }, + }; + const flags: MetadataBlock.Enumerator.Flags = .{ + .unsigned = switch (kind) { + .enumerator_unsigned => true, + .enumerator_signed_positive, + .enumerator_signed_negative, + => false, + else => unreachable, + }, + }; + const val: i64 = if (bigint.toInt(i64)) |val| + val + else |_| if (bigint.toInt(u64)) |val| + @bitCast(val) + else |_| { + const limbs_len = std.math.divCeil(u32, extra.bit_width, 64) catch unreachable; + try record.ensureTotalCapacity(self.gpa, 3 + limbs_len); + record.appendAssumeCapacity(@as( + @typeInfo(MetadataBlock.Enumerator.Flags).@"struct".backing_integer.?, + @bitCast(flags), + )); + record.appendAssumeCapacity(extra.bit_width); + record.appendAssumeCapacity(metadata_adapter.getMetadataStringIndex(extra.name)); + const limbs = record.addManyAsSliceAssumeCapacity(limbs_len); + bigint.writeTwosComplement(std.mem.sliceAsBytes(limbs), .little); + for (limbs) |*limb| { + const val = std.mem.littleToNative(i64, @bitCast(limb.*)); + limb.* = @bitCast(if (val >= 0) + val << 1 | 0 + else + -%val << 1 | 1); + } + try metadata_block.writeUnabbrev(@intFromEnum(MetadataBlock.Enumerator.id), record.items); + continue; + }; + try metadata_block.writeAbbrevAdapted(MetadataBlock.Enumerator{ + .flags = flags, + .bit_width = extra.bit_width, + .name = extra.name, + .value = @bitCast(if (val >= 0) + val << 1 | 0 + else + -%val << 1 | 1), + }, metadata_adapter); + }, + .subrange => { + const extra = self.metadataExtraData(Metadata.Subrange, data); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.Subrange{ + .count = extra.count, + .lower_bound = extra.lower_bound, + }, metadata_adapter); + }, + .expression => { + var extra = self.metadataExtraDataTrail(Metadata.Expression, data); + + const elements = extra.trail.next(extra.data.elements_len, u32, self); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.Expression{ + .elements = elements, + }, metadata_adapter); + }, + .tuple => { + var extra = self.metadataExtraDataTrail(Metadata.Tuple, data); + + const elements = extra.trail.next(extra.data.elements_len, Metadata, self); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.Node{ + .elements = elements, + }, metadata_adapter); + }, + .str_tuple => { + var extra = self.metadataExtraDataTrail(Metadata.StrTuple, data); + + const elements = extra.trail.next(extra.data.elements_len, Metadata, self); + + const all_elems = try self.gpa.alloc(Metadata, elements.len + 1); + defer self.gpa.free(all_elems); + all_elems[0] = @enumFromInt(metadata_adapter.getMetadataStringIndex(extra.data.str)); + for (elements, all_elems[1..]) |elem, *out_elem| { + out_elem.* = @enumFromInt(metadata_adapter.getMetadataIndex(elem)); + } + + try metadata_block.writeAbbrev(MetadataBlock.Node{ + .elements = all_elems, + }); + }, + .module_flag => { + const extra = self.metadataExtraData(Metadata.ModuleFlag, data); + try metadata_block.writeAbbrev(MetadataBlock.Node{ + .elements = &.{ + @enumFromInt(metadata_adapter.getMetadataIndex(extra.behavior)), + @enumFromInt(metadata_adapter.getMetadataStringIndex(extra.name)), + @enumFromInt(metadata_adapter.getMetadataIndex(extra.constant)), + }, + }); + }, + .local_var => { + const extra = self.metadataExtraData(Metadata.LocalVar, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.LocalVar{ + .scope = extra.scope, + .name = extra.name, + .file = extra.file, + .line = extra.line, + .ty = extra.ty, + }, metadata_adapter); + }, + .parameter => { + const extra = self.metadataExtraData(Metadata.Parameter, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.Parameter{ + .scope = extra.scope, + .name = extra.name, + .file = extra.file, + .line = extra.line, + .ty = extra.ty, + .arg = extra.arg_no, + }, metadata_adapter); + }, + .global_var, + .@"global_var local", + => |kind| { + const extra = self.metadataExtraData(Metadata.GlobalVar, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.GlobalVar{ + .scope = extra.scope, + .name = extra.name, + .linkage_name = extra.linkage_name, + .file = extra.file, + .line = extra.line, + .ty = extra.ty, + .local = kind == .@"global_var local", + }, metadata_adapter); + }, + .global_var_expression => { + const extra = self.metadataExtraData(Metadata.GlobalVarExpression, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.GlobalVarExpression{ + .variable = extra.variable, + .expression = extra.expression, + }, metadata_adapter); + }, + .constant => { + const constant: Constant = @enumFromInt(data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.Constant{ + .ty = constant.typeOf(self), + .constant = constant, + }, metadata_adapter); + }, + } + } + + // Write named metadata + for (self.metadata_named.keys(), self.metadata_named.values()) |name, operands| { + const slice = name.slice(self); + try metadata_block.writeAbbrev(MetadataBlock.Name{ + .name = slice, + }); + + const elements = self.metadata_extra.items[operands.index..][0..operands.len]; + for (elements) |*e| { + e.* = metadata_adapter.getMetadataIndex(@enumFromInt(e.*)) - 1; + } + + try metadata_block.writeAbbrev(MetadataBlock.NamedNode{ + .elements = @ptrCast(elements), + }); + } + + // Write global attached metadata + { + for (globals.keys()) |global| { + const global_ptr = global.ptrConst(self); + if (global_ptr.dbg == .none) continue; + + switch (global_ptr.kind) { + .function => |f| if (f.ptrConst(self).instructions.len != 0) continue, + else => {}, + } + + try metadata_block.writeAbbrev(MetadataBlock.GlobalDeclAttachment{ + .value = @enumFromInt(constant_adapter.getConstantIndex(global.toConst())), + .kind = .dbg, + .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(global_ptr.dbg) - 1), + }); + } + } + + try metadata_block.end(); + } + + // OPERAND_BUNDLE_TAGS_BLOCK + { + const OperandBundleTags = ir.OperandBundleTags; + var operand_bundle_tags_block = try module_block.enterSubBlock(OperandBundleTags, true); + + try operand_bundle_tags_block.writeAbbrev(OperandBundleTags.OperandBundleTag{ + .tag = "cold", + }); + + try operand_bundle_tags_block.end(); + } + + // Block info + { + const BlockInfo = ir.BlockInfo; + var block_info_block = try module_block.enterSubBlock(BlockInfo, true); + + try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.FunctionBlock.id}); + inline for (ir.FunctionBlock.abbrevs) |abbrev| { + try block_info_block.defineAbbrev(&abbrev.ops); + } + + try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.FunctionValueSymbolTable.id}); + inline for (ir.FunctionValueSymbolTable.abbrevs) |abbrev| { + try block_info_block.defineAbbrev(&abbrev.ops); + } + + try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.FunctionMetadataBlock.id}); + inline for (ir.FunctionMetadataBlock.abbrevs) |abbrev| { + try block_info_block.defineAbbrev(&abbrev.ops); + } + + try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.MetadataAttachmentBlock.id}); + inline for (ir.MetadataAttachmentBlock.abbrevs) |abbrev| { + try block_info_block.defineAbbrev(&abbrev.ops); + } + + try block_info_block.end(); + } + + // FUNCTION_BLOCKS + { + const FunctionAdapter = struct { + constant_adapter: ConstantAdapter, + metadata_adapter: MetadataAdapter, + func: *const Function, + instruction_index: Function.Instruction.Index, + + pub fn get(adapter: @This(), value: anytype, comptime field_name: []const u8) @TypeOf(value) { + _ = field_name; + const Ty = @TypeOf(value); + return switch (Ty) { + Value => @enumFromInt(adapter.getOffsetValueIndex(value)), + Constant => @enumFromInt(adapter.getOffsetConstantIndex(value)), + FunctionAttributes => @enumFromInt(switch (value) { + .none => 0, + else => 1 + adapter.constant_adapter.builder.function_attributes_set.getIndex(value).?, + }), + else => value, + }; + } + + pub fn getValueIndex(adapter: @This(), value: Value) u32 { + return @intCast(switch (value.unwrap()) { + .instruction => |instruction| instruction.valueIndex(adapter.func) + adapter.firstInstr(), + .constant => |constant| adapter.constant_adapter.getConstantIndex(constant), + .metadata => |metadata| { + const real_metadata = metadata.unwrap(adapter.metadata_adapter.builder); + if (@intFromEnum(real_metadata) < Metadata.first_local_metadata) + return adapter.metadata_adapter.getMetadataIndex(real_metadata) - 1; + + return @intCast(@intFromEnum(metadata) - + Metadata.first_local_metadata + + adapter.metadata_adapter.builder.metadata_string_map.count() - 1 + + adapter.metadata_adapter.builder.metadata_map.count() - 1); + }, + }); + } + + pub fn getOffsetValueIndex(adapter: @This(), value: Value) u32 { + return adapter.offset() -% adapter.getValueIndex(value); + } + + pub fn getOffsetValueSignedIndex(adapter: @This(), value: Value) i32 { + const signed_offset: i32 = @intCast(adapter.offset()); + const signed_value: i32 = @intCast(adapter.getValueIndex(value)); + return signed_offset - signed_value; + } + + pub fn getOffsetConstantIndex(adapter: @This(), constant: Constant) u32 { + return adapter.offset() - adapter.constant_adapter.getConstantIndex(constant); + } + + pub fn offset(adapter: @This()) u32 { + return adapter.instruction_index.valueIndex(adapter.func) + adapter.firstInstr(); + } + + fn firstInstr(adapter: @This()) u32 { + return adapter.constant_adapter.numConstants(); + } + }; + + for (self.functions.items, 0..) |func, func_index| { + const FunctionBlock = ir.FunctionBlock; + if (func.global.getReplacement(self) != .none) continue; + + if (func.instructions.len == 0) continue; + + var function_block = try module_block.enterSubBlock(FunctionBlock, false); + + try function_block.writeAbbrev(FunctionBlock.DeclareBlocks{ .num_blocks = func.blocks.len }); + + var adapter: FunctionAdapter = .{ + .constant_adapter = constant_adapter, + .metadata_adapter = metadata_adapter, + .func = &func, + .instruction_index = @enumFromInt(0), + }; + + // Emit function level metadata block + if (!func.strip and func.debug_values.len > 0) { + const MetadataBlock = ir.FunctionMetadataBlock; + var metadata_block = try function_block.enterSubBlock(MetadataBlock, false); + + for (func.debug_values) |value| { + try metadata_block.writeAbbrev(MetadataBlock.Value{ + .ty = value.typeOf(@enumFromInt(func_index), self), + .value = @enumFromInt(adapter.getValueIndex(value.toValue())), + }); + } + + try metadata_block.end(); + } + + const tags = func.instructions.items(.tag); + const datas = func.instructions.items(.data); + + var has_location = false; + + var block_incoming_len: u32 = undefined; + for (tags, datas, 0..) |tag, data, instr_index| { + adapter.instruction_index = @enumFromInt(instr_index); + record.clearRetainingCapacity(); + + switch (tag) { + .arg => continue, + .block => { + block_incoming_len = data; + continue; + }, + .@"unreachable" => try function_block.writeAbbrev(FunctionBlock.Unreachable{}), + .call, + .@"musttail call", + .@"notail call", + .@"tail call", + => |kind| { + var extra = func.extraDataTrail(Function.Instruction.Call, data); + + if (extra.data.info.has_op_bundle_cold) { + try function_block.writeAbbrev(FunctionBlock.ColdOperandBundle{}); + } + + const call_conv = extra.data.info.call_conv; + const args = extra.trail.next(extra.data.args_len, Value, &func); + try function_block.writeAbbrevAdapted(FunctionBlock.Call{ + .attributes = extra.data.attributes, + .call_type = switch (kind) { + .call => .{ .call_conv = call_conv }, + .@"tail call" => .{ .tail = true, .call_conv = call_conv }, + .@"musttail call" => .{ .must_tail = true, .call_conv = call_conv }, + .@"notail call" => .{ .no_tail = true, .call_conv = call_conv }, + else => unreachable, + }, + .type_id = extra.data.ty, + .callee = extra.data.callee, + .args = args, + }, adapter); + }, + .@"call fast", + .@"musttail call fast", + .@"notail call fast", + .@"tail call fast", + => |kind| { + var extra = func.extraDataTrail(Function.Instruction.Call, data); + + if (extra.data.info.has_op_bundle_cold) { + try function_block.writeAbbrev(FunctionBlock.ColdOperandBundle{}); + } + + const call_conv = extra.data.info.call_conv; + const args = extra.trail.next(extra.data.args_len, Value, &func); + try function_block.writeAbbrevAdapted(FunctionBlock.CallFast{ + .attributes = extra.data.attributes, + .call_type = switch (kind) { + .@"call fast" => .{ .call_conv = call_conv }, + .@"tail call fast" => .{ .tail = true, .call_conv = call_conv }, + .@"musttail call fast" => .{ .must_tail = true, .call_conv = call_conv }, + .@"notail call fast" => .{ .no_tail = true, .call_conv = call_conv }, + else => unreachable, + }, + .fast_math = FastMath.fast, + .type_id = extra.data.ty, + .callee = extra.data.callee, + .args = args, + }, adapter); + }, + .add, + .@"and", + .fadd, + .fdiv, + .fmul, + .mul, + .frem, + .fsub, + .sdiv, + .sub, + .udiv, + .xor, + .shl, + .lshr, + .@"or", + .urem, + .srem, + .ashr, + => |kind| { + const extra = func.extraData(Function.Instruction.Binary, data); + try function_block.writeAbbrev(FunctionBlock.Binary{ + .opcode = kind.toBinaryOpcode(), + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + }); + }, + .@"sdiv exact", + .@"udiv exact", + .@"lshr exact", + .@"ashr exact", + => |kind| { + const extra = func.extraData(Function.Instruction.Binary, data); + try function_block.writeAbbrev(FunctionBlock.BinaryExact{ + .opcode = kind.toBinaryOpcode(), + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + }); + }, + .@"add nsw", + .@"add nuw", + .@"add nuw nsw", + .@"mul nsw", + .@"mul nuw", + .@"mul nuw nsw", + .@"sub nsw", + .@"sub nuw", + .@"sub nuw nsw", + .@"shl nsw", + .@"shl nuw", + .@"shl nuw nsw", + => |kind| { + const extra = func.extraData(Function.Instruction.Binary, data); + try function_block.writeAbbrev(FunctionBlock.BinaryNoWrap{ + .opcode = kind.toBinaryOpcode(), + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .flags = switch (kind) { + .@"add nsw", + .@"mul nsw", + .@"sub nsw", + .@"shl nsw", + => .{ .no_unsigned_wrap = false, .no_signed_wrap = true }, + .@"add nuw", + .@"mul nuw", + .@"sub nuw", + .@"shl nuw", + => .{ .no_unsigned_wrap = true, .no_signed_wrap = false }, + .@"add nuw nsw", + .@"mul nuw nsw", + .@"sub nuw nsw", + .@"shl nuw nsw", + => .{ .no_unsigned_wrap = true, .no_signed_wrap = true }, + else => unreachable, + }, + }); + }, + .@"fadd fast", + .@"fdiv fast", + .@"fmul fast", + .@"frem fast", + .@"fsub fast", + => |kind| { + const extra = func.extraData(Function.Instruction.Binary, data); + try function_block.writeAbbrev(FunctionBlock.BinaryFast{ + .opcode = kind.toBinaryOpcode(), + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .fast_math = FastMath.fast, + }); + }, + .alloca, + .@"alloca inalloca", + => |kind| { + const extra = func.extraData(Function.Instruction.Alloca, data); + const alignment = extra.info.alignment.toLlvm(); + try function_block.writeAbbrev(FunctionBlock.Alloca{ + .inst_type = extra.type, + .len_type = extra.len.typeOf(@enumFromInt(func_index), self), + .len_value = adapter.getValueIndex(extra.len), + .flags = .{ + .align_lower = @truncate(alignment), + .inalloca = kind == .@"alloca inalloca", + .explicit_type = true, + .swift_error = false, + .align_upper = @truncate(alignment << 5), + }, + }); + }, + .bitcast, + .inttoptr, + .ptrtoint, + .fptosi, + .fptoui, + .sitofp, + .uitofp, + .addrspacecast, + .fptrunc, + .trunc, + .fpext, + .sext, + .zext, + => |kind| { + const extra = func.extraData(Function.Instruction.Cast, data); + try function_block.writeAbbrev(FunctionBlock.Cast{ + .val = adapter.getOffsetValueIndex(extra.val), + .type_index = extra.type, + .opcode = kind.toCastOpcode(), + }); + }, + .@"fcmp false", + .@"fcmp oeq", + .@"fcmp oge", + .@"fcmp ogt", + .@"fcmp ole", + .@"fcmp olt", + .@"fcmp one", + .@"fcmp ord", + .@"fcmp true", + .@"fcmp ueq", + .@"fcmp uge", + .@"fcmp ugt", + .@"fcmp ule", + .@"fcmp ult", + .@"fcmp une", + .@"fcmp uno", + .@"icmp eq", + .@"icmp ne", + .@"icmp sge", + .@"icmp sgt", + .@"icmp sle", + .@"icmp slt", + .@"icmp uge", + .@"icmp ugt", + .@"icmp ule", + .@"icmp ult", + => |kind| { + const extra = func.extraData(Function.Instruction.Binary, data); + try function_block.writeAbbrev(FunctionBlock.Cmp{ + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .pred = kind.toCmpPredicate(), + }); + }, + .@"fcmp fast false", + .@"fcmp fast oeq", + .@"fcmp fast oge", + .@"fcmp fast ogt", + .@"fcmp fast ole", + .@"fcmp fast olt", + .@"fcmp fast one", + .@"fcmp fast ord", + .@"fcmp fast true", + .@"fcmp fast ueq", + .@"fcmp fast uge", + .@"fcmp fast ugt", + .@"fcmp fast ule", + .@"fcmp fast ult", + .@"fcmp fast une", + .@"fcmp fast uno", + => |kind| { + const extra = func.extraData(Function.Instruction.Binary, data); + try function_block.writeAbbrev(FunctionBlock.CmpFast{ + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .pred = kind.toCmpPredicate(), + .fast_math = FastMath.fast, + }); + }, + .fneg => try function_block.writeAbbrev(FunctionBlock.FNeg{ + .val = adapter.getOffsetValueIndex(@enumFromInt(data)), + }), + .@"fneg fast" => try function_block.writeAbbrev(FunctionBlock.FNegFast{ + .val = adapter.getOffsetValueIndex(@enumFromInt(data)), + .fast_math = FastMath.fast, + }), + .extractvalue => { + var extra = func.extraDataTrail(Function.Instruction.ExtractValue, data); + const indices = extra.trail.next(extra.data.indices_len, u32, &func); + try function_block.writeAbbrev(FunctionBlock.ExtractValue{ + .val = adapter.getOffsetValueIndex(extra.data.val), + .indices = indices, + }); + }, + .extractelement => { + const extra = func.extraData(Function.Instruction.ExtractElement, data); + try function_block.writeAbbrev(FunctionBlock.ExtractElement{ + .val = adapter.getOffsetValueIndex(extra.val), + .index = adapter.getOffsetValueIndex(extra.index), + }); + }, + .indirectbr => { + var extra = + func.extraDataTrail(Function.Instruction.IndirectBr, datas[instr_index]); + const targets = + extra.trail.next(extra.data.targets_len, Function.Block.Index, &func); + try function_block.writeAbbrevAdapted( + FunctionBlock.IndirectBr{ + .ty = extra.data.addr.typeOf(@enumFromInt(func_index), self), + .addr = extra.data.addr, + .targets = targets, + }, + adapter, + ); + }, + .insertelement => { + const extra = func.extraData(Function.Instruction.InsertElement, data); + try function_block.writeAbbrev(FunctionBlock.InsertElement{ + .val = adapter.getOffsetValueIndex(extra.val), + .elem = adapter.getOffsetValueIndex(extra.elem), + .index = adapter.getOffsetValueIndex(extra.index), + }); + }, + .insertvalue => { + var extra = func.extraDataTrail(Function.Instruction.InsertValue, datas[instr_index]); + const indices = extra.trail.next(extra.data.indices_len, u32, &func); + try function_block.writeAbbrev(FunctionBlock.InsertValue{ + .val = adapter.getOffsetValueIndex(extra.data.val), + .elem = adapter.getOffsetValueIndex(extra.data.elem), + .indices = indices, + }); + }, + .select => { + const extra = func.extraData(Function.Instruction.Select, data); + try function_block.writeAbbrev(FunctionBlock.Select{ + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .cond = adapter.getOffsetValueIndex(extra.cond), + }); + }, + .@"select fast" => { + const extra = func.extraData(Function.Instruction.Select, data); + try function_block.writeAbbrev(FunctionBlock.SelectFast{ + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .cond = adapter.getOffsetValueIndex(extra.cond), + .fast_math = FastMath.fast, + }); + }, + .shufflevector => { + const extra = func.extraData(Function.Instruction.ShuffleVector, data); + try function_block.writeAbbrev(FunctionBlock.ShuffleVector{ + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .mask = adapter.getOffsetValueIndex(extra.mask), + }); + }, + .getelementptr, + .@"getelementptr inbounds", + => |kind| { + var extra = func.extraDataTrail(Function.Instruction.GetElementPtr, data); + const indices = extra.trail.next(extra.data.indices_len, Value, &func); + try function_block.writeAbbrevAdapted( + FunctionBlock.GetElementPtr{ + .is_inbounds = kind == .@"getelementptr inbounds", + .type_index = extra.data.type, + .base = extra.data.base, + .indices = indices, + }, + adapter, + ); + }, + .load => { + const extra = func.extraData(Function.Instruction.Load, data); + try function_block.writeAbbrev(FunctionBlock.Load{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .ty = extra.type, + .alignment = extra.info.alignment.toLlvm(), + .is_volatile = extra.info.access_kind == .@"volatile", + }); + }, + .@"load atomic" => { + const extra = func.extraData(Function.Instruction.Load, data); + try function_block.writeAbbrev(FunctionBlock.LoadAtomic{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .ty = extra.type, + .alignment = extra.info.alignment.toLlvm(), + .is_volatile = extra.info.access_kind == .@"volatile", + .success_ordering = extra.info.success_ordering, + .sync_scope = extra.info.sync_scope, + }); + }, + .store => { + const extra = func.extraData(Function.Instruction.Store, data); + try function_block.writeAbbrev(FunctionBlock.Store{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .val = adapter.getOffsetValueIndex(extra.val), + .alignment = extra.info.alignment.toLlvm(), + .is_volatile = extra.info.access_kind == .@"volatile", + }); + }, + .@"store atomic" => { + const extra = func.extraData(Function.Instruction.Store, data); + try function_block.writeAbbrev(FunctionBlock.StoreAtomic{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .val = adapter.getOffsetValueIndex(extra.val), + .alignment = extra.info.alignment.toLlvm(), + .is_volatile = extra.info.access_kind == .@"volatile", + .success_ordering = extra.info.success_ordering, + .sync_scope = extra.info.sync_scope, + }); + }, + .br => { + try function_block.writeAbbrev(FunctionBlock.BrUnconditional{ + .block = data, + }); + }, + .br_cond => { + const extra = func.extraData(Function.Instruction.BrCond, data); + try function_block.writeAbbrev(FunctionBlock.BrConditional{ + .then_block = @intFromEnum(extra.then), + .else_block = @intFromEnum(extra.@"else"), + .condition = adapter.getOffsetValueIndex(extra.cond), + }); + }, + .@"switch" => { + var extra = func.extraDataTrail(Function.Instruction.Switch, data); + + try record.ensureUnusedCapacity(self.gpa, 3 + extra.data.cases_len * 2); + + // Conditional type + record.appendAssumeCapacity(@intFromEnum(extra.data.val.typeOf(@enumFromInt(func_index), self))); + + // Conditional + record.appendAssumeCapacity(adapter.getOffsetValueIndex(extra.data.val)); + + // Default block + record.appendAssumeCapacity(@intFromEnum(extra.data.default)); + + const vals = extra.trail.next(extra.data.cases_len, Constant, &func); + const blocks = extra.trail.next(extra.data.cases_len, Function.Block.Index, &func); + for (vals, blocks) |val, block| { + record.appendAssumeCapacity(adapter.constant_adapter.getConstantIndex(val)); + record.appendAssumeCapacity(@intFromEnum(block)); + } + + try function_block.writeUnabbrev(12, record.items); + }, + .va_arg => { + const extra = func.extraData(Function.Instruction.VaArg, data); + try function_block.writeAbbrev(FunctionBlock.VaArg{ + .list_type = extra.list.typeOf(@enumFromInt(func_index), self), + .list = adapter.getOffsetValueIndex(extra.list), + .type = extra.type, + }); + }, + .phi, + .@"phi fast", + => |kind| { + var extra = func.extraDataTrail(Function.Instruction.Phi, data); + const vals = extra.trail.next(block_incoming_len, Value, &func); + const blocks = extra.trail.next(block_incoming_len, Function.Block.Index, &func); + + try record.ensureUnusedCapacity( + self.gpa, + 1 + block_incoming_len * 2 + @intFromBool(kind == .@"phi fast"), + ); + + record.appendAssumeCapacity(@intFromEnum(extra.data.type)); + + for (vals, blocks) |val, block| { + const offset_value = adapter.getOffsetValueSignedIndex(val); + const abs_value: u32 = @intCast(@abs(offset_value)); + const signed_vbr = if (offset_value > 0) abs_value << 1 else ((abs_value << 1) | 1); + record.appendAssumeCapacity(signed_vbr); + record.appendAssumeCapacity(@intFromEnum(block)); + } + + if (kind == .@"phi fast") record.appendAssumeCapacity(@as(u8, @bitCast(FastMath{}))); + + try function_block.writeUnabbrev(16, record.items); + }, + .ret => try function_block.writeAbbrev(FunctionBlock.Ret{ + .val = adapter.getOffsetValueIndex(@enumFromInt(data)), + }), + .@"ret void" => try function_block.writeAbbrev(FunctionBlock.RetVoid{}), + .atomicrmw => { + const extra = func.extraData(Function.Instruction.AtomicRmw, data); + try function_block.writeAbbrev(FunctionBlock.AtomicRmw{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .val = adapter.getOffsetValueIndex(extra.val), + .operation = extra.info.atomic_rmw_operation, + .is_volatile = extra.info.access_kind == .@"volatile", + .success_ordering = extra.info.success_ordering, + .sync_scope = extra.info.sync_scope, + .alignment = extra.info.alignment.toLlvm(), + }); + }, + .cmpxchg, + .@"cmpxchg weak", + => |kind| { + const extra = func.extraData(Function.Instruction.CmpXchg, data); + + try function_block.writeAbbrev(FunctionBlock.CmpXchg{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .cmp = adapter.getOffsetValueIndex(extra.cmp), + .new = adapter.getOffsetValueIndex(extra.new), + .is_volatile = extra.info.access_kind == .@"volatile", + .success_ordering = extra.info.success_ordering, + .sync_scope = extra.info.sync_scope, + .failure_ordering = extra.info.failure_ordering, + .is_weak = kind == .@"cmpxchg weak", + .alignment = extra.info.alignment.toLlvm(), + }); + }, + .fence => { + const info: MemoryAccessInfo = @bitCast(data); + try function_block.writeAbbrev(FunctionBlock.Fence{ + .ordering = info.success_ordering, + .sync_scope = info.sync_scope, + }); + }, + } + + if (!func.strip) { + if (func.debug_locations.get(adapter.instruction_index)) |debug_location| { + switch (debug_location) { + .no_location => has_location = false, + .location => |location| { + try function_block.writeAbbrev(FunctionBlock.DebugLoc{ + .line = location.line, + .column = location.column, + .scope = @enumFromInt(metadata_adapter.getMetadataIndex(location.scope)), + .inlined_at = @enumFromInt(metadata_adapter.getMetadataIndex(location.inlined_at)), + }); + has_location = true; + }, + } + } else if (has_location) { + try function_block.writeAbbrev(FunctionBlock.DebugLocAgain{}); + } + } + } + + // VALUE_SYMTAB + if (!func.strip) { + const ValueSymbolTable = ir.FunctionValueSymbolTable; + + var value_symtab_block = try function_block.enterSubBlock(ValueSymbolTable, false); + + for (func.blocks, 0..) |block, block_index| { + const name = block.instruction.name(&func); + + if (name == .none or name == .empty) continue; + + try value_symtab_block.writeAbbrev(ValueSymbolTable.BlockEntry{ + .value_id = @intCast(block_index), + .string = name.slice(self).?, + }); + } + + // TODO: Emit non block entries if the builder ever starts assigning names to non blocks + + try value_symtab_block.end(); + } + + // METADATA_ATTACHMENT_BLOCK + { + const MetadataAttachmentBlock = ir.MetadataAttachmentBlock; + var metadata_attach_block = try function_block.enterSubBlock(MetadataAttachmentBlock, false); + + dbg: { + if (func.strip) break :dbg; + const dbg = func.global.ptrConst(self).dbg; + if (dbg == .none) break :dbg; + try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentGlobalSingle{ + .kind = .dbg, + .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(dbg) - 1), + }); + } + + var instr_index: u32 = 0; + for (func.instructions.items(.tag), func.instructions.items(.data)) |instr_tag, data| switch (instr_tag) { + .arg, .block => {}, // not an actual instruction + else => { + instr_index += 1; + }, + .br_cond, .@"switch" => { + const weights = switch (instr_tag) { + .br_cond => func.extraData(Function.Instruction.BrCond, data).weights, + .@"switch" => func.extraData(Function.Instruction.Switch, data).weights, + else => unreachable, + }; + switch (weights) { + .none => {}, + .unpredictable => try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentInstructionSingle{ + .inst = instr_index, + .kind = .unpredictable, + .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(.empty_tuple) - 1), + }), + _ => try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentInstructionSingle{ + .inst = instr_index, + .kind = .prof, + .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(@enumFromInt(@intFromEnum(weights))) - 1), + }), + } + instr_index += 1; + }, + }; + + try metadata_attach_block.end(); + } + + try function_block.end(); + } + } + + try module_block.end(); + } + + // STRTAB_BLOCK + { + const Strtab = ir.Strtab; + var strtab_block = try bitcode.enterTopBlock(Strtab); + + try strtab_block.writeAbbrev(Strtab.Blob{ .blob = self.strtab_string_bytes.items }); + + try strtab_block.end(); + } + + return bitcode.toOwnedSlice(); +} + +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const bitcode_writer = @import("bitcode_writer.zig"); +const Builder = @This(); +const builtin = @import("builtin"); +const DW = std.dwarf; +const ir = @import("ir.zig"); +const log = std.log.scoped(.llvm); +const std = @import("../../std.zig"); diff --git a/lib/std/zig/llvm/bitcode_writer.zig b/lib/std/zig/llvm/bitcode_writer.zig new file mode 100644 index 0000000000000000000000000000000000000000..73d4f1aed9f83367e2ad7144368f805b89ec407d --- /dev/null +++ b/lib/std/zig/llvm/bitcode_writer.zig @@ -0,0 +1,433 @@ +const std = @import("../../std.zig"); + +pub const AbbrevOp = union(enum) { + literal: u32, // 0 + fixed: u16, // 1 + fixed_runtime: type, // 1 + vbr: u16, // 2 + char6: void, // 4 + blob: void, // 5 + array_fixed: u16, // 3, 1 + array_fixed_runtime: type, // 3, 1 + array_vbr: u16, // 3, 2 + array_char6: void, // 3, 4 +}; + +pub const Error = error{OutOfMemory}; + +pub fn BitcodeWriter(comptime types: []const type) type { + return struct { + const BcWriter = @This(); + + buffer: std.ArrayList(u32), + bit_buffer: u32 = 0, + bit_count: u5 = 0, + + widths: [types.len]u16, + + pub fn getTypeWidth(self: BcWriter, comptime Type: type) u16 { + return self.widths[comptime std.mem.indexOfScalar(type, types, Type).?]; + } + + pub fn init(allocator: std.mem.Allocator, widths: [types.len]u16) BcWriter { + return .{ + .buffer = std.ArrayList(u32).init(allocator), + .widths = widths, + }; + } + + pub fn deinit(self: BcWriter) void { + self.buffer.deinit(); + } + + pub fn toOwnedSlice(self: *BcWriter) Error![]const u32 { + std.debug.assert(self.bit_count == 0); + return self.buffer.toOwnedSlice(); + } + + pub fn length(self: BcWriter) usize { + std.debug.assert(self.bit_count == 0); + return self.buffer.items.len; + } + + pub fn writeBits(self: *BcWriter, value: anytype, bits: u16) Error!void { + if (bits == 0) return; + + var in_buffer = bufValue(value, 32); + var in_bits = bits; + + // Store input bits in buffer if they fit otherwise store as many as possible and flush + if (self.bit_count > 0) { + const bits_remaining = 31 - self.bit_count + 1; + const n: u5 = @intCast(@min(bits_remaining, in_bits)); + const v = @as(u32, @truncate(in_buffer)) << self.bit_count; + self.bit_buffer |= v; + in_buffer >>= n; + + self.bit_count +%= n; + in_bits -= n; + + if (self.bit_count != 0) return; + try self.buffer.append(self.bit_buffer); + self.bit_buffer = 0; + } + + // Write 32-bit chunks of input bits + while (in_bits >= 32) { + try self.buffer.append(@truncate(in_buffer)); + + in_buffer >>= 31; + in_buffer >>= 1; + in_bits -= 32; + } + + // Store remaining input bits in buffer + if (in_bits > 0) { + self.bit_count = @intCast(in_bits); + self.bit_buffer = @truncate(in_buffer); + } + } + + pub fn writeVBR(self: *BcWriter, value: anytype, comptime vbr_bits: usize) Error!void { + comptime { + std.debug.assert(vbr_bits > 1); + if (@bitSizeOf(@TypeOf(value)) > 64) @compileError("Unsupported VBR block type: " ++ @typeName(@TypeOf(value))); + } + + var in_buffer = bufValue(value, vbr_bits); + + const continue_bit = @as(@TypeOf(in_buffer), 1) << @intCast(vbr_bits - 1); + const mask = continue_bit - 1; + + // If input is larger than one VBR block can store + // then store vbr_bits - 1 bits and a continue bit + while (in_buffer > mask) { + try self.writeBits(in_buffer & mask | continue_bit, vbr_bits); + in_buffer >>= @intCast(vbr_bits - 1); + } + + // Store remaining bits + try self.writeBits(in_buffer, vbr_bits); + } + + pub fn bitsVBR(_: *const BcWriter, value: anytype, comptime vbr_bits: usize) u16 { + comptime { + std.debug.assert(vbr_bits > 1); + if (@bitSizeOf(@TypeOf(value)) > 64) @compileError("Unsupported VBR block type: " ++ @typeName(@TypeOf(value))); + } + + var bits: u16 = 0; + + var in_buffer = bufValue(value, vbr_bits); + + const continue_bit = @as(@TypeOf(in_buffer), 1) << @intCast(vbr_bits - 1); + const mask = continue_bit - 1; + + // If input is larger than one VBR block can store + // then store vbr_bits - 1 bits and a continue bit + while (in_buffer > mask) { + bits += @intCast(vbr_bits); + in_buffer >>= @intCast(vbr_bits - 1); + } + + // Store remaining bits + bits += @intCast(vbr_bits); + return bits; + } + + pub fn write6BitChar(self: *BcWriter, c: u8) Error!void { + try self.writeBits(charTo6Bit(c), 6); + } + + pub fn writeBlob(self: *BcWriter, blob: []const u8) Error!void { + const blob_word_size = std.mem.alignForward(usize, blob.len, 4); + try self.buffer.ensureUnusedCapacity(blob_word_size + 1); + self.alignTo32() catch unreachable; + + const slice = self.buffer.addManyAsSliceAssumeCapacity(blob_word_size / 4); + const slice_bytes = std.mem.sliceAsBytes(slice); + @memcpy(slice_bytes[0..blob.len], blob); + @memset(slice_bytes[blob.len..], 0); + } + + pub fn alignTo32(self: *BcWriter) Error!void { + if (self.bit_count == 0) return; + + try self.buffer.append(self.bit_buffer); + self.bit_buffer = 0; + self.bit_count = 0; + } + + pub fn enterTopBlock(self: *BcWriter, comptime SubBlock: type) Error!BlockWriter(SubBlock) { + return BlockWriter(SubBlock).init(self, 2, true); + } + + fn BlockWriter(comptime Block: type) type { + return struct { + const Self = @This(); + + // The minimum abbrev id length based on the number of abbrevs present in the block + pub const abbrev_len = std.math.log2_int_ceil( + u6, + 4 + (if (@hasDecl(Block, "abbrevs")) Block.abbrevs.len else 0), + ); + + start: usize, + bitcode: *BcWriter, + + pub fn init(bitcode: *BcWriter, comptime parent_abbrev_len: u6, comptime define_abbrevs: bool) Error!Self { + try bitcode.writeBits(1, parent_abbrev_len); + try bitcode.writeVBR(Block.id, 8); + try bitcode.writeVBR(abbrev_len, 4); + try bitcode.alignTo32(); + + // We store the index of the block size and store a dummy value as the number of words in the block + const start = bitcode.length(); + try bitcode.writeBits(0, 32); + + var self = Self{ + .start = start, + .bitcode = bitcode, + }; + + // Predefine all block abbrevs + if (define_abbrevs) { + inline for (Block.abbrevs) |Abbrev| { + try self.defineAbbrev(&Abbrev.ops); + } + } + + return self; + } + + pub fn enterSubBlock(self: Self, comptime SubBlock: type, comptime define_abbrevs: bool) Error!BlockWriter(SubBlock) { + return BlockWriter(SubBlock).init(self.bitcode, abbrev_len, define_abbrevs); + } + + pub fn end(self: *Self) Error!void { + try self.bitcode.writeBits(0, abbrev_len); + try self.bitcode.alignTo32(); + + // Set the number of words in the block at the start of the block + self.bitcode.buffer.items[self.start] = @truncate(self.bitcode.length() - self.start - 1); + } + + pub fn writeUnabbrev(self: *Self, code: u32, values: []const u64) Error!void { + try self.bitcode.writeBits(3, abbrev_len); + try self.bitcode.writeVBR(code, 6); + try self.bitcode.writeVBR(values.len, 6); + for (values) |val| { + try self.bitcode.writeVBR(val, 6); + } + } + + pub fn writeAbbrev(self: *Self, params: anytype) Error!void { + return self.writeAbbrevAdapted(params, struct { + pub fn get(_: @This(), param: anytype, comptime _: []const u8) @TypeOf(param) { + return param; + } + }{}); + } + + pub fn abbrevId(comptime Abbrev: type) u32 { + inline for (Block.abbrevs, 0..) |abbrev, i| { + if (Abbrev == abbrev) return i + 4; + } + + @compileError("Unknown abbrev: " ++ @typeName(Abbrev)); + } + + pub fn writeAbbrevAdapted( + self: *Self, + params: anytype, + adapter: anytype, + ) Error!void { + const Abbrev = @TypeOf(params); + + try self.bitcode.writeBits(comptime abbrevId(Abbrev), abbrev_len); + + const fields = std.meta.fields(Abbrev); + + // This abbreviation might only contain literals + if (fields.len == 0) return; + + comptime var field_index: usize = 0; + inline for (Abbrev.ops) |ty| { + const field_name = fields[field_index].name; + const param = @field(params, field_name); + + switch (ty) { + .literal => continue, + .fixed => |len| try self.bitcode.writeBits(adapter.get(param, field_name), len), + .fixed_runtime => |width_ty| try self.bitcode.writeBits( + adapter.get(param, field_name), + self.bitcode.getTypeWidth(width_ty), + ), + .vbr => |len| try self.bitcode.writeVBR(adapter.get(param, field_name), len), + .char6 => try self.bitcode.write6BitChar(adapter.get(param, field_name)), + .blob => { + try self.bitcode.writeVBR(param.len, 6); + try self.bitcode.writeBlob(param); + }, + .array_fixed => |len| { + try self.bitcode.writeVBR(param.len, 6); + for (param) |x| { + try self.bitcode.writeBits(adapter.get(x, field_name), len); + } + }, + .array_fixed_runtime => |width_ty| { + try self.bitcode.writeVBR(param.len, 6); + for (param) |x| { + try self.bitcode.writeBits( + adapter.get(x, field_name), + self.bitcode.getTypeWidth(width_ty), + ); + } + }, + .array_vbr => |len| { + try self.bitcode.writeVBR(param.len, 6); + for (param) |x| { + try self.bitcode.writeVBR(adapter.get(x, field_name), len); + } + }, + .array_char6 => { + try self.bitcode.writeVBR(param.len, 6); + for (param) |x| { + try self.bitcode.write6BitChar(adapter.get(x, field_name)); + } + }, + } + field_index += 1; + if (field_index == fields.len) break; + } + } + + pub fn defineAbbrev(self: *Self, comptime ops: []const AbbrevOp) Error!void { + const bitcode = self.bitcode; + try bitcode.writeBits(2, abbrev_len); + + // ops.len is not accurate because arrays are actually two ops + try bitcode.writeVBR(blk: { + var count: usize = 0; + inline for (ops) |op| { + count += switch (op) { + .literal, .fixed, .fixed_runtime, .vbr, .char6, .blob => 1, + .array_fixed, .array_fixed_runtime, .array_vbr, .array_char6 => 2, + }; + } + break :blk count; + }, 5); + + inline for (ops) |op| { + switch (op) { + .literal => |value| { + try bitcode.writeBits(1, 1); + try bitcode.writeVBR(value, 8); + }, + .fixed => |width| { + try bitcode.writeBits(0, 1); + try bitcode.writeBits(1, 3); + try bitcode.writeVBR(width, 5); + }, + .fixed_runtime => |width_ty| { + try bitcode.writeBits(0, 1); + try bitcode.writeBits(1, 3); + try bitcode.writeVBR(bitcode.getTypeWidth(width_ty), 5); + }, + .vbr => |width| { + try bitcode.writeBits(0, 1); + try bitcode.writeBits(2, 3); + try bitcode.writeVBR(width, 5); + }, + .char6 => { + try bitcode.writeBits(0, 1); + try bitcode.writeBits(4, 3); + }, + .blob => { + try bitcode.writeBits(0, 1); + try bitcode.writeBits(5, 3); + }, + .array_fixed => |width| { + // Array op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(3, 3); + + // Fixed or VBR op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(1, 3); + try bitcode.writeVBR(width, 5); + }, + .array_fixed_runtime => |width_ty| { + // Array op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(3, 3); + + // Fixed or VBR op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(1, 3); + try bitcode.writeVBR(bitcode.getTypeWidth(width_ty), 5); + }, + .array_vbr => |width| { + // Array op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(3, 3); + + // Fixed or VBR op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(2, 3); + try bitcode.writeVBR(width, 5); + }, + .array_char6 => { + // Array op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(3, 3); + + // Char6 op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(4, 3); + }, + } + } + } + }; + } + }; +} + +fn charTo6Bit(c: u8) u8 { + return switch (c) { + 'a'...'z' => c - 'a', + 'A'...'Z' => c - 'A' + 26, + '0'...'9' => c - '0' + 52, + '.' => 62, + '_' => 63, + else => @panic("Failed to encode byte as 6-bit char"), + }; +} + +fn BufType(comptime T: type, comptime min_len: usize) type { + return std.meta.Int(.unsigned, @max(min_len, @bitSizeOf(switch (@typeInfo(T)) { + .comptime_int => u32, + .int => |info| if (info.signedness == .unsigned) + T + else + @compileError("Unsupported type: " ++ @typeName(T)), + .@"enum" => |info| info.tag_type, + .bool => u1, + .@"struct" => |info| switch (info.layout) { + .auto, .@"extern" => @compileError("Unsupported type: " ++ @typeName(T)), + .@"packed" => std.meta.Int(.unsigned, @bitSizeOf(T)), + }, + else => @compileError("Unsupported type: " ++ @typeName(T)), + }))); +} + +fn bufValue(value: anytype, comptime min_len: usize) BufType(@TypeOf(value), min_len) { + return switch (@typeInfo(@TypeOf(value))) { + .comptime_int, .int => @intCast(value), + .@"enum" => @intFromEnum(value), + .bool => @intFromBool(value), + .@"struct" => @intCast(@as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(value))), @bitCast(value))), + else => unreachable, + }; +} diff --git a/lib/std/zig/llvm/ir.zig b/lib/std/zig/llvm/ir.zig new file mode 100644 index 0000000000000000000000000000000000000000..b83f0ddf685a4e0d70d8f827c89c86e0d3755b65 --- /dev/null +++ b/lib/std/zig/llvm/ir.zig @@ -0,0 +1,1862 @@ +const std = @import("../../std.zig"); +const Builder = @import("Builder.zig"); +const bitcode_writer = @import("bitcode_writer.zig"); + +const AbbrevOp = bitcode_writer.AbbrevOp; + +pub const MAGIC: u32 = 0xdec04342; + +const ValueAbbrev = AbbrevOp{ .vbr = 6 }; +const ValueArrayAbbrev = AbbrevOp{ .array_vbr = 6 }; + +const ConstantAbbrev = AbbrevOp{ .vbr = 6 }; +const ConstantArrayAbbrev = AbbrevOp{ .array_vbr = 6 }; + +const MetadataAbbrev = AbbrevOp{ .vbr = 16 }; +const MetadataArrayAbbrev = AbbrevOp{ .array_vbr = 16 }; + +const LineAbbrev = AbbrevOp{ .vbr = 8 }; +const ColumnAbbrev = AbbrevOp{ .vbr = 8 }; + +const BlockAbbrev = AbbrevOp{ .vbr = 6 }; +const BlockArrayAbbrev = AbbrevOp{ .array_vbr = 6 }; + +/// Unused tags are commented out so that they are omitted in the generated +/// bitcode, which scans over this enum using reflection. +pub const FixedMetadataKind = enum(u8) { + dbg = 0, + //tbaa = 1, + prof = 2, + //fpmath = 3, + //range = 4, + //@"tbaa.struct" = 5, + //@"invariant.load" = 6, + //@"alias.scope" = 7, + //@"noalias" = 8, + //nontemporal = 9, + //@"llvm.mem.parallel_loop_access" = 10, + //nonnull = 11, + //dereferenceable = 12, + //dereferenceable_or_null = 13, + //@"make.implicit" = 14, + unpredictable = 15, + //@"invariant.group" = 16, + //@"align" = 17, + //@"llvm.loop" = 18, + //type = 19, + //section_prefix = 20, + //absolute_symbol = 21, + //associated = 22, + //callees = 23, + //irr_loop = 24, + //@"llvm.access.group" = 25, + //callback = 26, + //@"llvm.preserve.access.index" = 27, + //vcall_visibility = 28, + //noundef = 29, + //annotation = 30, + //nosanitize = 31, + //func_sanitize = 32, + //exclude = 33, + //memprof = 34, + //callsite = 35, + //kcfi_type = 36, + //pcsections = 37, + //DIAssignID = 38, + //@"coro.outside.frame" = 39, +}; + +pub const MetadataCode = enum(u8) { + /// MDSTRING: [values] + STRING_OLD = 1, + /// VALUE: [type num, value num] + VALUE = 2, + /// NODE: [n x md num] + NODE = 3, + /// STRING: [values] + NAME = 4, + /// DISTINCT_NODE: [n x md num] + DISTINCT_NODE = 5, + /// [n x [id, name]] + KIND = 6, + /// [distinct, line, col, scope, inlined-at?] + LOCATION = 7, + /// OLD_NODE: [n x (type num, value num)] + OLD_NODE = 8, + /// OLD_FN_NODE: [n x (type num, value num)] + OLD_FN_NODE = 9, + /// NAMED_NODE: [n x mdnodes] + NAMED_NODE = 10, + /// [m x [value, [n x [id, mdnode]]] + ATTACHMENT = 11, + /// [distinct, tag, vers, header, n x md num] + GENERIC_DEBUG = 12, + /// [distinct, count, lo] + SUBRANGE = 13, + /// [isUnsigned|distinct, value, name] + ENUMERATOR = 14, + /// [distinct, tag, name, size, align, enc] + BASIC_TYPE = 15, + /// [distinct, filename, directory, checksumkind, checksum] + FILE = 16, + /// [distinct, ...] + DERIVED_TYPE = 17, + /// [distinct, ...] + COMPOSITE_TYPE = 18, + /// [distinct, flags, types, cc] + SUBROUTINE_TYPE = 19, + /// [distinct, ...] + COMPILE_UNIT = 20, + /// [distinct, ...] + SUBPROGRAM = 21, + /// [distinct, scope, file, line, column] + LEXICAL_BLOCK = 22, + ///[distinct, scope, file, discriminator] + LEXICAL_BLOCK_FILE = 23, + /// [distinct, scope, file, name, line, exportSymbols] + NAMESPACE = 24, + /// [distinct, scope, name, type, ...] + TEMPLATE_TYPE = 25, + /// [distinct, scope, name, type, value, ...] + TEMPLATE_VALUE = 26, + /// [distinct, ...] + GLOBAL_VAR = 27, + /// [distinct, ...] + LOCAL_VAR = 28, + /// [distinct, n x element] + EXPRESSION = 29, + /// [distinct, name, file, line, ...] + OBJC_PROPERTY = 30, + /// [distinct, tag, scope, entity, line, name] + IMPORTED_ENTITY = 31, + /// [distinct, scope, name, ...] + MODULE = 32, + /// [distinct, macinfo, line, name, value] + MACRO = 33, + /// [distinct, macinfo, line, file, ...] + MACRO_FILE = 34, + /// [count, offset] blob([lengths][chars]) + STRINGS = 35, + /// [valueid, n x [id, mdnode]] + GLOBAL_DECL_ATTACHMENT = 36, + /// [distinct, var, expr] + GLOBAL_VAR_EXPR = 37, + /// [offset] + INDEX_OFFSET = 38, + /// [bitpos] + INDEX = 39, + /// [distinct, scope, name, file, line] + LABEL = 40, + /// [distinct, name, size, align,...] + STRING_TYPE = 41, + /// [distinct, scope, name, variable,...] + COMMON_BLOCK = 44, + /// [distinct, count, lo, up, stride] + GENERIC_SUBRANGE = 45, + /// [n x [type num, value num]] + ARG_LIST = 46, + /// [distinct, ...] + ASSIGN_ID = 47, +}; + +pub const Identification = struct { + pub const id = 13; + + pub const abbrevs = [_]type{ + Version, + Epoch, + }; + + pub const Version = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .{ .array_fixed = 8 }, + }; + string: []const u8, + }; + + pub const Epoch = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + .{ .vbr = 6 }, + }; + epoch: u32, + }; +}; + +pub const Module = struct { + pub const id = 8; + + pub const abbrevs = [_]type{ + Version, + String, + Variable, + Function, + Alias, + }; + + pub const Version = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .{ .literal = 2 }, + }; + }; + + pub const String = struct { + pub const ops = [_]AbbrevOp{ + .{ .vbr = 4 }, + .{ .array_fixed = 8 }, + }; + code: u16, + string: []const u8, + }; + + pub const Variable = struct { + const AddrSpaceAndIsConst = packed struct { + is_const: bool, + one: u1 = 1, + addr_space: Builder.AddrSpace, + }; + + pub const ops = [_]AbbrevOp{ + .{ .literal = 7 }, // Code + .{ .vbr = 16 }, // strtab_offset + .{ .vbr = 16 }, // strtab_size + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(AddrSpaceAndIsConst) }, // isconst + ConstantAbbrev, // initid + .{ .fixed = @bitSizeOf(Builder.Linkage) }, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .vbr = 16 }, // section + .{ .fixed = @bitSizeOf(Builder.Visibility) }, + .{ .fixed = @bitSizeOf(Builder.ThreadLocal) }, // threadlocal + .{ .fixed = @bitSizeOf(Builder.UnnamedAddr) }, + .{ .fixed = @bitSizeOf(Builder.ExternallyInitialized) }, + .{ .fixed = @bitSizeOf(Builder.DllStorageClass) }, + .{ .literal = 0 }, // comdat + .{ .literal = 0 }, // attributes + .{ .fixed = @bitSizeOf(Builder.Preemption) }, + }; + strtab_offset: usize, + strtab_size: usize, + type_index: Builder.Type, + is_const: AddrSpaceAndIsConst, + initid: u32, + linkage: Builder.Linkage, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + section: usize, + visibility: Builder.Visibility, + thread_local: Builder.ThreadLocal, + unnamed_addr: Builder.UnnamedAddr, + externally_initialized: Builder.ExternallyInitialized, + dllstorageclass: Builder.DllStorageClass, + preemption: Builder.Preemption, + }; + + pub const Function = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 8 }, // Code + .{ .vbr = 16 }, // strtab_offset + .{ .vbr = 16 }, // strtab_size + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(Builder.CallConv) }, + .{ .fixed = 1 }, // isproto + .{ .fixed = @bitSizeOf(Builder.Linkage) }, + .{ .vbr = 16 }, // paramattr + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .vbr = 16 }, // section + .{ .fixed = @bitSizeOf(Builder.Visibility) }, + .{ .literal = 0 }, // gc + .{ .fixed = @bitSizeOf(Builder.UnnamedAddr) }, + .{ .literal = 0 }, // prologuedata + .{ .fixed = @bitSizeOf(Builder.DllStorageClass) }, + .{ .literal = 0 }, // comdat + .{ .literal = 0 }, // prefixdata + .{ .literal = 0 }, // personalityfn + .{ .fixed = @bitSizeOf(Builder.Preemption) }, + .{ .fixed = @bitSizeOf(Builder.AddrSpace) }, + }; + strtab_offset: usize, + strtab_size: usize, + type_index: Builder.Type, + call_conv: Builder.CallConv, + is_proto: bool, + linkage: Builder.Linkage, + paramattr: usize, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + section: usize, + visibility: Builder.Visibility, + unnamed_addr: Builder.UnnamedAddr, + dllstorageclass: Builder.DllStorageClass, + preemption: Builder.Preemption, + addr_space: Builder.AddrSpace, + }; + + pub const Alias = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 14 }, // Code + .{ .vbr = 16 }, // strtab_offset + .{ .vbr = 16 }, // strtab_size + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(Builder.AddrSpace) }, + ConstantAbbrev, // aliasee val + .{ .fixed = @bitSizeOf(Builder.Linkage) }, + .{ .fixed = @bitSizeOf(Builder.Visibility) }, + .{ .fixed = @bitSizeOf(Builder.DllStorageClass) }, + .{ .fixed = @bitSizeOf(Builder.ThreadLocal) }, + .{ .fixed = @bitSizeOf(Builder.UnnamedAddr) }, + .{ .fixed = @bitSizeOf(Builder.Preemption) }, + }; + strtab_offset: usize, + strtab_size: usize, + type_index: Builder.Type, + addr_space: Builder.AddrSpace, + aliasee: u32, + linkage: Builder.Linkage, + visibility: Builder.Visibility, + dllstorageclass: Builder.DllStorageClass, + thread_local: Builder.ThreadLocal, + unnamed_addr: Builder.UnnamedAddr, + preemption: Builder.Preemption, + }; +}; + +pub const BlockInfo = struct { + pub const id = 0; + + pub const set_block_id = 1; + + pub const abbrevs = [_]type{}; +}; + +pub const Type = struct { + pub const id = 17; + + pub const abbrevs = [_]type{ + NumEntry, + Simple, + Opaque, + Integer, + StructAnon, + StructNamed, + StructName, + Array, + Vector, + Pointer, + Target, + Function, + }; + + pub const NumEntry = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .{ .fixed = 32 }, + }; + num: u32, + }; + + pub const Simple = struct { + pub const ops = [_]AbbrevOp{ + .{ .vbr = 4 }, + }; + code: u5, + }; + + pub const Opaque = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .literal = 0 }, + }; + }; + + pub const Integer = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 7 }, + .{ .fixed = 28 }, + }; + width: u28, + }; + + pub const StructAnon = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 18 }, + .{ .fixed = 1 }, + .{ .array_fixed_runtime = Builder.Type }, + }; + is_packed: bool, + types: []const Builder.Type, + }; + + pub const StructNamed = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 20 }, + .{ .fixed = 1 }, + .{ .array_fixed_runtime = Builder.Type }, + }; + is_packed: bool, + types: []const Builder.Type, + }; + + pub const StructName = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 19 }, + .{ .array_fixed = 8 }, + }; + string: []const u8, + }; + + pub const Array = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 11 }, + .{ .vbr = 16 }, + .{ .fixed_runtime = Builder.Type }, + }; + len: u64, + child: Builder.Type, + }; + + pub const Vector = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 12 }, + .{ .vbr = 16 }, + .{ .fixed_runtime = Builder.Type }, + }; + len: u64, + child: Builder.Type, + }; + + pub const Pointer = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 25 }, + .{ .vbr = 4 }, + }; + addr_space: Builder.AddrSpace, + }; + + pub const Target = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 26 }, + .{ .vbr = 4 }, + .{ .array_fixed_runtime = Builder.Type }, + .{ .array_fixed = 32 }, + }; + num_types: u32, + types: []const Builder.Type, + ints: []const u32, + }; + + pub const Function = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 21 }, + .{ .fixed = 1 }, + .{ .fixed_runtime = Builder.Type }, + .{ .array_fixed_runtime = Builder.Type }, + }; + is_vararg: bool, + return_type: Builder.Type, + param_types: []const Builder.Type, + }; +}; + +pub const Paramattr = struct { + pub const id = 9; + + pub const abbrevs = [_]type{ + Entry, + }; + + pub const Entry = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + .{ .array_vbr = 8 }, + }; + group_indices: []const u64, + }; +}; + +pub const ParamattrGroup = struct { + pub const id = 10; + + pub const abbrevs = [_]type{}; +}; + +pub const Constants = struct { + pub const id = 11; + + pub const abbrevs = [_]type{ + SetType, + Null, + Undef, + Poison, + Integer, + Half, + Float, + Double, + Fp80, + Fp128, + Aggregate, + String, + CString, + Cast, + Binary, + Cmp, + ExtractElement, + InsertElement, + ShuffleVector, + ShuffleVectorEx, + BlockAddress, + DsoLocalEquivalentOrNoCfi, + }; + + pub const SetType = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .{ .fixed_runtime = Builder.Type }, + }; + type_id: Builder.Type, + }; + + pub const Null = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + }; + }; + + pub const Undef = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 3 }, + }; + }; + + pub const Poison = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 26 }, + }; + }; + + pub const Integer = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 4 }, + .{ .vbr = 16 }, + }; + value: u64, + }; + + pub const Half = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .fixed = 16 }, + }; + value: u16, + }; + + pub const Float = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .fixed = 32 }, + }; + value: u32, + }; + + pub const Double = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .vbr = 6 }, + }; + value: u64, + }; + + pub const Fp80 = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .vbr = 6 }, + .{ .vbr = 6 }, + }; + hi: u64, + lo: u16, + }; + + pub const Fp128 = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .vbr = 6 }, + .{ .vbr = 6 }, + }; + lo: u64, + hi: u64, + }; + + pub const Aggregate = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 7 }, + .{ .array_fixed = 32 }, + }; + values: []const Builder.Constant, + }; + + pub const String = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 8 }, + .{ .array_fixed = 8 }, + }; + string: []const u8, + }; + + pub const CString = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 9 }, + .{ .array_fixed = 8 }, + }; + string: []const u8, + }; + + pub const Cast = struct { + const CastOpcode = Builder.CastOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 11 }, + .{ .fixed = @bitSizeOf(CastOpcode) }, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + }; + + opcode: CastOpcode, + type_index: Builder.Type, + val: Builder.Constant, + }; + + pub const Binary = struct { + const BinaryOpcode = Builder.BinaryOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 10 }, + .{ .fixed = @bitSizeOf(BinaryOpcode) }, + ConstantAbbrev, + ConstantAbbrev, + }; + + opcode: BinaryOpcode, + lhs: Builder.Constant, + rhs: Builder.Constant, + }; + + pub const Cmp = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 17 }, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + ConstantAbbrev, + .{ .vbr = 6 }, + }; + + ty: Builder.Type, + lhs: Builder.Constant, + rhs: Builder.Constant, + pred: u32, + }; + + pub const ExtractElement = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 14 }, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + }; + + val_type: Builder.Type, + val: Builder.Constant, + index_type: Builder.Type, + index: Builder.Constant, + }; + + pub const InsertElement = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 15 }, + ConstantAbbrev, + ConstantAbbrev, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + }; + + val: Builder.Constant, + elem: Builder.Constant, + index_type: Builder.Type, + index: Builder.Constant, + }; + + pub const ShuffleVector = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 16 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + }; + + lhs: Builder.Constant, + rhs: Builder.Constant, + mask: Builder.Constant, + }; + + pub const ShuffleVectorEx = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 19 }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + }; + + ty: Builder.Type, + lhs: Builder.Constant, + rhs: Builder.Constant, + mask: Builder.Constant, + }; + + pub const BlockAddress = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 21 }, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + BlockAbbrev, + }; + type_id: Builder.Type, + function: u32, + block: u32, + }; + + pub const DsoLocalEquivalentOrNoCfi = struct { + pub const ops = [_]AbbrevOp{ + .{ .fixed = 5 }, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + }; + code: u5, + type_id: Builder.Type, + function: u32, + }; +}; + +pub const MetadataKindBlock = struct { + pub const id = 22; + + pub const abbrevs = [_]type{ + Kind, + }; + + pub const Kind = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .vbr = 4 }, + .{ .array_fixed = 8 }, + }; + id: u32, + name: []const u8, + }; +}; + +pub const MetadataAttachmentBlock = struct { + pub const id = 16; + + pub const abbrevs = [_]type{ + AttachmentGlobalSingle, + AttachmentInstructionSingle, + }; + + pub const AttachmentGlobalSingle = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.ATTACHMENT) }, + .{ .fixed = 1 }, + MetadataAbbrev, + }; + kind: FixedMetadataKind, + metadata: Builder.Metadata, + }; + + pub const AttachmentInstructionSingle = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.ATTACHMENT) }, + ValueAbbrev, + .{ .fixed = 5 }, + MetadataAbbrev, + }; + inst: u32, + kind: FixedMetadataKind, + metadata: Builder.Metadata, + }; +}; + +pub const MetadataBlock = struct { + pub const id = 15; + + pub const abbrevs = [_]type{ + Strings, + File, + CompileUnit, + Subprogram, + LexicalBlock, + Location, + BasicType, + CompositeType, + DerivedType, + SubroutineType, + Enumerator, + Subrange, + Expression, + Node, + LocalVar, + Parameter, + GlobalVar, + GlobalVarExpression, + Constant, + Name, + NamedNode, + GlobalDeclAttachment, + }; + + pub const Strings = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.STRINGS) }, + .{ .vbr = 6 }, + .{ .vbr = 6 }, + .blob, + }; + num_strings: u32, + strings_offset: u32, + blob: []const u8, + }; + + pub const File = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.FILE) }, + .{ .literal = 0 }, // is distinct + MetadataAbbrev, // filename + MetadataAbbrev, // directory + .{ .literal = 0 }, // checksum + .{ .literal = 0 }, // checksum + }; + + filename: Builder.MetadataString, + directory: Builder.MetadataString, + }; + + pub const CompileUnit = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.COMPILE_UNIT) }, + .{ .literal = 1 }, // is distinct + .{ .literal = std.dwarf.LANG.C99 }, // source language + MetadataAbbrev, // file + MetadataAbbrev, // producer + .{ .fixed = 1 }, // isOptimized + .{ .literal = 0 }, // raw flags + .{ .literal = 0 }, // runtime version + .{ .literal = 0 }, // split debug file name + .{ .literal = 1 }, // emission kind + MetadataAbbrev, // enums + .{ .literal = 0 }, // retained types + .{ .literal = 0 }, // subprograms + MetadataAbbrev, // globals + .{ .literal = 0 }, // imported entities + .{ .literal = 0 }, // DWO ID + .{ .literal = 0 }, // macros + .{ .literal = 0 }, // split debug inlining + .{ .literal = 0 }, // debug info profiling + .{ .literal = 0 }, // name table kind + .{ .literal = 0 }, // ranges base address + .{ .literal = 0 }, // raw sysroot + .{ .literal = 0 }, // raw SDK + }; + + file: Builder.Metadata, + producer: Builder.MetadataString, + is_optimized: bool, + enums: Builder.Metadata, + globals: Builder.Metadata, + }; + + pub const Subprogram = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.SUBPROGRAM) }, + .{ .literal = 0b111 }, // is distinct | has sp flags | has flags + MetadataAbbrev, // scope + MetadataAbbrev, // name + MetadataAbbrev, // linkage name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // type + LineAbbrev, // scope line + .{ .literal = 0 }, // containing type + .{ .fixed = 32 }, // sp flags + .{ .literal = 0 }, // virtual index + .{ .fixed = 32 }, // flags + MetadataAbbrev, // compile unit + .{ .literal = 0 }, // template params + .{ .literal = 0 }, // declaration + .{ .literal = 0 }, // retained nodes + .{ .literal = 0 }, // this adjustment + .{ .literal = 0 }, // thrown types + .{ .literal = 0 }, // annotations + .{ .literal = 0 }, // target function name + }; + + scope: Builder.Metadata, + name: Builder.MetadataString, + linkage_name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + ty: Builder.Metadata, + scope_line: u32, + sp_flags: Builder.Metadata.Subprogram.DISPFlags, + flags: Builder.Metadata.DIFlags, + compile_unit: Builder.Metadata, + }; + + pub const LexicalBlock = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.LEXICAL_BLOCK) }, + .{ .literal = 0 }, // is distinct + MetadataAbbrev, // scope + MetadataAbbrev, // file + LineAbbrev, // line + ColumnAbbrev, // column + }; + + scope: Builder.Metadata, + file: Builder.Metadata, + line: u32, + column: u32, + }; + + pub const Location = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.LOCATION) }, + .{ .literal = 0 }, // is distinct + LineAbbrev, // line + ColumnAbbrev, // column + MetadataAbbrev, // scope + MetadataAbbrev, // inlined at + .{ .literal = 0 }, // is implicit code + }; + + line: u32, + column: u32, + scope: u32, + inlined_at: Builder.Metadata, + }; + + pub const BasicType = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.BASIC_TYPE) }, + .{ .literal = 0 }, // is distinct + .{ .literal = std.dwarf.TAG.base_type }, // tag + MetadataAbbrev, // name + .{ .vbr = 6 }, // size in bits + .{ .literal = 0 }, // align in bits + .{ .vbr = 8 }, // encoding + .{ .literal = 0 }, // flags + }; + + name: Builder.MetadataString, + size_in_bits: u64, + encoding: u32, + }; + + pub const CompositeType = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.COMPOSITE_TYPE) }, + .{ .literal = 0 | 0x2 }, // is distinct | is not used in old type ref + .{ .fixed = 32 }, // tag + MetadataAbbrev, // name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // scope + MetadataAbbrev, // underlying type + .{ .vbr = 6 }, // size in bits + .{ .vbr = 6 }, // align in bits + .{ .literal = 0 }, // offset in bits + .{ .fixed = 32 }, // flags + MetadataAbbrev, // elements + .{ .literal = 0 }, // runtime lang + .{ .literal = 0 }, // vtable holder + .{ .literal = 0 }, // template params + .{ .literal = 0 }, // raw id + .{ .literal = 0 }, // discriminator + .{ .literal = 0 }, // data location + .{ .literal = 0 }, // associated + .{ .literal = 0 }, // allocated + .{ .literal = 0 }, // rank + .{ .literal = 0 }, // annotations + }; + + tag: u32, + name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + scope: Builder.Metadata, + underlying_type: Builder.Metadata, + size_in_bits: u64, + align_in_bits: u64, + flags: Builder.Metadata.DIFlags, + elements: Builder.Metadata, + }; + + pub const DerivedType = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.DERIVED_TYPE) }, + .{ .literal = 0 }, // is distinct + .{ .fixed = 32 }, // tag + MetadataAbbrev, // name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // scope + MetadataAbbrev, // underlying type + .{ .vbr = 6 }, // size in bits + .{ .vbr = 6 }, // align in bits + .{ .vbr = 6 }, // offset in bits + .{ .literal = 0 }, // flags + .{ .literal = 0 }, // extra data + }; + + tag: u32, + name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + scope: Builder.Metadata, + underlying_type: Builder.Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, + }; + + pub const SubroutineType = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.SUBROUTINE_TYPE) }, + .{ .literal = 0 | 0x2 }, // is distinct | has no old type refs + .{ .literal = 0 }, // flags + MetadataAbbrev, // types + .{ .literal = 0 }, // cc + }; + + types: Builder.Metadata, + }; + + pub const Enumerator = struct { + pub const id: MetadataCode = .ENUMERATOR; + + pub const Flags = packed struct(u3) { + distinct: bool = false, + unsigned: bool, + bigint: bool = true, + }; + + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(Enumerator.id) }, + .{ .fixed = @bitSizeOf(Flags) }, // flags + .{ .vbr = 6 }, // bit width + MetadataAbbrev, // name + .{ .vbr = 16 }, // integer value + }; + + flags: Flags, + bit_width: u32, + name: Builder.MetadataString, + value: u64, + }; + + pub const Subrange = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.SUBRANGE) }, + .{ .literal = 0b10 }, // is distinct | version + MetadataAbbrev, // count + MetadataAbbrev, // lower bound + .{ .literal = 0 }, // upper bound + .{ .literal = 0 }, // stride + }; + + count: Builder.Metadata, + lower_bound: Builder.Metadata, + }; + + pub const Expression = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.EXPRESSION) }, + .{ .literal = 0 | (3 << 1) }, // is distinct | version + MetadataArrayAbbrev, // elements + }; + + elements: []const u32, + }; + + pub const Node = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.NODE) }, + MetadataArrayAbbrev, // elements + }; + + elements: []const Builder.Metadata, + }; + + pub const LocalVar = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.LOCAL_VAR) }, + .{ .literal = 0b10 }, // is distinct | has alignment + MetadataAbbrev, // scope + MetadataAbbrev, // name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // type + .{ .literal = 0 }, // arg + .{ .literal = 0 }, // flags + .{ .literal = 0 }, // align bits + .{ .literal = 0 }, // annotations + }; + + scope: Builder.Metadata, + name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + ty: Builder.Metadata, + }; + + pub const Parameter = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.LOCAL_VAR) }, + .{ .literal = 0b10 }, // is distinct | has alignment + MetadataAbbrev, // scope + MetadataAbbrev, // name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // type + .{ .vbr = 4 }, // arg + .{ .literal = 0 }, // flags + .{ .literal = 0 }, // align bits + .{ .literal = 0 }, // annotations + }; + + scope: Builder.Metadata, + name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + ty: Builder.Metadata, + arg: u32, + }; + + pub const GlobalVar = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.GLOBAL_VAR) }, + .{ .literal = 0b101 }, // is distinct | version + MetadataAbbrev, // scope + MetadataAbbrev, // name + MetadataAbbrev, // linkage name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // type + .{ .fixed = 1 }, // local + .{ .literal = 1 }, // defined + .{ .literal = 0 }, // static data members declaration + .{ .literal = 0 }, // template params + .{ .literal = 0 }, // align in bits + .{ .literal = 0 }, // annotations + }; + + scope: Builder.Metadata, + name: Builder.MetadataString, + linkage_name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + ty: Builder.Metadata, + local: bool, + }; + + pub const GlobalVarExpression = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.GLOBAL_VAR_EXPR) }, + .{ .literal = 0 }, // is distinct + MetadataAbbrev, // variable + MetadataAbbrev, // expression + }; + + variable: Builder.Metadata, + expression: Builder.Metadata, + }; + + pub const Constant = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.VALUE) }, + MetadataAbbrev, // type + MetadataAbbrev, // value + }; + + ty: Builder.Type, + constant: Builder.Constant, + }; + + pub const Name = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.NAME) }, + .{ .array_fixed = 8 }, // name + }; + + name: []const u8, + }; + + pub const NamedNode = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.NAMED_NODE) }, + MetadataArrayAbbrev, // elements + }; + + elements: []const Builder.Metadata, + }; + + pub const GlobalDeclAttachment = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.GLOBAL_DECL_ATTACHMENT) }, + ValueAbbrev, // value id + .{ .fixed = 1 }, // kind + MetadataAbbrev, // elements + }; + + value: Builder.Constant, + kind: FixedMetadataKind, + metadata: Builder.Metadata, + }; +}; + +pub const OperandBundleTags = struct { + pub const id = 21; + + pub const abbrevs = [_]type{OperandBundleTag}; + + pub const OperandBundleTag = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .array_char6, + }; + tag: []const u8, + }; +}; + +pub const FunctionMetadataBlock = struct { + pub const id = 15; + + pub const abbrevs = [_]type{ + Value, + }; + + pub const Value = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + .{ .fixed = 32 }, // variable + .{ .fixed = 32 }, // expression + }; + + ty: Builder.Type, + value: Builder.Value, + }; +}; + +pub const FunctionBlock = struct { + pub const id = 12; + + pub const abbrevs = [_]type{ + DeclareBlocks, + Call, + CallFast, + FNeg, + FNegFast, + Binary, + BinaryNoWrap, + BinaryExact, + BinaryFast, + Cmp, + CmpFast, + Select, + SelectFast, + Cast, + Alloca, + GetElementPtr, + ExtractValue, + InsertValue, + ExtractElement, + InsertElement, + ShuffleVector, + RetVoid, + Ret, + Unreachable, + Load, + LoadAtomic, + Store, + StoreAtomic, + BrUnconditional, + BrConditional, + VaArg, + AtomicRmw, + CmpXchg, + Fence, + DebugLoc, + DebugLocAgain, + ColdOperandBundle, + IndirectBr, + }; + + pub const DeclareBlocks = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .{ .vbr = 8 }, + }; + num_blocks: usize, + }; + + pub const Call = struct { + pub const CallType = packed struct(u17) { + tail: bool = false, + call_conv: Builder.CallConv, + reserved: u3 = 0, + must_tail: bool = false, + // We always use the explicit type version as that is what LLVM does + explicit_type: bool = true, + no_tail: bool = false, + }; + pub const ops = [_]AbbrevOp{ + .{ .literal = 34 }, + .{ .fixed_runtime = Builder.FunctionAttributes }, + .{ .fixed = @bitSizeOf(CallType) }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, // Callee + ValueArrayAbbrev, // Args + }; + + attributes: Builder.FunctionAttributes, + call_type: CallType, + type_id: Builder.Type, + callee: Builder.Value, + args: []const Builder.Value, + }; + + pub const CallFast = struct { + const CallType = packed struct(u18) { + tail: bool = false, + call_conv: Builder.CallConv, + reserved: u3 = 0, + must_tail: bool = false, + // We always use the explicit type version as that is what LLVM does + explicit_type: bool = true, + no_tail: bool = false, + fast: bool = true, + }; + + pub const ops = [_]AbbrevOp{ + .{ .literal = 34 }, + .{ .fixed_runtime = Builder.FunctionAttributes }, + .{ .fixed = @bitSizeOf(CallType) }, + .{ .fixed = @bitSizeOf(Builder.FastMath) }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, // Callee + ValueArrayAbbrev, // Args + }; + + attributes: Builder.FunctionAttributes, + call_type: CallType, + fast_math: Builder.FastMath, + type_id: Builder.Type, + callee: Builder.Value, + args: []const Builder.Value, + }; + + pub const FNeg = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 56 }, + ValueAbbrev, + .{ .literal = 0 }, + }; + + val: u32, + }; + + pub const FNegFast = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 56 }, + ValueAbbrev, + .{ .literal = 0 }, + .{ .fixed = @bitSizeOf(Builder.FastMath) }, + }; + + val: u32, + fast_math: Builder.FastMath, + }; + + pub const Binary = struct { + const BinaryOpcode = Builder.BinaryOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(BinaryOpcode) }, + }; + + lhs: u32, + rhs: u32, + opcode: BinaryOpcode, + }; + + pub const BinaryNoWrap = struct { + const BinaryOpcode = Builder.BinaryOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(BinaryOpcode) }, + .{ .fixed = 2 }, + }; + + lhs: u32, + rhs: u32, + opcode: BinaryOpcode, + flags: packed struct(u2) { + no_unsigned_wrap: bool, + no_signed_wrap: bool, + }, + }; + + pub const BinaryExact = struct { + const BinaryOpcode = Builder.BinaryOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(BinaryOpcode) }, + .{ .literal = 1 }, + }; + + lhs: u32, + rhs: u32, + opcode: BinaryOpcode, + }; + + pub const BinaryFast = struct { + const BinaryOpcode = Builder.BinaryOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(BinaryOpcode) }, + .{ .fixed = @bitSizeOf(Builder.FastMath) }, + }; + + lhs: u32, + rhs: u32, + opcode: BinaryOpcode, + fast_math: Builder.FastMath, + }; + + pub const Cmp = struct { + const CmpPredicate = Builder.CmpPredicate; + pub const ops = [_]AbbrevOp{ + .{ .literal = 28 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(CmpPredicate) }, + }; + + lhs: u32, + rhs: u32, + pred: CmpPredicate, + }; + + pub const CmpFast = struct { + const CmpPredicate = Builder.CmpPredicate; + pub const ops = [_]AbbrevOp{ + .{ .literal = 28 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(CmpPredicate) }, + .{ .fixed = @bitSizeOf(Builder.FastMath) }, + }; + + lhs: u32, + rhs: u32, + pred: CmpPredicate, + fast_math: Builder.FastMath, + }; + + pub const Select = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 29 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + }; + + lhs: u32, + rhs: u32, + cond: u32, + }; + + pub const SelectFast = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 29 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(Builder.FastMath) }, + }; + + lhs: u32, + rhs: u32, + cond: u32, + fast_math: Builder.FastMath, + }; + + pub const Cast = struct { + const CastOpcode = Builder.CastOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 3 }, + ValueAbbrev, + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(CastOpcode) }, + }; + + val: u32, + type_index: Builder.Type, + opcode: CastOpcode, + }; + + pub const Alloca = struct { + pub const Flags = packed struct(u11) { + align_lower: u5, + inalloca: bool, + explicit_type: bool, + swift_error: bool, + align_upper: u3, + }; + pub const ops = [_]AbbrevOp{ + .{ .literal = 19 }, + .{ .fixed_runtime = Builder.Type }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, + .{ .fixed = @bitSizeOf(Flags) }, + }; + + inst_type: Builder.Type, + len_type: Builder.Type, + len_value: u32, + flags: Flags, + }; + + pub const RetVoid = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 10 }, + }; + }; + + pub const Ret = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 10 }, + ValueAbbrev, + }; + val: u32, + }; + + pub const GetElementPtr = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 43 }, + .{ .fixed = 1 }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, + ValueArrayAbbrev, + }; + + is_inbounds: bool, + type_index: Builder.Type, + base: Builder.Value, + indices: []const Builder.Value, + }; + + pub const ExtractValue = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 26 }, + ValueAbbrev, + ValueArrayAbbrev, + }; + + val: u32, + indices: []const u32, + }; + + pub const InsertValue = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 27 }, + ValueAbbrev, + ValueAbbrev, + ValueArrayAbbrev, + }; + + val: u32, + elem: u32, + indices: []const u32, + }; + + pub const ExtractElement = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + ValueAbbrev, + ValueAbbrev, + }; + + val: u32, + index: u32, + }; + + pub const InsertElement = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 7 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + }; + + val: u32, + elem: u32, + index: u32, + }; + + pub const ShuffleVector = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 8 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + }; + + lhs: u32, + rhs: u32, + mask: u32, + }; + + pub const Unreachable = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 15 }, + }; + }; + + pub const Load = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 20 }, + ValueAbbrev, + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .fixed = 1 }, + }; + ptr: u32, + ty: Builder.Type, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + is_volatile: bool, + }; + + pub const LoadAtomic = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 41 }, + ValueAbbrev, + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .fixed = 1 }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = @bitSizeOf(Builder.SyncScope) }, + }; + ptr: u32, + ty: Builder.Type, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + is_volatile: bool, + success_ordering: Builder.AtomicOrdering, + sync_scope: Builder.SyncScope, + }; + + pub const Store = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 44 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .fixed = 1 }, + }; + ptr: u32, + val: u32, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + is_volatile: bool, + }; + + pub const StoreAtomic = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 45 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .fixed = 1 }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = @bitSizeOf(Builder.SyncScope) }, + }; + ptr: u32, + val: u32, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + is_volatile: bool, + success_ordering: Builder.AtomicOrdering, + sync_scope: Builder.SyncScope, + }; + + pub const BrUnconditional = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 11 }, + BlockAbbrev, + }; + block: u32, + }; + + pub const BrConditional = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 11 }, + BlockAbbrev, + BlockAbbrev, + BlockAbbrev, + }; + then_block: u32, + else_block: u32, + condition: u32, + }; + + pub const VaArg = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 23 }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, + .{ .fixed_runtime = Builder.Type }, + }; + list_type: Builder.Type, + list: u32, + type: Builder.Type, + }; + + pub const AtomicRmw = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 59 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(Builder.Function.Instruction.AtomicRmw.Operation) }, + .{ .fixed = 1 }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = @bitSizeOf(Builder.SyncScope) }, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + }; + ptr: u32, + val: u32, + operation: Builder.Function.Instruction.AtomicRmw.Operation, + is_volatile: bool, + success_ordering: Builder.AtomicOrdering, + sync_scope: Builder.SyncScope, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + }; + + pub const CmpXchg = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 46 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = 1 }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = @bitSizeOf(Builder.SyncScope) }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = 1 }, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + }; + ptr: u32, + cmp: u32, + new: u32, + is_volatile: bool, + success_ordering: Builder.AtomicOrdering, + sync_scope: Builder.SyncScope, + failure_ordering: Builder.AtomicOrdering, + is_weak: bool, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + }; + + pub const Fence = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 36 }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = @bitSizeOf(Builder.SyncScope) }, + }; + ordering: Builder.AtomicOrdering, + sync_scope: Builder.SyncScope, + }; + + pub const DebugLoc = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 35 }, + LineAbbrev, + ColumnAbbrev, + MetadataAbbrev, + MetadataAbbrev, + .{ .literal = 0 }, + }; + line: u32, + column: u32, + scope: Builder.Metadata, + inlined_at: Builder.Metadata, + }; + + pub const DebugLocAgain = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 33 }, + }; + }; + + pub const ColdOperandBundle = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 55 }, + .{ .literal = 0 }, + }; + }; + + pub const IndirectBr = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 31 }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, + BlockArrayAbbrev, + }; + ty: Builder.Type, + addr: Builder.Value, + targets: []const Builder.Function.Block.Index, + }; +}; + +pub const FunctionValueSymbolTable = struct { + pub const id = 14; + + pub const abbrevs = [_]type{ + BlockEntry, + }; + + pub const BlockEntry = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + ValueAbbrev, + .{ .array_fixed = 8 }, + }; + value_id: u32, + string: []const u8, + }; +}; + +pub const Strtab = struct { + pub const id = 23; + + pub const abbrevs = [_]type{Blob}; + + pub const Blob = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .blob, + }; + blob: []const u8, + }; +}; diff --git a/src/Compilation.zig b/src/Compilation.zig index 7331bf209710112414fbef4383ef24df1eadd737..2e0f998a2fc534e77c1cd19b38f254474b2186e8 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -551,7 +551,6 @@ pub const CObject = struct { } pub fn parse(gpa: Allocator, path: []const u8) !*Bundle { - const BitcodeReader = @import("codegen/llvm/BitcodeReader.zig"); const BlockId = enum(u32) { Meta = 8, Diag, @@ -588,7 +587,7 @@ pub const CObject = struct { defer file.close(); var br = std.io.bufferedReader(file.reader()); const reader = br.reader(); - var bc = BitcodeReader.init(gpa, .{ .reader = reader.any() }); + var bc = std.zig.llvm.BitcodeReader.init(gpa, .{ .reader = reader.any() }); defer bc.deinit(); var file_names: std.AutoArrayHashMapUnmanaged(u32, []const u8) = .empty; diff --git a/src/InternPool.zig b/src/InternPool.zig index 7f02b548b461f714cadabd27b5b6d04b78edfef5..ab264d0e8641f0a17460585bcfec7a1fc748feba 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -6261,7 +6261,7 @@ pub const Alignment = enum(u6) { return n + 1; } - const LlvmBuilderAlignment = @import("codegen/llvm/Builder.zig").Alignment; + const LlvmBuilderAlignment = std.zig.llvm.Builder.Alignment; pub fn toLlvm(this: @This()) LlvmBuilderAlignment { return @enumFromInt(@intFromEnum(this)); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2f58dc78970bba653f2641b6229502beb8c5ec10..e82d75311ed4490ef4ea2751c974138083809a4f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6,7 +6,7 @@ const log = std.log.scoped(.codegen); const math = std.math; const DW = std.dwarf; -const Builder = @import("llvm/Builder.zig"); +const Builder = std.zig.llvm.Builder; const llvm = if (build_options.have_llvm) @import("llvm/bindings.zig") else @@ -1216,7 +1216,10 @@ pub const Object = struct { } } - const bitcode = try o.builder.toBitcode(o.gpa); + const bitcode = try o.builder.toBitcode(o.gpa, .{ + .name = "zig", + .version = build_options.semver, + }); defer o.gpa.free(bitcode); o.builder.clearAndFree(); diff --git a/src/codegen/llvm/BitcodeReader.zig b/src/codegen/llvm/BitcodeReader.zig deleted file mode 100644 index 8a9af1562921baee3c7d03294c3cfb394dda7c40..0000000000000000000000000000000000000000 --- a/src/codegen/llvm/BitcodeReader.zig +++ /dev/null @@ -1,515 +0,0 @@ -allocator: std.mem.Allocator, -record_arena: std.heap.ArenaAllocator.State, -reader: std.io.AnyReader, -keep_names: bool, -bit_buffer: u32, -bit_offset: u5, -stack: std.ArrayListUnmanaged(State), -block_info: std.AutoHashMapUnmanaged(u32, Block.Info), - -pub const Item = union(enum) { - start_block: Block, - record: Record, - end_block: Block, -}; - -pub const Block = struct { - name: []const u8, - id: u32, - len: u32, - - const block_info: u32 = 0; - const first_reserved: u32 = 1; - const last_standard: u32 = 7; - - const Info = struct { - block_name: []const u8, - record_names: std.AutoHashMapUnmanaged(u32, []const u8), - abbrevs: Abbrev.Store, - - const default: Info = .{ - .block_name = &.{}, - .record_names = .{}, - .abbrevs = .{ .abbrevs = .{} }, - }; - - const set_bid_id: u32 = 1; - const block_name_id: u32 = 2; - const set_record_name_id: u32 = 3; - - fn deinit(info: *Info, allocator: std.mem.Allocator) void { - allocator.free(info.block_name); - var record_names_it = info.record_names.valueIterator(); - while (record_names_it.next()) |record_name| allocator.free(record_name.*); - info.record_names.deinit(allocator); - info.abbrevs.deinit(allocator); - info.* = undefined; - } - }; -}; - -pub const Record = struct { - name: []const u8, - id: u32, - operands: []const u64, - blob: []const u8, - - fn toOwnedAbbrev(record: Record, allocator: std.mem.Allocator) !Abbrev { - var operands = std.ArrayList(Abbrev.Operand).init(allocator); - defer operands.deinit(); - - assert(record.id == Abbrev.Builtin.define_abbrev.toRecordId()); - var i: usize = 0; - while (i < record.operands.len) switch (record.operands[i]) { - Abbrev.Operand.literal_id => { - try operands.append(.{ .literal = record.operands[i + 1] }); - i += 2; - }, - @intFromEnum(Abbrev.Operand.Encoding.fixed) => { - try operands.append(.{ .encoding = .{ .fixed = @intCast(record.operands[i + 1]) } }); - i += 2; - }, - @intFromEnum(Abbrev.Operand.Encoding.vbr) => { - try operands.append(.{ .encoding = .{ .vbr = @intCast(record.operands[i + 1]) } }); - i += 2; - }, - @intFromEnum(Abbrev.Operand.Encoding.array) => { - try operands.append(.{ .encoding = .{ .array = 6 } }); - i += 1; - }, - @intFromEnum(Abbrev.Operand.Encoding.char6) => { - try operands.append(.{ .encoding = .char6 }); - i += 1; - }, - @intFromEnum(Abbrev.Operand.Encoding.blob) => { - try operands.append(.{ .encoding = .{ .blob = 6 } }); - i += 1; - }, - else => unreachable, - }; - - return .{ .operands = try operands.toOwnedSlice() }; - } -}; - -pub const InitOptions = struct { - reader: std.io.AnyReader, - keep_names: bool = false, -}; -pub fn init(allocator: std.mem.Allocator, options: InitOptions) BitcodeReader { - return .{ - .allocator = allocator, - .record_arena = .{}, - .reader = options.reader, - .keep_names = options.keep_names, - .bit_buffer = 0, - .bit_offset = 0, - .stack = .{}, - .block_info = .{}, - }; -} - -pub fn deinit(bc: *BitcodeReader) void { - var block_info_it = bc.block_info.valueIterator(); - while (block_info_it.next()) |block_info| block_info.deinit(bc.allocator); - bc.block_info.deinit(bc.allocator); - for (bc.stack.items) |*state| state.deinit(bc.allocator); - bc.stack.deinit(bc.allocator); - bc.record_arena.promote(bc.allocator).deinit(); - bc.* = undefined; -} - -pub fn checkMagic(bc: *BitcodeReader, magic: *const [4]u8) !void { - var buffer: [4]u8 = undefined; - try bc.readBytes(&buffer); - if (!std.mem.eql(u8, &buffer, magic)) return error.InvalidMagic; - - try bc.startBlock(null, 2); - try bc.block_info.put(bc.allocator, Block.block_info, Block.Info.default); -} - -pub fn next(bc: *BitcodeReader) !?Item { - while (true) { - const record = (try bc.nextRecord()) orelse - return if (bc.stack.items.len > 1) error.EndOfStream else null; - switch (record.id) { - else => return .{ .record = record }, - Abbrev.Builtin.end_block.toRecordId() => { - const block_id = bc.stack.items[bc.stack.items.len - 1].block_id.?; - try bc.endBlock(); - return .{ .end_block = .{ - .name = if (bc.block_info.get(block_id)) |block_info| - block_info.block_name - else - &.{}, - .id = block_id, - .len = 0, - } }; - }, - Abbrev.Builtin.enter_subblock.toRecordId() => { - const block_id: u32 = @intCast(record.operands[0]); - switch (block_id) { - Block.block_info => try bc.parseBlockInfoBlock(), - Block.first_reserved...Block.last_standard => return error.UnsupportedBlockId, - else => { - try bc.startBlock(block_id, @intCast(record.operands[1])); - return .{ .start_block = .{ - .name = if (bc.block_info.get(block_id)) |block_info| - block_info.block_name - else - &.{}, - .id = block_id, - .len = @intCast(record.operands[2]), - } }; - }, - } - }, - Abbrev.Builtin.define_abbrev.toRecordId() => try bc.stack.items[bc.stack.items.len - 1] - .abbrevs.addOwnedAbbrev(bc.allocator, try record.toOwnedAbbrev(bc.allocator)), - } - } -} - -pub fn skipBlock(bc: *BitcodeReader, block: Block) !void { - assert(bc.bit_offset == 0); - try bc.reader.skipBytes(@as(u34, block.len) * 4, .{}); - try bc.endBlock(); -} - -fn nextRecord(bc: *BitcodeReader) !?Record { - const state = &bc.stack.items[bc.stack.items.len - 1]; - const abbrev_id = bc.readFixed(u32, state.abbrev_id_width) catch |err| switch (err) { - error.EndOfStream => return null, - else => |e| return e, - }; - if (abbrev_id >= state.abbrevs.abbrevs.items.len) return error.InvalidAbbrevId; - const abbrev = state.abbrevs.abbrevs.items[abbrev_id]; - - var record_arena = bc.record_arena.promote(bc.allocator); - defer bc.record_arena = record_arena.state; - _ = record_arena.reset(.retain_capacity); - - var operands = try std.ArrayList(u64).initCapacity(record_arena.allocator(), abbrev.operands.len); - var blob = std.ArrayList(u8).init(record_arena.allocator()); - for (abbrev.operands, 0..) |abbrev_operand, abbrev_operand_i| switch (abbrev_operand) { - .literal => |value| operands.appendAssumeCapacity(value), - .encoding => |abbrev_encoding| switch (abbrev_encoding) { - .fixed => |width| operands.appendAssumeCapacity(try bc.readFixed(u64, width)), - .vbr => |width| operands.appendAssumeCapacity(try bc.readVbr(u64, width)), - .array => |len_width| { - assert(abbrev_operand_i + 2 == abbrev.operands.len); - const len: usize = @intCast(try bc.readVbr(u32, len_width)); - try operands.ensureUnusedCapacity(len); - for (0..len) |_| switch (abbrev.operands[abbrev.operands.len - 1]) { - .literal => |elem_value| operands.appendAssumeCapacity(elem_value), - .encoding => |elem_encoding| switch (elem_encoding) { - .fixed => |elem_width| operands.appendAssumeCapacity(try bc.readFixed(u64, elem_width)), - .vbr => |elem_width| operands.appendAssumeCapacity(try bc.readVbr(u64, elem_width)), - .array, .blob => return error.InvalidArrayElement, - .char6 => operands.appendAssumeCapacity(try bc.readChar6()), - }, - .align_32_bits, .block_len => return error.UnsupportedArrayElement, - .abbrev_op => switch (try bc.readFixed(u1, 1)) { - 1 => try operands.appendSlice(&.{ - Abbrev.Operand.literal_id, - try bc.readVbr(u64, 8), - }), - 0 => { - const encoding: Abbrev.Operand.Encoding = - @enumFromInt(try bc.readFixed(u3, 3)); - try operands.append(@intFromEnum(encoding)); - switch (encoding) { - .fixed, .vbr => try operands.append(try bc.readVbr(u7, 5)), - .array, .char6, .blob => {}, - _ => return error.UnsuportedAbbrevEncoding, - } - }, - }, - }; - break; - }, - .char6 => operands.appendAssumeCapacity(try bc.readChar6()), - .blob => |len_width| { - assert(abbrev_operand_i + 1 == abbrev.operands.len); - const len = std.math.cast(usize, try bc.readVbr(u32, len_width)) orelse - return error.Overflow; - bc.align32Bits(); - try bc.readBytes(try blob.addManyAsSlice(len)); - bc.align32Bits(); - }, - }, - .align_32_bits => bc.align32Bits(), - .block_len => operands.appendAssumeCapacity(try bc.read32Bits()), - .abbrev_op => unreachable, - }; - return .{ - .name = name: { - if (operands.items.len < 1) break :name &.{}; - const record_id = std.math.cast(u32, operands.items[0]) orelse break :name &.{}; - if (state.block_id) |block_id| { - if (bc.block_info.get(block_id)) |block_info| { - break :name block_info.record_names.get(record_id) orelse break :name &.{}; - } - } - break :name &.{}; - }, - .id = std.math.cast(u32, operands.items[0]) orelse return error.InvalidRecordId, - .operands = operands.items[1..], - .blob = blob.items, - }; -} - -fn startBlock(bc: *BitcodeReader, block_id: ?u32, new_abbrev_len: u6) !void { - const abbrevs = if (block_id) |id| - if (bc.block_info.get(id)) |block_info| block_info.abbrevs.abbrevs.items else &.{} - else - &.{}; - - const state = try bc.stack.addOne(bc.allocator); - state.* = .{ - .block_id = block_id, - .abbrev_id_width = new_abbrev_len, - .abbrevs = .{ .abbrevs = .{} }, - }; - try state.abbrevs.abbrevs.ensureTotalCapacity( - bc.allocator, - @typeInfo(Abbrev.Builtin).@"enum".fields.len + abbrevs.len, - ); - - assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.end_block)); - try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ - .operands = &.{ - .{ .literal = Abbrev.Builtin.end_block.toRecordId() }, - .align_32_bits, - }, - }); - assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.enter_subblock)); - try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ - .operands = &.{ - .{ .literal = Abbrev.Builtin.enter_subblock.toRecordId() }, - .{ .encoding = .{ .vbr = 8 } }, // blockid - .{ .encoding = .{ .vbr = 4 } }, // newabbrevlen - .align_32_bits, - .block_len, - }, - }); - assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.define_abbrev)); - try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ - .operands = &.{ - .{ .literal = Abbrev.Builtin.define_abbrev.toRecordId() }, - .{ .encoding = .{ .array = 5 } }, // numabbrevops - .abbrev_op, - }, - }); - assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.unabbrev_record)); - try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ - .operands = &.{ - .{ .encoding = .{ .vbr = 6 } }, // code - .{ .encoding = .{ .array = 6 } }, // numops - .{ .encoding = .{ .vbr = 6 } }, // ops - }, - }); - assert(state.abbrevs.abbrevs.items.len == @typeInfo(Abbrev.Builtin).@"enum".fields.len); - for (abbrevs) |abbrev| try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, abbrev); -} - -fn endBlock(bc: *BitcodeReader) !void { - if (bc.stack.items.len == 0) return error.InvalidEndBlock; - bc.stack.items[bc.stack.items.len - 1].deinit(bc.allocator); - bc.stack.items.len -= 1; -} - -fn parseBlockInfoBlock(bc: *BitcodeReader) !void { - var block_id: ?u32 = null; - while (true) { - const record = (try bc.nextRecord()) orelse return error.EndOfStream; - switch (record.id) { - Abbrev.Builtin.end_block.toRecordId() => break, - Abbrev.Builtin.define_abbrev.toRecordId() => { - const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse - return error.UnspecifiedBlockId); - if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; - try gop.value_ptr.abbrevs.addOwnedAbbrev( - bc.allocator, - try record.toOwnedAbbrev(bc.allocator), - ); - }, - Block.Info.set_bid_id => block_id = std.math.cast(u32, record.operands[0]) orelse - return error.Overflow, - Block.Info.block_name_id => if (bc.keep_names) { - const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse - return error.UnspecifiedBlockId); - if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; - const name = try bc.allocator.alloc(u8, record.operands.len); - errdefer bc.allocator.free(name); - for (name, record.operands) |*byte, operand| - byte.* = std.math.cast(u8, operand) orelse return error.InvalidName; - gop.value_ptr.block_name = name; - }, - Block.Info.set_record_name_id => if (bc.keep_names) { - const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse - return error.UnspecifiedBlockId); - if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; - const name = try bc.allocator.alloc(u8, record.operands.len - 1); - errdefer bc.allocator.free(name); - for (name, record.operands[1..]) |*byte, operand| - byte.* = std.math.cast(u8, operand) orelse return error.InvalidName; - try gop.value_ptr.record_names.put( - bc.allocator, - std.math.cast(u32, record.operands[0]) orelse return error.Overflow, - name, - ); - }, - else => return error.UnsupportedBlockInfoRecord, - } - } -} - -fn align32Bits(bc: *BitcodeReader) void { - bc.bit_offset = 0; -} - -fn read32Bits(bc: *BitcodeReader) !u32 { - assert(bc.bit_offset == 0); - return bc.reader.readInt(u32, .little); -} - -fn readBytes(bc: *BitcodeReader, bytes: []u8) !void { - assert(bc.bit_offset == 0); - try bc.reader.readNoEof(bytes); - - const trailing_bytes = bytes.len % 4; - if (trailing_bytes > 0) { - var bit_buffer = [1]u8{0} ** 4; - try bc.reader.readNoEof(bit_buffer[trailing_bytes..]); - bc.bit_buffer = std.mem.readInt(u32, &bit_buffer, .little); - bc.bit_offset = @intCast(trailing_bytes * 8); - } -} - -fn readFixed(bc: *BitcodeReader, comptime T: type, bits: u7) !T { - var result: T = 0; - var shift: std.math.Log2IntCeil(T) = 0; - var remaining = bits; - while (remaining > 0) { - if (bc.bit_offset == 0) bc.bit_buffer = try bc.read32Bits(); - const chunk_len = @min(@as(u6, 32) - bc.bit_offset, remaining); - const chunk_mask = @as(u32, std.math.maxInt(u32)) >> @intCast(32 - chunk_len); - result |= @as(T, @intCast(bc.bit_buffer >> bc.bit_offset & chunk_mask)) << @intCast(shift); - shift += @intCast(chunk_len); - remaining -= chunk_len; - bc.bit_offset = @truncate(bc.bit_offset + chunk_len); - } - return result; -} - -fn readVbr(bc: *BitcodeReader, comptime T: type, bits: u7) !T { - const chunk_bits: u6 = @intCast(bits - 1); - const chunk_msb = @as(u64, 1) << chunk_bits; - - var result: u64 = 0; - var shift: u6 = 0; - while (true) { - const chunk = try bc.readFixed(u64, bits); - result |= (chunk & (chunk_msb - 1)) << shift; - if (chunk & chunk_msb == 0) break; - shift += chunk_bits; - } - return @intCast(result); -} - -fn readChar6(bc: *BitcodeReader) !u8 { - return switch (try bc.readFixed(u6, 6)) { - 0...25 => |c| @as(u8, c - 0) + 'a', - 26...51 => |c| @as(u8, c - 26) + 'A', - 52...61 => |c| @as(u8, c - 52) + '0', - 62 => '.', - 63 => '_', - }; -} - -const State = struct { - block_id: ?u32, - abbrev_id_width: u6, - abbrevs: Abbrev.Store, - - fn deinit(state: *State, allocator: std.mem.Allocator) void { - state.abbrevs.deinit(allocator); - state.* = undefined; - } -}; - -const Abbrev = struct { - operands: []const Operand, - - const Builtin = enum(u2) { - end_block, - enter_subblock, - define_abbrev, - unabbrev_record, - - const first_record_id: u32 = std.math.maxInt(u32) - @typeInfo(Builtin).@"enum".fields.len + 1; - fn toRecordId(builtin: Builtin) u32 { - return first_record_id + @intFromEnum(builtin); - } - }; - - const Operand = union(enum) { - literal: u64, - encoding: union(Encoding) { - fixed: u7, - vbr: u6, - array: u3, - char6, - blob: u3, - }, - align_32_bits, - block_len, - abbrev_op, - - const literal_id = std.math.maxInt(u64); - const Encoding = enum(u3) { - fixed = 1, - vbr = 2, - array = 3, - char6 = 4, - blob = 5, - _, - }; - }; - - const Store = struct { - abbrevs: std.ArrayListUnmanaged(Abbrev), - - fn deinit(store: *Store, allocator: std.mem.Allocator) void { - for (store.abbrevs.items) |abbrev| allocator.free(abbrev.operands); - store.abbrevs.deinit(allocator); - store.* = undefined; - } - - fn addAbbrev(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void { - try store.ensureUnusedCapacity(allocator, 1); - store.addAbbrevAssumeCapacity(abbrev); - } - - fn addAbbrevAssumeCapacity(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void { - store.abbrevs.appendAssumeCapacity(.{ - .operands = try allocator.dupe(Abbrev.Operand, abbrev.operands), - }); - } - - fn addOwnedAbbrev(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void { - try store.abbrevs.ensureUnusedCapacity(allocator, 1); - store.addOwnedAbbrevAssumeCapacity(abbrev); - } - - fn addOwnedAbbrevAssumeCapacity(store: *Store, abbrev: Abbrev) void { - store.abbrevs.appendAssumeCapacity(abbrev); - } - }; -}; - -const assert = std.debug.assert; -const std = @import("std"); - -const BitcodeReader = @This(); diff --git a/src/codegen/llvm/Builder.zig b/src/codegen/llvm/Builder.zig deleted file mode 100644 index 74618545106be1f0c2dffceca1ceb431da781ee9..0000000000000000000000000000000000000000 --- a/src/codegen/llvm/Builder.zig +++ /dev/null @@ -1,15225 +0,0 @@ -gpa: Allocator, -strip: bool, - -source_filename: String, -data_layout: String, -target_triple: String, -module_asm: std.ArrayListUnmanaged(u8), - -string_map: std.AutoArrayHashMapUnmanaged(void, void), -string_indices: std.ArrayListUnmanaged(u32), -string_bytes: std.ArrayListUnmanaged(u8), - -types: std.AutoArrayHashMapUnmanaged(String, Type), -next_unnamed_type: String, -next_unique_type_id: std.AutoHashMapUnmanaged(String, u32), -type_map: std.AutoArrayHashMapUnmanaged(void, void), -type_items: std.ArrayListUnmanaged(Type.Item), -type_extra: std.ArrayListUnmanaged(u32), - -attributes: std.AutoArrayHashMapUnmanaged(Attribute.Storage, void), -attributes_map: std.AutoArrayHashMapUnmanaged(void, void), -attributes_indices: std.ArrayListUnmanaged(u32), -attributes_extra: std.ArrayListUnmanaged(u32), - -function_attributes_set: std.AutoArrayHashMapUnmanaged(FunctionAttributes, void), - -globals: std.AutoArrayHashMapUnmanaged(StrtabString, Global), -next_unnamed_global: StrtabString, -next_replaced_global: StrtabString, -next_unique_global_id: std.AutoHashMapUnmanaged(StrtabString, u32), -aliases: std.ArrayListUnmanaged(Alias), -variables: std.ArrayListUnmanaged(Variable), -functions: std.ArrayListUnmanaged(Function), - -strtab_string_map: std.AutoArrayHashMapUnmanaged(void, void), -strtab_string_indices: std.ArrayListUnmanaged(u32), -strtab_string_bytes: std.ArrayListUnmanaged(u8), - -constant_map: std.AutoArrayHashMapUnmanaged(void, void), -constant_items: std.MultiArrayList(Constant.Item), -constant_extra: std.ArrayListUnmanaged(u32), -constant_limbs: std.ArrayListUnmanaged(std.math.big.Limb), - -metadata_map: std.AutoArrayHashMapUnmanaged(void, void), -metadata_items: std.MultiArrayList(Metadata.Item), -metadata_extra: std.ArrayListUnmanaged(u32), -metadata_limbs: std.ArrayListUnmanaged(std.math.big.Limb), -metadata_forward_references: std.ArrayListUnmanaged(Metadata), -metadata_named: std.AutoArrayHashMapUnmanaged(MetadataString, struct { - len: u32, - index: Metadata.Item.ExtraIndex, -}), - -metadata_string_map: std.AutoArrayHashMapUnmanaged(void, void), -metadata_string_indices: std.ArrayListUnmanaged(u32), -metadata_string_bytes: std.ArrayListUnmanaged(u8), - -pub const expected_args_len = 16; -pub const expected_attrs_len = 16; -pub const expected_fields_len = 32; -pub const expected_gep_indices_len = 8; -pub const expected_cases_len = 8; -pub const expected_incoming_len = 8; - -pub const Options = struct { - allocator: Allocator, - strip: bool = true, - name: []const u8 = &.{}, - target: std.Target = builtin.target, - triple: []const u8 = &.{}, -}; - -pub const String = enum(u32) { - none = std.math.maxInt(u31), - empty, - _, - - pub fn isAnon(self: String) bool { - assert(self != .none); - return self.toIndex() == null; - } - - pub fn slice(self: String, builder: *const Builder) ?[]const u8 { - const index = self.toIndex() orelse return null; - const start = builder.string_indices.items[index]; - const end = builder.string_indices.items[index + 1]; - return builder.string_bytes.items[start..end]; - } - - const FormatData = struct { - string: String, - builder: *const Builder, - }; - fn format( - data: FormatData, - comptime fmt_str: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_| - @compileError("invalid format string: '" ++ fmt_str ++ "'"); - assert(data.string != .none); - const string_slice = data.string.slice(data.builder) orelse - return writer.print("{d}", .{@intFromEnum(data.string)}); - if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_| - return writer.writeAll(string_slice); - try printEscapedString( - string_slice, - if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_| - .always_quote - else - .quote_unless_valid_identifier, - writer, - ); - } - pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .string = self, .builder = builder } }; - } - - fn fromIndex(index: ?usize) String { - return @enumFromInt(@as(u32, @intCast((index orelse return .none) + - @intFromEnum(String.empty)))); - } - - fn toIndex(self: String) ?usize { - return std.math.sub(u32, @intFromEnum(self), @intFromEnum(String.empty)) catch null; - } - - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: Adapter, key: []const u8) u32 { - return @truncate(std.hash.Wyhash.hash(0, key)); - } - pub fn eql(ctx: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool { - return std.mem.eql(u8, lhs_key, String.fromIndex(rhs_index).slice(ctx.builder).?); - } - }; -}; - -pub const BinaryOpcode = enum(u4) { - add = 0, - sub = 1, - mul = 2, - udiv = 3, - sdiv = 4, - urem = 5, - srem = 6, - shl = 7, - lshr = 8, - ashr = 9, - @"and" = 10, - @"or" = 11, - xor = 12, -}; - -pub const CastOpcode = enum(u4) { - trunc = 0, - zext = 1, - sext = 2, - fptoui = 3, - fptosi = 4, - uitofp = 5, - sitofp = 6, - fptrunc = 7, - fpext = 8, - ptrtoint = 9, - inttoptr = 10, - bitcast = 11, - addrspacecast = 12, -}; - -pub const CmpPredicate = enum(u6) { - fcmp_false = 0, - fcmp_oeq = 1, - fcmp_ogt = 2, - fcmp_oge = 3, - fcmp_olt = 4, - fcmp_ole = 5, - fcmp_one = 6, - fcmp_ord = 7, - fcmp_uno = 8, - fcmp_ueq = 9, - fcmp_ugt = 10, - fcmp_uge = 11, - fcmp_ult = 12, - fcmp_ule = 13, - fcmp_une = 14, - fcmp_true = 15, - icmp_eq = 32, - icmp_ne = 33, - icmp_ugt = 34, - icmp_uge = 35, - icmp_ult = 36, - icmp_ule = 37, - icmp_sgt = 38, - icmp_sge = 39, - icmp_slt = 40, - icmp_sle = 41, -}; - -pub const Type = enum(u32) { - void, - half, - bfloat, - float, - double, - fp128, - x86_fp80, - ppc_fp128, - x86_amx, - x86_mmx, - label, - token, - metadata, - - i1, - i8, - i16, - i29, - i32, - i64, - i80, - i128, - ptr, - @"ptr addrspace(4)", - - none = std.math.maxInt(u32), - _, - - pub const ptr_amdgpu_constant = - @field(Type, std.fmt.comptimePrint("ptr{ }", .{AddrSpace.amdgpu.constant})); - - pub const Tag = enum(u4) { - simple, - function, - vararg_function, - integer, - pointer, - target, - vector, - scalable_vector, - small_array, - array, - structure, - packed_structure, - named_structure, - }; - - pub const Simple = enum(u5) { - void = 2, - half = 10, - bfloat = 23, - float = 3, - double = 4, - fp128 = 14, - x86_fp80 = 13, - ppc_fp128 = 15, - x86_amx = 24, - x86_mmx = 17, - label = 5, - token = 22, - metadata = 16, - }; - - pub const Function = struct { - ret: Type, - params_len: u32, - //params: [params_len]Value, - - pub const Kind = enum { normal, vararg }; - }; - - pub const Target = extern struct { - name: String, - types_len: u32, - ints_len: u32, - //types: [types_len]Type, - //ints: [ints_len]u32, - }; - - pub const Vector = extern struct { - len: u32, - child: Type, - - fn length(self: Vector) u32 { - return self.len; - } - - pub const Kind = enum { normal, scalable }; - }; - - pub const Array = extern struct { - len_lo: u32, - len_hi: u32, - child: Type, - - fn length(self: Array) u64 { - return @as(u64, self.len_hi) << 32 | self.len_lo; - } - }; - - pub const Structure = struct { - fields_len: u32, - //fields: [fields_len]Type, - - pub const Kind = enum { normal, @"packed" }; - }; - - pub const NamedStructure = struct { - id: String, - body: Type, - }; - - pub const Item = packed struct(u32) { - tag: Tag, - data: ExtraIndex, - - pub const ExtraIndex = u28; - }; - - pub fn tag(self: Type, builder: *const Builder) Tag { - return builder.type_items.items[@intFromEnum(self)].tag; - } - - pub fn unnamedTag(self: Type, builder: *const Builder) Tag { - const item = builder.type_items.items[@intFromEnum(self)]; - return switch (item.tag) { - .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body - .unnamedTag(builder), - else => item.tag, - }; - } - - pub fn scalarTag(self: Type, builder: *const Builder) Tag { - const item = builder.type_items.items[@intFromEnum(self)]; - return switch (item.tag) { - .vector, .scalable_vector => builder.typeExtraData(Type.Vector, item.data) - .child.tag(builder), - else => item.tag, - }; - } - - pub fn isFloatingPoint(self: Type) bool { - return switch (self) { - .half, .bfloat, .float, .double, .fp128, .x86_fp80, .ppc_fp128 => true, - else => false, - }; - } - - pub fn isInteger(self: Type, builder: *const Builder) bool { - return switch (self) { - .i1, .i8, .i16, .i29, .i32, .i64, .i80, .i128 => true, - else => switch (self.tag(builder)) { - .integer => true, - else => false, - }, - }; - } - - pub fn isPointer(self: Type, builder: *const Builder) bool { - return switch (self) { - .ptr => true, - else => switch (self.tag(builder)) { - .pointer => true, - else => false, - }, - }; - } - - pub fn pointerAddrSpace(self: Type, builder: *const Builder) AddrSpace { - switch (self) { - .ptr => return .default, - else => { - const item = builder.type_items.items[@intFromEnum(self)]; - assert(item.tag == .pointer); - return @enumFromInt(item.data); - }, - } - } - - pub fn isFunction(self: Type, builder: *const Builder) bool { - return switch (self.tag(builder)) { - .function, .vararg_function => true, - else => false, - }; - } - - pub fn functionKind(self: Type, builder: *const Builder) Type.Function.Kind { - return switch (self.tag(builder)) { - .function => .normal, - .vararg_function => .vararg, - else => unreachable, - }; - } - - pub fn functionParameters(self: Type, builder: *const Builder) []const Type { - const item = builder.type_items.items[@intFromEnum(self)]; - switch (item.tag) { - .function, - .vararg_function, - => { - var extra = builder.typeExtraDataTrail(Type.Function, item.data); - return extra.trail.next(extra.data.params_len, Type, builder); - }, - else => unreachable, - } - } - - pub fn functionReturn(self: Type, builder: *const Builder) Type { - const item = builder.type_items.items[@intFromEnum(self)]; - switch (item.tag) { - .function, - .vararg_function, - => return builder.typeExtraData(Type.Function, item.data).ret, - else => unreachable, - } - } - - pub fn isVector(self: Type, builder: *const Builder) bool { - return switch (self.tag(builder)) { - .vector, .scalable_vector => true, - else => false, - }; - } - - pub fn vectorKind(self: Type, builder: *const Builder) Type.Vector.Kind { - return switch (self.tag(builder)) { - .vector => .normal, - .scalable_vector => .scalable, - else => unreachable, - }; - } - - pub fn isStruct(self: Type, builder: *const Builder) bool { - return switch (self.tag(builder)) { - .structure, .packed_structure, .named_structure => true, - else => false, - }; - } - - pub fn structKind(self: Type, builder: *const Builder) Type.Structure.Kind { - return switch (self.unnamedTag(builder)) { - .structure => .normal, - .packed_structure => .@"packed", - else => unreachable, - }; - } - - pub fn isAggregate(self: Type, builder: *const Builder) bool { - return switch (self.tag(builder)) { - .small_array, .array, .structure, .packed_structure, .named_structure => true, - else => false, - }; - } - - pub fn scalarBits(self: Type, builder: *const Builder) u24 { - return switch (self) { - .void, .label, .token, .metadata, .none, .x86_amx => unreachable, - .i1 => 1, - .i8 => 8, - .half, .bfloat, .i16 => 16, - .i29 => 29, - .float, .i32 => 32, - .double, .i64, .x86_mmx => 64, - .x86_fp80, .i80 => 80, - .fp128, .ppc_fp128, .i128 => 128, - .ptr, .@"ptr addrspace(4)" => @panic("TODO: query data layout"), - _ => { - const item = builder.type_items.items[@intFromEnum(self)]; - return switch (item.tag) { - .simple, - .function, - .vararg_function, - => unreachable, - .integer => @intCast(item.data), - .pointer => @panic("TODO: query data layout"), - .target => unreachable, - .vector, - .scalable_vector, - => builder.typeExtraData(Type.Vector, item.data).child.scalarBits(builder), - .small_array, - .array, - .structure, - .packed_structure, - .named_structure, - => unreachable, - }; - }, - }; - } - - pub fn childType(self: Type, builder: *const Builder) Type { - const item = builder.type_items.items[@intFromEnum(self)]; - return switch (item.tag) { - .vector, - .scalable_vector, - .small_array, - => builder.typeExtraData(Type.Vector, item.data).child, - .array => builder.typeExtraData(Type.Array, item.data).child, - .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body, - else => unreachable, - }; - } - - pub fn scalarType(self: Type, builder: *const Builder) Type { - if (self.isFloatingPoint()) return self; - const item = builder.type_items.items[@intFromEnum(self)]; - return switch (item.tag) { - .integer, - .pointer, - => self, - .vector, - .scalable_vector, - => builder.typeExtraData(Type.Vector, item.data).child, - else => unreachable, - }; - } - - pub fn changeScalar(self: Type, scalar: Type, builder: *Builder) Allocator.Error!Type { - try builder.ensureUnusedTypeCapacity(1, Type.Vector, 0); - return self.changeScalarAssumeCapacity(scalar, builder); - } - - pub fn changeScalarAssumeCapacity(self: Type, scalar: Type, builder: *Builder) Type { - if (self.isFloatingPoint()) return scalar; - const item = builder.type_items.items[@intFromEnum(self)]; - return switch (item.tag) { - .integer, - .pointer, - => scalar, - inline .vector, - .scalable_vector, - => |kind| builder.vectorTypeAssumeCapacity( - switch (kind) { - .vector => .normal, - .scalable_vector => .scalable, - else => unreachable, - }, - builder.typeExtraData(Type.Vector, item.data).len, - scalar, - ), - else => unreachable, - }; - } - - pub fn vectorLen(self: Type, builder: *const Builder) u32 { - const item = builder.type_items.items[@intFromEnum(self)]; - return switch (item.tag) { - .vector, - .scalable_vector, - => builder.typeExtraData(Type.Vector, item.data).len, - else => unreachable, - }; - } - - pub fn changeLength(self: Type, len: u32, builder: *Builder) Allocator.Error!Type { - try builder.ensureUnusedTypeCapacity(1, Type.Array, 0); - return self.changeLengthAssumeCapacity(len, builder); - } - - pub fn changeLengthAssumeCapacity(self: Type, len: u32, builder: *Builder) Type { - const item = builder.type_items.items[@intFromEnum(self)]; - return switch (item.tag) { - inline .vector, - .scalable_vector, - => |kind| builder.vectorTypeAssumeCapacity( - switch (kind) { - .vector => .normal, - .scalable_vector => .scalable, - else => unreachable, - }, - len, - builder.typeExtraData(Type.Vector, item.data).child, - ), - .small_array => builder.arrayTypeAssumeCapacity( - len, - builder.typeExtraData(Type.Vector, item.data).child, - ), - .array => builder.arrayTypeAssumeCapacity( - len, - builder.typeExtraData(Type.Array, item.data).child, - ), - else => unreachable, - }; - } - - pub fn aggregateLen(self: Type, builder: *const Builder) usize { - const item = builder.type_items.items[@intFromEnum(self)]; - return switch (item.tag) { - .vector, - .scalable_vector, - .small_array, - => builder.typeExtraData(Type.Vector, item.data).len, - .array => @intCast(builder.typeExtraData(Type.Array, item.data).length()), - .structure, - .packed_structure, - => builder.typeExtraData(Type.Structure, item.data).fields_len, - .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body - .aggregateLen(builder), - else => unreachable, - }; - } - - pub fn structFields(self: Type, builder: *const Builder) []const Type { - const item = builder.type_items.items[@intFromEnum(self)]; - switch (item.tag) { - .structure, - .packed_structure, - => { - var extra = builder.typeExtraDataTrail(Type.Structure, item.data); - return extra.trail.next(extra.data.fields_len, Type, builder); - }, - .named_structure => return builder.typeExtraData(Type.NamedStructure, item.data).body - .structFields(builder), - else => unreachable, - } - } - - pub fn childTypeAt(self: Type, indices: []const u32, builder: *const Builder) Type { - if (indices.len == 0) return self; - const item = builder.type_items.items[@intFromEnum(self)]; - return switch (item.tag) { - .small_array => builder.typeExtraData(Type.Vector, item.data).child - .childTypeAt(indices[1..], builder), - .array => builder.typeExtraData(Type.Array, item.data).child - .childTypeAt(indices[1..], builder), - .structure, - .packed_structure, - => { - var extra = builder.typeExtraDataTrail(Type.Structure, item.data); - const fields = extra.trail.next(extra.data.fields_len, Type, builder); - return fields[indices[0]].childTypeAt(indices[1..], builder); - }, - .named_structure => builder.typeExtraData(Type.NamedStructure, item.data).body - .childTypeAt(indices, builder), - else => unreachable, - }; - } - - pub fn targetLayoutType(self: Type, builder: *const Builder) Type { - _ = self; - _ = builder; - @panic("TODO: implement targetLayoutType"); - } - - pub fn isSized(self: Type, builder: *const Builder) Allocator.Error!bool { - var visited: IsSizedVisited = .{}; - defer visited.deinit(builder.gpa); - const result = try self.isSizedVisited(&visited, builder); - return result; - } - - const FormatData = struct { - type: Type, - builder: *const Builder, - }; - fn format( - data: FormatData, - comptime fmt_str: []const u8, - fmt_opts: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - assert(data.type != .none); - if (comptime std.mem.eql(u8, fmt_str, "m")) { - const item = data.builder.type_items.items[@intFromEnum(data.type)]; - switch (item.tag) { - .simple => try writer.writeAll(switch (@as(Simple, @enumFromInt(item.data))) { - .void => "isVoid", - .half => "f16", - .bfloat => "bf16", - .float => "f32", - .double => "f64", - .fp128 => "f128", - .x86_fp80 => "f80", - .ppc_fp128 => "ppcf128", - .x86_amx => "x86amx", - .x86_mmx => "x86mmx", - .label, .token => unreachable, - .metadata => "Metadata", - }), - .function, .vararg_function => |kind| { - var extra = data.builder.typeExtraDataTrail(Type.Function, item.data); - const params = extra.trail.next(extra.data.params_len, Type, data.builder); - try writer.print("f_{m}", .{extra.data.ret.fmt(data.builder)}); - for (params) |param| try writer.print("{m}", .{param.fmt(data.builder)}); - switch (kind) { - .function => {}, - .vararg_function => try writer.writeAll("vararg"), - else => unreachable, - } - try writer.writeByte('f'); - }, - .integer => try writer.print("i{d}", .{item.data}), - .pointer => try writer.print("p{d}", .{item.data}), - .target => { - var extra = data.builder.typeExtraDataTrail(Type.Target, item.data); - const types = extra.trail.next(extra.data.types_len, Type, data.builder); - const ints = extra.trail.next(extra.data.ints_len, u32, data.builder); - try writer.print("t{s}", .{extra.data.name.slice(data.builder).?}); - for (types) |ty| try writer.print("_{m}", .{ty.fmt(data.builder)}); - for (ints) |int| try writer.print("_{d}", .{int}); - try writer.writeByte('t'); - }, - .vector, .scalable_vector => |kind| { - const extra = data.builder.typeExtraData(Type.Vector, item.data); - try writer.print("{s}v{d}{m}", .{ - switch (kind) { - .vector => "", - .scalable_vector => "nx", - else => unreachable, - }, - extra.len, - extra.child.fmt(data.builder), - }); - }, - inline .small_array, .array => |kind| { - const extra = data.builder.typeExtraData(switch (kind) { - .small_array => Type.Vector, - .array => Type.Array, - else => unreachable, - }, item.data); - try writer.print("a{d}{m}", .{ extra.length(), extra.child.fmt(data.builder) }); - }, - .structure, .packed_structure => { - var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data); - const fields = extra.trail.next(extra.data.fields_len, Type, data.builder); - try writer.writeAll("sl_"); - for (fields) |field| try writer.print("{m}", .{field.fmt(data.builder)}); - try writer.writeByte('s'); - }, - .named_structure => { - const extra = data.builder.typeExtraData(Type.NamedStructure, item.data); - try writer.writeAll("s_"); - if (extra.id.slice(data.builder)) |id| try writer.writeAll(id); - }, - } - return; - } - if (std.enums.tagName(Type, data.type)) |name| return writer.writeAll(name); - const item = data.builder.type_items.items[@intFromEnum(data.type)]; - switch (item.tag) { - .simple => unreachable, - .function, .vararg_function => |kind| { - var extra = data.builder.typeExtraDataTrail(Type.Function, item.data); - const params = extra.trail.next(extra.data.params_len, Type, data.builder); - if (!comptime std.mem.eql(u8, fmt_str, ">")) - try writer.print("{%} ", .{extra.data.ret.fmt(data.builder)}); - if (!comptime std.mem.eql(u8, fmt_str, "<")) { - try writer.writeByte('('); - for (params, 0..) |param, index| { - if (index > 0) try writer.writeAll(", "); - try writer.print("{%}", .{param.fmt(data.builder)}); - } - switch (kind) { - .function => {}, - .vararg_function => { - if (params.len > 0) try writer.writeAll(", "); - try writer.writeAll("..."); - }, - else => unreachable, - } - try writer.writeByte(')'); - } - }, - .integer => try writer.print("i{d}", .{item.data}), - .pointer => try writer.print("ptr{ }", .{@as(AddrSpace, @enumFromInt(item.data))}), - .target => { - var extra = data.builder.typeExtraDataTrail(Type.Target, item.data); - const types = extra.trail.next(extra.data.types_len, Type, data.builder); - const ints = extra.trail.next(extra.data.ints_len, u32, data.builder); - try writer.print( - \\target({"} - , .{extra.data.name.fmt(data.builder)}); - for (types) |ty| try writer.print(", {%}", .{ty.fmt(data.builder)}); - for (ints) |int| try writer.print(", {d}", .{int}); - try writer.writeByte(')'); - }, - .vector, .scalable_vector => |kind| { - const extra = data.builder.typeExtraData(Type.Vector, item.data); - try writer.print("<{s}{d} x {%}>", .{ - switch (kind) { - .vector => "", - .scalable_vector => "vscale x ", - else => unreachable, - }, - extra.len, - extra.child.fmt(data.builder), - }); - }, - inline .small_array, .array => |kind| { - const extra = data.builder.typeExtraData(switch (kind) { - .small_array => Type.Vector, - .array => Type.Array, - else => unreachable, - }, item.data); - try writer.print("[{d} x {%}]", .{ extra.length(), extra.child.fmt(data.builder) }); - }, - .structure, .packed_structure => |kind| { - var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data); - const fields = extra.trail.next(extra.data.fields_len, Type, data.builder); - switch (kind) { - .structure => {}, - .packed_structure => try writer.writeByte('<'), - else => unreachable, - } - try writer.writeAll("{ "); - for (fields, 0..) |field, index| { - if (index > 0) try writer.writeAll(", "); - try writer.print("{%}", .{field.fmt(data.builder)}); - } - try writer.writeAll(" }"); - switch (kind) { - .structure => {}, - .packed_structure => try writer.writeByte('>'), - else => unreachable, - } - }, - .named_structure => { - const extra = data.builder.typeExtraData(Type.NamedStructure, item.data); - if (comptime std.mem.eql(u8, fmt_str, "%")) try writer.print("%{}", .{ - extra.id.fmt(data.builder), - }) else switch (extra.body) { - .none => try writer.writeAll("opaque"), - else => try format(.{ - .type = extra.body, - .builder = data.builder, - }, fmt_str, fmt_opts, writer), - } - }, - } - } - pub fn fmt(self: Type, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .type = self, .builder = builder } }; - } - - const IsSizedVisited = std.AutoHashMapUnmanaged(Type, void); - fn isSizedVisited( - self: Type, - visited: *IsSizedVisited, - builder: *const Builder, - ) Allocator.Error!bool { - return switch (self) { - .void, - .label, - .token, - .metadata, - => false, - .half, - .bfloat, - .float, - .double, - .fp128, - .x86_fp80, - .ppc_fp128, - .x86_amx, - .x86_mmx, - .i1, - .i8, - .i16, - .i29, - .i32, - .i64, - .i80, - .i128, - .ptr, - .@"ptr addrspace(4)", - => true, - .none => unreachable, - _ => { - const item = builder.type_items.items[@intFromEnum(self)]; - return switch (item.tag) { - .simple => unreachable, - .function, - .vararg_function, - => false, - .integer, - .pointer, - => true, - .target => self.targetLayoutType(builder).isSizedVisited(visited, builder), - .vector, - .scalable_vector, - .small_array, - => builder.typeExtraData(Type.Vector, item.data) - .child.isSizedVisited(visited, builder), - .array => builder.typeExtraData(Type.Array, item.data) - .child.isSizedVisited(visited, builder), - .structure, - .packed_structure, - => { - if (try visited.fetchPut(builder.gpa, self, {})) |_| return false; - - var extra = builder.typeExtraDataTrail(Type.Structure, item.data); - const fields = extra.trail.next(extra.data.fields_len, Type, builder); - for (fields) |field| { - if (field.isVector(builder) and field.vectorKind(builder) == .scalable) - return false; - if (!try field.isSizedVisited(visited, builder)) - return false; - } - return true; - }, - .named_structure => { - const body = builder.typeExtraData(Type.NamedStructure, item.data).body; - return body != .none and try body.isSizedVisited(visited, builder); - }, - }; - }, - }; - } -}; - -pub const Attribute = union(Kind) { - // Parameter Attributes - zeroext, - signext, - inreg, - byval: Type, - byref: Type, - preallocated: Type, - inalloca: Type, - sret: Type, - elementtype: Type, - @"align": Alignment, - @"noalias", - nocapture, - nofree, - nest, - returned, - nonnull, - dereferenceable: u32, - dereferenceable_or_null: u32, - swiftself, - swiftasync, - swifterror, - immarg, - noundef, - nofpclass: FpClass, - alignstack: Alignment, - allocalign, - allocptr, - readnone, - readonly, - writeonly, - - // Function Attributes - //alignstack: Alignment, - allockind: AllocKind, - allocsize: AllocSize, - alwaysinline, - builtin, - cold, - convergent, - disable_sanitizer_information, - fn_ret_thunk_extern, - hot, - inlinehint, - jumptable, - memory: Memory, - minsize, - naked, - nobuiltin, - nocallback, - noduplicate, - //nofree, - noimplicitfloat, - @"noinline", - nomerge, - nonlazybind, - noprofile, - skipprofile, - noredzone, - noreturn, - norecurse, - willreturn, - nosync, - nounwind, - nosanitize_bounds, - nosanitize_coverage, - null_pointer_is_valid, - optforfuzzing, - optnone, - optsize, - //preallocated: Type, - returns_twice, - safestack, - sanitize_address, - sanitize_memory, - sanitize_thread, - sanitize_hwaddress, - sanitize_memtag, - speculative_load_hardening, - speculatable, - ssp, - sspstrong, - sspreq, - strictfp, - uwtable: UwTable, - nocf_check, - shadowcallstack, - mustprogress, - vscale_range: VScaleRange, - - // Global Attributes - no_sanitize_address, - no_sanitize_hwaddress, - //sanitize_memtag, - sanitize_address_dyninit, - - string: struct { kind: String, value: String }, - none: noreturn, - - pub const Index = enum(u32) { - _, - - pub fn getKind(self: Index, builder: *const Builder) Kind { - return self.toStorage(builder).kind; - } - - pub fn toAttribute(self: Index, builder: *const Builder) Attribute { - @setEvalBranchQuota(2_000); - const storage = self.toStorage(builder); - if (storage.kind.toString()) |kind| return .{ .string = .{ - .kind = kind, - .value = @enumFromInt(storage.value), - } } else return switch (storage.kind) { - inline .zeroext, - .signext, - .inreg, - .byval, - .byref, - .preallocated, - .inalloca, - .sret, - .elementtype, - .@"align", - .@"noalias", - .nocapture, - .nofree, - .nest, - .returned, - .nonnull, - .dereferenceable, - .dereferenceable_or_null, - .swiftself, - .swiftasync, - .swifterror, - .immarg, - .noundef, - .nofpclass, - .alignstack, - .allocalign, - .allocptr, - .readnone, - .readonly, - .writeonly, - //.alignstack, - .allockind, - .allocsize, - .alwaysinline, - .builtin, - .cold, - .convergent, - .disable_sanitizer_information, - .fn_ret_thunk_extern, - .hot, - .inlinehint, - .jumptable, - .memory, - .minsize, - .naked, - .nobuiltin, - .nocallback, - .noduplicate, - //.nofree, - .noimplicitfloat, - .@"noinline", - .nomerge, - .nonlazybind, - .noprofile, - .skipprofile, - .noredzone, - .noreturn, - .norecurse, - .willreturn, - .nosync, - .nounwind, - .nosanitize_bounds, - .nosanitize_coverage, - .null_pointer_is_valid, - .optforfuzzing, - .optnone, - .optsize, - //.preallocated, - .returns_twice, - .safestack, - .sanitize_address, - .sanitize_memory, - .sanitize_thread, - .sanitize_hwaddress, - .sanitize_memtag, - .speculative_load_hardening, - .speculatable, - .ssp, - .sspstrong, - .sspreq, - .strictfp, - .uwtable, - .nocf_check, - .shadowcallstack, - .mustprogress, - .vscale_range, - .no_sanitize_address, - .no_sanitize_hwaddress, - .sanitize_address_dyninit, - => |kind| { - const field = comptime blk: { - @setEvalBranchQuota(10_000); - for (@typeInfo(Attribute).@"union".fields) |field| { - if (std.mem.eql(u8, field.name, @tagName(kind))) break :blk field; - } - unreachable; - }; - comptime assert(std.mem.eql(u8, @tagName(kind), field.name)); - return @unionInit(Attribute, field.name, switch (field.type) { - void => {}, - u32 => storage.value, - Alignment, String, Type, UwTable => @enumFromInt(storage.value), - AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(storage.value), - else => @compileError("bad payload type: " ++ field.name ++ ": " ++ - @typeName(field.type)), - }); - }, - .string, .none => unreachable, - _ => unreachable, - }; - } - - const FormatData = struct { - attribute_index: Index, - builder: *const Builder, - }; - fn format( - data: FormatData, - comptime fmt_str: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (comptime std.mem.indexOfNone(u8, fmt_str, "\"#")) |_| - @compileError("invalid format string: '" ++ fmt_str ++ "'"); - const attribute = data.attribute_index.toAttribute(data.builder); - switch (attribute) { - .zeroext, - .signext, - .inreg, - .@"noalias", - .nocapture, - .nofree, - .nest, - .returned, - .nonnull, - .swiftself, - .swiftasync, - .swifterror, - .immarg, - .noundef, - .allocalign, - .allocptr, - .readnone, - .readonly, - .writeonly, - .alwaysinline, - .builtin, - .cold, - .convergent, - .disable_sanitizer_information, - .fn_ret_thunk_extern, - .hot, - .inlinehint, - .jumptable, - .minsize, - .naked, - .nobuiltin, - .nocallback, - .noduplicate, - .noimplicitfloat, - .@"noinline", - .nomerge, - .nonlazybind, - .noprofile, - .skipprofile, - .noredzone, - .noreturn, - .norecurse, - .willreturn, - .nosync, - .nounwind, - .nosanitize_bounds, - .nosanitize_coverage, - .null_pointer_is_valid, - .optforfuzzing, - .optnone, - .optsize, - .returns_twice, - .safestack, - .sanitize_address, - .sanitize_memory, - .sanitize_thread, - .sanitize_hwaddress, - .sanitize_memtag, - .speculative_load_hardening, - .speculatable, - .ssp, - .sspstrong, - .sspreq, - .strictfp, - .nocf_check, - .shadowcallstack, - .mustprogress, - .no_sanitize_address, - .no_sanitize_hwaddress, - .sanitize_address_dyninit, - => try writer.print(" {s}", .{@tagName(attribute)}), - .byval, - .byref, - .preallocated, - .inalloca, - .sret, - .elementtype, - => |ty| try writer.print(" {s}({%})", .{ @tagName(attribute), ty.fmt(data.builder) }), - .@"align" => |alignment| try writer.print("{ }", .{alignment}), - .dereferenceable, - .dereferenceable_or_null, - => |size| try writer.print(" {s}({d})", .{ @tagName(attribute), size }), - .nofpclass => |fpclass| { - const Int = @typeInfo(FpClass).@"struct".backing_integer.?; - try writer.print(" {s}(", .{@tagName(attribute)}); - var any = false; - var remaining: Int = @bitCast(fpclass); - inline for (@typeInfo(FpClass).@"struct".decls) |decl| { - const pattern: Int = @bitCast(@field(FpClass, decl.name)); - if (remaining & pattern == pattern) { - if (!any) { - try writer.writeByte(' '); - any = true; - } - try writer.writeAll(decl.name); - remaining &= ~pattern; - } - } - try writer.writeByte(')'); - }, - .alignstack => |alignment| try writer.print( - if (comptime std.mem.indexOfScalar(u8, fmt_str, '#') != null) - " {s}={d}" - else - " {s}({d})", - .{ @tagName(attribute), alignment.toByteUnits() orelse return }, - ), - .allockind => |allockind| { - try writer.print(" {s}(\"", .{@tagName(attribute)}); - var any = false; - inline for (@typeInfo(AllocKind).@"struct".fields) |field| { - if (comptime std.mem.eql(u8, field.name, "_")) continue; - if (@field(allockind, field.name)) { - if (!any) { - try writer.writeByte(','); - any = true; - } - try writer.writeAll(field.name); - } - } - try writer.writeAll("\")"); - }, - .allocsize => |allocsize| { - try writer.print(" {s}({d}", .{ @tagName(attribute), allocsize.elem_size }); - if (allocsize.num_elems != AllocSize.none) - try writer.print(",{d}", .{allocsize.num_elems}); - try writer.writeByte(')'); - }, - .memory => |memory| { - try writer.print(" {s}(", .{@tagName(attribute)}); - var any = memory.other != .none or - (memory.argmem == .none and memory.inaccessiblemem == .none); - if (any) try writer.writeAll(@tagName(memory.other)); - inline for (.{ "argmem", "inaccessiblemem" }) |kind| { - if (@field(memory, kind) != memory.other) { - if (any) try writer.writeAll(", "); - try writer.print("{s}: {s}", .{ kind, @tagName(@field(memory, kind)) }); - any = true; - } - } - try writer.writeByte(')'); - }, - .uwtable => |uwtable| if (uwtable != .none) { - try writer.print(" {s}", .{@tagName(attribute)}); - if (uwtable != UwTable.default) try writer.print("({s})", .{@tagName(uwtable)}); - }, - .vscale_range => |vscale_range| try writer.print(" {s}({d},{d})", .{ - @tagName(attribute), - vscale_range.min.toByteUnits().?, - vscale_range.max.toByteUnits() orelse 0, - }), - .string => |string_attr| if (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) { - try writer.print(" {\"}", .{string_attr.kind.fmt(data.builder)}); - if (string_attr.value != .empty) - try writer.print("={\"}", .{string_attr.value.fmt(data.builder)}); - }, - .none => unreachable, - } - } - pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .attribute_index = self, .builder = builder } }; - } - - fn toStorage(self: Index, builder: *const Builder) Storage { - return builder.attributes.keys()[@intFromEnum(self)]; - } - }; - - pub const Kind = enum(u32) { - // Parameter Attributes - zeroext = 34, - signext = 24, - inreg = 5, - byval = 3, - byref = 69, - preallocated = 65, - inalloca = 38, - sret = 29, // TODO: ? - elementtype = 77, - @"align" = 1, - @"noalias" = 9, - nocapture = 11, - nofree = 62, - nest = 8, - returned = 22, - nonnull = 39, - dereferenceable = 41, - dereferenceable_or_null = 42, - swiftself = 46, - swiftasync = 75, - swifterror = 47, - immarg = 60, - noundef = 68, - nofpclass = 87, - alignstack = 25, - allocalign = 80, - allocptr = 81, - readnone = 20, - readonly = 21, - writeonly = 52, - - // Function Attributes - //alignstack, - allockind = 82, - allocsize = 51, - alwaysinline = 2, - builtin = 35, - cold = 36, - convergent = 43, - disable_sanitizer_information = 78, - fn_ret_thunk_extern = 84, - hot = 72, - inlinehint = 4, - jumptable = 40, - memory = 86, - minsize = 6, - naked = 7, - nobuiltin = 10, - nocallback = 71, - noduplicate = 12, - //nofree, - noimplicitfloat = 13, - @"noinline" = 14, - nomerge = 66, - nonlazybind = 15, - noprofile = 73, - skipprofile = 85, - noredzone = 16, - noreturn = 17, - norecurse = 48, - willreturn = 61, - nosync = 63, - nounwind = 18, - nosanitize_bounds = 79, - nosanitize_coverage = 76, - null_pointer_is_valid = 67, - optforfuzzing = 57, - optnone = 37, - optsize = 19, - //preallocated, - returns_twice = 23, - safestack = 44, - sanitize_address = 30, - sanitize_memory = 32, - sanitize_thread = 31, - sanitize_hwaddress = 55, - sanitize_memtag = 64, - speculative_load_hardening = 59, - speculatable = 53, - ssp = 26, - sspstrong = 28, - sspreq = 27, - strictfp = 54, - uwtable = 33, - nocf_check = 56, - shadowcallstack = 58, - mustprogress = 70, - vscale_range = 74, - - // Global Attributes - no_sanitize_address = 100, - no_sanitize_hwaddress = 101, - //sanitize_memtag, - sanitize_address_dyninit = 102, - - string = std.math.maxInt(u31), - none = std.math.maxInt(u32), - _, - - pub const len = @typeInfo(Kind).@"enum".fields.len - 2; - - pub fn fromString(str: String) Kind { - assert(!str.isAnon()); - const kind: Kind = @enumFromInt(@intFromEnum(str)); - assert(kind != .none); - return kind; - } - - fn toString(self: Kind) ?String { - assert(self != .none); - const str: String = @enumFromInt(@intFromEnum(self)); - return if (str.isAnon()) null else str; - } - }; - - pub const FpClass = packed struct(u32) { - signaling_nan: bool = false, - quiet_nan: bool = false, - negative_infinity: bool = false, - negative_normal: bool = false, - negative_subnormal: bool = false, - negative_zero: bool = false, - positive_zero: bool = false, - positive_subnormal: bool = false, - positive_normal: bool = false, - positive_infinity: bool = false, - _: u22 = 0, - - pub const all = FpClass{ - .signaling_nan = true, - .quiet_nan = true, - .negative_infinity = true, - .negative_normal = true, - .negative_subnormal = true, - .negative_zero = true, - .positive_zero = true, - .positive_subnormal = true, - .positive_normal = true, - .positive_infinity = true, - }; - - pub const nan = FpClass{ .signaling_nan = true, .quiet_nan = true }; - pub const snan = FpClass{ .signaling_nan = true }; - pub const qnan = FpClass{ .quiet_nan = true }; - - pub const inf = FpClass{ .negative_infinity = true, .positive_infinity = true }; - pub const ninf = FpClass{ .negative_infinity = true }; - pub const pinf = FpClass{ .positive_infinity = true }; - - pub const zero = FpClass{ .positive_zero = true, .negative_zero = true }; - pub const nzero = FpClass{ .negative_zero = true }; - pub const pzero = FpClass{ .positive_zero = true }; - - pub const sub = FpClass{ .positive_subnormal = true, .negative_subnormal = true }; - pub const nsub = FpClass{ .negative_subnormal = true }; - pub const psub = FpClass{ .positive_subnormal = true }; - - pub const norm = FpClass{ .positive_normal = true, .negative_normal = true }; - pub const nnorm = FpClass{ .negative_normal = true }; - pub const pnorm = FpClass{ .positive_normal = true }; - }; - - pub const AllocKind = packed struct(u32) { - alloc: bool, - realloc: bool, - free: bool, - uninitialized: bool, - zeroed: bool, - aligned: bool, - _: u26 = 0, - }; - - pub const AllocSize = packed struct(u32) { - elem_size: u16, - num_elems: u16, - - pub const none = std.math.maxInt(u16); - - fn toLlvm(self: AllocSize) packed struct(u64) { num_elems: u32, elem_size: u32 } { - return .{ .num_elems = switch (self.num_elems) { - else => self.num_elems, - none => std.math.maxInt(u32), - }, .elem_size = self.elem_size }; - } - }; - - pub const Memory = packed struct(u32) { - argmem: Effect = .none, - inaccessiblemem: Effect = .none, - other: Effect = .none, - _: u26 = 0, - - pub const Effect = enum(u2) { none, read, write, readwrite }; - - fn all(effect: Effect) Memory { - return .{ .argmem = effect, .inaccessiblemem = effect, .other = effect }; - } - }; - - pub const UwTable = enum(u32) { - none, - sync, - @"async", - - pub const default = UwTable.@"async"; - }; - - pub const VScaleRange = packed struct(u32) { - min: Alignment, - max: Alignment, - _: u20 = 0, - - fn toLlvm(self: VScaleRange) packed struct(u64) { max: u32, min: u32 } { - return .{ - .max = @intCast(self.max.toByteUnits() orelse 0), - .min = @intCast(self.min.toByteUnits().?), - }; - } - }; - - pub fn getKind(self: Attribute) Kind { - return switch (self) { - else => self, - .string => |string_attr| Kind.fromString(string_attr.kind), - }; - } - - const Storage = extern struct { - kind: Kind, - value: u32, - }; - - fn toStorage(self: Attribute) Storage { - return switch (self) { - inline else => |value, tag| .{ .kind = @as(Kind, self), .value = switch (@TypeOf(value)) { - void => 0, - u32 => value, - Alignment, String, Type, UwTable => @intFromEnum(value), - AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(value), - else => @compileError("bad payload type: " ++ @tagName(tag) ++ @typeName(@TypeOf(value))), - } }, - .string => |string_attr| .{ - .kind = Kind.fromString(string_attr.kind), - .value = @intFromEnum(string_attr.value), - }, - .none => unreachable, - }; - } -}; - -pub const Attributes = enum(u32) { - none, - _, - - pub fn slice(self: Attributes, builder: *const Builder) []const Attribute.Index { - const start = builder.attributes_indices.items[@intFromEnum(self)]; - const end = builder.attributes_indices.items[@intFromEnum(self) + 1]; - return @ptrCast(builder.attributes_extra.items[start..end]); - } - - const FormatData = struct { - attributes: Attributes, - builder: *const Builder, - }; - fn format( - data: FormatData, - comptime fmt_str: []const u8, - fmt_opts: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{ - .attribute_index = attribute_index, - .builder = data.builder, - }, fmt_str, fmt_opts, writer); - } - pub fn fmt(self: Attributes, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .attributes = self, .builder = builder } }; - } -}; - -pub const FunctionAttributes = enum(u32) { - none, - _, - - const function_index = 0; - const return_index = 1; - const params_index = 2; - - pub const Wip = struct { - maps: Maps = .{}, - - const Map = std.AutoArrayHashMapUnmanaged(Attribute.Kind, Attribute.Index); - const Maps = std.ArrayListUnmanaged(Map); - - pub fn deinit(self: *Wip, builder: *const Builder) void { - for (self.maps.items) |*map| map.deinit(builder.gpa); - self.maps.deinit(builder.gpa); - self.* = undefined; - } - - pub fn addFnAttr(self: *Wip, attribute: Attribute, builder: *Builder) Allocator.Error!void { - try self.addAttr(function_index, attribute, builder); - } - - pub fn addFnAttrIndex( - self: *Wip, - attribute_index: Attribute.Index, - builder: *const Builder, - ) Allocator.Error!void { - try self.addAttrIndex(function_index, attribute_index, builder); - } - - pub fn removeFnAttr(self: *Wip, attribute_kind: Attribute.Kind) Allocator.Error!bool { - return self.removeAttr(function_index, attribute_kind); - } - - pub fn addRetAttr(self: *Wip, attribute: Attribute, builder: *Builder) Allocator.Error!void { - try self.addAttr(return_index, attribute, builder); - } - - pub fn addRetAttrIndex( - self: *Wip, - attribute_index: Attribute.Index, - builder: *const Builder, - ) Allocator.Error!void { - try self.addAttrIndex(return_index, attribute_index, builder); - } - - pub fn removeRetAttr(self: *Wip, attribute_kind: Attribute.Kind) Allocator.Error!bool { - return self.removeAttr(return_index, attribute_kind); - } - - pub fn addParamAttr( - self: *Wip, - param_index: usize, - attribute: Attribute, - builder: *Builder, - ) Allocator.Error!void { - try self.addAttr(params_index + param_index, attribute, builder); - } - - pub fn addParamAttrIndex( - self: *Wip, - param_index: usize, - attribute_index: Attribute.Index, - builder: *const Builder, - ) Allocator.Error!void { - try self.addAttrIndex(params_index + param_index, attribute_index, builder); - } - - pub fn removeParamAttr( - self: *Wip, - param_index: usize, - attribute_kind: Attribute.Kind, - ) Allocator.Error!bool { - return self.removeAttr(params_index + param_index, attribute_kind); - } - - pub fn finish(self: *const Wip, builder: *Builder) Allocator.Error!FunctionAttributes { - const attributes = try builder.gpa.alloc(Attributes, self.maps.items.len); - defer builder.gpa.free(attributes); - for (attributes, self.maps.items) |*attribute, map| - attribute.* = try builder.attrs(map.values()); - return builder.fnAttrs(attributes); - } - - fn addAttr( - self: *Wip, - index: usize, - attribute: Attribute, - builder: *Builder, - ) Allocator.Error!void { - const map = try self.getOrPutMap(builder.gpa, index); - try map.put(builder.gpa, attribute.getKind(), try builder.attr(attribute)); - } - - fn addAttrIndex( - self: *Wip, - index: usize, - attribute_index: Attribute.Index, - builder: *const Builder, - ) Allocator.Error!void { - const map = try self.getOrPutMap(builder.gpa, index); - try map.put(builder.gpa, attribute_index.getKind(builder), attribute_index); - } - - fn removeAttr(self: *Wip, index: usize, attribute_kind: Attribute.Kind) Allocator.Error!bool { - const map = self.getMap(index) orelse return false; - return map.swapRemove(attribute_kind); - } - - fn getOrPutMap(self: *Wip, allocator: Allocator, index: usize) Allocator.Error!*Map { - if (index >= self.maps.items.len) - try self.maps.appendNTimes(allocator, .{}, index + 1 - self.maps.items.len); - return &self.maps.items[index]; - } - - fn getMap(self: *Wip, index: usize) ?*Map { - return if (index >= self.maps.items.len) null else &self.maps.items[index]; - } - - fn ensureTotalLength(self: *Wip, new_len: usize) Allocator.Error!void { - try self.maps.appendNTimes( - .{}, - std.math.sub(usize, new_len, self.maps.items.len) catch return, - ); - } - }; - - pub fn func(self: FunctionAttributes, builder: *const Builder) Attributes { - return self.get(function_index, builder); - } - - pub fn ret(self: FunctionAttributes, builder: *const Builder) Attributes { - return self.get(return_index, builder); - } - - pub fn param(self: FunctionAttributes, param_index: usize, builder: *const Builder) Attributes { - return self.get(params_index + param_index, builder); - } - - pub fn toWip(self: FunctionAttributes, builder: *const Builder) Allocator.Error!Wip { - var wip: Wip = .{}; - errdefer wip.deinit(builder); - const attributes_slice = self.slice(builder); - try wip.maps.ensureTotalCapacityPrecise(builder.gpa, attributes_slice.len); - for (attributes_slice) |attributes| { - const map = wip.maps.addOneAssumeCapacity(); - map.* = .{}; - const attribute_slice = attributes.slice(builder); - try map.ensureTotalCapacity(builder.gpa, attribute_slice.len); - for (attributes.slice(builder)) |attribute| - map.putAssumeCapacityNoClobber(attribute.getKind(builder), attribute); - } - return wip; - } - - fn get(self: FunctionAttributes, index: usize, builder: *const Builder) Attributes { - const attribute_slice = self.slice(builder); - return if (index < attribute_slice.len) attribute_slice[index] else .none; - } - - fn slice(self: FunctionAttributes, builder: *const Builder) []const Attributes { - const start = builder.attributes_indices.items[@intFromEnum(self)]; - const end = builder.attributes_indices.items[@intFromEnum(self) + 1]; - return @ptrCast(builder.attributes_extra.items[start..end]); - } -}; - -pub const Linkage = enum(u4) { - private = 9, - internal = 3, - weak = 1, - weak_odr = 10, - linkonce = 4, - linkonce_odr = 11, - available_externally = 12, - appending = 2, - common = 8, - extern_weak = 7, - external = 0, - - pub fn format( - self: Linkage, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (self != .external) try writer.print(" {s}", .{@tagName(self)}); - } - - fn formatOptional( - data: ?Linkage, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (data) |linkage| try writer.print(" {s}", .{@tagName(linkage)}); - } - pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(formatOptional) { - return .{ .data = self }; - } -}; - -pub const Preemption = enum { - dso_preemptable, - dso_local, - implicit_dso_local, - - pub fn format( - self: Preemption, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (self == .dso_local) try writer.print(" {s}", .{@tagName(self)}); - } -}; - -pub const Visibility = enum(u2) { - default = 0, - hidden = 1, - protected = 2, - - pub fn format( - self: Visibility, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (self != .default) try writer.print(" {s}", .{@tagName(self)}); - } -}; - -pub const DllStorageClass = enum(u2) { - default = 0, - dllimport = 1, - dllexport = 2, - - pub fn format( - self: DllStorageClass, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (self != .default) try writer.print(" {s}", .{@tagName(self)}); - } -}; - -pub const ThreadLocal = enum(u3) { - default = 0, - generaldynamic = 1, - localdynamic = 2, - initialexec = 3, - localexec = 4, - - pub fn format( - self: ThreadLocal, - comptime prefix: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (self == .default) return; - try writer.print("{s}thread_local", .{prefix}); - if (self != .generaldynamic) try writer.print("({s})", .{@tagName(self)}); - } -}; - -pub const Mutability = enum { global, constant }; - -pub const UnnamedAddr = enum(u2) { - default = 0, - unnamed_addr = 1, - local_unnamed_addr = 2, - - pub fn format( - self: UnnamedAddr, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (self != .default) try writer.print(" {s}", .{@tagName(self)}); - } -}; - -pub const AddrSpace = enum(u24) { - default, - _, - - // See llvm/lib/Target/X86/X86.h - pub const x86 = struct { - pub const gs: AddrSpace = @enumFromInt(256); - pub const fs: AddrSpace = @enumFromInt(257); - pub const ss: AddrSpace = @enumFromInt(258); - - pub const ptr32_sptr: AddrSpace = @enumFromInt(270); - pub const ptr32_uptr: AddrSpace = @enumFromInt(271); - pub const ptr64: AddrSpace = @enumFromInt(272); - }; - pub const x86_64 = x86; - - // See llvm/lib/Target/AVR/AVR.h - pub const avr = struct { - pub const data: AddrSpace = @enumFromInt(0); - pub const program: AddrSpace = @enumFromInt(1); - pub const program1: AddrSpace = @enumFromInt(2); - pub const program2: AddrSpace = @enumFromInt(3); - pub const program3: AddrSpace = @enumFromInt(4); - pub const program4: AddrSpace = @enumFromInt(5); - pub const program5: AddrSpace = @enumFromInt(6); - }; - - // See llvm/lib/Target/NVPTX/NVPTX.h - pub const nvptx = struct { - pub const generic: AddrSpace = @enumFromInt(0); - pub const global: AddrSpace = @enumFromInt(1); - pub const constant: AddrSpace = @enumFromInt(2); - pub const shared: AddrSpace = @enumFromInt(3); - pub const param: AddrSpace = @enumFromInt(4); - pub const local: AddrSpace = @enumFromInt(5); - }; - - // See llvm/lib/Target/AMDGPU/AMDGPU.h - pub const amdgpu = struct { - pub const flat: AddrSpace = @enumFromInt(0); - pub const global: AddrSpace = @enumFromInt(1); - pub const region: AddrSpace = @enumFromInt(2); - pub const local: AddrSpace = @enumFromInt(3); - pub const constant: AddrSpace = @enumFromInt(4); - pub const private: AddrSpace = @enumFromInt(5); - pub const constant_32bit: AddrSpace = @enumFromInt(6); - pub const buffer_fat_pointer: AddrSpace = @enumFromInt(7); - pub const buffer_resource: AddrSpace = @enumFromInt(8); - pub const buffer_strided_pointer: AddrSpace = @enumFromInt(9); - pub const param_d: AddrSpace = @enumFromInt(6); - pub const param_i: AddrSpace = @enumFromInt(7); - pub const constant_buffer_0: AddrSpace = @enumFromInt(8); - pub const constant_buffer_1: AddrSpace = @enumFromInt(9); - pub const constant_buffer_2: AddrSpace = @enumFromInt(10); - pub const constant_buffer_3: AddrSpace = @enumFromInt(11); - pub const constant_buffer_4: AddrSpace = @enumFromInt(12); - pub const constant_buffer_5: AddrSpace = @enumFromInt(13); - pub const constant_buffer_6: AddrSpace = @enumFromInt(14); - pub const constant_buffer_7: AddrSpace = @enumFromInt(15); - pub const constant_buffer_8: AddrSpace = @enumFromInt(16); - pub const constant_buffer_9: AddrSpace = @enumFromInt(17); - pub const constant_buffer_10: AddrSpace = @enumFromInt(18); - pub const constant_buffer_11: AddrSpace = @enumFromInt(19); - pub const constant_buffer_12: AddrSpace = @enumFromInt(20); - pub const constant_buffer_13: AddrSpace = @enumFromInt(21); - pub const constant_buffer_14: AddrSpace = @enumFromInt(22); - pub const constant_buffer_15: AddrSpace = @enumFromInt(23); - pub const streamout_register: AddrSpace = @enumFromInt(128); - }; - - pub const spirv = struct { - pub const function: AddrSpace = @enumFromInt(0); - pub const cross_workgroup: AddrSpace = @enumFromInt(1); - pub const uniform_constant: AddrSpace = @enumFromInt(2); - pub const workgroup: AddrSpace = @enumFromInt(3); - pub const generic: AddrSpace = @enumFromInt(4); - pub const device_only_intel: AddrSpace = @enumFromInt(5); - pub const host_only_intel: AddrSpace = @enumFromInt(6); - pub const input: AddrSpace = @enumFromInt(7); - }; - - // See llvm/include/llvm/CodeGen/WasmAddressSpaces.h - pub const wasm = struct { - pub const default: AddrSpace = @enumFromInt(0); - pub const variable: AddrSpace = @enumFromInt(1); - pub const externref: AddrSpace = @enumFromInt(10); - pub const funcref: AddrSpace = @enumFromInt(20); - }; - - pub fn format( - self: AddrSpace, - comptime prefix: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (self != .default) try writer.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) }); - } -}; - -pub const ExternallyInitialized = enum { - default, - externally_initialized, - - pub fn format( - self: ExternallyInitialized, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (self == .default) return; - try writer.writeByte(' '); - try writer.writeAll(@tagName(self)); - } -}; - -pub const Alignment = enum(u6) { - default = std.math.maxInt(u6), - _, - - pub fn fromByteUnits(bytes: u64) Alignment { - if (bytes == 0) return .default; - assert(std.math.isPowerOfTwo(bytes)); - assert(bytes <= 1 << 32); - return @enumFromInt(@ctz(bytes)); - } - - pub fn toByteUnits(self: Alignment) ?u64 { - return if (self == .default) null else @as(u64, 1) << @intFromEnum(self); - } - - pub fn toLlvm(self: Alignment) u6 { - return if (self == .default) 0 else (@intFromEnum(self) + 1); - } - - pub fn format( - self: Alignment, - comptime prefix: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - try writer.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return }); - } -}; - -pub const CallConv = enum(u10) { - ccc, - - fastcc = 8, - coldcc, - ghccc, - - webkit_jscc = 12, - anyregcc, - preserve_mostcc, - preserve_allcc, - swiftcc, - cxx_fast_tlscc, - tailcc, - cfguard_checkcc, - swifttailcc, - - x86_stdcallcc = 64, - x86_fastcallcc, - arm_apcscc, - arm_aapcscc, - arm_aapcs_vfpcc, - msp430_intrcc, - x86_thiscallcc, - ptx_kernel, - ptx_device, - - spir_func = 75, - spir_kernel, - intel_ocl_bicc, - x86_64_sysvcc, - win64cc, - x86_vectorcallcc, - hhvmcc, - hhvm_ccc, - x86_intrcc, - avr_intrcc, - avr_signalcc, - avr_builtincc, - - amdgpu_vs = 87, - amdgpu_gs, - amdgpu_ps, - amdgpu_cs, - amdgpu_kernel, - x86_regcallcc, - amdgpu_hs, - msp430_builtincc, - - amdgpu_ls = 95, - amdgpu_es, - aarch64_vector_pcs, - aarch64_sve_vector_pcs, - - amdgpu_gfx = 100, - - m68k_intrcc, - - aarch64_sme_preservemost_from_x0 = 102, - aarch64_sme_preservemost_from_x2, - - m68k_rtdcc = 106, - - riscv_vectorcallcc = 110, - - _, - - pub const default = CallConv.ccc; - - pub fn format( - self: CallConv, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - switch (self) { - default => {}, - .fastcc, - .coldcc, - .ghccc, - .webkit_jscc, - .anyregcc, - .preserve_mostcc, - .preserve_allcc, - .swiftcc, - .cxx_fast_tlscc, - .tailcc, - .cfguard_checkcc, - .swifttailcc, - .x86_stdcallcc, - .x86_fastcallcc, - .arm_apcscc, - .arm_aapcscc, - .arm_aapcs_vfpcc, - .msp430_intrcc, - .x86_thiscallcc, - .ptx_kernel, - .ptx_device, - .spir_func, - .spir_kernel, - .intel_ocl_bicc, - .x86_64_sysvcc, - .win64cc, - .x86_vectorcallcc, - .hhvmcc, - .hhvm_ccc, - .x86_intrcc, - .avr_intrcc, - .avr_signalcc, - .avr_builtincc, - .amdgpu_vs, - .amdgpu_gs, - .amdgpu_ps, - .amdgpu_cs, - .amdgpu_kernel, - .x86_regcallcc, - .amdgpu_hs, - .msp430_builtincc, - .amdgpu_ls, - .amdgpu_es, - .aarch64_vector_pcs, - .aarch64_sve_vector_pcs, - .amdgpu_gfx, - .m68k_intrcc, - .aarch64_sme_preservemost_from_x0, - .aarch64_sme_preservemost_from_x2, - .m68k_rtdcc, - .riscv_vectorcallcc, - => try writer.print(" {s}", .{@tagName(self)}), - _ => try writer.print(" cc{d}", .{@intFromEnum(self)}), - } - } -}; - -pub const StrtabString = enum(u32) { - none = std.math.maxInt(u31), - empty, - _, - - pub fn isAnon(self: StrtabString) bool { - assert(self != .none); - return self.toIndex() == null; - } - - pub fn slice(self: StrtabString, builder: *const Builder) ?[]const u8 { - const index = self.toIndex() orelse return null; - const start = builder.strtab_string_indices.items[index]; - const end = builder.strtab_string_indices.items[index + 1]; - return builder.strtab_string_bytes.items[start..end]; - } - - const FormatData = struct { - string: StrtabString, - builder: *const Builder, - }; - fn format( - data: FormatData, - comptime fmt_str: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_| - @compileError("invalid format string: '" ++ fmt_str ++ "'"); - assert(data.string != .none); - const string_slice = data.string.slice(data.builder) orelse - return writer.print("{d}", .{@intFromEnum(data.string)}); - if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_| - return writer.writeAll(string_slice); - try printEscapedString( - string_slice, - if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_| - .always_quote - else - .quote_unless_valid_identifier, - writer, - ); - } - pub fn fmt(self: StrtabString, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .string = self, .builder = builder } }; - } - - fn fromIndex(index: ?usize) StrtabString { - return @enumFromInt(@as(u32, @intCast((index orelse return .none) + - @intFromEnum(StrtabString.empty)))); - } - - fn toIndex(self: StrtabString) ?usize { - return std.math.sub(u32, @intFromEnum(self), @intFromEnum(StrtabString.empty)) catch null; - } - - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: Adapter, key: []const u8) u32 { - return @truncate(std.hash.Wyhash.hash(0, key)); - } - pub fn eql(ctx: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool { - return std.mem.eql(u8, lhs_key, StrtabString.fromIndex(rhs_index).slice(ctx.builder).?); - } - }; -}; - -pub fn strtabString(self: *Builder, bytes: []const u8) Allocator.Error!StrtabString { - try self.strtab_string_bytes.ensureUnusedCapacity(self.gpa, bytes.len); - try self.strtab_string_indices.ensureUnusedCapacity(self.gpa, 1); - try self.strtab_string_map.ensureUnusedCapacity(self.gpa, 1); - - const gop = self.strtab_string_map.getOrPutAssumeCapacityAdapted(bytes, StrtabString.Adapter{ .builder = self }); - if (!gop.found_existing) { - self.strtab_string_bytes.appendSliceAssumeCapacity(bytes); - self.strtab_string_indices.appendAssumeCapacity(@intCast(self.strtab_string_bytes.items.len)); - } - return StrtabString.fromIndex(gop.index); -} - -pub fn strtabStringIfExists(self: *const Builder, bytes: []const u8) ?StrtabString { - return StrtabString.fromIndex( - self.strtab_string_map.getIndexAdapted(bytes, StrtabString.Adapter{ .builder = self }) orelse return null, - ); -} - -pub fn strtabStringFmt(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) Allocator.Error!StrtabString { - try self.strtab_string_map.ensureUnusedCapacity(self.gpa, 1); - try self.strtab_string_bytes.ensureUnusedCapacity(self.gpa, @intCast(std.fmt.count(fmt_str, fmt_args))); - try self.strtab_string_indices.ensureUnusedCapacity(self.gpa, 1); - return self.strtabStringFmtAssumeCapacity(fmt_str, fmt_args); -} - -pub fn strtabStringFmtAssumeCapacity(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) StrtabString { - self.strtab_string_bytes.writer(undefined).print(fmt_str, fmt_args) catch unreachable; - return self.trailingStrtabStringAssumeCapacity(); -} - -pub fn trailingStrtabString(self: *Builder) Allocator.Error!StrtabString { - try self.strtab_string_indices.ensureUnusedCapacity(self.gpa, 1); - try self.strtab_string_map.ensureUnusedCapacity(self.gpa, 1); - return self.trailingStrtabStringAssumeCapacity(); -} - -pub fn trailingStrtabStringAssumeCapacity(self: *Builder) StrtabString { - const start = self.strtab_string_indices.getLast(); - const bytes: []const u8 = self.strtab_string_bytes.items[start..]; - const gop = self.strtab_string_map.getOrPutAssumeCapacityAdapted(bytes, StrtabString.Adapter{ .builder = self }); - if (gop.found_existing) { - self.strtab_string_bytes.shrinkRetainingCapacity(start); - } else { - self.strtab_string_indices.appendAssumeCapacity(@intCast(self.strtab_string_bytes.items.len)); - } - return StrtabString.fromIndex(gop.index); -} - -pub const Global = struct { - linkage: Linkage = .external, - preemption: Preemption = .dso_preemptable, - visibility: Visibility = .default, - dll_storage_class: DllStorageClass = .default, - unnamed_addr: UnnamedAddr = .default, - addr_space: AddrSpace = .default, - externally_initialized: ExternallyInitialized = .default, - type: Type, - partition: String = .none, - dbg: Metadata = .none, - kind: union(enum) { - alias: Alias.Index, - variable: Variable.Index, - function: Function.Index, - replaced: Global.Index, - }, - - pub const Index = enum(u32) { - none = std.math.maxInt(u32), - _, - - pub fn unwrap(self: Index, builder: *const Builder) Index { - var cur = self; - while (true) { - const replacement = cur.getReplacement(builder); - if (replacement == .none) return cur; - cur = replacement; - } - } - - pub fn eql(self: Index, other: Index, builder: *const Builder) bool { - return self.unwrap(builder) == other.unwrap(builder); - } - - pub fn ptr(self: Index, builder: *Builder) *Global { - return &builder.globals.values()[@intFromEnum(self.unwrap(builder))]; - } - - pub fn ptrConst(self: Index, builder: *const Builder) *const Global { - return &builder.globals.values()[@intFromEnum(self.unwrap(builder))]; - } - - pub fn name(self: Index, builder: *const Builder) StrtabString { - return builder.globals.keys()[@intFromEnum(self.unwrap(builder))]; - } - - pub fn strtab(self: Index, builder: *const Builder) struct { - offset: u32, - size: u32, - } { - const name_index = self.name(builder).toIndex() orelse return .{ - .offset = 0, - .size = 0, - }; - - return .{ - .offset = builder.strtab_string_indices.items[name_index], - .size = builder.strtab_string_indices.items[name_index + 1] - - builder.strtab_string_indices.items[name_index], - }; - } - - pub fn typeOf(self: Index, builder: *const Builder) Type { - return self.ptrConst(builder).type; - } - - pub fn toConst(self: Index) Constant { - return @enumFromInt(@intFromEnum(Constant.first_global) + @intFromEnum(self)); - } - - pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void { - self.ptr(builder).linkage = linkage; - self.updateDsoLocal(builder); - } - - pub fn setVisibility(self: Index, visibility: Visibility, builder: *Builder) void { - self.ptr(builder).visibility = visibility; - self.updateDsoLocal(builder); - } - - pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void { - self.ptr(builder).dll_storage_class = class; - } - - pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void { - self.ptr(builder).unnamed_addr = unnamed_addr; - } - - pub fn setDebugMetadata(self: Index, dbg: Metadata, builder: *Builder) void { - self.ptr(builder).dbg = dbg; - } - - const FormatData = struct { - global: Index, - builder: *const Builder, - }; - fn format( - data: FormatData, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - try writer.print("@{}", .{ - data.global.unwrap(data.builder).name(data.builder).fmt(data.builder), - }); - } - pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .global = self, .builder = builder } }; - } - - pub fn rename(self: Index, new_name: StrtabString, builder: *Builder) Allocator.Error!void { - try builder.ensureUnusedGlobalCapacity(new_name); - self.renameAssumeCapacity(new_name, builder); - } - - pub fn takeName(self: Index, other: Index, builder: *Builder) Allocator.Error!void { - try builder.ensureUnusedGlobalCapacity(.empty); - self.takeNameAssumeCapacity(other, builder); - } - - pub fn replace(self: Index, other: Index, builder: *Builder) Allocator.Error!void { - try builder.ensureUnusedGlobalCapacity(.empty); - self.replaceAssumeCapacity(other, builder); - } - - pub fn delete(self: Index, builder: *Builder) void { - self.ptr(builder).kind = .{ .replaced = .none }; - } - - fn updateDsoLocal(self: Index, builder: *Builder) void { - const self_ptr = self.ptr(builder); - switch (self_ptr.linkage) { - .private, .internal => { - self_ptr.visibility = .default; - self_ptr.dll_storage_class = .default; - self_ptr.preemption = .implicit_dso_local; - }, - .extern_weak => if (self_ptr.preemption == .implicit_dso_local) { - self_ptr.preemption = .dso_local; - }, - else => switch (self_ptr.visibility) { - .default => if (self_ptr.preemption == .implicit_dso_local) { - self_ptr.preemption = .dso_local; - }, - else => self_ptr.preemption = .implicit_dso_local, - }, - } - } - - fn renameAssumeCapacity(self: Index, new_name: StrtabString, builder: *Builder) void { - const old_name = self.name(builder); - if (new_name == old_name) return; - const index = @intFromEnum(self.unwrap(builder)); - _ = builder.addGlobalAssumeCapacity(new_name, builder.globals.values()[index]); - builder.globals.swapRemoveAt(index); - if (!old_name.isAnon()) return; - builder.next_unnamed_global = @enumFromInt(@intFromEnum(builder.next_unnamed_global) - 1); - if (builder.next_unnamed_global == old_name) return; - builder.getGlobal(builder.next_unnamed_global).?.renameAssumeCapacity(old_name, builder); - } - - fn takeNameAssumeCapacity(self: Index, other: Index, builder: *Builder) void { - const other_name = other.name(builder); - other.renameAssumeCapacity(.empty, builder); - self.renameAssumeCapacity(other_name, builder); - } - - fn replaceAssumeCapacity(self: Index, other: Index, builder: *Builder) void { - if (self.eql(other, builder)) return; - builder.next_replaced_global = @enumFromInt(@intFromEnum(builder.next_replaced_global) - 1); - self.renameAssumeCapacity(builder.next_replaced_global, builder); - self.ptr(builder).kind = .{ .replaced = other.unwrap(builder) }; - } - - fn getReplacement(self: Index, builder: *const Builder) Index { - return switch (builder.globals.values()[@intFromEnum(self)].kind) { - .replaced => |replacement| replacement, - else => .none, - }; - } - }; -}; - -pub const Alias = struct { - global: Global.Index, - thread_local: ThreadLocal = .default, - aliasee: Constant = .no_init, - - pub const Index = enum(u32) { - none = std.math.maxInt(u32), - _, - - pub fn ptr(self: Index, builder: *Builder) *Alias { - return &builder.aliases.items[@intFromEnum(self)]; - } - - pub fn ptrConst(self: Index, builder: *const Builder) *const Alias { - return &builder.aliases.items[@intFromEnum(self)]; - } - - pub fn name(self: Index, builder: *const Builder) StrtabString { - return self.ptrConst(builder).global.name(builder); - } - - pub fn rename(self: Index, new_name: StrtabString, builder: *Builder) Allocator.Error!void { - return self.ptrConst(builder).global.rename(new_name, builder); - } - - pub fn typeOf(self: Index, builder: *const Builder) Type { - return self.ptrConst(builder).global.typeOf(builder); - } - - pub fn toConst(self: Index, builder: *const Builder) Constant { - return self.ptrConst(builder).global.toConst(); - } - - pub fn toValue(self: Index, builder: *const Builder) Value { - return self.toConst(builder).toValue(); - } - - pub fn getAliasee(self: Index, builder: *const Builder) Global.Index { - const aliasee = self.ptrConst(builder).aliasee.getBase(builder); - assert(aliasee != .none); - return aliasee; - } - - pub fn setAliasee(self: Index, aliasee: Constant, builder: *Builder) void { - self.ptr(builder).aliasee = aliasee; - } - }; -}; - -pub const Variable = struct { - global: Global.Index, - thread_local: ThreadLocal = .default, - mutability: Mutability = .global, - init: Constant = .no_init, - section: String = .none, - alignment: Alignment = .default, - - pub const Index = enum(u32) { - none = std.math.maxInt(u32), - _, - - pub fn ptr(self: Index, builder: *Builder) *Variable { - return &builder.variables.items[@intFromEnum(self)]; - } - - pub fn ptrConst(self: Index, builder: *const Builder) *const Variable { - return &builder.variables.items[@intFromEnum(self)]; - } - - pub fn name(self: Index, builder: *const Builder) StrtabString { - return self.ptrConst(builder).global.name(builder); - } - - pub fn rename(self: Index, new_name: StrtabString, builder: *Builder) Allocator.Error!void { - return self.ptrConst(builder).global.rename(new_name, builder); - } - - pub fn typeOf(self: Index, builder: *const Builder) Type { - return self.ptrConst(builder).global.typeOf(builder); - } - - pub fn toConst(self: Index, builder: *const Builder) Constant { - return self.ptrConst(builder).global.toConst(); - } - - pub fn toValue(self: Index, builder: *const Builder) Value { - return self.toConst(builder).toValue(); - } - - pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void { - return self.ptrConst(builder).global.setLinkage(linkage, builder); - } - - pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void { - return self.ptrConst(builder).global.setDllStorageClass(class, builder); - } - - pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void { - return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder); - } - - pub fn setThreadLocal(self: Index, thread_local: ThreadLocal, builder: *Builder) void { - self.ptr(builder).thread_local = thread_local; - } - - pub fn setMutability(self: Index, mutability: Mutability, builder: *Builder) void { - self.ptr(builder).mutability = mutability; - } - - pub fn setInitializer( - self: Index, - initializer: Constant, - builder: *Builder, - ) Allocator.Error!void { - if (initializer != .no_init) { - const variable = self.ptrConst(builder); - const global = variable.global.ptr(builder); - const initializer_type = initializer.typeOf(builder); - global.type = initializer_type; - } - self.ptr(builder).init = initializer; - } - - pub fn setSection(self: Index, section: String, builder: *Builder) void { - self.ptr(builder).section = section; - } - - pub fn setAlignment(self: Index, alignment: Alignment, builder: *Builder) void { - self.ptr(builder).alignment = alignment; - } - - pub fn getAlignment(self: Index, builder: *Builder) Alignment { - return self.ptr(builder).alignment; - } - - pub fn setGlobalVariableExpression(self: Index, expression: Metadata, builder: *Builder) void { - self.ptrConst(builder).global.setDebugMetadata(expression, builder); - } - }; -}; - -pub const Intrinsic = enum { - // Variable Argument Handling - va_start, - va_end, - va_copy, - - // Code Generator - returnaddress, - addressofreturnaddress, - sponentry, - frameaddress, - prefetch, - @"thread.pointer", - - // Standard C/C++ Library - abs, - smax, - smin, - umax, - umin, - memcpy, - @"memcpy.inline", - memmove, - memset, - @"memset.inline", - sqrt, - powi, - sin, - cos, - pow, - exp, - exp10, - exp2, - ldexp, - frexp, - log, - log10, - log2, - fma, - fabs, - minnum, - maxnum, - minimum, - maximum, - copysign, - floor, - ceil, - trunc, - rint, - nearbyint, - round, - roundeven, - lround, - llround, - lrint, - llrint, - - // Bit Manipulation - bitreverse, - bswap, - ctpop, - ctlz, - cttz, - fshl, - fshr, - - // Arithmetic with Overflow - @"sadd.with.overflow", - @"uadd.with.overflow", - @"ssub.with.overflow", - @"usub.with.overflow", - @"smul.with.overflow", - @"umul.with.overflow", - - // Saturation Arithmetic - @"sadd.sat", - @"uadd.sat", - @"ssub.sat", - @"usub.sat", - @"sshl.sat", - @"ushl.sat", - - // Fixed Point Arithmetic - @"smul.fix", - @"umul.fix", - @"smul.fix.sat", - @"umul.fix.sat", - @"sdiv.fix", - @"udiv.fix", - @"sdiv.fix.sat", - @"udiv.fix.sat", - - // Specialised Arithmetic - canonicalize, - fmuladd, - - // Vector Reduction - @"vector.reduce.add", - @"vector.reduce.fadd", - @"vector.reduce.mul", - @"vector.reduce.fmul", - @"vector.reduce.and", - @"vector.reduce.or", - @"vector.reduce.xor", - @"vector.reduce.smax", - @"vector.reduce.smin", - @"vector.reduce.umax", - @"vector.reduce.umin", - @"vector.reduce.fmax", - @"vector.reduce.fmin", - @"vector.reduce.fmaximum", - @"vector.reduce.fminimum", - @"vector.insert", - @"vector.extract", - - // Floating-Point Test - @"is.fpclass", - - // General - @"var.annotation", - @"ptr.annotation", - annotation, - @"codeview.annotation", - trap, - debugtrap, - ubsantrap, - stackprotector, - stackguard, - objectsize, - expect, - @"expect.with.probability", - assume, - @"ssa.copy", - @"type.test", - @"type.checked.load", - @"type.checked.load.relative", - @"arithmetic.fence", - donothing, - @"load.relative", - sideeffect, - @"is.constant", - ptrmask, - @"threadlocal.address", - vscale, - - // Debug - @"dbg.declare", - @"dbg.value", - - // AMDGPU - @"amdgcn.workitem.id.x", - @"amdgcn.workitem.id.y", - @"amdgcn.workitem.id.z", - @"amdgcn.workgroup.id.x", - @"amdgcn.workgroup.id.y", - @"amdgcn.workgroup.id.z", - @"amdgcn.dispatch.ptr", - - // NVPTX - @"nvvm.read.ptx.sreg.tid.x", - @"nvvm.read.ptx.sreg.tid.y", - @"nvvm.read.ptx.sreg.tid.z", - @"nvvm.read.ptx.sreg.ntid.x", - @"nvvm.read.ptx.sreg.ntid.y", - @"nvvm.read.ptx.sreg.ntid.z", - @"nvvm.read.ptx.sreg.ctaid.x", - @"nvvm.read.ptx.sreg.ctaid.y", - @"nvvm.read.ptx.sreg.ctaid.z", - - // WebAssembly - @"wasm.memory.size", - @"wasm.memory.grow", - - const Signature = struct { - ret_len: u8, - params: []const Parameter, - attrs: []const Attribute = &.{}, - - const Parameter = struct { - kind: Kind, - attrs: []const Attribute = &.{}, - - const Kind = union(enum) { - type: Type, - overloaded, - matches: u8, - matches_scalar: u8, - matches_changed_scalar: struct { - index: u8, - scalar: Type, - }, - }; - }; - }; - - const signatures = std.enums.EnumArray(Intrinsic, Signature).init(.{ - .va_start = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, - }, - .va_end = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, - }, - .va_copy = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, - }, - - .returnaddress = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .ptr } }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .addressofreturnaddress = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .sponentry = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .frameaddress = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .prefetch = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .overloaded, .attrs = &.{ .nocapture, .readonly } }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.readwrite) } }, - }, - .@"thread.pointer" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .ptr } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .abs = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .smax = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .smin = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .umax = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .umin = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .memcpy = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .overloaded, .attrs = &.{ .@"noalias", .nocapture, .writeonly } }, - .{ .kind = .overloaded, .attrs = &.{ .@"noalias", .nocapture, .readonly } }, - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nounwind, .willreturn, .{ .memory = .{ .argmem = .readwrite } } }, - }, - .@"memcpy.inline" = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .overloaded, .attrs = &.{ .@"noalias", .nocapture, .writeonly } }, - .{ .kind = .overloaded, .attrs = &.{ .@"noalias", .nocapture, .readonly } }, - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nounwind, .willreturn, .{ .memory = .{ .argmem = .readwrite } } }, - }, - .memmove = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .overloaded, .attrs = &.{ .nocapture, .writeonly } }, - .{ .kind = .overloaded, .attrs = &.{ .nocapture, .readonly } }, - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nounwind, .willreturn, .{ .memory = .{ .argmem = .readwrite } } }, - }, - .memset = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .overloaded, .attrs = &.{ .nocapture, .writeonly } }, - .{ .kind = .{ .type = .i8 } }, - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nounwind, .willreturn, .{ .memory = .{ .argmem = .write } } }, - }, - .@"memset.inline" = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .overloaded, .attrs = &.{ .nocapture, .writeonly } }, - .{ .kind = .{ .type = .i8 } }, - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nounwind, .willreturn, .{ .memory = .{ .argmem = .write } } }, - }, - .sqrt = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .powi = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .sin = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .cos = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .pow = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .exp = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .exp2 = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .exp10 = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .ldexp = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .frexp = .{ - .ret_len = 2, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .log = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .log10 = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .log2 = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .fma = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .fabs = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .minnum = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .maxnum = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .minimum = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .maximum = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .copysign = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .floor = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .ceil = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .trunc = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .rint = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .nearbyint = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .round = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .roundeven = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .lround = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .llround = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .lrint = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .llrint = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .bitreverse = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .bswap = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .ctpop = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .ctlz = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .cttz = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .fshl = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .fshr = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .@"sadd.with.overflow" = .{ - .ret_len = 2, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"uadd.with.overflow" = .{ - .ret_len = 2, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"ssub.with.overflow" = .{ - .ret_len = 2, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"usub.with.overflow" = .{ - .ret_len = 2, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"smul.with.overflow" = .{ - .ret_len = 2, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"umul.with.overflow" = .{ - .ret_len = 2, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches_changed_scalar = .{ .index = 0, .scalar = .i1 } } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .@"sadd.sat" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"uadd.sat" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"ssub.sat" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"usub.sat" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"sshl.sat" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"ushl.sat" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .@"smul.fix" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"umul.fix" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"smul.fix.sat" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"umul.fix.sat" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"sdiv.fix" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"udiv.fix" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"sdiv.fix.sat" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"udiv.fix.sat" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .canonicalize = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .fmuladd = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .@"vector.reduce.add" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.fadd" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 2 } }, - .{ .kind = .{ .matches_scalar = 2 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.mul" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.fmul" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 2 } }, - .{ .kind = .{ .matches_scalar = 2 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.and" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.or" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.xor" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.smax" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.smin" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.umax" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.umin" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.fmax" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.fmin" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.fmaximum" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.reduce.fminimum" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_scalar = 1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.insert" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i64 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"vector.extract" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i64 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .@"is.fpclass" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .matches_changed_scalar = .{ .index = 1, .scalar = .i1 } } }, - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i32 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .@"var.annotation" = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 1 } }, - .{ .kind = .{ .type = .i32 } }, - .{ .kind = .{ .matches = 1 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .readwrite } } }, - }, - .@"ptr.annotation" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 2 } }, - .{ .kind = .{ .type = .i32 } }, - .{ .kind = .{ .matches = 2 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .readwrite } } }, - }, - .annotation = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 2 } }, - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .readwrite } } }, - }, - .@"codeview.annotation" = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .{ .type = .metadata } }, - }, - .attrs = &.{ .nocallback, .noduplicate, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .readwrite } } }, - }, - .trap = .{ - .ret_len = 0, - .params = &.{}, - .attrs = &.{ .cold, .noreturn, .nounwind, .{ .memory = .{ .inaccessiblemem = .write } } }, - }, - .debugtrap = .{ - .ret_len = 0, - .params = &.{}, - .attrs = &.{.nounwind}, - }, - .ubsantrap = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .{ .type = .i8 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .cold, .noreturn, .nounwind }, - }, - .stackprotector = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .{ .type = .ptr } }, - .{ .kind = .{ .type = .ptr } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, - }, - .stackguard = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .ptr } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, - }, - .objectsize = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, - .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, - .{ .kind = .{ .type = .i1 }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .expect = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"expect.with.probability" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .{ .type = .double }, .attrs = &.{.immarg} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .assume = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .{ .type = .i1 }, .attrs = &.{.noundef} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .write } } }, - }, - .@"ssa.copy" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 }, .attrs = &.{.returned} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"type.test" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i1 } }, - .{ .kind = .{ .type = .ptr } }, - .{ .kind = .{ .type = .metadata } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"type.checked.load" = .{ - .ret_len = 2, - .params = &.{ - .{ .kind = .{ .type = .ptr } }, - .{ .kind = .{ .type = .i1 } }, - .{ .kind = .{ .type = .ptr } }, - .{ .kind = .{ .type = .i32 } }, - .{ .kind = .{ .type = .metadata } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"type.checked.load.relative" = .{ - .ret_len = 2, - .params = &.{ - .{ .kind = .{ .type = .ptr } }, - .{ .kind = .{ .type = .i1 } }, - .{ .kind = .{ .type = .ptr } }, - .{ .kind = .{ .type = .i32 } }, - .{ .kind = .{ .type = .metadata } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"arithmetic.fence" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .donothing = .{ - .ret_len = 0, - .params = &.{}, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"load.relative" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .ptr } }, - .{ .kind = .{ .type = .ptr } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .argmem = .read } } }, - }, - .sideeffect = .{ - .ret_len = 0, - .params = &.{}, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = .{ .inaccessiblemem = .readwrite } } }, - }, - .@"is.constant" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i1 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .convergent, .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .ptrmask = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .matches = 0 } }, - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"threadlocal.address" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded, .attrs = &.{.nonnull} }, - .{ .kind = .{ .matches = 0 }, .attrs = &.{.nonnull} }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .vscale = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .@"dbg.declare" = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .{ .type = .metadata } }, - .{ .kind = .{ .type = .metadata } }, - .{ .kind = .{ .type = .metadata } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"dbg.value" = .{ - .ret_len = 0, - .params = &.{ - .{ .kind = .{ .type = .metadata } }, - .{ .kind = .{ .type = .metadata } }, - .{ .kind = .{ .type = .metadata } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .@"amdgcn.workitem.id.x" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"amdgcn.workitem.id.y" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"amdgcn.workitem.id.z" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"amdgcn.workgroup.id.x" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"amdgcn.workgroup.id.y" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"amdgcn.workgroup.id.z" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"amdgcn.dispatch.ptr" = .{ - .ret_len = 1, - .params = &.{ - .{ - .kind = .{ .type = Type.ptr_amdgpu_constant }, - .attrs = &.{.{ .@"align" = Builder.Alignment.fromByteUnits(4) }}, - }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - - .@"nvvm.read.ptx.sreg.tid.x" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nounwind, .readnone }, - }, - .@"nvvm.read.ptx.sreg.tid.y" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nounwind, .readnone }, - }, - .@"nvvm.read.ptx.sreg.tid.z" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nounwind, .readnone }, - }, - - .@"nvvm.read.ptx.sreg.ntid.x" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nounwind, .readnone }, - }, - .@"nvvm.read.ptx.sreg.ntid.y" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nounwind, .readnone }, - }, - .@"nvvm.read.ptx.sreg.ntid.z" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nounwind, .readnone }, - }, - - .@"nvvm.read.ptx.sreg.ctaid.x" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nounwind, .readnone }, - }, - .@"nvvm.read.ptx.sreg.ctaid.y" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nounwind, .readnone }, - }, - .@"nvvm.read.ptx.sreg.ctaid.z" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nounwind, .readnone }, - }, - - .@"wasm.memory.size" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i32 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, - }, - .@"wasm.memory.grow" = .{ - .ret_len = 1, - .params = &.{ - .{ .kind = .overloaded }, - .{ .kind = .{ .type = .i32 } }, - .{ .kind = .{ .matches = 0 } }, - }, - .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn }, - }, - }); -}; - -pub const Function = struct { - global: Global.Index, - call_conv: CallConv = CallConv.default, - attributes: FunctionAttributes = .none, - section: String = .none, - alignment: Alignment = .default, - blocks: []const Block = &.{}, - instructions: std.MultiArrayList(Instruction) = .{}, - names: [*]const String = &[0]String{}, - value_indices: [*]const u32 = &[0]u32{}, - strip: bool, - debug_locations: std.AutoHashMapUnmanaged(Instruction.Index, DebugLocation) = .empty, - debug_values: []const Instruction.Index = &.{}, - extra: []const u32 = &.{}, - - pub const Index = enum(u32) { - none = std.math.maxInt(u32), - _, - - pub fn ptr(self: Index, builder: *Builder) *Function { - return &builder.functions.items[@intFromEnum(self)]; - } - - pub fn ptrConst(self: Index, builder: *const Builder) *const Function { - return &builder.functions.items[@intFromEnum(self)]; - } - - pub fn name(self: Index, builder: *const Builder) StrtabString { - return self.ptrConst(builder).global.name(builder); - } - - pub fn rename(self: Index, new_name: StrtabString, builder: *Builder) Allocator.Error!void { - return self.ptrConst(builder).global.rename(new_name, builder); - } - - pub fn typeOf(self: Index, builder: *const Builder) Type { - return self.ptrConst(builder).global.typeOf(builder); - } - - pub fn toConst(self: Index, builder: *const Builder) Constant { - return self.ptrConst(builder).global.toConst(); - } - - pub fn toValue(self: Index, builder: *const Builder) Value { - return self.toConst(builder).toValue(); - } - - pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void { - return self.ptrConst(builder).global.setLinkage(linkage, builder); - } - - pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void { - return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder); - } - - pub fn setCallConv(self: Index, call_conv: CallConv, builder: *Builder) void { - self.ptr(builder).call_conv = call_conv; - } - - pub fn setAttributes( - self: Index, - new_function_attributes: FunctionAttributes, - builder: *Builder, - ) void { - self.ptr(builder).attributes = new_function_attributes; - } - - pub fn setSection(self: Index, section: String, builder: *Builder) void { - self.ptr(builder).section = section; - } - - pub fn setAlignment(self: Index, alignment: Alignment, builder: *Builder) void { - self.ptr(builder).alignment = alignment; - } - - pub fn setSubprogram(self: Index, subprogram: Metadata, builder: *Builder) void { - self.ptrConst(builder).global.setDebugMetadata(subprogram, builder); - } - }; - - pub const Block = struct { - instruction: Instruction.Index, - - pub const Index = WipFunction.Block.Index; - }; - - pub const Instruction = struct { - tag: Tag, - data: u32, - - pub const Tag = enum(u8) { - add, - @"add nsw", - @"add nuw", - @"add nuw nsw", - addrspacecast, - alloca, - @"alloca inalloca", - @"and", - arg, - ashr, - @"ashr exact", - atomicrmw, - bitcast, - block, - br, - br_cond, - call, - @"call fast", - cmpxchg, - @"cmpxchg weak", - extractelement, - extractvalue, - fadd, - @"fadd fast", - @"fcmp false", - @"fcmp fast false", - @"fcmp fast oeq", - @"fcmp fast oge", - @"fcmp fast ogt", - @"fcmp fast ole", - @"fcmp fast olt", - @"fcmp fast one", - @"fcmp fast ord", - @"fcmp fast true", - @"fcmp fast ueq", - @"fcmp fast uge", - @"fcmp fast ugt", - @"fcmp fast ule", - @"fcmp fast ult", - @"fcmp fast une", - @"fcmp fast uno", - @"fcmp oeq", - @"fcmp oge", - @"fcmp ogt", - @"fcmp ole", - @"fcmp olt", - @"fcmp one", - @"fcmp ord", - @"fcmp true", - @"fcmp ueq", - @"fcmp uge", - @"fcmp ugt", - @"fcmp ule", - @"fcmp ult", - @"fcmp une", - @"fcmp uno", - fdiv, - @"fdiv fast", - fence, - fmul, - @"fmul fast", - fneg, - @"fneg fast", - fpext, - fptosi, - fptoui, - fptrunc, - frem, - @"frem fast", - fsub, - @"fsub fast", - getelementptr, - @"getelementptr inbounds", - @"icmp eq", - @"icmp ne", - @"icmp sge", - @"icmp sgt", - @"icmp sle", - @"icmp slt", - @"icmp uge", - @"icmp ugt", - @"icmp ule", - @"icmp ult", - indirectbr, - insertelement, - insertvalue, - inttoptr, - load, - @"load atomic", - lshr, - @"lshr exact", - mul, - @"mul nsw", - @"mul nuw", - @"mul nuw nsw", - @"musttail call", - @"musttail call fast", - @"notail call", - @"notail call fast", - @"or", - phi, - @"phi fast", - ptrtoint, - ret, - @"ret void", - sdiv, - @"sdiv exact", - select, - @"select fast", - sext, - shl, - @"shl nsw", - @"shl nuw", - @"shl nuw nsw", - shufflevector, - sitofp, - srem, - store, - @"store atomic", - sub, - @"sub nsw", - @"sub nuw", - @"sub nuw nsw", - @"switch", - @"tail call", - @"tail call fast", - trunc, - udiv, - @"udiv exact", - urem, - uitofp, - @"unreachable", - va_arg, - xor, - zext, - - pub fn toBinaryOpcode(self: Tag) BinaryOpcode { - return switch (self) { - .add, - .@"add nsw", - .@"add nuw", - .@"add nuw nsw", - .fadd, - .@"fadd fast", - => .add, - .sub, - .@"sub nsw", - .@"sub nuw", - .@"sub nuw nsw", - .fsub, - .@"fsub fast", - => .sub, - .sdiv, - .@"sdiv exact", - .fdiv, - .@"fdiv fast", - => .sdiv, - .fmul, - .@"fmul fast", - .mul, - .@"mul nsw", - .@"mul nuw", - .@"mul nuw nsw", - => .mul, - .srem, - .frem, - .@"frem fast", - => .srem, - .udiv, - .@"udiv exact", - => .udiv, - .shl, - .@"shl nsw", - .@"shl nuw", - .@"shl nuw nsw", - => .shl, - .lshr, - .@"lshr exact", - => .lshr, - .ashr, - .@"ashr exact", - => .ashr, - .@"and" => .@"and", - .@"or" => .@"or", - .xor => .xor, - .urem => .urem, - else => unreachable, - }; - } - - pub fn toCastOpcode(self: Tag) CastOpcode { - return switch (self) { - .trunc => .trunc, - .zext => .zext, - .sext => .sext, - .fptoui => .fptoui, - .fptosi => .fptosi, - .uitofp => .uitofp, - .sitofp => .sitofp, - .fptrunc => .fptrunc, - .fpext => .fpext, - .ptrtoint => .ptrtoint, - .inttoptr => .inttoptr, - .bitcast => .bitcast, - .addrspacecast => .addrspacecast, - else => unreachable, - }; - } - - pub fn toCmpPredicate(self: Tag) CmpPredicate { - return switch (self) { - .@"fcmp false", - .@"fcmp fast false", - => .fcmp_false, - .@"fcmp oeq", - .@"fcmp fast oeq", - => .fcmp_oeq, - .@"fcmp oge", - .@"fcmp fast oge", - => .fcmp_oge, - .@"fcmp ogt", - .@"fcmp fast ogt", - => .fcmp_ogt, - .@"fcmp ole", - .@"fcmp fast ole", - => .fcmp_ole, - .@"fcmp olt", - .@"fcmp fast olt", - => .fcmp_olt, - .@"fcmp one", - .@"fcmp fast one", - => .fcmp_one, - .@"fcmp ord", - .@"fcmp fast ord", - => .fcmp_ord, - .@"fcmp true", - .@"fcmp fast true", - => .fcmp_true, - .@"fcmp ueq", - .@"fcmp fast ueq", - => .fcmp_ueq, - .@"fcmp uge", - .@"fcmp fast uge", - => .fcmp_uge, - .@"fcmp ugt", - .@"fcmp fast ugt", - => .fcmp_ugt, - .@"fcmp ule", - .@"fcmp fast ule", - => .fcmp_ule, - .@"fcmp ult", - .@"fcmp fast ult", - => .fcmp_ult, - .@"fcmp une", - .@"fcmp fast une", - => .fcmp_une, - .@"fcmp uno", - .@"fcmp fast uno", - => .fcmp_uno, - .@"icmp eq" => .icmp_eq, - .@"icmp ne" => .icmp_ne, - .@"icmp sge" => .icmp_sge, - .@"icmp sgt" => .icmp_sgt, - .@"icmp sle" => .icmp_sle, - .@"icmp slt" => .icmp_slt, - .@"icmp uge" => .icmp_uge, - .@"icmp ugt" => .icmp_ugt, - .@"icmp ule" => .icmp_ule, - .@"icmp ult" => .icmp_ult, - else => unreachable, - }; - } - }; - - pub const Index = enum(u32) { - none = std.math.maxInt(u31), - _, - - pub fn name(self: Instruction.Index, function: *const Function) String { - return function.names[@intFromEnum(self)]; - } - - pub fn valueIndex(self: Instruction.Index, function: *const Function) u32 { - return function.value_indices[@intFromEnum(self)]; - } - - pub fn toValue(self: Instruction.Index) Value { - return @enumFromInt(@intFromEnum(self)); - } - - pub fn isTerminatorWip(self: Instruction.Index, wip: *const WipFunction) bool { - return switch (wip.instructions.items(.tag)[@intFromEnum(self)]) { - .br, - .br_cond, - .indirectbr, - .ret, - .@"ret void", - .@"switch", - .@"unreachable", - => true, - else => false, - }; - } - - pub fn hasResultWip(self: Instruction.Index, wip: *const WipFunction) bool { - return switch (wip.instructions.items(.tag)[@intFromEnum(self)]) { - .br, - .br_cond, - .fence, - .indirectbr, - .ret, - .@"ret void", - .store, - .@"store atomic", - .@"switch", - .@"unreachable", - .block, - => false, - .call, - .@"call fast", - .@"musttail call", - .@"musttail call fast", - .@"notail call", - .@"notail call fast", - .@"tail call", - .@"tail call fast", - => self.typeOfWip(wip) != .void, - else => true, - }; - } - - pub fn typeOfWip(self: Instruction.Index, wip: *const WipFunction) Type { - const instruction = wip.instructions.get(@intFromEnum(self)); - return switch (instruction.tag) { - .add, - .@"add nsw", - .@"add nuw", - .@"add nuw nsw", - .@"and", - .ashr, - .@"ashr exact", - .fadd, - .@"fadd fast", - .fdiv, - .@"fdiv fast", - .fmul, - .@"fmul fast", - .frem, - .@"frem fast", - .fsub, - .@"fsub fast", - .lshr, - .@"lshr exact", - .mul, - .@"mul nsw", - .@"mul nuw", - .@"mul nuw nsw", - .@"or", - .sdiv, - .@"sdiv exact", - .shl, - .@"shl nsw", - .@"shl nuw", - .@"shl nuw nsw", - .srem, - .sub, - .@"sub nsw", - .@"sub nuw", - .@"sub nuw nsw", - .udiv, - .@"udiv exact", - .urem, - .xor, - => wip.extraData(Binary, instruction.data).lhs.typeOfWip(wip), - .addrspacecast, - .bitcast, - .fpext, - .fptosi, - .fptoui, - .fptrunc, - .inttoptr, - .ptrtoint, - .sext, - .sitofp, - .trunc, - .uitofp, - .zext, - => wip.extraData(Cast, instruction.data).type, - .alloca, - .@"alloca inalloca", - => wip.builder.ptrTypeAssumeCapacity( - wip.extraData(Alloca, instruction.data).info.addr_space, - ), - .arg => wip.function.typeOf(wip.builder) - .functionParameters(wip.builder)[instruction.data], - .atomicrmw => wip.extraData(AtomicRmw, instruction.data).val.typeOfWip(wip), - .block => .label, - .br, - .br_cond, - .fence, - .indirectbr, - .ret, - .@"ret void", - .store, - .@"store atomic", - .@"switch", - .@"unreachable", - => .none, - .call, - .@"call fast", - .@"musttail call", - .@"musttail call fast", - .@"notail call", - .@"notail call fast", - .@"tail call", - .@"tail call fast", - => wip.extraData(Call, instruction.data).ty.functionReturn(wip.builder), - .cmpxchg, - .@"cmpxchg weak", - => wip.builder.structTypeAssumeCapacity(.normal, &.{ - wip.extraData(CmpXchg, instruction.data).cmp.typeOfWip(wip), - .i1, - }), - .extractelement => wip.extraData(ExtractElement, instruction.data) - .val.typeOfWip(wip).childType(wip.builder), - .extractvalue => { - var extra = wip.extraDataTrail(ExtractValue, instruction.data); - const indices = extra.trail.next(extra.data.indices_len, u32, wip); - return extra.data.val.typeOfWip(wip).childTypeAt(indices, wip.builder); - }, - .@"fcmp false", - .@"fcmp fast false", - .@"fcmp fast oeq", - .@"fcmp fast oge", - .@"fcmp fast ogt", - .@"fcmp fast ole", - .@"fcmp fast olt", - .@"fcmp fast one", - .@"fcmp fast ord", - .@"fcmp fast true", - .@"fcmp fast ueq", - .@"fcmp fast uge", - .@"fcmp fast ugt", - .@"fcmp fast ule", - .@"fcmp fast ult", - .@"fcmp fast une", - .@"fcmp fast uno", - .@"fcmp oeq", - .@"fcmp oge", - .@"fcmp ogt", - .@"fcmp ole", - .@"fcmp olt", - .@"fcmp one", - .@"fcmp ord", - .@"fcmp true", - .@"fcmp ueq", - .@"fcmp uge", - .@"fcmp ugt", - .@"fcmp ule", - .@"fcmp ult", - .@"fcmp une", - .@"fcmp uno", - .@"icmp eq", - .@"icmp ne", - .@"icmp sge", - .@"icmp sgt", - .@"icmp sle", - .@"icmp slt", - .@"icmp uge", - .@"icmp ugt", - .@"icmp ule", - .@"icmp ult", - => wip.extraData(Binary, instruction.data).lhs.typeOfWip(wip) - .changeScalarAssumeCapacity(.i1, wip.builder), - .fneg, - .@"fneg fast", - => @as(Value, @enumFromInt(instruction.data)).typeOfWip(wip), - .getelementptr, - .@"getelementptr inbounds", - => { - var extra = wip.extraDataTrail(GetElementPtr, instruction.data); - const indices = extra.trail.next(extra.data.indices_len, Value, wip); - const base_ty = extra.data.base.typeOfWip(wip); - if (!base_ty.isVector(wip.builder)) for (indices) |index| { - const index_ty = index.typeOfWip(wip); - if (!index_ty.isVector(wip.builder)) continue; - return index_ty.changeScalarAssumeCapacity(base_ty, wip.builder); - }; - return base_ty; - }, - .insertelement => wip.extraData(InsertElement, instruction.data).val.typeOfWip(wip), - .insertvalue => wip.extraData(InsertValue, instruction.data).val.typeOfWip(wip), - .load, - .@"load atomic", - => wip.extraData(Load, instruction.data).type, - .phi, - .@"phi fast", - => wip.extraData(Phi, instruction.data).type, - .select, - .@"select fast", - => wip.extraData(Select, instruction.data).lhs.typeOfWip(wip), - .shufflevector => { - const extra = wip.extraData(ShuffleVector, instruction.data); - return extra.lhs.typeOfWip(wip).changeLengthAssumeCapacity( - extra.mask.typeOfWip(wip).vectorLen(wip.builder), - wip.builder, - ); - }, - .va_arg => wip.extraData(VaArg, instruction.data).type, - }; - } - - pub fn typeOf( - self: Instruction.Index, - function_index: Function.Index, - builder: *Builder, - ) Type { - const function = function_index.ptrConst(builder); - const instruction = function.instructions.get(@intFromEnum(self)); - return switch (instruction.tag) { - .add, - .@"add nsw", - .@"add nuw", - .@"add nuw nsw", - .@"and", - .ashr, - .@"ashr exact", - .fadd, - .@"fadd fast", - .fdiv, - .@"fdiv fast", - .fmul, - .@"fmul fast", - .frem, - .@"frem fast", - .fsub, - .@"fsub fast", - .lshr, - .@"lshr exact", - .mul, - .@"mul nsw", - .@"mul nuw", - .@"mul nuw nsw", - .@"or", - .sdiv, - .@"sdiv exact", - .shl, - .@"shl nsw", - .@"shl nuw", - .@"shl nuw nsw", - .srem, - .sub, - .@"sub nsw", - .@"sub nuw", - .@"sub nuw nsw", - .udiv, - .@"udiv exact", - .urem, - .xor, - => function.extraData(Binary, instruction.data).lhs.typeOf(function_index, builder), - .addrspacecast, - .bitcast, - .fpext, - .fptosi, - .fptoui, - .fptrunc, - .inttoptr, - .ptrtoint, - .sext, - .sitofp, - .trunc, - .uitofp, - .zext, - => function.extraData(Cast, instruction.data).type, - .alloca, - .@"alloca inalloca", - => builder.ptrTypeAssumeCapacity( - function.extraData(Alloca, instruction.data).info.addr_space, - ), - .arg => function.global.typeOf(builder) - .functionParameters(builder)[instruction.data], - .atomicrmw => function.extraData(AtomicRmw, instruction.data) - .val.typeOf(function_index, builder), - .block => .label, - .br, - .br_cond, - .fence, - .indirectbr, - .ret, - .@"ret void", - .store, - .@"store atomic", - .@"switch", - .@"unreachable", - => .none, - .call, - .@"call fast", - .@"musttail call", - .@"musttail call fast", - .@"notail call", - .@"notail call fast", - .@"tail call", - .@"tail call fast", - => function.extraData(Call, instruction.data).ty.functionReturn(builder), - .cmpxchg, - .@"cmpxchg weak", - => builder.structTypeAssumeCapacity(.normal, &.{ - function.extraData(CmpXchg, instruction.data) - .cmp.typeOf(function_index, builder), - .i1, - }), - .extractelement => function.extraData(ExtractElement, instruction.data) - .val.typeOf(function_index, builder).childType(builder), - .extractvalue => { - var extra = function.extraDataTrail(ExtractValue, instruction.data); - const indices = extra.trail.next(extra.data.indices_len, u32, function); - return extra.data.val.typeOf(function_index, builder) - .childTypeAt(indices, builder); - }, - .@"fcmp false", - .@"fcmp fast false", - .@"fcmp fast oeq", - .@"fcmp fast oge", - .@"fcmp fast ogt", - .@"fcmp fast ole", - .@"fcmp fast olt", - .@"fcmp fast one", - .@"fcmp fast ord", - .@"fcmp fast true", - .@"fcmp fast ueq", - .@"fcmp fast uge", - .@"fcmp fast ugt", - .@"fcmp fast ule", - .@"fcmp fast ult", - .@"fcmp fast une", - .@"fcmp fast uno", - .@"fcmp oeq", - .@"fcmp oge", - .@"fcmp ogt", - .@"fcmp ole", - .@"fcmp olt", - .@"fcmp one", - .@"fcmp ord", - .@"fcmp true", - .@"fcmp ueq", - .@"fcmp uge", - .@"fcmp ugt", - .@"fcmp ule", - .@"fcmp ult", - .@"fcmp une", - .@"fcmp uno", - .@"icmp eq", - .@"icmp ne", - .@"icmp sge", - .@"icmp sgt", - .@"icmp sle", - .@"icmp slt", - .@"icmp uge", - .@"icmp ugt", - .@"icmp ule", - .@"icmp ult", - => function.extraData(Binary, instruction.data).lhs.typeOf(function_index, builder) - .changeScalarAssumeCapacity(.i1, builder), - .fneg, - .@"fneg fast", - => @as(Value, @enumFromInt(instruction.data)).typeOf(function_index, builder), - .getelementptr, - .@"getelementptr inbounds", - => { - var extra = function.extraDataTrail(GetElementPtr, instruction.data); - const indices = extra.trail.next(extra.data.indices_len, Value, function); - const base_ty = extra.data.base.typeOf(function_index, builder); - if (!base_ty.isVector(builder)) for (indices) |index| { - const index_ty = index.typeOf(function_index, builder); - if (!index_ty.isVector(builder)) continue; - return index_ty.changeScalarAssumeCapacity(base_ty, builder); - }; - return base_ty; - }, - .insertelement => function.extraData(InsertElement, instruction.data) - .val.typeOf(function_index, builder), - .insertvalue => function.extraData(InsertValue, instruction.data) - .val.typeOf(function_index, builder), - .load, - .@"load atomic", - => function.extraData(Load, instruction.data).type, - .phi, - .@"phi fast", - => function.extraData(Phi, instruction.data).type, - .select, - .@"select fast", - => function.extraData(Select, instruction.data).lhs.typeOf(function_index, builder), - .shufflevector => { - const extra = function.extraData(ShuffleVector, instruction.data); - return extra.lhs.typeOf(function_index, builder).changeLengthAssumeCapacity( - extra.mask.typeOf(function_index, builder).vectorLen(builder), - builder, - ); - }, - .va_arg => function.extraData(VaArg, instruction.data).type, - }; - } - - const FormatData = struct { - instruction: Instruction.Index, - function: Function.Index, - builder: *Builder, - }; - fn format( - data: FormatData, - comptime fmt_str: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_| - @compileError("invalid format string: '" ++ fmt_str ++ "'"); - if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) { - if (data.instruction == .none) return; - try writer.writeByte(','); - } - if (comptime std.mem.indexOfScalar(u8, fmt_str, ' ') != null) { - if (data.instruction == .none) return; - try writer.writeByte(' '); - } - if (comptime std.mem.indexOfScalar(u8, fmt_str, '%') != null) try writer.print( - "{%} ", - .{data.instruction.typeOf(data.function, data.builder).fmt(data.builder)}, - ); - assert(data.instruction != .none); - try writer.print("%{}", .{ - data.instruction.name(data.function.ptrConst(data.builder)).fmt(data.builder), - }); - } - pub fn fmt( - self: Instruction.Index, - function: Function.Index, - builder: *Builder, - ) std.fmt.Formatter(format) { - return .{ .data = .{ .instruction = self, .function = function, .builder = builder } }; - } - }; - - pub const ExtraIndex = u32; - - pub const BrCond = struct { - cond: Value, - then: Block.Index, - @"else": Block.Index, - weights: Weights, - pub const Weights = enum(u32) { - // We can do this as metadata indices 0 and 1 are reserved. - none = 0, - unpredictable = 1, - /// These values should be converted to `Metadata` to be used - /// in a `prof` annotation providing branch weights. - _, - }; - }; - - pub const Switch = struct { - val: Value, - default: Block.Index, - cases_len: u32, - weights: BrCond.Weights, - //case_vals: [cases_len]Constant, - //case_blocks: [cases_len]Block.Index, - }; - - pub const IndirectBr = struct { - addr: Value, - targets_len: u32, - //targets: [targets_len]Block.Index, - }; - - pub const Binary = struct { - lhs: Value, - rhs: Value, - }; - - pub const ExtractElement = struct { - val: Value, - index: Value, - }; - - pub const InsertElement = struct { - val: Value, - elem: Value, - index: Value, - }; - - pub const ShuffleVector = struct { - lhs: Value, - rhs: Value, - mask: Value, - }; - - pub const ExtractValue = struct { - val: Value, - indices_len: u32, - //indices: [indices_len]u32, - }; - - pub const InsertValue = struct { - val: Value, - elem: Value, - indices_len: u32, - //indices: [indices_len]u32, - }; - - pub const Alloca = struct { - type: Type, - len: Value, - info: Info, - - pub const Kind = enum { normal, inalloca }; - pub const Info = packed struct(u32) { - alignment: Alignment, - addr_space: AddrSpace, - _: u2 = undefined, - }; - }; - - pub const Load = struct { - info: MemoryAccessInfo, - type: Type, - ptr: Value, - }; - - pub const Store = struct { - info: MemoryAccessInfo, - val: Value, - ptr: Value, - }; - - pub const CmpXchg = struct { - info: MemoryAccessInfo, - ptr: Value, - cmp: Value, - new: Value, - - pub const Kind = enum { strong, weak }; - }; - - pub const AtomicRmw = struct { - info: MemoryAccessInfo, - ptr: Value, - val: Value, - - pub const Operation = enum(u5) { - xchg = 0, - add = 1, - sub = 2, - @"and" = 3, - nand = 4, - @"or" = 5, - xor = 6, - max = 7, - min = 8, - umax = 9, - umin = 10, - fadd = 11, - fsub = 12, - fmax = 13, - fmin = 14, - none = std.math.maxInt(u5), - }; - }; - - pub const GetElementPtr = struct { - type: Type, - base: Value, - indices_len: u32, - //indices: [indices_len]Value, - - pub const Kind = Constant.GetElementPtr.Kind; - }; - - pub const Cast = struct { - val: Value, - type: Type, - - pub const Signedness = Constant.Cast.Signedness; - }; - - pub const Phi = struct { - type: Type, - //incoming_vals: [block.incoming]Value, - //incoming_blocks: [block.incoming]Block.Index, - }; - - pub const Select = struct { - cond: Value, - lhs: Value, - rhs: Value, - }; - - pub const Call = struct { - info: Info, - attributes: FunctionAttributes, - ty: Type, - callee: Value, - args_len: u32, - //args: [args_len]Value, - - pub const Kind = enum { - normal, - fast, - musttail, - musttail_fast, - notail, - notail_fast, - tail, - tail_fast, - }; - pub const Info = packed struct(u32) { - call_conv: CallConv, - has_op_bundle_cold: bool, - _: u21 = undefined, - }; - }; - - pub const VaArg = struct { - list: Value, - type: Type, - }; - }; - - pub fn deinit(self: *Function, gpa: Allocator) void { - gpa.free(self.extra); - gpa.free(self.debug_values); - self.debug_locations.deinit(gpa); - gpa.free(self.value_indices[0..self.instructions.len]); - gpa.free(self.names[0..self.instructions.len]); - self.instructions.deinit(gpa); - gpa.free(self.blocks); - self.* = undefined; - } - - pub fn arg(self: *const Function, index: u32) Value { - const argument = self.instructions.get(index); - assert(argument.tag == .arg); - assert(argument.data == index); - - const argument_index: Instruction.Index = @enumFromInt(index); - return argument_index.toValue(); - } - - const ExtraDataTrail = struct { - index: Instruction.ExtraIndex, - - fn nextMut(self: *ExtraDataTrail, len: u32, comptime Item: type, function: *Function) []Item { - const items: []Item = @ptrCast(function.extra[self.index..][0..len]); - self.index += @intCast(len); - return items; - } - - fn next( - self: *ExtraDataTrail, - len: u32, - comptime Item: type, - function: *const Function, - ) []const Item { - const items: []const Item = @ptrCast(function.extra[self.index..][0..len]); - self.index += @intCast(len); - return items; - } - }; - - fn extraDataTrail( - self: *const Function, - comptime T: type, - index: Instruction.ExtraIndex, - ) struct { data: T, trail: ExtraDataTrail } { - var result: T = undefined; - const fields = @typeInfo(T).@"struct".fields; - inline for (fields, self.extra[index..][0..fields.len]) |field, value| - @field(result, field.name) = switch (field.type) { - u32 => value, - Alignment, - AtomicOrdering, - Block.Index, - FunctionAttributes, - Type, - Value, - Instruction.BrCond.Weights, - => @enumFromInt(value), - MemoryAccessInfo, - Instruction.Alloca.Info, - Instruction.Call.Info, - => @bitCast(value), - else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), - }; - return .{ - .data = result, - .trail = .{ .index = index + @as(Type.Item.ExtraIndex, @intCast(fields.len)) }, - }; - } - - fn extraData(self: *const Function, comptime T: type, index: Instruction.ExtraIndex) T { - return self.extraDataTrail(T, index).data; - } -}; - -pub const DebugLocation = union(enum) { - no_location: void, - location: Location, - - pub const Location = struct { - line: u32, - column: u32, - scope: Builder.Metadata, - inlined_at: Builder.Metadata, - }; - - pub fn toMetadata(self: DebugLocation, builder: *Builder) Allocator.Error!Metadata { - return switch (self) { - .no_location => .none, - .location => |location| try builder.debugLocation( - location.line, - location.column, - location.scope, - location.inlined_at, - ), - }; - } -}; - -pub const WipFunction = struct { - builder: *Builder, - function: Function.Index, - prev_debug_location: DebugLocation, - debug_location: DebugLocation, - cursor: Cursor, - blocks: std.ArrayListUnmanaged(Block), - instructions: std.MultiArrayList(Instruction), - names: std.ArrayListUnmanaged(String), - strip: bool, - debug_locations: std.AutoArrayHashMapUnmanaged(Instruction.Index, DebugLocation), - debug_values: std.AutoArrayHashMapUnmanaged(Instruction.Index, void), - extra: std.ArrayListUnmanaged(u32), - - pub const Cursor = struct { block: Block.Index, instruction: u32 = 0 }; - - pub const Block = struct { - name: String, - incoming: u32, - branches: u32 = 0, - instructions: std.ArrayListUnmanaged(Instruction.Index), - - const Index = enum(u32) { - entry, - _, - - pub fn ptr(self: Index, wip: *WipFunction) *Block { - return &wip.blocks.items[@intFromEnum(self)]; - } - - pub fn ptrConst(self: Index, wip: *const WipFunction) *const Block { - return &wip.blocks.items[@intFromEnum(self)]; - } - - pub fn toInst(self: Index, function: *const Function) Instruction.Index { - return function.blocks[@intFromEnum(self)].instruction; - } - }; - }; - - pub const Instruction = Function.Instruction; - - pub fn init(builder: *Builder, options: struct { - function: Function.Index, - strip: bool, - }) Allocator.Error!WipFunction { - var self: WipFunction = .{ - .builder = builder, - .function = options.function, - .prev_debug_location = .no_location, - .debug_location = .no_location, - .cursor = undefined, - .blocks = .{}, - .instructions = .{}, - .names = .{}, - .strip = options.strip, - .debug_locations = .{}, - .debug_values = .{}, - .extra = .{}, - }; - errdefer self.deinit(); - - const params_len = options.function.typeOf(self.builder).functionParameters(self.builder).len; - try self.ensureUnusedExtraCapacity(params_len, NoExtra, 0); - try self.instructions.ensureUnusedCapacity(self.builder.gpa, params_len); - if (!self.strip) { - try self.names.ensureUnusedCapacity(self.builder.gpa, params_len); - } - for (0..params_len) |param_index| { - self.instructions.appendAssumeCapacity(.{ .tag = .arg, .data = @intCast(param_index) }); - if (!self.strip) { - self.names.appendAssumeCapacity(.empty); // TODO: param names - } - } - - return self; - } - - pub fn arg(self: *const WipFunction, index: u32) Value { - const argument = self.instructions.get(index); - assert(argument.tag == .arg); - assert(argument.data == index); - - const argument_index: Instruction.Index = @enumFromInt(index); - return argument_index.toValue(); - } - - pub fn block(self: *WipFunction, incoming: u32, name: []const u8) Allocator.Error!Block.Index { - try self.blocks.ensureUnusedCapacity(self.builder.gpa, 1); - - const index: Block.Index = @enumFromInt(self.blocks.items.len); - const final_name = if (self.strip) .empty else try self.builder.string(name); - self.blocks.appendAssumeCapacity(.{ - .name = final_name, - .incoming = incoming, - .instructions = .{}, - }); - return index; - } - - pub fn ret(self: *WipFunction, val: Value) Allocator.Error!Instruction.Index { - assert(val.typeOfWip(self) == self.function.typeOf(self.builder).functionReturn(self.builder)); - try self.ensureUnusedExtraCapacity(1, NoExtra, 0); - return try self.addInst(null, .{ .tag = .ret, .data = @intFromEnum(val) }); - } - - pub fn retVoid(self: *WipFunction) Allocator.Error!Instruction.Index { - try self.ensureUnusedExtraCapacity(1, NoExtra, 0); - return try self.addInst(null, .{ .tag = .@"ret void", .data = undefined }); - } - - pub fn br(self: *WipFunction, dest: Block.Index) Allocator.Error!Instruction.Index { - try self.ensureUnusedExtraCapacity(1, NoExtra, 0); - const instruction = try self.addInst(null, .{ .tag = .br, .data = @intFromEnum(dest) }); - dest.ptr(self).branches += 1; - return instruction; - } - - pub fn brCond( - self: *WipFunction, - cond: Value, - then: Block.Index, - @"else": Block.Index, - weights: enum { none, unpredictable, then_likely, else_likely }, - ) Allocator.Error!Instruction.Index { - assert(cond.typeOfWip(self) == .i1); - try self.ensureUnusedExtraCapacity(1, Instruction.BrCond, 0); - const instruction = try self.addInst(null, .{ - .tag = .br_cond, - .data = self.addExtraAssumeCapacity(Instruction.BrCond{ - .cond = cond, - .then = then, - .@"else" = @"else", - .weights = switch (weights) { - .none => .none, - .unpredictable => .unpredictable, - .then_likely, .else_likely => w: { - const branch_weights_str = try self.builder.metadataString("branch_weights"); - const unlikely_const = try self.builder.metadataConstant(try self.builder.intConst(.i32, 1)); - const likely_const = try self.builder.metadataConstant(try self.builder.intConst(.i32, 2000)); - const weight_vals: [2]Metadata = switch (weights) { - .none, .unpredictable => unreachable, - .then_likely => .{ likely_const, unlikely_const }, - .else_likely => .{ unlikely_const, likely_const }, - }; - const tuple = try self.builder.strTuple(branch_weights_str, &weight_vals); - break :w @enumFromInt(@intFromEnum(tuple)); - }, - }, - }), - }); - then.ptr(self).branches += 1; - @"else".ptr(self).branches += 1; - return instruction; - } - - pub const WipSwitch = struct { - index: u32, - instruction: Instruction.Index, - - pub fn addCase( - self: *WipSwitch, - val: Constant, - dest: Block.Index, - wip: *WipFunction, - ) Allocator.Error!void { - const instruction = wip.instructions.get(@intFromEnum(self.instruction)); - var extra = wip.extraDataTrail(Instruction.Switch, instruction.data); - assert(val.typeOf(wip.builder) == extra.data.val.typeOfWip(wip)); - extra.trail.nextMut(extra.data.cases_len, Constant, wip)[self.index] = val; - extra.trail.nextMut(extra.data.cases_len, Block.Index, wip)[self.index] = dest; - self.index += 1; - dest.ptr(wip).branches += 1; - } - - pub fn finish(self: WipSwitch, wip: *WipFunction) void { - const instruction = wip.instructions.get(@intFromEnum(self.instruction)); - const extra = wip.extraData(Instruction.Switch, instruction.data); - assert(self.index == extra.cases_len); - } - }; - - pub fn @"switch"( - self: *WipFunction, - val: Value, - default: Block.Index, - cases_len: u32, - weights: Instruction.BrCond.Weights, - ) Allocator.Error!WipSwitch { - try self.ensureUnusedExtraCapacity(1, Instruction.Switch, cases_len * 2); - const instruction = try self.addInst(null, .{ - .tag = .@"switch", - .data = self.addExtraAssumeCapacity(Instruction.Switch{ - .val = val, - .default = default, - .cases_len = cases_len, - .weights = weights, - }), - }); - _ = self.extra.addManyAsSliceAssumeCapacity(cases_len * 2); - default.ptr(self).branches += 1; - return .{ .index = 0, .instruction = instruction }; - } - - pub fn indirectbr( - self: *WipFunction, - addr: Value, - targets: []const Block.Index, - ) Allocator.Error!Instruction.Index { - try self.ensureUnusedExtraCapacity(1, Instruction.IndirectBr, targets.len); - const instruction = try self.addInst(null, .{ - .tag = .indirectbr, - .data = self.addExtraAssumeCapacity(Instruction.IndirectBr{ - .addr = addr, - .targets_len = @intCast(targets.len), - }), - }); - _ = self.extra.appendSliceAssumeCapacity(@ptrCast(targets)); - for (targets) |target| target.ptr(self).branches += 1; - return instruction; - } - - pub fn @"unreachable"(self: *WipFunction) Allocator.Error!Instruction.Index { - try self.ensureUnusedExtraCapacity(1, NoExtra, 0); - return try self.addInst(null, .{ .tag = .@"unreachable", .data = undefined }); - } - - pub fn un( - self: *WipFunction, - tag: Instruction.Tag, - val: Value, - name: []const u8, - ) Allocator.Error!Value { - switch (tag) { - .fneg, - .@"fneg fast", - => assert(val.typeOfWip(self).scalarType(self.builder).isFloatingPoint()), - else => unreachable, - } - try self.ensureUnusedExtraCapacity(1, NoExtra, 0); - const instruction = try self.addInst(name, .{ .tag = tag, .data = @intFromEnum(val) }); - return instruction.toValue(); - } - - pub fn not(self: *WipFunction, val: Value, name: []const u8) Allocator.Error!Value { - const ty = val.typeOfWip(self); - const all_ones = try self.builder.splatValue( - ty, - try self.builder.intConst(ty.scalarType(self.builder), -1), - ); - return self.bin(.xor, val, all_ones, name); - } - - pub fn neg(self: *WipFunction, val: Value, name: []const u8) Allocator.Error!Value { - return self.bin(.sub, try self.builder.zeroInitValue(val.typeOfWip(self)), val, name); - } - - pub fn bin( - self: *WipFunction, - tag: Instruction.Tag, - lhs: Value, - rhs: Value, - name: []const u8, - ) Allocator.Error!Value { - switch (tag) { - .add, - .@"add nsw", - .@"add nuw", - .@"and", - .ashr, - .@"ashr exact", - .fadd, - .@"fadd fast", - .fdiv, - .@"fdiv fast", - .fmul, - .@"fmul fast", - .frem, - .@"frem fast", - .fsub, - .@"fsub fast", - .lshr, - .@"lshr exact", - .mul, - .@"mul nsw", - .@"mul nuw", - .@"or", - .sdiv, - .@"sdiv exact", - .shl, - .@"shl nsw", - .@"shl nuw", - .srem, - .sub, - .@"sub nsw", - .@"sub nuw", - .udiv, - .@"udiv exact", - .urem, - .xor, - => assert(lhs.typeOfWip(self) == rhs.typeOfWip(self)), - else => unreachable, - } - try self.ensureUnusedExtraCapacity(1, Instruction.Binary, 0); - const instruction = try self.addInst(name, .{ - .tag = tag, - .data = self.addExtraAssumeCapacity(Instruction.Binary{ .lhs = lhs, .rhs = rhs }), - }); - return instruction.toValue(); - } - - pub fn extractElement( - self: *WipFunction, - val: Value, - index: Value, - name: []const u8, - ) Allocator.Error!Value { - assert(val.typeOfWip(self).isVector(self.builder)); - assert(index.typeOfWip(self).isInteger(self.builder)); - try self.ensureUnusedExtraCapacity(1, Instruction.ExtractElement, 0); - const instruction = try self.addInst(name, .{ - .tag = .extractelement, - .data = self.addExtraAssumeCapacity(Instruction.ExtractElement{ - .val = val, - .index = index, - }), - }); - return instruction.toValue(); - } - - pub fn insertElement( - self: *WipFunction, - val: Value, - elem: Value, - index: Value, - name: []const u8, - ) Allocator.Error!Value { - assert(val.typeOfWip(self).scalarType(self.builder) == elem.typeOfWip(self)); - assert(index.typeOfWip(self).isInteger(self.builder)); - try self.ensureUnusedExtraCapacity(1, Instruction.InsertElement, 0); - const instruction = try self.addInst(name, .{ - .tag = .insertelement, - .data = self.addExtraAssumeCapacity(Instruction.InsertElement{ - .val = val, - .elem = elem, - .index = index, - }), - }); - return instruction.toValue(); - } - - pub fn shuffleVector( - self: *WipFunction, - lhs: Value, - rhs: Value, - mask: Value, - name: []const u8, - ) Allocator.Error!Value { - assert(lhs.typeOfWip(self).isVector(self.builder)); - assert(lhs.typeOfWip(self) == rhs.typeOfWip(self)); - assert(mask.typeOfWip(self).scalarType(self.builder).isInteger(self.builder)); - _ = try self.ensureUnusedExtraCapacity(1, Instruction.ShuffleVector, 0); - const instruction = try self.addInst(name, .{ - .tag = .shufflevector, - .data = self.addExtraAssumeCapacity(Instruction.ShuffleVector{ - .lhs = lhs, - .rhs = rhs, - .mask = mask, - }), - }); - return instruction.toValue(); - } - - pub fn splatVector( - self: *WipFunction, - ty: Type, - elem: Value, - name: []const u8, - ) Allocator.Error!Value { - const scalar_ty = try ty.changeLength(1, self.builder); - const mask_ty = try ty.changeScalar(.i32, self.builder); - const poison = try self.builder.poisonValue(scalar_ty); - const mask = try self.builder.splatValue(mask_ty, .@"0"); - const scalar = try self.insertElement(poison, elem, .@"0", name); - return self.shuffleVector(scalar, poison, mask, name); - } - - pub fn extractValue( - self: *WipFunction, - val: Value, - indices: []const u32, - name: []const u8, - ) Allocator.Error!Value { - assert(indices.len > 0); - _ = val.typeOfWip(self).childTypeAt(indices, self.builder); - try self.ensureUnusedExtraCapacity(1, Instruction.ExtractValue, indices.len); - const instruction = try self.addInst(name, .{ - .tag = .extractvalue, - .data = self.addExtraAssumeCapacity(Instruction.ExtractValue{ - .val = val, - .indices_len = @intCast(indices.len), - }), - }); - self.extra.appendSliceAssumeCapacity(indices); - return instruction.toValue(); - } - - pub fn insertValue( - self: *WipFunction, - val: Value, - elem: Value, - indices: []const u32, - name: []const u8, - ) Allocator.Error!Value { - assert(indices.len > 0); - assert(val.typeOfWip(self).childTypeAt(indices, self.builder) == elem.typeOfWip(self)); - try self.ensureUnusedExtraCapacity(1, Instruction.InsertValue, indices.len); - const instruction = try self.addInst(name, .{ - .tag = .insertvalue, - .data = self.addExtraAssumeCapacity(Instruction.InsertValue{ - .val = val, - .elem = elem, - .indices_len = @intCast(indices.len), - }), - }); - self.extra.appendSliceAssumeCapacity(indices); - return instruction.toValue(); - } - - pub fn buildAggregate( - self: *WipFunction, - ty: Type, - elems: []const Value, - name: []const u8, - ) Allocator.Error!Value { - assert(ty.aggregateLen(self.builder) == elems.len); - var cur = try self.builder.poisonValue(ty); - for (elems, 0..) |elem, index| - cur = try self.insertValue(cur, elem, &[_]u32{@intCast(index)}, name); - return cur; - } - - pub fn alloca( - self: *WipFunction, - kind: Instruction.Alloca.Kind, - ty: Type, - len: Value, - alignment: Alignment, - addr_space: AddrSpace, - name: []const u8, - ) Allocator.Error!Value { - assert(len == .none or len.typeOfWip(self).isInteger(self.builder)); - _ = try self.builder.ptrType(addr_space); - try self.ensureUnusedExtraCapacity(1, Instruction.Alloca, 0); - const instruction = try self.addInst(name, .{ - .tag = switch (kind) { - .normal => .alloca, - .inalloca => .@"alloca inalloca", - }, - .data = self.addExtraAssumeCapacity(Instruction.Alloca{ - .type = ty, - .len = switch (len) { - .none => .@"1", - else => len, - }, - .info = .{ .alignment = alignment, .addr_space = addr_space }, - }), - }); - return instruction.toValue(); - } - - pub fn load( - self: *WipFunction, - access_kind: MemoryAccessKind, - ty: Type, - ptr: Value, - alignment: Alignment, - name: []const u8, - ) Allocator.Error!Value { - return self.loadAtomic(access_kind, ty, ptr, .system, .none, alignment, name); - } - - pub fn loadAtomic( - self: *WipFunction, - access_kind: MemoryAccessKind, - ty: Type, - ptr: Value, - sync_scope: SyncScope, - ordering: AtomicOrdering, - alignment: Alignment, - name: []const u8, - ) Allocator.Error!Value { - assert(ptr.typeOfWip(self).isPointer(self.builder)); - try self.ensureUnusedExtraCapacity(1, Instruction.Load, 0); - const instruction = try self.addInst(name, .{ - .tag = switch (ordering) { - .none => .load, - else => .@"load atomic", - }, - .data = self.addExtraAssumeCapacity(Instruction.Load{ - .info = .{ - .access_kind = access_kind, - .sync_scope = switch (ordering) { - .none => .system, - else => sync_scope, - }, - .success_ordering = ordering, - .alignment = alignment, - }, - .type = ty, - .ptr = ptr, - }), - }); - return instruction.toValue(); - } - - pub fn store( - self: *WipFunction, - kind: MemoryAccessKind, - val: Value, - ptr: Value, - alignment: Alignment, - ) Allocator.Error!Instruction.Index { - return self.storeAtomic(kind, val, ptr, .system, .none, alignment); - } - - pub fn storeAtomic( - self: *WipFunction, - access_kind: MemoryAccessKind, - val: Value, - ptr: Value, - sync_scope: SyncScope, - ordering: AtomicOrdering, - alignment: Alignment, - ) Allocator.Error!Instruction.Index { - assert(ptr.typeOfWip(self).isPointer(self.builder)); - try self.ensureUnusedExtraCapacity(1, Instruction.Store, 0); - const instruction = try self.addInst(null, .{ - .tag = switch (ordering) { - .none => .store, - else => .@"store atomic", - }, - .data = self.addExtraAssumeCapacity(Instruction.Store{ - .info = .{ - .access_kind = access_kind, - .sync_scope = switch (ordering) { - .none => .system, - else => sync_scope, - }, - .success_ordering = ordering, - .alignment = alignment, - }, - .val = val, - .ptr = ptr, - }), - }); - return instruction; - } - - pub fn fence( - self: *WipFunction, - sync_scope: SyncScope, - ordering: AtomicOrdering, - ) Allocator.Error!Instruction.Index { - assert(ordering != .none); - try self.ensureUnusedExtraCapacity(1, NoExtra, 0); - const instruction = try self.addInst(null, .{ - .tag = .fence, - .data = @bitCast(MemoryAccessInfo{ - .sync_scope = sync_scope, - .success_ordering = ordering, - }), - }); - return instruction; - } - - pub fn cmpxchg( - self: *WipFunction, - kind: Instruction.CmpXchg.Kind, - access_kind: MemoryAccessKind, - ptr: Value, - cmp: Value, - new: Value, - sync_scope: SyncScope, - success_ordering: AtomicOrdering, - failure_ordering: AtomicOrdering, - alignment: Alignment, - name: []const u8, - ) Allocator.Error!Value { - assert(ptr.typeOfWip(self).isPointer(self.builder)); - const ty = cmp.typeOfWip(self); - assert(ty == new.typeOfWip(self)); - assert(success_ordering != .none); - assert(failure_ordering != .none); - - _ = try self.builder.structType(.normal, &.{ ty, .i1 }); - try self.ensureUnusedExtraCapacity(1, Instruction.CmpXchg, 0); - const instruction = try self.addInst(name, .{ - .tag = switch (kind) { - .strong => .cmpxchg, - .weak => .@"cmpxchg weak", - }, - .data = self.addExtraAssumeCapacity(Instruction.CmpXchg{ - .info = .{ - .access_kind = access_kind, - .sync_scope = sync_scope, - .success_ordering = success_ordering, - .failure_ordering = failure_ordering, - .alignment = alignment, - }, - .ptr = ptr, - .cmp = cmp, - .new = new, - }), - }); - return instruction.toValue(); - } - - pub fn atomicrmw( - self: *WipFunction, - access_kind: MemoryAccessKind, - operation: Instruction.AtomicRmw.Operation, - ptr: Value, - val: Value, - sync_scope: SyncScope, - ordering: AtomicOrdering, - alignment: Alignment, - name: []const u8, - ) Allocator.Error!Value { - assert(ptr.typeOfWip(self).isPointer(self.builder)); - assert(ordering != .none); - - try self.ensureUnusedExtraCapacity(1, Instruction.AtomicRmw, 0); - const instruction = try self.addInst(name, .{ - .tag = .atomicrmw, - .data = self.addExtraAssumeCapacity(Instruction.AtomicRmw{ - .info = .{ - .access_kind = access_kind, - .atomic_rmw_operation = operation, - .sync_scope = sync_scope, - .success_ordering = ordering, - .alignment = alignment, - }, - .ptr = ptr, - .val = val, - }), - }); - return instruction.toValue(); - } - - pub fn gep( - self: *WipFunction, - kind: Instruction.GetElementPtr.Kind, - ty: Type, - base: Value, - indices: []const Value, - name: []const u8, - ) Allocator.Error!Value { - const base_ty = base.typeOfWip(self); - const base_is_vector = base_ty.isVector(self.builder); - - const VectorInfo = struct { - kind: Type.Vector.Kind, - len: u32, - - fn init(vector_ty: Type, builder: *const Builder) @This() { - return .{ .kind = vector_ty.vectorKind(builder), .len = vector_ty.vectorLen(builder) }; - } - }; - var vector_info: ?VectorInfo = - if (base_is_vector) VectorInfo.init(base_ty, self.builder) else null; - for (indices) |index| { - const index_ty = index.typeOfWip(self); - switch (index_ty.tag(self.builder)) { - .integer => {}, - .vector, .scalable_vector => { - const index_info = VectorInfo.init(index_ty, self.builder); - if (vector_info) |info| - assert(std.meta.eql(info, index_info)) - else - vector_info = index_info; - }, - else => unreachable, - } - } - if (!base_is_vector) if (vector_info) |info| switch (info.kind) { - inline else => |vector_kind| _ = try self.builder.vectorType( - vector_kind, - info.len, - base_ty, - ), - }; - - try self.ensureUnusedExtraCapacity(1, Instruction.GetElementPtr, indices.len); - const instruction = try self.addInst(name, .{ - .tag = switch (kind) { - .normal => .getelementptr, - .inbounds => .@"getelementptr inbounds", - }, - .data = self.addExtraAssumeCapacity(Instruction.GetElementPtr{ - .type = ty, - .base = base, - .indices_len = @intCast(indices.len), - }), - }); - self.extra.appendSliceAssumeCapacity(@ptrCast(indices)); - return instruction.toValue(); - } - - pub fn gepStruct( - self: *WipFunction, - ty: Type, - base: Value, - index: usize, - name: []const u8, - ) Allocator.Error!Value { - assert(ty.isStruct(self.builder)); - return self.gep(.inbounds, ty, base, &.{ .@"0", try self.builder.intValue(.i32, index) }, name); - } - - pub fn conv( - self: *WipFunction, - signedness: Instruction.Cast.Signedness, - val: Value, - ty: Type, - name: []const u8, - ) Allocator.Error!Value { - const val_ty = val.typeOfWip(self); - if (val_ty == ty) return val; - return self.cast(self.builder.convTag(signedness, val_ty, ty), val, ty, name); - } - - pub fn cast( - self: *WipFunction, - tag: Instruction.Tag, - val: Value, - ty: Type, - name: []const u8, - ) Allocator.Error!Value { - switch (tag) { - .addrspacecast, - .bitcast, - .fpext, - .fptosi, - .fptoui, - .fptrunc, - .inttoptr, - .ptrtoint, - .sext, - .sitofp, - .trunc, - .uitofp, - .zext, - => {}, - else => unreachable, - } - if (val.typeOfWip(self) == ty) return val; - try self.ensureUnusedExtraCapacity(1, Instruction.Cast, 0); - const instruction = try self.addInst(name, .{ - .tag = tag, - .data = self.addExtraAssumeCapacity(Instruction.Cast{ - .val = val, - .type = ty, - }), - }); - return instruction.toValue(); - } - - pub fn icmp( - self: *WipFunction, - cond: IntegerCondition, - lhs: Value, - rhs: Value, - name: []const u8, - ) Allocator.Error!Value { - return self.cmpTag(switch (cond) { - inline else => |tag| @field(Instruction.Tag, "icmp " ++ @tagName(tag)), - }, lhs, rhs, name); - } - - pub fn fcmp( - self: *WipFunction, - fast: FastMathKind, - cond: FloatCondition, - lhs: Value, - rhs: Value, - name: []const u8, - ) Allocator.Error!Value { - return self.cmpTag(switch (fast) { - inline else => |fast_tag| switch (cond) { - inline else => |cond_tag| @field(Instruction.Tag, "fcmp " ++ switch (fast_tag) { - .normal => "", - .fast => "fast ", - } ++ @tagName(cond_tag)), - }, - }, lhs, rhs, name); - } - - pub const WipPhi = struct { - block: Block.Index, - instruction: Instruction.Index, - - pub fn toValue(self: WipPhi) Value { - return self.instruction.toValue(); - } - - pub fn finish( - self: WipPhi, - vals: []const Value, - blocks: []const Block.Index, - wip: *WipFunction, - ) void { - const incoming_len = self.block.ptrConst(wip).incoming; - assert(vals.len == incoming_len and blocks.len == incoming_len); - const instruction = wip.instructions.get(@intFromEnum(self.instruction)); - var extra = wip.extraDataTrail(Instruction.Phi, instruction.data); - for (vals) |val| assert(val.typeOfWip(wip) == extra.data.type); - @memcpy(extra.trail.nextMut(incoming_len, Value, wip), vals); - @memcpy(extra.trail.nextMut(incoming_len, Block.Index, wip), blocks); - } - }; - - pub fn phi(self: *WipFunction, ty: Type, name: []const u8) Allocator.Error!WipPhi { - return self.phiTag(.phi, ty, name); - } - - pub fn phiFast(self: *WipFunction, ty: Type, name: []const u8) Allocator.Error!WipPhi { - return self.phiTag(.@"phi fast", ty, name); - } - - pub fn select( - self: *WipFunction, - fast: FastMathKind, - cond: Value, - lhs: Value, - rhs: Value, - name: []const u8, - ) Allocator.Error!Value { - return self.selectTag(switch (fast) { - .normal => .select, - .fast => .@"select fast", - }, cond, lhs, rhs, name); - } - - pub fn call( - self: *WipFunction, - kind: Instruction.Call.Kind, - call_conv: CallConv, - function_attributes: FunctionAttributes, - ty: Type, - callee: Value, - args: []const Value, - name: []const u8, - ) Allocator.Error!Value { - return self.callInner(kind, call_conv, function_attributes, ty, callee, args, name, false); - } - - fn callInner( - self: *WipFunction, - kind: Instruction.Call.Kind, - call_conv: CallConv, - function_attributes: FunctionAttributes, - ty: Type, - callee: Value, - args: []const Value, - name: []const u8, - has_op_bundle_cold: bool, - ) Allocator.Error!Value { - const ret_ty = ty.functionReturn(self.builder); - assert(ty.isFunction(self.builder)); - assert(callee.typeOfWip(self).isPointer(self.builder)); - const params = ty.functionParameters(self.builder); - for (params, args[0..params.len]) |param, arg_val| assert(param == arg_val.typeOfWip(self)); - - try self.ensureUnusedExtraCapacity(1, Instruction.Call, args.len); - const instruction = try self.addInst(switch (ret_ty) { - .void => null, - else => name, - }, .{ - .tag = switch (kind) { - .normal => .call, - .fast => .@"call fast", - .musttail => .@"musttail call", - .musttail_fast => .@"musttail call fast", - .notail => .@"notail call", - .notail_fast => .@"notail call fast", - .tail => .@"tail call", - .tail_fast => .@"tail call fast", - }, - .data = self.addExtraAssumeCapacity(Instruction.Call{ - .info = .{ - .call_conv = call_conv, - .has_op_bundle_cold = has_op_bundle_cold, - }, - .attributes = function_attributes, - .ty = ty, - .callee = callee, - .args_len = @intCast(args.len), - }), - }); - self.extra.appendSliceAssumeCapacity(@ptrCast(args)); - return instruction.toValue(); - } - - pub fn callAsm( - self: *WipFunction, - function_attributes: FunctionAttributes, - ty: Type, - kind: Constant.Assembly.Info, - assembly: String, - constraints: String, - args: []const Value, - name: []const u8, - ) Allocator.Error!Value { - const callee = try self.builder.asmValue(ty, kind, assembly, constraints); - return self.call(.normal, CallConv.default, function_attributes, ty, callee, args, name); - } - - pub fn callIntrinsic( - self: *WipFunction, - fast: FastMathKind, - function_attributes: FunctionAttributes, - id: Intrinsic, - overload: []const Type, - args: []const Value, - name: []const u8, - ) Allocator.Error!Value { - const intrinsic = try self.builder.getIntrinsic(id, overload); - return self.call( - fast.toCallKind(), - CallConv.default, - function_attributes, - intrinsic.typeOf(self.builder), - intrinsic.toValue(self.builder), - args, - name, - ); - } - - pub fn callIntrinsicAssumeCold(self: *WipFunction) Allocator.Error!Value { - const intrinsic = try self.builder.getIntrinsic(.assume, &.{}); - return self.callInner( - .normal, - CallConv.default, - .none, - intrinsic.typeOf(self.builder), - intrinsic.toValue(self.builder), - &.{try self.builder.intValue(.i1, 1)}, - "", - true, - ); - } - - pub fn callMemCpy( - self: *WipFunction, - dst: Value, - dst_align: Alignment, - src: Value, - src_align: Alignment, - len: Value, - kind: MemoryAccessKind, - @"inline": bool, - ) Allocator.Error!Instruction.Index { - var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })}; - var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = src_align })}; - const value = try self.callIntrinsic( - .normal, - try self.builder.fnAttrs(&.{ - .none, - .none, - try self.builder.attrs(&dst_attrs), - try self.builder.attrs(&src_attrs), - }), - if (@"inline") .@"memcpy.inline" else .memcpy, - &.{ dst.typeOfWip(self), src.typeOfWip(self), len.typeOfWip(self) }, - &.{ dst, src, len, switch (kind) { - .normal => Value.false, - .@"volatile" => Value.true, - } }, - undefined, - ); - return value.unwrap().instruction; - } - - pub fn callMemSet( - self: *WipFunction, - dst: Value, - dst_align: Alignment, - val: Value, - len: Value, - kind: MemoryAccessKind, - @"inline": bool, - ) Allocator.Error!Instruction.Index { - var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })}; - const value = try self.callIntrinsic( - .normal, - try self.builder.fnAttrs(&.{ .none, .none, try self.builder.attrs(&dst_attrs) }), - if (@"inline") .@"memset.inline" else .memset, - &.{ dst.typeOfWip(self), len.typeOfWip(self) }, - &.{ dst, val, len, switch (kind) { - .normal => Value.false, - .@"volatile" => Value.true, - } }, - undefined, - ); - return value.unwrap().instruction; - } - - pub fn vaArg(self: *WipFunction, list: Value, ty: Type, name: []const u8) Allocator.Error!Value { - try self.ensureUnusedExtraCapacity(1, Instruction.VaArg, 0); - const instruction = try self.addInst(name, .{ - .tag = .va_arg, - .data = self.addExtraAssumeCapacity(Instruction.VaArg{ - .list = list, - .type = ty, - }), - }); - return instruction.toValue(); - } - - pub fn debugValue(self: *WipFunction, value: Value) Allocator.Error!Metadata { - if (self.strip) return .none; - return switch (value.unwrap()) { - .instruction => |instr_index| blk: { - const gop = try self.debug_values.getOrPut(self.builder.gpa, instr_index); - - const metadata: Metadata = @enumFromInt(Metadata.first_local_metadata + gop.index); - if (!gop.found_existing) gop.key_ptr.* = instr_index; - - break :blk metadata; - }, - .constant => |constant| try self.builder.metadataConstant(constant), - .metadata => |metadata| metadata, - }; - } - - pub fn finish(self: *WipFunction) Allocator.Error!void { - const gpa = self.builder.gpa; - const function = self.function.ptr(self.builder); - const params_len = self.function.typeOf(self.builder).functionParameters(self.builder).len; - const final_instructions_len = self.blocks.items.len + self.instructions.len; - - const blocks = try gpa.alloc(Function.Block, self.blocks.items.len); - errdefer gpa.free(blocks); - - const instructions: struct { - items: []Instruction.Index, - - fn map(instructions: @This(), val: Value) Value { - if (val == .none) return .none; - return switch (val.unwrap()) { - .instruction => |instruction| instructions.items[ - @intFromEnum(instruction) - ].toValue(), - .constant => |constant| constant.toValue(), - .metadata => |metadata| metadata.toValue(), - }; - } - } = .{ .items = try gpa.alloc(Instruction.Index, self.instructions.len) }; - defer gpa.free(instructions.items); - - const names = try gpa.alloc(String, final_instructions_len); - errdefer gpa.free(names); - - const value_indices = try gpa.alloc(u32, final_instructions_len); - errdefer gpa.free(value_indices); - - var debug_locations: std.AutoHashMapUnmanaged(Instruction.Index, DebugLocation) = .empty; - errdefer debug_locations.deinit(gpa); - try debug_locations.ensureUnusedCapacity(gpa, @intCast(self.debug_locations.count())); - - const debug_values = try gpa.alloc(Instruction.Index, self.debug_values.count()); - errdefer gpa.free(debug_values); - - var wip_extra: struct { - index: Instruction.ExtraIndex = 0, - items: []u32, - - fn addExtra(wip_extra: *@This(), extra: anytype) Instruction.ExtraIndex { - const result = wip_extra.index; - inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| { - const value = @field(extra, field.name); - wip_extra.items[wip_extra.index] = switch (field.type) { - u32 => value, - Alignment, - AtomicOrdering, - Block.Index, - FunctionAttributes, - Type, - Value, - Instruction.BrCond.Weights, - => @intFromEnum(value), - MemoryAccessInfo, - Instruction.Alloca.Info, - Instruction.Call.Info, - => @bitCast(value), - else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), - }; - wip_extra.index += 1; - } - return result; - } - - fn appendSlice(wip_extra: *@This(), slice: anytype) void { - if (@typeInfo(@TypeOf(slice)).pointer.child == Value) - @compileError("use appendMappedValues"); - const data: []const u32 = @ptrCast(slice); - @memcpy(wip_extra.items[wip_extra.index..][0..data.len], data); - wip_extra.index += @intCast(data.len); - } - - fn appendMappedValues(wip_extra: *@This(), vals: []const Value, ctx: anytype) void { - for (wip_extra.items[wip_extra.index..][0..vals.len], vals) |*extra, val| - extra.* = @intFromEnum(ctx.map(val)); - wip_extra.index += @intCast(vals.len); - } - - fn finish(wip_extra: *const @This()) []const u32 { - assert(wip_extra.index == wip_extra.items.len); - return wip_extra.items; - } - } = .{ .items = try gpa.alloc(u32, self.extra.items.len) }; - errdefer gpa.free(wip_extra.items); - - gpa.free(function.blocks); - function.blocks = &.{}; - gpa.free(function.names[0..function.instructions.len]); - function.debug_locations.deinit(gpa); - function.debug_locations = .{}; - gpa.free(function.debug_values); - function.debug_values = &.{}; - gpa.free(function.extra); - function.extra = &.{}; - - function.instructions.shrinkRetainingCapacity(0); - try function.instructions.setCapacity(gpa, final_instructions_len); - errdefer function.instructions.shrinkRetainingCapacity(0); - - { - var final_instruction_index: Instruction.Index = @enumFromInt(0); - for (0..params_len) |param_index| { - instructions.items[param_index] = final_instruction_index; - final_instruction_index = @enumFromInt(@intFromEnum(final_instruction_index) + 1); - } - for (blocks, self.blocks.items) |*final_block, current_block| { - assert(current_block.incoming == current_block.branches); - final_block.instruction = final_instruction_index; - final_instruction_index = @enumFromInt(@intFromEnum(final_instruction_index) + 1); - for (current_block.instructions.items) |instruction| { - instructions.items[@intFromEnum(instruction)] = final_instruction_index; - final_instruction_index = @enumFromInt(@intFromEnum(final_instruction_index) + 1); - } - } - } - - var wip_name: struct { - next_name: String = @enumFromInt(0), - next_unique_name: std.AutoHashMap(String, String), - builder: *Builder, - - fn map(wip_name: *@This(), name: String, sep: []const u8) Allocator.Error!String { - switch (name) { - .none => return .none, - .empty => { - assert(wip_name.next_name != .none); - defer wip_name.next_name = @enumFromInt(@intFromEnum(wip_name.next_name) + 1); - return wip_name.next_name; - }, - _ => { - assert(!name.isAnon()); - const gop = try wip_name.next_unique_name.getOrPut(name); - if (!gop.found_existing) { - gop.value_ptr.* = @enumFromInt(0); - return name; - } - - while (true) { - gop.value_ptr.* = @enumFromInt(@intFromEnum(gop.value_ptr.*) + 1); - const unique_name = try wip_name.builder.fmt("{r}{s}{r}", .{ - name.fmt(wip_name.builder), - sep, - gop.value_ptr.fmt(wip_name.builder), - }); - const unique_gop = try wip_name.next_unique_name.getOrPut(unique_name); - if (!unique_gop.found_existing) { - unique_gop.value_ptr.* = @enumFromInt(0); - return unique_name; - } - } - }, - } - } - } = .{ - .next_unique_name = std.AutoHashMap(String, String).init(gpa), - .builder = self.builder, - }; - defer wip_name.next_unique_name.deinit(); - - var value_index: u32 = 0; - for (0..params_len) |param_index| { - const old_argument_index: Instruction.Index = @enumFromInt(param_index); - const new_argument_index: Instruction.Index = @enumFromInt(function.instructions.len); - const argument = self.instructions.get(@intFromEnum(old_argument_index)); - assert(argument.tag == .arg); - assert(argument.data == param_index); - value_indices[function.instructions.len] = value_index; - value_index += 1; - function.instructions.appendAssumeCapacity(argument); - names[@intFromEnum(new_argument_index)] = try wip_name.map( - if (self.strip) .empty else self.names.items[@intFromEnum(old_argument_index)], - ".", - ); - if (self.debug_locations.get(old_argument_index)) |location| { - debug_locations.putAssumeCapacity(new_argument_index, location); - } - if (self.debug_values.getIndex(old_argument_index)) |index| { - debug_values[index] = new_argument_index; - } - } - for (self.blocks.items) |current_block| { - const new_block_index: Instruction.Index = @enumFromInt(function.instructions.len); - value_indices[function.instructions.len] = value_index; - function.instructions.appendAssumeCapacity(.{ - .tag = .block, - .data = current_block.incoming, - }); - names[@intFromEnum(new_block_index)] = try wip_name.map(current_block.name, ""); - for (current_block.instructions.items) |old_instruction_index| { - const new_instruction_index: Instruction.Index = @enumFromInt(function.instructions.len); - var instruction = self.instructions.get(@intFromEnum(old_instruction_index)); - switch (instruction.tag) { - .add, - .@"add nsw", - .@"add nuw", - .@"add nuw nsw", - .@"and", - .ashr, - .@"ashr exact", - .fadd, - .@"fadd fast", - .@"fcmp false", - .@"fcmp fast false", - .@"fcmp fast oeq", - .@"fcmp fast oge", - .@"fcmp fast ogt", - .@"fcmp fast ole", - .@"fcmp fast olt", - .@"fcmp fast one", - .@"fcmp fast ord", - .@"fcmp fast true", - .@"fcmp fast ueq", - .@"fcmp fast uge", - .@"fcmp fast ugt", - .@"fcmp fast ule", - .@"fcmp fast ult", - .@"fcmp fast une", - .@"fcmp fast uno", - .@"fcmp oeq", - .@"fcmp oge", - .@"fcmp ogt", - .@"fcmp ole", - .@"fcmp olt", - .@"fcmp one", - .@"fcmp ord", - .@"fcmp true", - .@"fcmp ueq", - .@"fcmp uge", - .@"fcmp ugt", - .@"fcmp ule", - .@"fcmp ult", - .@"fcmp une", - .@"fcmp uno", - .fdiv, - .@"fdiv fast", - .fmul, - .@"fmul fast", - .frem, - .@"frem fast", - .fsub, - .@"fsub fast", - .@"icmp eq", - .@"icmp ne", - .@"icmp sge", - .@"icmp sgt", - .@"icmp sle", - .@"icmp slt", - .@"icmp uge", - .@"icmp ugt", - .@"icmp ule", - .@"icmp ult", - .lshr, - .@"lshr exact", - .mul, - .@"mul nsw", - .@"mul nuw", - .@"mul nuw nsw", - .@"or", - .sdiv, - .@"sdiv exact", - .shl, - .@"shl nsw", - .@"shl nuw", - .@"shl nuw nsw", - .srem, - .sub, - .@"sub nsw", - .@"sub nuw", - .@"sub nuw nsw", - .udiv, - .@"udiv exact", - .urem, - .xor, - => { - const extra = self.extraData(Instruction.Binary, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.Binary{ - .lhs = instructions.map(extra.lhs), - .rhs = instructions.map(extra.rhs), - }); - }, - .addrspacecast, - .bitcast, - .fpext, - .fptosi, - .fptoui, - .fptrunc, - .inttoptr, - .ptrtoint, - .sext, - .sitofp, - .trunc, - .uitofp, - .zext, - => { - const extra = self.extraData(Instruction.Cast, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.Cast{ - .val = instructions.map(extra.val), - .type = extra.type, - }); - }, - .alloca, - .@"alloca inalloca", - => { - const extra = self.extraData(Instruction.Alloca, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.Alloca{ - .type = extra.type, - .len = instructions.map(extra.len), - .info = extra.info, - }); - }, - .arg, - .block, - => unreachable, - .atomicrmw => { - const extra = self.extraData(Instruction.AtomicRmw, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.AtomicRmw{ - .info = extra.info, - .ptr = instructions.map(extra.ptr), - .val = instructions.map(extra.val), - }); - }, - .br, - .fence, - .@"ret void", - .@"unreachable", - => {}, - .br_cond => { - const extra = self.extraData(Instruction.BrCond, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.BrCond{ - .cond = instructions.map(extra.cond), - .then = extra.then, - .@"else" = extra.@"else", - .weights = extra.weights, - }); - }, - .call, - .@"call fast", - .@"musttail call", - .@"musttail call fast", - .@"notail call", - .@"notail call fast", - .@"tail call", - .@"tail call fast", - => { - var extra = self.extraDataTrail(Instruction.Call, instruction.data); - const args = extra.trail.next(extra.data.args_len, Value, self); - instruction.data = wip_extra.addExtra(Instruction.Call{ - .info = extra.data.info, - .attributes = extra.data.attributes, - .ty = extra.data.ty, - .callee = instructions.map(extra.data.callee), - .args_len = extra.data.args_len, - }); - wip_extra.appendMappedValues(args, instructions); - }, - .cmpxchg, - .@"cmpxchg weak", - => { - const extra = self.extraData(Instruction.CmpXchg, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.CmpXchg{ - .info = extra.info, - .ptr = instructions.map(extra.ptr), - .cmp = instructions.map(extra.cmp), - .new = instructions.map(extra.new), - }); - }, - .extractelement => { - const extra = self.extraData(Instruction.ExtractElement, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.ExtractElement{ - .val = instructions.map(extra.val), - .index = instructions.map(extra.index), - }); - }, - .extractvalue => { - var extra = self.extraDataTrail(Instruction.ExtractValue, instruction.data); - const indices = extra.trail.next(extra.data.indices_len, u32, self); - instruction.data = wip_extra.addExtra(Instruction.ExtractValue{ - .val = instructions.map(extra.data.val), - .indices_len = extra.data.indices_len, - }); - wip_extra.appendSlice(indices); - }, - .fneg, - .@"fneg fast", - .ret, - => instruction.data = @intFromEnum(instructions.map(@enumFromInt(instruction.data))), - .getelementptr, - .@"getelementptr inbounds", - => { - var extra = self.extraDataTrail(Instruction.GetElementPtr, instruction.data); - const indices = extra.trail.next(extra.data.indices_len, Value, self); - instruction.data = wip_extra.addExtra(Instruction.GetElementPtr{ - .type = extra.data.type, - .base = instructions.map(extra.data.base), - .indices_len = extra.data.indices_len, - }); - wip_extra.appendMappedValues(indices, instructions); - }, - .indirectbr => { - var extra = self.extraDataTrail(Instruction.IndirectBr, instruction.data); - const targets = extra.trail.next(extra.data.targets_len, Block.Index, self); - instruction.data = wip_extra.addExtra(Instruction.IndirectBr{ - .addr = instructions.map(extra.data.addr), - .targets_len = extra.data.targets_len, - }); - wip_extra.appendSlice(targets); - }, - .insertelement => { - const extra = self.extraData(Instruction.InsertElement, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.InsertElement{ - .val = instructions.map(extra.val), - .elem = instructions.map(extra.elem), - .index = instructions.map(extra.index), - }); - }, - .insertvalue => { - var extra = self.extraDataTrail(Instruction.InsertValue, instruction.data); - const indices = extra.trail.next(extra.data.indices_len, u32, self); - instruction.data = wip_extra.addExtra(Instruction.InsertValue{ - .val = instructions.map(extra.data.val), - .elem = instructions.map(extra.data.elem), - .indices_len = extra.data.indices_len, - }); - wip_extra.appendSlice(indices); - }, - .load, - .@"load atomic", - => { - const extra = self.extraData(Instruction.Load, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.Load{ - .type = extra.type, - .ptr = instructions.map(extra.ptr), - .info = extra.info, - }); - }, - .phi, - .@"phi fast", - => { - const incoming_len = current_block.incoming; - var extra = self.extraDataTrail(Instruction.Phi, instruction.data); - const incoming_vals = extra.trail.next(incoming_len, Value, self); - const incoming_blocks = extra.trail.next(incoming_len, Block.Index, self); - instruction.data = wip_extra.addExtra(Instruction.Phi{ - .type = extra.data.type, - }); - wip_extra.appendMappedValues(incoming_vals, instructions); - wip_extra.appendSlice(incoming_blocks); - }, - .select, - .@"select fast", - => { - const extra = self.extraData(Instruction.Select, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.Select{ - .cond = instructions.map(extra.cond), - .lhs = instructions.map(extra.lhs), - .rhs = instructions.map(extra.rhs), - }); - }, - .shufflevector => { - const extra = self.extraData(Instruction.ShuffleVector, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.ShuffleVector{ - .lhs = instructions.map(extra.lhs), - .rhs = instructions.map(extra.rhs), - .mask = instructions.map(extra.mask), - }); - }, - .store, - .@"store atomic", - => { - const extra = self.extraData(Instruction.Store, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.Store{ - .val = instructions.map(extra.val), - .ptr = instructions.map(extra.ptr), - .info = extra.info, - }); - }, - .@"switch" => { - var extra = self.extraDataTrail(Instruction.Switch, instruction.data); - const case_vals = extra.trail.next(extra.data.cases_len, Constant, self); - const case_blocks = extra.trail.next(extra.data.cases_len, Block.Index, self); - instruction.data = wip_extra.addExtra(Instruction.Switch{ - .val = instructions.map(extra.data.val), - .default = extra.data.default, - .cases_len = extra.data.cases_len, - .weights = extra.data.weights, - }); - wip_extra.appendSlice(case_vals); - wip_extra.appendSlice(case_blocks); - }, - .va_arg => { - const extra = self.extraData(Instruction.VaArg, instruction.data); - instruction.data = wip_extra.addExtra(Instruction.VaArg{ - .list = instructions.map(extra.list), - .type = extra.type, - }); - }, - } - function.instructions.appendAssumeCapacity(instruction); - names[@intFromEnum(new_instruction_index)] = try wip_name.map(if (self.strip) - if (old_instruction_index.hasResultWip(self)) .empty else .none - else - self.names.items[@intFromEnum(old_instruction_index)], "."); - - if (self.debug_locations.get(old_instruction_index)) |location| { - debug_locations.putAssumeCapacity(new_instruction_index, location); - } - - if (self.debug_values.getIndex(old_instruction_index)) |index| { - debug_values[index] = new_instruction_index; - } - - value_indices[@intFromEnum(new_instruction_index)] = value_index; - if (old_instruction_index.hasResultWip(self)) value_index += 1; - } - } - - assert(function.instructions.len == final_instructions_len); - function.extra = wip_extra.finish(); - function.blocks = blocks; - function.names = names.ptr; - function.value_indices = value_indices.ptr; - function.strip = self.strip; - function.debug_locations = debug_locations; - function.debug_values = debug_values; - } - - pub fn deinit(self: *WipFunction) void { - self.extra.deinit(self.builder.gpa); - self.debug_values.deinit(self.builder.gpa); - self.debug_locations.deinit(self.builder.gpa); - self.names.deinit(self.builder.gpa); - self.instructions.deinit(self.builder.gpa); - for (self.blocks.items) |*b| b.instructions.deinit(self.builder.gpa); - self.blocks.deinit(self.builder.gpa); - self.* = undefined; - } - - fn cmpTag( - self: *WipFunction, - tag: Instruction.Tag, - lhs: Value, - rhs: Value, - name: []const u8, - ) Allocator.Error!Value { - switch (tag) { - .@"fcmp false", - .@"fcmp fast false", - .@"fcmp fast oeq", - .@"fcmp fast oge", - .@"fcmp fast ogt", - .@"fcmp fast ole", - .@"fcmp fast olt", - .@"fcmp fast one", - .@"fcmp fast ord", - .@"fcmp fast true", - .@"fcmp fast ueq", - .@"fcmp fast uge", - .@"fcmp fast ugt", - .@"fcmp fast ule", - .@"fcmp fast ult", - .@"fcmp fast une", - .@"fcmp fast uno", - .@"fcmp oeq", - .@"fcmp oge", - .@"fcmp ogt", - .@"fcmp ole", - .@"fcmp olt", - .@"fcmp one", - .@"fcmp ord", - .@"fcmp true", - .@"fcmp ueq", - .@"fcmp uge", - .@"fcmp ugt", - .@"fcmp ule", - .@"fcmp ult", - .@"fcmp une", - .@"fcmp uno", - .@"icmp eq", - .@"icmp ne", - .@"icmp sge", - .@"icmp sgt", - .@"icmp sle", - .@"icmp slt", - .@"icmp uge", - .@"icmp ugt", - .@"icmp ule", - .@"icmp ult", - => assert(lhs.typeOfWip(self) == rhs.typeOfWip(self)), - else => unreachable, - } - _ = try lhs.typeOfWip(self).changeScalar(.i1, self.builder); - try self.ensureUnusedExtraCapacity(1, Instruction.Binary, 0); - const instruction = try self.addInst(name, .{ - .tag = tag, - .data = self.addExtraAssumeCapacity(Instruction.Binary{ - .lhs = lhs, - .rhs = rhs, - }), - }); - return instruction.toValue(); - } - - fn phiTag( - self: *WipFunction, - tag: Instruction.Tag, - ty: Type, - name: []const u8, - ) Allocator.Error!WipPhi { - switch (tag) { - .phi, .@"phi fast" => assert(try ty.isSized(self.builder)), - else => unreachable, - } - const incoming = self.cursor.block.ptrConst(self).incoming; - assert(incoming > 0); - try self.ensureUnusedExtraCapacity(1, Instruction.Phi, incoming * 2); - const instruction = try self.addInst(name, .{ - .tag = tag, - .data = self.addExtraAssumeCapacity(Instruction.Phi{ .type = ty }), - }); - _ = self.extra.addManyAsSliceAssumeCapacity(incoming * 2); - return .{ .block = self.cursor.block, .instruction = instruction }; - } - - fn selectTag( - self: *WipFunction, - tag: Instruction.Tag, - cond: Value, - lhs: Value, - rhs: Value, - name: []const u8, - ) Allocator.Error!Value { - switch (tag) { - .select, .@"select fast" => { - assert(cond.typeOfWip(self).scalarType(self.builder) == .i1); - assert(lhs.typeOfWip(self) == rhs.typeOfWip(self)); - }, - else => unreachable, - } - try self.ensureUnusedExtraCapacity(1, Instruction.Select, 0); - const instruction = try self.addInst(name, .{ - .tag = tag, - .data = self.addExtraAssumeCapacity(Instruction.Select{ - .cond = cond, - .lhs = lhs, - .rhs = rhs, - }), - }); - return instruction.toValue(); - } - - fn ensureUnusedExtraCapacity( - self: *WipFunction, - count: usize, - comptime Extra: type, - trail_len: usize, - ) Allocator.Error!void { - try self.extra.ensureUnusedCapacity( - self.builder.gpa, - count * (@typeInfo(Extra).@"struct".fields.len + trail_len), - ); - } - - fn addInst( - self: *WipFunction, - name: ?[]const u8, - instruction: Instruction, - ) Allocator.Error!Instruction.Index { - const block_instructions = &self.cursor.block.ptr(self).instructions; - try self.instructions.ensureUnusedCapacity(self.builder.gpa, 1); - if (!self.strip) { - try self.names.ensureUnusedCapacity(self.builder.gpa, 1); - try self.debug_locations.ensureUnusedCapacity(self.builder.gpa, 1); - } - try block_instructions.ensureUnusedCapacity(self.builder.gpa, 1); - const final_name = if (name) |n| - if (self.strip) .empty else try self.builder.string(n) - else - .none; - - const index: Instruction.Index = @enumFromInt(self.instructions.len); - self.instructions.appendAssumeCapacity(instruction); - if (!self.strip) { - self.names.appendAssumeCapacity(final_name); - if (block_instructions.items.len == 0 or - !std.meta.eql(self.debug_location, self.prev_debug_location)) - { - self.debug_locations.putAssumeCapacity(index, self.debug_location); - self.prev_debug_location = self.debug_location; - } - } - block_instructions.insertAssumeCapacity(self.cursor.instruction, index); - self.cursor.instruction += 1; - return index; - } - - fn addExtraAssumeCapacity(self: *WipFunction, extra: anytype) Instruction.ExtraIndex { - const result: Instruction.ExtraIndex = @intCast(self.extra.items.len); - inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| { - const value = @field(extra, field.name); - self.extra.appendAssumeCapacity(switch (field.type) { - u32 => value, - Alignment, - AtomicOrdering, - Block.Index, - FunctionAttributes, - Type, - Value, - Instruction.BrCond.Weights, - => @intFromEnum(value), - MemoryAccessInfo, - Instruction.Alloca.Info, - Instruction.Call.Info, - => @bitCast(value), - else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), - }); - } - return result; - } - - const ExtraDataTrail = struct { - index: Instruction.ExtraIndex, - - fn nextMut(self: *ExtraDataTrail, len: u32, comptime Item: type, wip: *WipFunction) []Item { - const items: []Item = @ptrCast(wip.extra.items[self.index..][0..len]); - self.index += @intCast(len); - return items; - } - - fn next( - self: *ExtraDataTrail, - len: u32, - comptime Item: type, - wip: *const WipFunction, - ) []const Item { - const items: []const Item = @ptrCast(wip.extra.items[self.index..][0..len]); - self.index += @intCast(len); - return items; - } - }; - - fn extraDataTrail( - self: *const WipFunction, - comptime T: type, - index: Instruction.ExtraIndex, - ) struct { data: T, trail: ExtraDataTrail } { - var result: T = undefined; - const fields = @typeInfo(T).@"struct".fields; - inline for (fields, self.extra.items[index..][0..fields.len]) |field, value| - @field(result, field.name) = switch (field.type) { - u32 => value, - Alignment, - AtomicOrdering, - Block.Index, - FunctionAttributes, - Type, - Value, - Instruction.BrCond.Weights, - => @enumFromInt(value), - MemoryAccessInfo, - Instruction.Alloca.Info, - Instruction.Call.Info, - => @bitCast(value), - else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), - }; - return .{ - .data = result, - .trail = .{ .index = index + @as(Type.Item.ExtraIndex, @intCast(fields.len)) }, - }; - } - - fn extraData(self: *const WipFunction, comptime T: type, index: Instruction.ExtraIndex) T { - return self.extraDataTrail(T, index).data; - } -}; - -pub const FloatCondition = enum(u4) { - oeq = 1, - ogt = 2, - oge = 3, - olt = 4, - ole = 5, - one = 6, - ord = 7, - uno = 8, - ueq = 9, - ugt = 10, - uge = 11, - ult = 12, - ule = 13, - une = 14, -}; - -pub const IntegerCondition = enum(u6) { - eq = 32, - ne = 33, - ugt = 34, - uge = 35, - ult = 36, - ule = 37, - sgt = 38, - sge = 39, - slt = 40, - sle = 41, -}; - -pub const MemoryAccessKind = enum(u1) { - normal, - @"volatile", - - pub fn format( - self: MemoryAccessKind, - comptime prefix: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (self != .normal) try writer.print("{s}{s}", .{ prefix, @tagName(self) }); - } -}; - -pub const SyncScope = enum(u1) { - singlethread, - system, - - pub fn format( - self: SyncScope, - comptime prefix: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (self != .system) try writer.print( - \\{s}syncscope("{s}") - , .{ prefix, @tagName(self) }); - } -}; - -pub const AtomicOrdering = enum(u3) { - none = 0, - unordered = 1, - monotonic = 2, - acquire = 3, - release = 4, - acq_rel = 5, - seq_cst = 6, - - pub fn format( - self: AtomicOrdering, - comptime prefix: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (self != .none) try writer.print("{s}{s}", .{ prefix, @tagName(self) }); - } -}; - -const MemoryAccessInfo = packed struct(u32) { - access_kind: MemoryAccessKind = .normal, - atomic_rmw_operation: Function.Instruction.AtomicRmw.Operation = .none, - sync_scope: SyncScope, - success_ordering: AtomicOrdering, - failure_ordering: AtomicOrdering = .none, - alignment: Alignment = .default, - _: u13 = undefined, -}; - -pub const FastMath = packed struct(u8) { - unsafe_algebra: bool = false, // Legacy - nnan: bool = false, - ninf: bool = false, - nsz: bool = false, - arcp: bool = false, - contract: bool = false, - afn: bool = false, - reassoc: bool = false, - - pub const fast = FastMath{ - .nnan = true, - .ninf = true, - .nsz = true, - .arcp = true, - .contract = true, - .afn = true, - .reassoc = true, - }; -}; - -pub const FastMathKind = enum { - normal, - fast, - - pub fn toCallKind(self: FastMathKind) Function.Instruction.Call.Kind { - return switch (self) { - .normal => .normal, - .fast => .fast, - }; - } -}; - -pub const Constant = enum(u32) { - false, - true, - @"0", - @"1", - none, - no_init = (1 << 30) - 1, - _, - - const first_global: Constant = @enumFromInt(1 << 29); - - pub const Tag = enum(u7) { - positive_integer, - negative_integer, - half, - bfloat, - float, - double, - fp128, - x86_fp80, - ppc_fp128, - null, - none, - structure, - packed_structure, - array, - string, - vector, - splat, - zeroinitializer, - undef, - poison, - blockaddress, - dso_local_equivalent, - no_cfi, - trunc, - ptrtoint, - inttoptr, - bitcast, - addrspacecast, - getelementptr, - @"getelementptr inbounds", - add, - @"add nsw", - @"add nuw", - sub, - @"sub nsw", - @"sub nuw", - shl, - xor, - @"asm", - @"asm sideeffect", - @"asm alignstack", - @"asm sideeffect alignstack", - @"asm inteldialect", - @"asm sideeffect inteldialect", - @"asm alignstack inteldialect", - @"asm sideeffect alignstack inteldialect", - @"asm unwind", - @"asm sideeffect unwind", - @"asm alignstack unwind", - @"asm sideeffect alignstack unwind", - @"asm inteldialect unwind", - @"asm sideeffect inteldialect unwind", - @"asm alignstack inteldialect unwind", - @"asm sideeffect alignstack inteldialect unwind", - - pub fn toBinaryOpcode(self: Tag) BinaryOpcode { - return switch (self) { - .add, - .@"add nsw", - .@"add nuw", - => .add, - .sub, - .@"sub nsw", - .@"sub nuw", - => .sub, - .shl => .shl, - .xor => .xor, - else => unreachable, - }; - } - - pub fn toCastOpcode(self: Tag) CastOpcode { - return switch (self) { - .trunc => .trunc, - .ptrtoint => .ptrtoint, - .inttoptr => .inttoptr, - .bitcast => .bitcast, - .addrspacecast => .addrspacecast, - else => unreachable, - }; - } - }; - - pub const Item = struct { - tag: Tag, - data: ExtraIndex, - - const ExtraIndex = u32; - }; - - pub const Integer = packed struct(u64) { - type: Type, - limbs_len: u32, - - pub const limbs = @divExact(@bitSizeOf(Integer), @bitSizeOf(std.math.big.Limb)); - }; - - pub const Double = struct { - lo: u32, - hi: u32, - }; - - pub const Fp80 = struct { - lo_lo: u32, - lo_hi: u32, - hi: u32, - }; - - pub const Fp128 = struct { - lo_lo: u32, - lo_hi: u32, - hi_lo: u32, - hi_hi: u32, - }; - - pub const Aggregate = struct { - type: Type, - //fields: [type.aggregateLen(builder)]Constant, - }; - - pub const Splat = extern struct { - type: Type, - value: Constant, - }; - - pub const BlockAddress = extern struct { - function: Function.Index, - block: Function.Block.Index, - }; - - pub const Cast = extern struct { - val: Constant, - type: Type, - - pub const Signedness = enum { unsigned, signed, unneeded }; - }; - - pub const GetElementPtr = struct { - type: Type, - base: Constant, - info: Info, - //indices: [info.indices_len]Constant, - - pub const Kind = enum { normal, inbounds }; - pub const InRangeIndex = enum(u16) { none = std.math.maxInt(u16), _ }; - pub const Info = packed struct(u32) { indices_len: u16, inrange: InRangeIndex }; - }; - - pub const Binary = extern struct { - lhs: Constant, - rhs: Constant, - }; - - pub const Assembly = extern struct { - type: Type, - assembly: String, - constraints: String, - - pub const Info = packed struct { - sideeffect: bool = false, - alignstack: bool = false, - inteldialect: bool = false, - unwind: bool = false, - }; - }; - - pub fn unwrap(self: Constant) union(enum) { - constant: u30, - global: Global.Index, - } { - return if (@intFromEnum(self) < @intFromEnum(first_global)) - .{ .constant = @intCast(@intFromEnum(self)) } - else - .{ .global = @enumFromInt(@intFromEnum(self) - @intFromEnum(first_global)) }; - } - - pub fn toValue(self: Constant) Value { - return @enumFromInt(Value.first_constant + @intFromEnum(self)); - } - - pub fn typeOf(self: Constant, builder: *Builder) Type { - switch (self.unwrap()) { - .constant => |constant| { - const item = builder.constant_items.get(constant); - return switch (item.tag) { - .positive_integer, - .negative_integer, - => @as( - *align(@alignOf(std.math.big.Limb)) Integer, - @ptrCast(builder.constant_limbs.items[item.data..][0..Integer.limbs]), - ).type, - .half => .half, - .bfloat => .bfloat, - .float => .float, - .double => .double, - .fp128 => .fp128, - .x86_fp80 => .x86_fp80, - .ppc_fp128 => .ppc_fp128, - .null, - .none, - .zeroinitializer, - .undef, - .poison, - => @enumFromInt(item.data), - .structure, - .packed_structure, - .array, - .vector, - => builder.constantExtraData(Aggregate, item.data).type, - .splat => builder.constantExtraData(Splat, item.data).type, - .string => builder.arrayTypeAssumeCapacity( - @as(String, @enumFromInt(item.data)).slice(builder).?.len, - .i8, - ), - .blockaddress => builder.ptrTypeAssumeCapacity( - builder.constantExtraData(BlockAddress, item.data) - .function.ptrConst(builder).global.ptrConst(builder).addr_space, - ), - .dso_local_equivalent, - .no_cfi, - => builder.ptrTypeAssumeCapacity(@as(Function.Index, @enumFromInt(item.data)) - .ptrConst(builder).global.ptrConst(builder).addr_space), - .trunc, - .ptrtoint, - .inttoptr, - .bitcast, - .addrspacecast, - => builder.constantExtraData(Cast, item.data).type, - .getelementptr, - .@"getelementptr inbounds", - => { - var extra = builder.constantExtraDataTrail(GetElementPtr, item.data); - const indices = - extra.trail.next(extra.data.info.indices_len, Constant, builder); - const base_ty = extra.data.base.typeOf(builder); - if (!base_ty.isVector(builder)) for (indices) |index| { - const index_ty = index.typeOf(builder); - if (!index_ty.isVector(builder)) continue; - return index_ty.changeScalarAssumeCapacity(base_ty, builder); - }; - return base_ty; - }, - .add, - .@"add nsw", - .@"add nuw", - .sub, - .@"sub nsw", - .@"sub nuw", - .shl, - .xor, - => builder.constantExtraData(Binary, item.data).lhs.typeOf(builder), - .@"asm", - .@"asm sideeffect", - .@"asm alignstack", - .@"asm sideeffect alignstack", - .@"asm inteldialect", - .@"asm sideeffect inteldialect", - .@"asm alignstack inteldialect", - .@"asm sideeffect alignstack inteldialect", - .@"asm unwind", - .@"asm sideeffect unwind", - .@"asm alignstack unwind", - .@"asm sideeffect alignstack unwind", - .@"asm inteldialect unwind", - .@"asm sideeffect inteldialect unwind", - .@"asm alignstack inteldialect unwind", - .@"asm sideeffect alignstack inteldialect unwind", - => .ptr, - }; - }, - .global => |global| return builder.ptrTypeAssumeCapacity( - global.ptrConst(builder).addr_space, - ), - } - } - - pub fn isZeroInit(self: Constant, builder: *const Builder) bool { - switch (self.unwrap()) { - .constant => |constant| { - const item = builder.constant_items.get(constant); - return switch (item.tag) { - .positive_integer => { - const extra: *align(@alignOf(std.math.big.Limb)) Integer = - @ptrCast(builder.constant_limbs.items[item.data..][0..Integer.limbs]); - const limbs = builder.constant_limbs - .items[item.data + Integer.limbs ..][0..extra.limbs_len]; - return std.mem.eql(std.math.big.Limb, limbs, &.{0}); - }, - .half, .bfloat, .float => item.data == 0, - .double => { - const extra = builder.constantExtraData(Constant.Double, item.data); - return extra.lo == 0 and extra.hi == 0; - }, - .fp128, .ppc_fp128 => { - const extra = builder.constantExtraData(Constant.Fp128, item.data); - return extra.lo_lo == 0 and extra.lo_hi == 0 and - extra.hi_lo == 0 and extra.hi_hi == 0; - }, - .x86_fp80 => { - const extra = builder.constantExtraData(Constant.Fp80, item.data); - return extra.lo_lo == 0 and extra.lo_hi == 0 and extra.hi == 0; - }, - .vector => { - var extra = builder.constantExtraDataTrail(Aggregate, item.data); - const len: u32 = @intCast(extra.data.type.aggregateLen(builder)); - const vals = extra.trail.next(len, Constant, builder); - for (vals) |val| if (!val.isZeroInit(builder)) return false; - return true; - }, - .null, .zeroinitializer => true, - else => false, - }; - }, - .global => return false, - } - } - - pub fn getBase(self: Constant, builder: *const Builder) Global.Index { - var cur = self; - while (true) switch (cur.unwrap()) { - .constant => |constant| { - const item = builder.constant_items.get(constant); - switch (item.tag) { - .ptrtoint, - .inttoptr, - .bitcast, - => cur = builder.constantExtraData(Cast, item.data).val, - .getelementptr => cur = builder.constantExtraData(GetElementPtr, item.data).base, - .add => { - const extra = builder.constantExtraData(Binary, item.data); - const lhs_base = extra.lhs.getBase(builder); - const rhs_base = extra.rhs.getBase(builder); - return if (lhs_base != .none and rhs_base != .none) - .none - else if (lhs_base != .none) lhs_base else rhs_base; - }, - .sub => { - const extra = builder.constantExtraData(Binary, item.data); - if (extra.rhs.getBase(builder) != .none) return .none; - cur = extra.lhs; - }, - else => return .none, - } - }, - .global => |global| switch (global.ptrConst(builder).kind) { - .alias => |alias| cur = alias.ptrConst(builder).aliasee, - .variable, .function => return global, - .replaced => unreachable, - }, - }; - } - - const FormatData = struct { - constant: Constant, - builder: *Builder, - }; - fn format( - data: FormatData, - comptime fmt_str: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_| - @compileError("invalid format string: '" ++ fmt_str ++ "'"); - if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) { - if (data.constant == .no_init) return; - try writer.writeByte(','); - } - if (comptime std.mem.indexOfScalar(u8, fmt_str, ' ') != null) { - if (data.constant == .no_init) return; - try writer.writeByte(' '); - } - if (comptime std.mem.indexOfScalar(u8, fmt_str, '%') != null) - try writer.print("{%} ", .{data.constant.typeOf(data.builder).fmt(data.builder)}); - assert(data.constant != .no_init); - if (std.enums.tagName(Constant, data.constant)) |name| return writer.writeAll(name); - switch (data.constant.unwrap()) { - .constant => |constant| { - const item = data.builder.constant_items.get(constant); - switch (item.tag) { - .positive_integer, - .negative_integer, - => |tag| { - const extra: *align(@alignOf(std.math.big.Limb)) const Integer = - @ptrCast(data.builder.constant_limbs.items[item.data..][0..Integer.limbs]); - const limbs = data.builder.constant_limbs - .items[item.data + Integer.limbs ..][0..extra.limbs_len]; - const bigint: std.math.big.int.Const = .{ - .limbs = limbs, - .positive = switch (tag) { - .positive_integer => true, - .negative_integer => false, - else => unreachable, - }, - }; - const ExpectedContents = extern struct { - const expected_limbs = @divExact(512, @bitSizeOf(std.math.big.Limb)); - string: [ - (std.math.big.int.Const{ - .limbs = &([1]std.math.big.Limb{ - std.math.maxInt(std.math.big.Limb), - } ** expected_limbs), - .positive = false, - }).sizeInBaseUpperBound(10) - ]u8, - limbs: [ - std.math.big.int.calcToStringLimbsBufferLen(expected_limbs, 10) - ]std.math.big.Limb, - }; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), data.builder.gpa); - const allocator = stack.get(); - const str = try bigint.toStringAlloc(allocator, 10, undefined); - defer allocator.free(str); - try writer.writeAll(str); - }, - .half, - .bfloat, - => |tag| try writer.print("0x{c}{X:0>4}", .{ @as(u8, switch (tag) { - .half => 'H', - .bfloat => 'R', - else => unreachable, - }), item.data >> switch (tag) { - .half => 0, - .bfloat => 16, - else => unreachable, - } }), - .float => { - const Float = struct { - fn Repr(comptime T: type) type { - return packed struct(std.meta.Int(.unsigned, @bitSizeOf(T))) { - mantissa: std.meta.Int(.unsigned, std.math.floatMantissaBits(T)), - exponent: std.meta.Int(.unsigned, std.math.floatExponentBits(T)), - sign: u1, - }; - } - }; - const Mantissa64 = std.meta.FieldType(Float.Repr(f64), .mantissa); - const Exponent32 = std.meta.FieldType(Float.Repr(f32), .exponent); - const Exponent64 = std.meta.FieldType(Float.Repr(f64), .exponent); - - const repr: Float.Repr(f32) = @bitCast(item.data); - const denormal_shift = switch (repr.exponent) { - std.math.minInt(Exponent32) => @as( - std.math.Log2Int(Mantissa64), - @clz(repr.mantissa), - ) + 1, - else => 0, - }; - try writer.print("0x{X:0>16}", .{@as(u64, @bitCast(Float.Repr(f64){ - .mantissa = std.math.shl( - Mantissa64, - repr.mantissa, - std.math.floatMantissaBits(f64) - std.math.floatMantissaBits(f32) + - denormal_shift, - ), - .exponent = switch (repr.exponent) { - std.math.minInt(Exponent32) => if (repr.mantissa > 0) - @as(Exponent64, std.math.floatExponentMin(f32) + - std.math.floatExponentMax(f64)) - denormal_shift - else - std.math.minInt(Exponent64), - else => @as(Exponent64, repr.exponent) + - (std.math.floatExponentMax(f64) - std.math.floatExponentMax(f32)), - std.math.maxInt(Exponent32) => std.math.maxInt(Exponent64), - }, - .sign = repr.sign, - }))}); - }, - .double => { - const extra = data.builder.constantExtraData(Double, item.data); - try writer.print("0x{X:0>8}{X:0>8}", .{ extra.hi, extra.lo }); - }, - .fp128, - .ppc_fp128, - => |tag| { - const extra = data.builder.constantExtraData(Fp128, item.data); - try writer.print("0x{c}{X:0>8}{X:0>8}{X:0>8}{X:0>8}", .{ - @as(u8, switch (tag) { - .fp128 => 'L', - .ppc_fp128 => 'M', - else => unreachable, - }), - extra.lo_hi, - extra.lo_lo, - extra.hi_hi, - extra.hi_lo, - }); - }, - .x86_fp80 => { - const extra = data.builder.constantExtraData(Fp80, item.data); - try writer.print("0xK{X:0>4}{X:0>8}{X:0>8}", .{ - extra.hi, extra.lo_hi, extra.lo_lo, - }); - }, - .null, - .none, - .zeroinitializer, - .undef, - .poison, - => |tag| try writer.writeAll(@tagName(tag)), - .structure, - .packed_structure, - .array, - .vector, - => |tag| { - var extra = data.builder.constantExtraDataTrail(Aggregate, item.data); - const len: u32 = @intCast(extra.data.type.aggregateLen(data.builder)); - const vals = extra.trail.next(len, Constant, data.builder); - try writer.writeAll(switch (tag) { - .structure => "{ ", - .packed_structure => "<{ ", - .array => "[", - .vector => "<", - else => unreachable, - }); - for (vals, 0..) |val, index| { - if (index > 0) try writer.writeAll(", "); - try writer.print("{%}", .{val.fmt(data.builder)}); - } - try writer.writeAll(switch (tag) { - .structure => " }", - .packed_structure => " }>", - .array => "]", - .vector => ">", - else => unreachable, - }); - }, - .splat => { - const extra = data.builder.constantExtraData(Splat, item.data); - const len = extra.type.vectorLen(data.builder); - try writer.writeByte('<'); - for (0..len) |index| { - if (index > 0) try writer.writeAll(", "); - try writer.print("{%}", .{extra.value.fmt(data.builder)}); - } - try writer.writeByte('>'); - }, - .string => try writer.print("c{\"}", .{ - @as(String, @enumFromInt(item.data)).fmt(data.builder), - }), - .blockaddress => |tag| { - const extra = data.builder.constantExtraData(BlockAddress, item.data); - const function = extra.function.ptrConst(data.builder); - try writer.print("{s}({}, {})", .{ - @tagName(tag), - function.global.fmt(data.builder), - extra.block.toInst(function).fmt(extra.function, data.builder), - }); - }, - .dso_local_equivalent, - .no_cfi, - => |tag| { - const function: Function.Index = @enumFromInt(item.data); - try writer.print("{s} {}", .{ - @tagName(tag), - function.ptrConst(data.builder).global.fmt(data.builder), - }); - }, - .trunc, - .ptrtoint, - .inttoptr, - .bitcast, - .addrspacecast, - => |tag| { - const extra = data.builder.constantExtraData(Cast, item.data); - try writer.print("{s} ({%} to {%})", .{ - @tagName(tag), - extra.val.fmt(data.builder), - extra.type.fmt(data.builder), - }); - }, - .getelementptr, - .@"getelementptr inbounds", - => |tag| { - var extra = data.builder.constantExtraDataTrail(GetElementPtr, item.data); - const indices = - extra.trail.next(extra.data.info.indices_len, Constant, data.builder); - try writer.print("{s} ({%}, {%}", .{ - @tagName(tag), - extra.data.type.fmt(data.builder), - extra.data.base.fmt(data.builder), - }); - for (indices) |index| try writer.print(", {%}", .{index.fmt(data.builder)}); - try writer.writeByte(')'); - }, - .add, - .@"add nsw", - .@"add nuw", - .sub, - .@"sub nsw", - .@"sub nuw", - .shl, - .xor, - => |tag| { - const extra = data.builder.constantExtraData(Binary, item.data); - try writer.print("{s} ({%}, {%})", .{ - @tagName(tag), - extra.lhs.fmt(data.builder), - extra.rhs.fmt(data.builder), - }); - }, - .@"asm", - .@"asm sideeffect", - .@"asm alignstack", - .@"asm sideeffect alignstack", - .@"asm inteldialect", - .@"asm sideeffect inteldialect", - .@"asm alignstack inteldialect", - .@"asm sideeffect alignstack inteldialect", - .@"asm unwind", - .@"asm sideeffect unwind", - .@"asm alignstack unwind", - .@"asm sideeffect alignstack unwind", - .@"asm inteldialect unwind", - .@"asm sideeffect inteldialect unwind", - .@"asm alignstack inteldialect unwind", - .@"asm sideeffect alignstack inteldialect unwind", - => |tag| { - const extra = data.builder.constantExtraData(Assembly, item.data); - try writer.print("{s} {\"}, {\"}", .{ - @tagName(tag), - extra.assembly.fmt(data.builder), - extra.constraints.fmt(data.builder), - }); - }, - } - }, - .global => |global| try writer.print("{}", .{global.fmt(data.builder)}), - } - } - pub fn fmt(self: Constant, builder: *Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .constant = self, .builder = builder } }; - } -}; - -pub const Value = enum(u32) { - none = std.math.maxInt(u31), - false = first_constant + @intFromEnum(Constant.false), - true = first_constant + @intFromEnum(Constant.true), - @"0" = first_constant + @intFromEnum(Constant.@"0"), - @"1" = first_constant + @intFromEnum(Constant.@"1"), - _, - - const first_constant = 1 << 30; - const first_metadata = 1 << 31; - - pub fn unwrap(self: Value) union(enum) { - instruction: Function.Instruction.Index, - constant: Constant, - metadata: Metadata, - } { - return if (@intFromEnum(self) < first_constant) - .{ .instruction = @enumFromInt(@intFromEnum(self)) } - else if (@intFromEnum(self) < first_metadata) - .{ .constant = @enumFromInt(@intFromEnum(self) - first_constant) } - else - .{ .metadata = @enumFromInt(@intFromEnum(self) - first_metadata) }; - } - - pub fn typeOfWip(self: Value, wip: *const WipFunction) Type { - return switch (self.unwrap()) { - .instruction => |instruction| instruction.typeOfWip(wip), - .constant => |constant| constant.typeOf(wip.builder), - .metadata => .metadata, - }; - } - - pub fn typeOf(self: Value, function: Function.Index, builder: *Builder) Type { - return switch (self.unwrap()) { - .instruction => |instruction| instruction.typeOf(function, builder), - .constant => |constant| constant.typeOf(builder), - .metadata => .metadata, - }; - } - - pub fn toConst(self: Value) ?Constant { - return switch (self.unwrap()) { - .instruction, .metadata => null, - .constant => |constant| constant, - }; - } - - const FormatData = struct { - value: Value, - function: Function.Index, - builder: *Builder, - }; - fn format( - data: FormatData, - comptime fmt_str: []const u8, - fmt_opts: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - switch (data.value.unwrap()) { - .instruction => |instruction| try Function.Instruction.Index.format(.{ - .instruction = instruction, - .function = data.function, - .builder = data.builder, - }, fmt_str, fmt_opts, writer), - .constant => |constant| try Constant.format(.{ - .constant = constant, - .builder = data.builder, - }, fmt_str, fmt_opts, writer), - .metadata => unreachable, - } - } - pub fn fmt(self: Value, function: Function.Index, builder: *Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .value = self, .function = function, .builder = builder } }; - } -}; - -pub const MetadataString = enum(u32) { - none = 0, - _, - - pub fn slice(self: MetadataString, builder: *const Builder) []const u8 { - const index = @intFromEnum(self); - const start = builder.metadata_string_indices.items[index]; - const end = builder.metadata_string_indices.items[index + 1]; - return builder.metadata_string_bytes.items[start..end]; - } - - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: Adapter, key: []const u8) u32 { - return @truncate(std.hash.Wyhash.hash(0, key)); - } - pub fn eql(ctx: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool { - const rhs_metadata_string: MetadataString = @enumFromInt(rhs_index); - return std.mem.eql(u8, lhs_key, rhs_metadata_string.slice(ctx.builder)); - } - }; - - const FormatData = struct { - metadata_string: MetadataString, - builder: *const Builder, - }; - fn format( - data: FormatData, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, writer); - } - fn fmt(self: MetadataString, builder: *const Builder) std.fmt.Formatter(format) { - return .{ .data = .{ .metadata_string = self, .builder = builder } }; - } -}; - -pub const Metadata = enum(u32) { - none = 0, - empty_tuple = 1, - _, - - const first_forward_reference = 1 << 29; - const first_local_metadata = 1 << 30; - - pub const Tag = enum(u6) { - none, - file, - compile_unit, - @"compile_unit optimized", - subprogram, - @"subprogram local", - @"subprogram definition", - @"subprogram local definition", - @"subprogram optimized", - @"subprogram optimized local", - @"subprogram optimized definition", - @"subprogram optimized local definition", - lexical_block, - location, - basic_bool_type, - basic_unsigned_type, - basic_signed_type, - basic_float_type, - composite_struct_type, - composite_union_type, - composite_enumeration_type, - composite_array_type, - composite_vector_type, - derived_pointer_type, - derived_member_type, - subroutine_type, - enumerator_unsigned, - enumerator_signed_positive, - enumerator_signed_negative, - subrange, - tuple, - str_tuple, - module_flag, - expression, - local_var, - parameter, - global_var, - @"global_var local", - global_var_expression, - constant, - - pub fn isInline(tag: Tag) bool { - return switch (tag) { - .none, - .expression, - .constant, - => true, - .file, - .compile_unit, - .@"compile_unit optimized", - .subprogram, - .@"subprogram local", - .@"subprogram definition", - .@"subprogram local definition", - .@"subprogram optimized", - .@"subprogram optimized local", - .@"subprogram optimized definition", - .@"subprogram optimized local definition", - .lexical_block, - .location, - .basic_bool_type, - .basic_unsigned_type, - .basic_signed_type, - .basic_float_type, - .composite_struct_type, - .composite_union_type, - .composite_enumeration_type, - .composite_array_type, - .composite_vector_type, - .derived_pointer_type, - .derived_member_type, - .subroutine_type, - .enumerator_unsigned, - .enumerator_signed_positive, - .enumerator_signed_negative, - .subrange, - .tuple, - .str_tuple, - .module_flag, - .local_var, - .parameter, - .global_var, - .@"global_var local", - .global_var_expression, - => false, - }; - } - }; - - pub fn isInline(self: Metadata, builder: *const Builder) bool { - return builder.metadata_items.items(.tag)[@intFromEnum(self)].isInline(); - } - - pub fn unwrap(self: Metadata, builder: *const Builder) Metadata { - var metadata = self; - while (@intFromEnum(metadata) >= Metadata.first_forward_reference and - @intFromEnum(metadata) < Metadata.first_local_metadata) - { - const index = @intFromEnum(metadata) - Metadata.first_forward_reference; - metadata = builder.metadata_forward_references.items[index]; - assert(metadata != .none); - } - return metadata; - } - - pub const Item = struct { - tag: Tag, - data: ExtraIndex, - - const ExtraIndex = u32; - }; - - pub const DIFlags = packed struct(u32) { - Visibility: enum(u2) { Zero, Private, Protected, Public } = .Zero, - FwdDecl: bool = false, - AppleBlock: bool = false, - ReservedBit4: u1 = 0, - Virtual: bool = false, - Artificial: bool = false, - Explicit: bool = false, - Prototyped: bool = false, - ObjcClassComplete: bool = false, - ObjectPointer: bool = false, - Vector: bool = false, - StaticMember: bool = false, - LValueReference: bool = false, - RValueReference: bool = false, - ExportSymbols: bool = false, - Inheritance: enum(u2) { - Zero, - SingleInheritance, - MultipleInheritance, - VirtualInheritance, - } = .Zero, - IntroducedVirtual: bool = false, - BitField: bool = false, - NoReturn: bool = false, - ReservedBit21: u1 = 0, - TypePassbyValue: bool = false, - TypePassbyReference: bool = false, - EnumClass: bool = false, - Thunk: bool = false, - NonTrivial: bool = false, - BigEndian: bool = false, - LittleEndian: bool = false, - AllCallsDescribed: bool = false, - Unused: u2 = 0, - - pub fn format( - self: DIFlags, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - var need_pipe = false; - inline for (@typeInfo(DIFlags).@"struct".fields) |field| { - switch (@typeInfo(field.type)) { - .bool => if (@field(self, field.name)) { - if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; - try writer.print("DIFlag{s}", .{field.name}); - }, - .@"enum" => if (@field(self, field.name) != .Zero) { - if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; - try writer.print("DIFlag{s}", .{@tagName(@field(self, field.name))}); - }, - .int => assert(@field(self, field.name) == 0), - else => @compileError("bad field type: " ++ field.name ++ ": " ++ - @typeName(field.type)), - } - } - if (!need_pipe) try writer.writeByte('0'); - } - }; - - pub const File = struct { - filename: MetadataString, - directory: MetadataString, - }; - - pub const CompileUnit = struct { - pub const Options = struct { - optimized: bool, - }; - - file: Metadata, - producer: MetadataString, - enums: Metadata, - globals: Metadata, - }; - - pub const Subprogram = struct { - pub const Options = struct { - di_flags: DIFlags, - sp_flags: DISPFlags, - }; - - pub const DISPFlags = packed struct(u32) { - Virtuality: enum(u2) { Zero, Virtual, PureVirtual } = .Zero, - LocalToUnit: bool = false, - Definition: bool = false, - Optimized: bool = false, - Pure: bool = false, - Elemental: bool = false, - Recursive: bool = false, - MainSubprogram: bool = false, - Deleted: bool = false, - ReservedBit10: u1 = 0, - ObjCDirect: bool = false, - Unused: u20 = 0, - - pub fn format( - self: DISPFlags, - comptime _: []const u8, - _: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - var need_pipe = false; - inline for (@typeInfo(DISPFlags).@"struct".fields) |field| { - switch (@typeInfo(field.type)) { - .bool => if (@field(self, field.name)) { - if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; - try writer.print("DISPFlag{s}", .{field.name}); - }, - .@"enum" => if (@field(self, field.name) != .Zero) { - if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; - try writer.print("DISPFlag{s}", .{@tagName(@field(self, field.name))}); - }, - .int => assert(@field(self, field.name) == 0), - else => @compileError("bad field type: " ++ field.name ++ ": " ++ - @typeName(field.type)), - } - } - if (!need_pipe) try writer.writeByte('0'); - } - }; - - file: Metadata, - name: MetadataString, - linkage_name: MetadataString, - line: u32, - scope_line: u32, - ty: Metadata, - di_flags: DIFlags, - compile_unit: Metadata, - }; - - pub const LexicalBlock = struct { - scope: Metadata, - file: Metadata, - line: u32, - column: u32, - }; - - pub const Location = struct { - line: u32, - column: u32, - scope: Metadata, - inlined_at: Metadata, - }; - - pub const BasicType = struct { - name: MetadataString, - size_in_bits_lo: u32, - size_in_bits_hi: u32, - - pub fn bitSize(self: BasicType) u64 { - return @as(u64, self.size_in_bits_hi) << 32 | self.size_in_bits_lo; - } - }; - - pub const CompositeType = struct { - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits_lo: u32, - size_in_bits_hi: u32, - align_in_bits_lo: u32, - align_in_bits_hi: u32, - fields_tuple: Metadata, - - pub fn bitSize(self: CompositeType) u64 { - return @as(u64, self.size_in_bits_hi) << 32 | self.size_in_bits_lo; - } - pub fn bitAlign(self: CompositeType) u64 { - return @as(u64, self.align_in_bits_hi) << 32 | self.align_in_bits_lo; - } - }; - - pub const DerivedType = struct { - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits_lo: u32, - size_in_bits_hi: u32, - align_in_bits_lo: u32, - align_in_bits_hi: u32, - offset_in_bits_lo: u32, - offset_in_bits_hi: u32, - - pub fn bitSize(self: DerivedType) u64 { - return @as(u64, self.size_in_bits_hi) << 32 | self.size_in_bits_lo; - } - pub fn bitAlign(self: DerivedType) u64 { - return @as(u64, self.align_in_bits_hi) << 32 | self.align_in_bits_lo; - } - pub fn bitOffset(self: DerivedType) u64 { - return @as(u64, self.offset_in_bits_hi) << 32 | self.offset_in_bits_lo; - } - }; - - pub const SubroutineType = struct { - types_tuple: Metadata, - }; - - pub const Enumerator = struct { - name: MetadataString, - bit_width: u32, - limbs_index: u32, - limbs_len: u32, - }; - - pub const Subrange = struct { - lower_bound: Metadata, - count: Metadata, - }; - - pub const Expression = struct { - elements_len: u32, - - // elements: [elements_len]u32 - }; - - pub const Tuple = struct { - elements_len: u32, - - // elements: [elements_len]Metadata - }; - - pub const StrTuple = struct { - str: MetadataString, - elements_len: u32, - - // elements: [elements_len]Metadata - }; - - pub const ModuleFlag = struct { - behavior: Metadata, - name: MetadataString, - constant: Metadata, - }; - - pub const LocalVar = struct { - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - ty: Metadata, - }; - - pub const Parameter = struct { - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - ty: Metadata, - arg_no: u32, - }; - - pub const GlobalVar = struct { - pub const Options = struct { - local: bool, - }; - - name: MetadataString, - linkage_name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - ty: Metadata, - variable: Variable.Index, - }; - - pub const GlobalVarExpression = struct { - variable: Metadata, - expression: Metadata, - }; - - pub fn toValue(self: Metadata) Value { - return @enumFromInt(Value.first_metadata + @intFromEnum(self)); - } - - const Formatter = struct { - builder: *Builder, - need_comma: bool, - map: std.AutoArrayHashMapUnmanaged(union(enum) { - metadata: Metadata, - debug_location: DebugLocation.Location, - }, void) = .{}, - - const FormatData = struct { - formatter: *Formatter, - prefix: []const u8 = "", - node: Node, - - const Node = union(enum) { - none, - @"inline": Metadata, - index: u32, - - local_value: ValueData, - local_metadata: ValueData, - local_inline: Metadata, - local_index: u32, - - string: MetadataString, - bool: bool, - u32: u32, - u64: u64, - di_flags: DIFlags, - sp_flags: Subprogram.DISPFlags, - raw: []const u8, - - const ValueData = struct { - value: Value, - function: Function.Index, - }; - }; - }; - fn format( - data: FormatData, - comptime fmt_str: []const u8, - fmt_opts: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - if (data.node == .none) return; - - const is_specialized = fmt_str.len > 0 and fmt_str[0] == 'S'; - const recurse_fmt_str = if (is_specialized) fmt_str[1..] else fmt_str; - - if (data.formatter.need_comma) try writer.writeAll(", "); - defer data.formatter.need_comma = true; - try writer.writeAll(data.prefix); - - const builder = data.formatter.builder; - switch (data.node) { - .none => unreachable, - .@"inline" => |node| { - const needed_comma = data.formatter.need_comma; - defer data.formatter.need_comma = needed_comma; - data.formatter.need_comma = false; - - const item = builder.metadata_items.get(@intFromEnum(node)); - switch (item.tag) { - .expression => { - var extra = builder.metadataExtraDataTrail(Expression, item.data); - const elements = extra.trail.next(extra.data.elements_len, u32, builder); - try writer.writeAll("!DIExpression("); - for (elements) |element| try format(.{ - .formatter = data.formatter, - .node = .{ .u64 = element }, - }, "%", fmt_opts, writer); - try writer.writeByte(')'); - }, - .constant => try Constant.format(.{ - .constant = @enumFromInt(item.data), - .builder = builder, - }, recurse_fmt_str, fmt_opts, writer), - else => unreachable, - } - }, - .index => |node| try writer.print("!{d}", .{node}), - inline .local_value, .local_metadata => |node, tag| try Value.format(.{ - .value = node.value, - .function = node.function, - .builder = builder, - }, switch (tag) { - .local_value => recurse_fmt_str, - .local_metadata => "%", - else => unreachable, - }, fmt_opts, writer), - inline .local_inline, .local_index => |node, tag| { - if (comptime std.mem.eql(u8, recurse_fmt_str, "%")) - try writer.print("{%} ", .{Type.metadata.fmt(builder)}); - try format(.{ - .formatter = data.formatter, - .node = @unionInit(FormatData.Node, @tagName(tag)["local_".len..], node), - }, "%", fmt_opts, writer); - }, - .string => |node| try writer.print((if (is_specialized) "" else "!") ++ "{}", .{ - node.fmt(builder), - }), - inline .bool, - .u32, - .u64, - .di_flags, - .sp_flags, - => |node| try writer.print("{}", .{node}), - .raw => |node| try writer.writeAll(node), - } - } - inline fn fmt(formatter: *Formatter, prefix: []const u8, node: anytype) switch (@TypeOf(node)) { - Metadata => Allocator.Error, - else => error{}, - }!std.fmt.Formatter(format) { - const Node = @TypeOf(node); - const MaybeNode = switch (@typeInfo(Node)) { - .optional => Node, - .null => ?noreturn, - else => ?Node, - }; - const Some = @typeInfo(MaybeNode).optional.child; - return .{ .data = .{ - .formatter = formatter, - .prefix = prefix, - .node = if (@as(MaybeNode, node)) |some| switch (@typeInfo(Some)) { - .@"enum" => |enum_info| switch (Some) { - Metadata => switch (some) { - .none => .none, - else => try formatter.refUnwrapped(some.unwrap(formatter.builder)), - }, - MetadataString => .{ .string = some }, - else => if (enum_info.is_exhaustive) - .{ .raw = @tagName(some) } - else - @compileError("unknown type to format: " ++ @typeName(Node)), - }, - .enum_literal => .{ .raw = @tagName(some) }, - .bool => .{ .bool = some }, - .@"struct" => switch (Some) { - DIFlags => .{ .di_flags = some }, - Subprogram.DISPFlags => .{ .sp_flags = some }, - else => @compileError("unknown type to format: " ++ @typeName(Node)), - }, - .int, .comptime_int => .{ .u64 = some }, - .pointer => .{ .raw = some }, - else => @compileError("unknown type to format: " ++ @typeName(Node)), - } else switch (@typeInfo(Node)) { - .optional, .null => .none, - else => unreachable, - }, - } }; - } - inline fn fmtLocal( - formatter: *Formatter, - prefix: []const u8, - value: Value, - function: Function.Index, - ) Allocator.Error!std.fmt.Formatter(format) { - return .{ .data = .{ - .formatter = formatter, - .prefix = prefix, - .node = switch (value.unwrap()) { - .instruction, .constant => .{ .local_value = .{ - .value = value, - .function = function, - } }, - .metadata => |metadata| if (value == .none) .none else node: { - const unwrapped = metadata.unwrap(formatter.builder); - break :node if (@intFromEnum(unwrapped) >= first_local_metadata) - .{ .local_metadata = .{ - .value = function.ptrConst(formatter.builder).debug_values[ - @intFromEnum(unwrapped) - first_local_metadata - ].toValue(), - .function = function, - } } - else switch (try formatter.refUnwrapped(unwrapped)) { - .@"inline" => |node| .{ .local_inline = node }, - .index => |node| .{ .local_index = node }, - else => unreachable, - }; - }, - }, - } }; - } - fn refUnwrapped(formatter: *Formatter, node: Metadata) Allocator.Error!FormatData.Node { - assert(node != .none); - assert(@intFromEnum(node) < first_forward_reference); - const builder = formatter.builder; - const unwrapped_metadata = node.unwrap(builder); - const tag = formatter.builder.metadata_items.items(.tag)[@intFromEnum(unwrapped_metadata)]; - switch (tag) { - .none => unreachable, - .expression, .constant => return .{ .@"inline" = unwrapped_metadata }, - else => { - assert(!tag.isInline()); - const gop = try formatter.map.getOrPut(builder.gpa, .{ .metadata = unwrapped_metadata }); - return .{ .index = @intCast(gop.index) }; - }, - } - } - - inline fn specialized( - formatter: *Formatter, - distinct: enum { @"!", @"distinct !" }, - node: enum { - DIFile, - DICompileUnit, - DISubprogram, - DILexicalBlock, - DILocation, - DIBasicType, - DICompositeType, - DIDerivedType, - DISubroutineType, - DIEnumerator, - DISubrange, - DILocalVariable, - DIGlobalVariable, - DIGlobalVariableExpression, - }, - nodes: anytype, - writer: anytype, - ) !void { - comptime var fmt_str: []const u8 = ""; - const names = comptime std.meta.fieldNames(@TypeOf(nodes)); - comptime var fields: [2 + names.len]std.builtin.Type.StructField = undefined; - inline for (fields[0..2], .{ "distinct", "node" }) |*field, name| { - fmt_str = fmt_str ++ "{[" ++ name ++ "]s}"; - field.* = .{ - .name = name, - .type = []const u8, - .default_value_ptr = null, - .is_comptime = false, - .alignment = 0, - }; - } - fmt_str = fmt_str ++ "("; - inline for (fields[2..], names) |*field, name| { - fmt_str = fmt_str ++ "{[" ++ name ++ "]S}"; - field.* = .{ - .name = name, - .type = std.fmt.Formatter(format), - .default_value_ptr = null, - .is_comptime = false, - .alignment = 0, - }; - } - fmt_str = fmt_str ++ ")\n"; - - var fmt_args: @Type(.{ .@"struct" = .{ - .layout = .auto, - .fields = &fields, - .decls = &.{}, - .is_tuple = false, - } }) = undefined; - fmt_args.distinct = @tagName(distinct); - fmt_args.node = @tagName(node); - inline for (names) |name| @field(fmt_args, name) = try formatter.fmt( - name ++ ": ", - @field(nodes, name), - ); - try writer.print(fmt_str, fmt_args); - } - }; -}; - -pub fn init(options: Options) Allocator.Error!Builder { - var self: Builder = .{ - .gpa = options.allocator, - .strip = options.strip, - - .source_filename = .none, - .data_layout = .none, - .target_triple = .none, - .module_asm = .{}, - - .string_map = .{}, - .string_indices = .{}, - .string_bytes = .{}, - - .types = .{}, - .next_unnamed_type = @enumFromInt(0), - .next_unique_type_id = .{}, - .type_map = .{}, - .type_items = .{}, - .type_extra = .{}, - - .attributes = .{}, - .attributes_map = .{}, - .attributes_indices = .{}, - .attributes_extra = .{}, - - .function_attributes_set = .{}, - - .globals = .{}, - .next_unnamed_global = @enumFromInt(0), - .next_replaced_global = .none, - .next_unique_global_id = .{}, - .aliases = .{}, - .variables = .{}, - .functions = .{}, - - .strtab_string_map = .{}, - .strtab_string_indices = .{}, - .strtab_string_bytes = .{}, - - .constant_map = .{}, - .constant_items = .{}, - .constant_extra = .{}, - .constant_limbs = .{}, - - .metadata_map = .{}, - .metadata_items = .{}, - .metadata_extra = .{}, - .metadata_limbs = .{}, - .metadata_forward_references = .{}, - .metadata_named = .{}, - .metadata_string_map = .{}, - .metadata_string_indices = .{}, - .metadata_string_bytes = .{}, - }; - errdefer self.deinit(); - - try self.string_indices.append(self.gpa, 0); - assert(try self.string("") == .empty); - - try self.strtab_string_indices.append(self.gpa, 0); - assert(try self.strtabString("") == .empty); - - if (options.name.len > 0) self.source_filename = try self.string(options.name); - - if (options.triple.len > 0) { - self.target_triple = try self.string(options.triple); - } - - { - const static_len = @typeInfo(Type).@"enum".fields.len - 1; - try self.type_map.ensureTotalCapacity(self.gpa, static_len); - try self.type_items.ensureTotalCapacity(self.gpa, static_len); - inline for (@typeInfo(Type.Simple).@"enum".fields) |simple_field| { - const result = self.getOrPutTypeNoExtraAssumeCapacity( - .{ .tag = .simple, .data = simple_field.value }, - ); - assert(result.new and result.type == @field(Type, simple_field.name)); - } - 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, 4 }) |addr_space_index| { - const addr_space: AddrSpace = @enumFromInt(addr_space_index); - assert(self.ptrTypeAssumeCapacity(addr_space) == - @field(Type, std.fmt.comptimePrint("ptr{ }", .{addr_space}))); - } - } - - { - try self.attributes_indices.append(self.gpa, 0); - assert(try self.attrs(&.{}) == .none); - assert(try self.fnAttrs(&.{}) == .none); - } - - assert(try self.intConst(.i1, 0) == .false); - assert(try self.intConst(.i1, 1) == .true); - assert(try self.intConst(.i32, 0) == .@"0"); - assert(try self.intConst(.i32, 1) == .@"1"); - assert(try self.noneConst(.token) == .none); - - assert(try self.metadataNone() == .none); - assert(try self.metadataTuple(&.{}) == .empty_tuple); - - try self.metadata_string_indices.append(self.gpa, 0); - assert(try self.metadataString("") == .none); - - return self; -} - -pub fn clearAndFree(self: *Builder) void { - self.module_asm.clearAndFree(self.gpa); - - self.string_map.clearAndFree(self.gpa); - self.string_indices.clearAndFree(self.gpa); - self.string_bytes.clearAndFree(self.gpa); - - self.types.clearAndFree(self.gpa); - self.next_unique_type_id.clearAndFree(self.gpa); - self.type_map.clearAndFree(self.gpa); - self.type_items.clearAndFree(self.gpa); - self.type_extra.clearAndFree(self.gpa); - - self.attributes.clearAndFree(self.gpa); - self.attributes_map.clearAndFree(self.gpa); - self.attributes_indices.clearAndFree(self.gpa); - self.attributes_extra.clearAndFree(self.gpa); - - self.function_attributes_set.clearAndFree(self.gpa); - - self.globals.clearAndFree(self.gpa); - self.next_unique_global_id.clearAndFree(self.gpa); - self.aliases.clearAndFree(self.gpa); - self.variables.clearAndFree(self.gpa); - for (self.functions.items) |*function| function.deinit(self.gpa); - self.functions.clearAndFree(self.gpa); - - self.strtab_string_map.clearAndFree(self.gpa); - self.strtab_string_indices.clearAndFree(self.gpa); - self.strtab_string_bytes.clearAndFree(self.gpa); - - self.constant_map.clearAndFree(self.gpa); - self.constant_items.shrinkAndFree(self.gpa, 0); - self.constant_extra.clearAndFree(self.gpa); - self.constant_limbs.clearAndFree(self.gpa); - - self.metadata_map.clearAndFree(self.gpa); - self.metadata_items.shrinkAndFree(self.gpa, 0); - self.metadata_extra.clearAndFree(self.gpa); - self.metadata_limbs.clearAndFree(self.gpa); - self.metadata_forward_references.clearAndFree(self.gpa); - self.metadata_named.clearAndFree(self.gpa); - - self.metadata_string_map.clearAndFree(self.gpa); - self.metadata_string_indices.clearAndFree(self.gpa); - self.metadata_string_bytes.clearAndFree(self.gpa); -} - -pub fn deinit(self: *Builder) void { - self.module_asm.deinit(self.gpa); - - self.string_map.deinit(self.gpa); - self.string_indices.deinit(self.gpa); - self.string_bytes.deinit(self.gpa); - - self.types.deinit(self.gpa); - self.next_unique_type_id.deinit(self.gpa); - self.type_map.deinit(self.gpa); - self.type_items.deinit(self.gpa); - self.type_extra.deinit(self.gpa); - - self.attributes.deinit(self.gpa); - self.attributes_map.deinit(self.gpa); - self.attributes_indices.deinit(self.gpa); - self.attributes_extra.deinit(self.gpa); - - self.function_attributes_set.deinit(self.gpa); - - self.globals.deinit(self.gpa); - self.next_unique_global_id.deinit(self.gpa); - self.aliases.deinit(self.gpa); - self.variables.deinit(self.gpa); - for (self.functions.items) |*function| function.deinit(self.gpa); - self.functions.deinit(self.gpa); - - self.strtab_string_map.deinit(self.gpa); - self.strtab_string_indices.deinit(self.gpa); - self.strtab_string_bytes.deinit(self.gpa); - - self.constant_map.deinit(self.gpa); - self.constant_items.deinit(self.gpa); - self.constant_extra.deinit(self.gpa); - self.constant_limbs.deinit(self.gpa); - - self.metadata_map.deinit(self.gpa); - self.metadata_items.deinit(self.gpa); - self.metadata_extra.deinit(self.gpa); - self.metadata_limbs.deinit(self.gpa); - self.metadata_forward_references.deinit(self.gpa); - self.metadata_named.deinit(self.gpa); - - self.metadata_string_map.deinit(self.gpa); - self.metadata_string_indices.deinit(self.gpa); - self.metadata_string_bytes.deinit(self.gpa); - - self.* = undefined; -} - -pub fn setModuleAsm(self: *Builder) std.ArrayListUnmanaged(u8).Writer { - self.module_asm.clearRetainingCapacity(); - return self.appendModuleAsm(); -} - -pub fn appendModuleAsm(self: *Builder) std.ArrayListUnmanaged(u8).Writer { - return self.module_asm.writer(self.gpa); -} - -pub fn finishModuleAsm(self: *Builder) Allocator.Error!void { - if (self.module_asm.getLastOrNull()) |last| if (last != '\n') - try self.module_asm.append(self.gpa, '\n'); -} - -pub fn string(self: *Builder, bytes: []const u8) Allocator.Error!String { - try self.string_bytes.ensureUnusedCapacity(self.gpa, bytes.len); - try self.string_indices.ensureUnusedCapacity(self.gpa, 1); - try self.string_map.ensureUnusedCapacity(self.gpa, 1); - - const gop = self.string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self }); - if (!gop.found_existing) { - self.string_bytes.appendSliceAssumeCapacity(bytes); - self.string_indices.appendAssumeCapacity(@intCast(self.string_bytes.items.len)); - } - return String.fromIndex(gop.index); -} - -pub fn stringNull(self: *Builder, bytes: [:0]const u8) Allocator.Error!String { - return self.string(bytes[0 .. bytes.len + 1]); -} - -pub fn stringIfExists(self: *const Builder, bytes: []const u8) ?String { - return String.fromIndex( - self.string_map.getIndexAdapted(bytes, String.Adapter{ .builder = self }) orelse return null, - ); -} - -pub fn fmt(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) Allocator.Error!String { - try self.string_map.ensureUnusedCapacity(self.gpa, 1); - try self.string_bytes.ensureUnusedCapacity(self.gpa, @intCast(std.fmt.count(fmt_str, fmt_args))); - try self.string_indices.ensureUnusedCapacity(self.gpa, 1); - return self.fmtAssumeCapacity(fmt_str, fmt_args); -} - -pub fn fmtAssumeCapacity(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) String { - self.string_bytes.writer(undefined).print(fmt_str, fmt_args) catch unreachable; - return self.trailingStringAssumeCapacity(); -} - -pub fn trailingString(self: *Builder) Allocator.Error!String { - try self.string_indices.ensureUnusedCapacity(self.gpa, 1); - try self.string_map.ensureUnusedCapacity(self.gpa, 1); - return self.trailingStringAssumeCapacity(); -} - -pub fn trailingStringAssumeCapacity(self: *Builder) String { - const start = self.string_indices.getLast(); - const bytes: []const u8 = self.string_bytes.items[start..]; - const gop = self.string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self }); - if (gop.found_existing) { - self.string_bytes.shrinkRetainingCapacity(start); - } else { - self.string_indices.appendAssumeCapacity(@intCast(self.string_bytes.items.len)); - } - return String.fromIndex(gop.index); -} - -pub fn fnType( - self: *Builder, - ret: Type, - params: []const Type, - kind: Type.Function.Kind, -) Allocator.Error!Type { - try self.ensureUnusedTypeCapacity(1, Type.Function, params.len); - switch (kind) { - inline else => |comptime_kind| return self.fnTypeAssumeCapacity(ret, params, comptime_kind), - } -} - -pub fn intType(self: *Builder, bits: u24) Allocator.Error!Type { - try self.ensureUnusedTypeCapacity(1, NoExtra, 0); - return self.intTypeAssumeCapacity(bits); -} - -pub fn ptrType(self: *Builder, addr_space: AddrSpace) Allocator.Error!Type { - try self.ensureUnusedTypeCapacity(1, NoExtra, 0); - return self.ptrTypeAssumeCapacity(addr_space); -} - -pub fn vectorType( - self: *Builder, - kind: Type.Vector.Kind, - len: u32, - child: Type, -) Allocator.Error!Type { - try self.ensureUnusedTypeCapacity(1, Type.Vector, 0); - switch (kind) { - inline else => |comptime_kind| return self.vectorTypeAssumeCapacity(comptime_kind, len, child), - } -} - -pub fn arrayType(self: *Builder, len: u64, child: Type) Allocator.Error!Type { - comptime assert(@sizeOf(Type.Array) >= @sizeOf(Type.Vector)); - try self.ensureUnusedTypeCapacity(1, Type.Array, 0); - return self.arrayTypeAssumeCapacity(len, child); -} - -pub fn structType( - self: *Builder, - kind: Type.Structure.Kind, - fields: []const Type, -) Allocator.Error!Type { - try self.ensureUnusedTypeCapacity(1, Type.Structure, fields.len); - switch (kind) { - inline else => |comptime_kind| return self.structTypeAssumeCapacity(comptime_kind, fields), - } -} - -pub fn opaqueType(self: *Builder, name: String) Allocator.Error!Type { - try self.string_map.ensureUnusedCapacity(self.gpa, 1); - if (name.slice(self)) |id| { - const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)}); - try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); - } - 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.ensureUnusedTypeCapacity(1, Type.NamedStructure, 0); - return self.opaqueTypeAssumeCapacity(name); -} - -pub fn namedTypeSetBody( - self: *Builder, - named_type: Type, - body_type: Type, -) void { - const named_item = self.type_items.items[@intFromEnum(named_type)]; - self.type_extra.items[named_item.data + std.meta.fieldIndex(Type.NamedStructure, "body").?] = - @intFromEnum(body_type); -} - -pub fn attr(self: *Builder, attribute: Attribute) Allocator.Error!Attribute.Index { - try self.attributes.ensureUnusedCapacity(self.gpa, 1); - - const gop = self.attributes.getOrPutAssumeCapacity(attribute.toStorage()); - if (!gop.found_existing) gop.value_ptr.* = {}; - return @enumFromInt(gop.index); -} - -pub fn attrs(self: *Builder, attributes: []Attribute.Index) Allocator.Error!Attributes { - std.sort.heap(Attribute.Index, attributes, self, struct { - pub fn lessThan(builder: *const Builder, lhs: Attribute.Index, rhs: Attribute.Index) bool { - const lhs_kind = lhs.getKind(builder); - const rhs_kind = rhs.getKind(builder); - assert(lhs_kind != rhs_kind); - return @intFromEnum(lhs_kind) < @intFromEnum(rhs_kind); - } - }.lessThan); - return @enumFromInt(try self.attrGeneric(@ptrCast(attributes))); -} - -pub fn fnAttrs(self: *Builder, fn_attributes: []const Attributes) Allocator.Error!FunctionAttributes { - try self.function_attributes_set.ensureUnusedCapacity(self.gpa, 1); - const function_attributes: FunctionAttributes = @enumFromInt(try self.attrGeneric(@ptrCast( - fn_attributes[0..if (std.mem.lastIndexOfNone(Attributes, fn_attributes, &.{.none})) |last| - last + 1 - else - 0], - ))); - - _ = self.function_attributes_set.getOrPutAssumeCapacity(function_attributes); - return function_attributes; -} - -pub fn addGlobal(self: *Builder, name: StrtabString, global: Global) Allocator.Error!Global.Index { - assert(!name.isAnon()); - try self.ensureUnusedTypeCapacity(1, NoExtra, 0); - try self.ensureUnusedGlobalCapacity(name); - return self.addGlobalAssumeCapacity(name, global); -} - -pub fn addGlobalAssumeCapacity(self: *Builder, name: StrtabString, global: Global) Global.Index { - _ = self.ptrTypeAssumeCapacity(global.addr_space); - var id = name; - if (name == .empty) { - id = self.next_unnamed_global; - assert(id != self.next_replaced_global); - self.next_unnamed_global = @enumFromInt(@intFromEnum(id) + 1); - } - while (true) { - const global_gop = self.globals.getOrPutAssumeCapacity(id); - if (!global_gop.found_existing) { - global_gop.value_ptr.* = global; - const global_index: Global.Index = @enumFromInt(global_gop.index); - global_index.updateDsoLocal(self); - return global_index; - } - - const unique_gop = self.next_unique_global_id.getOrPutAssumeCapacity(name); - if (!unique_gop.found_existing) unique_gop.value_ptr.* = 2; - id = self.strtabStringFmtAssumeCapacity("{s}.{d}", .{ name.slice(self).?, unique_gop.value_ptr.* }); - unique_gop.value_ptr.* += 1; - } -} - -pub fn getGlobal(self: *const Builder, name: StrtabString) ?Global.Index { - return @enumFromInt(self.globals.getIndex(name) orelse return null); -} - -pub fn addAlias( - self: *Builder, - name: StrtabString, - ty: Type, - addr_space: AddrSpace, - aliasee: Constant, -) Allocator.Error!Alias.Index { - assert(!name.isAnon()); - try self.ensureUnusedTypeCapacity(1, NoExtra, 0); - try self.ensureUnusedGlobalCapacity(name); - try self.aliases.ensureUnusedCapacity(self.gpa, 1); - return self.addAliasAssumeCapacity(name, ty, addr_space, aliasee); -} - -pub fn addAliasAssumeCapacity( - self: *Builder, - name: StrtabString, - ty: Type, - addr_space: AddrSpace, - aliasee: Constant, -) Alias.Index { - const alias_index: Alias.Index = @enumFromInt(self.aliases.items.len); - self.aliases.appendAssumeCapacity(.{ .global = self.addGlobalAssumeCapacity(name, .{ - .addr_space = addr_space, - .type = ty, - .kind = .{ .alias = alias_index }, - }), .aliasee = aliasee }); - return alias_index; -} - -pub fn addVariable( - self: *Builder, - name: StrtabString, - ty: Type, - addr_space: AddrSpace, -) Allocator.Error!Variable.Index { - assert(!name.isAnon()); - try self.ensureUnusedTypeCapacity(1, NoExtra, 0); - try self.ensureUnusedGlobalCapacity(name); - try self.variables.ensureUnusedCapacity(self.gpa, 1); - return self.addVariableAssumeCapacity(ty, name, addr_space); -} - -pub fn addVariableAssumeCapacity( - self: *Builder, - ty: Type, - name: StrtabString, - addr_space: AddrSpace, -) Variable.Index { - const variable_index: Variable.Index = @enumFromInt(self.variables.items.len); - self.variables.appendAssumeCapacity(.{ .global = self.addGlobalAssumeCapacity(name, .{ - .addr_space = addr_space, - .type = ty, - .kind = .{ .variable = variable_index }, - }) }); - return variable_index; -} - -pub fn addFunction( - self: *Builder, - ty: Type, - name: StrtabString, - addr_space: AddrSpace, -) Allocator.Error!Function.Index { - assert(!name.isAnon()); - try self.ensureUnusedTypeCapacity(1, NoExtra, 0); - try self.ensureUnusedGlobalCapacity(name); - try self.functions.ensureUnusedCapacity(self.gpa, 1); - return self.addFunctionAssumeCapacity(ty, name, addr_space); -} - -pub fn addFunctionAssumeCapacity( - self: *Builder, - ty: Type, - name: StrtabString, - addr_space: AddrSpace, -) Function.Index { - assert(ty.isFunction(self)); - const function_index: Function.Index = @enumFromInt(self.functions.items.len); - self.functions.appendAssumeCapacity(.{ - .global = self.addGlobalAssumeCapacity(name, .{ - .addr_space = addr_space, - .type = ty, - .kind = .{ .function = function_index }, - }), - .strip = undefined, - }); - return function_index; -} - -pub fn getIntrinsic( - self: *Builder, - id: Intrinsic, - overload: []const Type, -) Allocator.Error!Function.Index { - const ExpectedContents = extern union { - attrs: extern struct { - params: [expected_args_len]Type, - fn_attrs: [FunctionAttributes.params_index + expected_args_len]Attributes, - attrs: [expected_attrs_len]Attribute.Index, - fields: [expected_fields_len]Type, - }, - }; - var stack align(@max(@alignOf(std.heap.StackFallbackAllocator(0)), @alignOf(ExpectedContents))) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); - const allocator = stack.get(); - - const name = name: { - const writer = self.strtab_string_bytes.writer(self.gpa); - try writer.print("llvm.{s}", .{@tagName(id)}); - for (overload) |ty| try writer.print(".{m}", .{ty.fmt(self)}); - break :name try self.trailingStrtabString(); - }; - if (self.getGlobal(name)) |global| return global.ptrConst(self).kind.function; - - const signature = Intrinsic.signatures.get(id); - const param_types = try allocator.alloc(Type, signature.params.len); - defer allocator.free(param_types); - const function_attributes = try allocator.alloc( - Attributes, - FunctionAttributes.params_index + (signature.params.len - signature.ret_len), - ); - defer allocator.free(function_attributes); - - var attributes: struct { - builder: *Builder, - list: std.ArrayList(Attribute.Index), - - fn deinit(state: *@This()) void { - state.list.deinit(); - state.* = undefined; - } - - fn get(state: *@This(), attributes: []const Attribute) Allocator.Error!Attributes { - try state.list.resize(attributes.len); - for (state.list.items, attributes) |*item, attribute| - item.* = try state.builder.attr(attribute); - return state.builder.attrs(state.list.items); - } - } = .{ .builder = self, .list = std.ArrayList(Attribute.Index).init(allocator) }; - defer attributes.deinit(); - - var overload_index: usize = 0; - function_attributes[FunctionAttributes.function_index] = try attributes.get(signature.attrs); - function_attributes[FunctionAttributes.return_index] = .none; // needed for void return - for (0.., param_types, signature.params) |param_index, *param_type, signature_param| { - switch (signature_param.kind) { - .type => |ty| param_type.* = ty, - .overloaded => { - param_type.* = overload[overload_index]; - overload_index += 1; - }, - .matches, .matches_scalar, .matches_changed_scalar => {}, - } - function_attributes[ - if (param_index < signature.ret_len) - FunctionAttributes.return_index - else - FunctionAttributes.params_index + (param_index - signature.ret_len) - ] = try attributes.get(signature_param.attrs); - } - assert(overload_index == overload.len); - for (param_types, signature.params) |*param_type, signature_param| { - param_type.* = switch (signature_param.kind) { - .type, .overloaded => continue, - .matches => |param_index| param_types[param_index], - .matches_scalar => |param_index| param_types[param_index].scalarType(self), - .matches_changed_scalar => |info| try param_types[info.index] - .changeScalar(info.scalar, self), - }; - } - - const function_index = try self.addFunction(try self.fnType(switch (signature.ret_len) { - 0 => .void, - 1 => param_types[0], - else => try self.structType(.normal, param_types[0..signature.ret_len]), - }, param_types[signature.ret_len..], .normal), name, .default); - function_index.ptr(self).attributes = try self.fnAttrs(function_attributes); - return function_index; -} - -pub fn intConst(self: *Builder, ty: Type, value: anytype) Allocator.Error!Constant { - const int_value = switch (@typeInfo(@TypeOf(value))) { - .int, .comptime_int => value, - .@"enum" => @intFromEnum(value), - else => @compileError("intConst expected an integral value, got " ++ @typeName(@TypeOf(value))), - }; - var limbs: [ - switch (@typeInfo(@TypeOf(int_value))) { - .int => |info| std.math.big.int.calcTwosCompLimbCount(info.bits), - .comptime_int => std.math.big.int.calcLimbLen(int_value), - else => unreachable, - } - ]std.math.big.Limb = undefined; - return self.bigIntConst(ty, std.math.big.int.Mutable.init(&limbs, int_value).toConst()); -} - -pub fn intValue(self: *Builder, ty: Type, value: anytype) Allocator.Error!Value { - return (try self.intConst(ty, value)).toValue(); -} - -pub fn bigIntConst(self: *Builder, ty: Type, value: std.math.big.int.Const) Allocator.Error!Constant { - try self.constant_map.ensureUnusedCapacity(self.gpa, 1); - try self.constant_items.ensureUnusedCapacity(self.gpa, 1); - try self.constant_limbs.ensureUnusedCapacity(self.gpa, Constant.Integer.limbs + value.limbs.len); - return self.bigIntConstAssumeCapacity(ty, value); -} - -pub fn bigIntValue(self: *Builder, ty: Type, value: std.math.big.int.Const) Allocator.Error!Value { - return (try self.bigIntConst(ty, value)).toValue(); -} - -pub fn fpConst(self: *Builder, ty: Type, comptime val: comptime_float) Allocator.Error!Constant { - return switch (ty) { - .half => try self.halfConst(val), - .bfloat => try self.bfloatConst(val), - .float => try self.floatConst(val), - .double => try self.doubleConst(val), - .fp128 => try self.fp128Const(val), - .x86_fp80 => try self.x86_fp80Const(val), - .ppc_fp128 => try self.ppc_fp128Const(.{ val, -0.0 }), - else => unreachable, - }; -} - -pub fn fpValue(self: *Builder, ty: Type, comptime value: comptime_float) Allocator.Error!Value { - return (try self.fpConst(ty, value)).toValue(); -} - -pub fn nanConst(self: *Builder, ty: Type) Allocator.Error!Constant { - return switch (ty) { - .half => try self.halfConst(std.math.nan(f16)), - .bfloat => try self.bfloatConst(std.math.nan(f32)), - .float => try self.floatConst(std.math.nan(f32)), - .double => try self.doubleConst(std.math.nan(f64)), - .fp128 => try self.fp128Const(std.math.nan(f128)), - .x86_fp80 => try self.x86_fp80Const(std.math.nan(f80)), - .ppc_fp128 => try self.ppc_fp128Const(.{std.math.nan(f64)} ** 2), - else => unreachable, - }; -} - -pub fn nanValue(self: *Builder, ty: Type) Allocator.Error!Value { - return (try self.nanConst(ty)).toValue(); -} - -pub fn halfConst(self: *Builder, val: f16) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, NoExtra, 0); - return self.halfConstAssumeCapacity(val); -} - -pub fn halfValue(self: *Builder, ty: Type, value: f16) Allocator.Error!Value { - return (try self.halfConst(ty, value)).toValue(); -} - -pub fn bfloatConst(self: *Builder, val: f32) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, NoExtra, 0); - return self.bfloatConstAssumeCapacity(val); -} - -pub fn bfloatValue(self: *Builder, ty: Type, value: f32) Allocator.Error!Value { - return (try self.bfloatConst(ty, value)).toValue(); -} - -pub fn floatConst(self: *Builder, val: f32) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, NoExtra, 0); - return self.floatConstAssumeCapacity(val); -} - -pub fn floatValue(self: *Builder, ty: Type, value: f32) Allocator.Error!Value { - return (try self.floatConst(ty, value)).toValue(); -} - -pub fn doubleConst(self: *Builder, val: f64) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Double, 0); - return self.doubleConstAssumeCapacity(val); -} - -pub fn doubleValue(self: *Builder, ty: Type, value: f64) Allocator.Error!Value { - return (try self.doubleConst(ty, value)).toValue(); -} - -pub fn fp128Const(self: *Builder, val: f128) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Fp128, 0); - return self.fp128ConstAssumeCapacity(val); -} - -pub fn fp128Value(self: *Builder, ty: Type, value: f128) Allocator.Error!Value { - return (try self.fp128Const(ty, value)).toValue(); -} - -pub fn x86_fp80Const(self: *Builder, val: f80) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Fp80, 0); - return self.x86_fp80ConstAssumeCapacity(val); -} - -pub fn x86_fp80Value(self: *Builder, ty: Type, value: f80) Allocator.Error!Value { - return (try self.x86_fp80Const(ty, value)).toValue(); -} - -pub fn ppc_fp128Const(self: *Builder, val: [2]f64) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Fp128, 0); - return self.ppc_fp128ConstAssumeCapacity(val); -} - -pub fn ppc_fp128Value(self: *Builder, ty: Type, value: [2]f64) Allocator.Error!Value { - return (try self.ppc_fp128Const(ty, value)).toValue(); -} - -pub fn nullConst(self: *Builder, ty: Type) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, NoExtra, 0); - return self.nullConstAssumeCapacity(ty); -} - -pub fn nullValue(self: *Builder, ty: Type) Allocator.Error!Value { - return (try self.nullConst(ty)).toValue(); -} - -pub fn noneConst(self: *Builder, ty: Type) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, NoExtra, 0); - return self.noneConstAssumeCapacity(ty); -} - -pub fn noneValue(self: *Builder, ty: Type) Allocator.Error!Value { - return (try self.noneConst(ty)).toValue(); -} - -pub fn structConst(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Aggregate, vals.len); - return self.structConstAssumeCapacity(ty, vals); -} - -pub fn structValue(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Value { - return (try self.structConst(ty, vals)).toValue(); -} - -pub fn arrayConst(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Aggregate, vals.len); - return self.arrayConstAssumeCapacity(ty, vals); -} - -pub fn arrayValue(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Value { - return (try self.arrayConst(ty, vals)).toValue(); -} - -pub fn stringConst(self: *Builder, val: String) Allocator.Error!Constant { - try self.ensureUnusedTypeCapacity(1, Type.Array, 0); - try self.ensureUnusedConstantCapacity(1, NoExtra, 0); - return self.stringConstAssumeCapacity(val); -} - -pub fn stringValue(self: *Builder, val: String) Allocator.Error!Value { - return (try self.stringConst(val)).toValue(); -} - -pub fn vectorConst(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Aggregate, vals.len); - return self.vectorConstAssumeCapacity(ty, vals); -} - -pub fn vectorValue(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Value { - return (try self.vectorConst(ty, vals)).toValue(); -} - -pub fn splatConst(self: *Builder, ty: Type, val: Constant) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Splat, 0); - return self.splatConstAssumeCapacity(ty, val); -} - -pub fn splatValue(self: *Builder, ty: Type, val: Constant) Allocator.Error!Value { - return (try self.splatConst(ty, val)).toValue(); -} - -pub fn zeroInitConst(self: *Builder, ty: Type) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Fp128, 0); - try self.constant_limbs.ensureUnusedCapacity( - self.gpa, - Constant.Integer.limbs + comptime std.math.big.int.calcLimbLen(0), - ); - return self.zeroInitConstAssumeCapacity(ty); -} - -pub fn zeroInitValue(self: *Builder, ty: Type) Allocator.Error!Value { - return (try self.zeroInitConst(ty)).toValue(); -} - -pub fn undefConst(self: *Builder, ty: Type) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, NoExtra, 0); - return self.undefConstAssumeCapacity(ty); -} - -pub fn undefValue(self: *Builder, ty: Type) Allocator.Error!Value { - return (try self.undefConst(ty)).toValue(); -} - -pub fn poisonConst(self: *Builder, ty: Type) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, NoExtra, 0); - return self.poisonConstAssumeCapacity(ty); -} - -pub fn poisonValue(self: *Builder, ty: Type) Allocator.Error!Value { - return (try self.poisonConst(ty)).toValue(); -} - -pub fn blockAddrConst( - self: *Builder, - function: Function.Index, - block: Function.Block.Index, -) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.BlockAddress, 0); - return self.blockAddrConstAssumeCapacity(function, block); -} - -pub fn blockAddrValue( - self: *Builder, - function: Function.Index, - block: Function.Block.Index, -) Allocator.Error!Value { - return (try self.blockAddrConst(function, block)).toValue(); -} - -pub fn dsoLocalEquivalentConst(self: *Builder, function: Function.Index) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, NoExtra, 0); - return self.dsoLocalEquivalentConstAssumeCapacity(function); -} - -pub fn dsoLocalEquivalentValue(self: *Builder, function: Function.Index) Allocator.Error!Value { - return (try self.dsoLocalEquivalentConst(function)).toValue(); -} - -pub fn noCfiConst(self: *Builder, function: Function.Index) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, NoExtra, 0); - return self.noCfiConstAssumeCapacity(function); -} - -pub fn noCfiValue(self: *Builder, function: Function.Index) Allocator.Error!Value { - return (try self.noCfiConst(function)).toValue(); -} - -pub fn convConst( - self: *Builder, - val: Constant, - ty: Type, -) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Cast, 0); - return self.convConstAssumeCapacity(val, ty); -} - -pub fn convValue( - self: *Builder, - val: Constant, - ty: Type, -) Allocator.Error!Value { - return (try self.convConst(val, ty)).toValue(); -} - -pub fn castConst(self: *Builder, tag: Constant.Tag, val: Constant, ty: Type) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Cast, 0); - return self.castConstAssumeCapacity(tag, val, ty); -} - -pub fn castValue(self: *Builder, tag: Constant.Tag, val: Constant, ty: Type) Allocator.Error!Value { - return (try self.castConst(tag, val, ty)).toValue(); -} - -pub fn gepConst( - self: *Builder, - comptime kind: Constant.GetElementPtr.Kind, - ty: Type, - base: Constant, - inrange: ?u16, - indices: []const Constant, -) Allocator.Error!Constant { - try self.ensureUnusedTypeCapacity(1, Type.Vector, 0); - try self.ensureUnusedConstantCapacity(1, Constant.GetElementPtr, indices.len); - return self.gepConstAssumeCapacity(kind, ty, base, inrange, indices); -} - -pub fn gepValue( - self: *Builder, - comptime kind: Constant.GetElementPtr.Kind, - ty: Type, - base: Constant, - inrange: ?u16, - indices: []const Constant, -) Allocator.Error!Value { - return (try self.gepConst(kind, ty, base, inrange, indices)).toValue(); -} - -pub fn binConst( - self: *Builder, - tag: Constant.Tag, - lhs: Constant, - rhs: Constant, -) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Binary, 0); - return self.binConstAssumeCapacity(tag, lhs, rhs); -} - -pub fn binValue(self: *Builder, tag: Constant.Tag, lhs: Constant, rhs: Constant) Allocator.Error!Value { - return (try self.binConst(tag, lhs, rhs)).toValue(); -} - -pub fn asmConst( - self: *Builder, - ty: Type, - info: Constant.Assembly.Info, - assembly: String, - constraints: String, -) Allocator.Error!Constant { - try self.ensureUnusedConstantCapacity(1, Constant.Assembly, 0); - return self.asmConstAssumeCapacity(ty, info, assembly, constraints); -} - -pub fn asmValue( - self: *Builder, - ty: Type, - info: Constant.Assembly.Info, - assembly: String, - constraints: String, -) Allocator.Error!Value { - return (try self.asmConst(ty, info, assembly, constraints)).toValue(); -} - -pub fn dump(self: *Builder) void { - self.print(std.io.getStdErr().writer()) catch {}; -} - -pub fn printToFile(self: *Builder, path: []const u8) Allocator.Error!bool { - var file = std.fs.cwd().createFile(path, .{}) catch |err| { - log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); - return false; - }; - defer file.close(); - self.print(file.writer()) catch |err| { - log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); - return false; - }; - return true; -} - -pub fn print(self: *Builder, writer: anytype) (@TypeOf(writer).Error || Allocator.Error)!void { - var bw = std.io.bufferedWriter(writer); - try self.printUnbuffered(bw.writer()); - try bw.flush(); -} - -fn WriterWithErrors(comptime BackingWriter: type, comptime ExtraErrors: type) type { - return struct { - backing_writer: BackingWriter, - - pub const Error = BackingWriter.Error || ExtraErrors; - pub const Writer = std.io.Writer(*const Self, Error, write); - - const Self = @This(); - - pub fn writer(self: *const Self) Writer { - return .{ .context = self }; - } - - pub fn write(self: *const Self, bytes: []const u8) Error!usize { - return self.backing_writer.write(bytes); - } - }; -} -fn writerWithErrors( - backing_writer: anytype, - comptime ExtraErrors: type, -) WriterWithErrors(@TypeOf(backing_writer), ExtraErrors) { - return .{ .backing_writer = backing_writer }; -} - -pub fn printUnbuffered( - self: *Builder, - backing_writer: anytype, -) (@TypeOf(backing_writer).Error || Allocator.Error)!void { - const writer_with_errors = writerWithErrors(backing_writer, Allocator.Error); - const writer = writer_with_errors.writer(); - - var need_newline = false; - var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined }; - defer metadata_formatter.map.deinit(self.gpa); - - if (self.source_filename != .none or self.data_layout != .none or self.target_triple != .none) { - if (need_newline) try writer.writeByte('\n') else need_newline = true; - if (self.source_filename != .none) try writer.print( - \\; ModuleID = '{s}' - \\source_filename = {"} - \\ - , .{ self.source_filename.slice(self).?, self.source_filename.fmt(self) }); - if (self.data_layout != .none) try writer.print( - \\target datalayout = {"} - \\ - , .{self.data_layout.fmt(self)}); - if (self.target_triple != .none) try writer.print( - \\target triple = {"} - \\ - , .{self.target_triple.fmt(self)}); - } - - if (self.module_asm.items.len > 0) { - if (need_newline) try writer.writeByte('\n') else need_newline = true; - var line_it = std.mem.tokenizeScalar(u8, self.module_asm.items, '\n'); - while (line_it.next()) |line| { - try writer.writeAll("module asm "); - try printEscapedString(line, .always_quote, writer); - try writer.writeByte('\n'); - } - } - - if (self.types.count() > 0) { - if (need_newline) try writer.writeByte('\n') else need_newline = true; - for (self.types.keys(), self.types.values()) |id, ty| try writer.print( - \\%{} = type {} - \\ - , .{ id.fmt(self), ty.fmt(self) }); - } - - if (self.variables.items.len > 0) { - if (need_newline) try writer.writeByte('\n') else need_newline = true; - for (self.variables.items) |variable| { - if (variable.global.getReplacement(self) != .none) continue; - const global = variable.global.ptrConst(self); - metadata_formatter.need_comma = true; - defer metadata_formatter.need_comma = undefined; - try writer.print( - \\{} ={}{}{}{}{ }{}{ }{} {s} {%}{ }{, }{} - \\ - , .{ - variable.global.fmt(self), - Linkage.fmtOptional(if (global.linkage == .external and - variable.init != .no_init) null else global.linkage), - global.preemption, - global.visibility, - global.dll_storage_class, - variable.thread_local, - global.unnamed_addr, - global.addr_space, - global.externally_initialized, - @tagName(variable.mutability), - global.type.fmt(self), - variable.init.fmt(self), - variable.alignment, - try metadata_formatter.fmt("!dbg ", global.dbg), - }); - } - } - - if (self.aliases.items.len > 0) { - if (need_newline) try writer.writeByte('\n') else need_newline = true; - for (self.aliases.items) |alias| { - if (alias.global.getReplacement(self) != .none) continue; - const global = alias.global.ptrConst(self); - metadata_formatter.need_comma = true; - defer metadata_formatter.need_comma = undefined; - try writer.print( - \\{} ={}{}{}{}{ }{} alias {%}, {%}{} - \\ - , .{ - alias.global.fmt(self), - global.linkage, - global.preemption, - global.visibility, - global.dll_storage_class, - alias.thread_local, - global.unnamed_addr, - global.type.fmt(self), - alias.aliasee.fmt(self), - try metadata_formatter.fmt("!dbg ", global.dbg), - }); - } - } - - var attribute_groups: std.AutoArrayHashMapUnmanaged(Attributes, void) = .empty; - defer attribute_groups.deinit(self.gpa); - - for (0.., self.functions.items) |function_i, function| { - if (function.global.getReplacement(self) != .none) continue; - if (need_newline) try writer.writeByte('\n') else need_newline = true; - const function_index: Function.Index = @enumFromInt(function_i); - const global = function.global.ptrConst(self); - const params_len = global.type.functionParameters(self).len; - const function_attributes = function.attributes.func(self); - if (function_attributes != .none) try writer.print( - \\; Function Attrs:{} - \\ - , .{function_attributes.fmt(self)}); - try writer.print( - \\{s}{}{}{}{}{}{"} {%} {}( - , .{ - if (function.instructions.len > 0) "define" else "declare", - global.linkage, - global.preemption, - global.visibility, - global.dll_storage_class, - function.call_conv, - function.attributes.ret(self).fmt(self), - global.type.functionReturn(self).fmt(self), - function.global.fmt(self), - }); - for (0..params_len) |arg| { - if (arg > 0) try writer.writeAll(", "); - try writer.print( - \\{%}{"} - , .{ - global.type.functionParameters(self)[arg].fmt(self), - function.attributes.param(arg, self).fmt(self), - }); - if (function.instructions.len > 0) - try writer.print(" {}", .{function.arg(@intCast(arg)).fmt(function_index, self)}) - else - try writer.print(" %{d}", .{arg}); - } - switch (global.type.functionKind(self)) { - .normal => {}, - .vararg => { - if (params_len > 0) try writer.writeAll(", "); - try writer.writeAll("..."); - }, - } - try writer.print("){}{ }", .{ global.unnamed_addr, global.addr_space }); - if (function_attributes != .none) try writer.print(" #{d}", .{ - (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index, - }); - { - metadata_formatter.need_comma = false; - defer metadata_formatter.need_comma = undefined; - try writer.print("{ }{}", .{ - function.alignment, - try metadata_formatter.fmt(" !dbg ", global.dbg), - }); - } - if (function.instructions.len > 0) { - var block_incoming_len: u32 = undefined; - try writer.writeAll(" {\n"); - var maybe_dbg_index: ?u32 = null; - for (params_len..function.instructions.len) |instruction_i| { - const instruction_index: Function.Instruction.Index = @enumFromInt(instruction_i); - const instruction = function.instructions.get(@intFromEnum(instruction_index)); - if (function.debug_locations.get(instruction_index)) |debug_location| switch (debug_location) { - .no_location => maybe_dbg_index = null, - .location => |location| { - const gop = try metadata_formatter.map.getOrPut(self.gpa, .{ - .debug_location = location, - }); - maybe_dbg_index = @intCast(gop.index); - }, - }; - switch (instruction.tag) { - .add, - .@"add nsw", - .@"add nuw", - .@"add nuw nsw", - .@"and", - .ashr, - .@"ashr exact", - .fadd, - .@"fadd fast", - .@"fcmp false", - .@"fcmp fast false", - .@"fcmp fast oeq", - .@"fcmp fast oge", - .@"fcmp fast ogt", - .@"fcmp fast ole", - .@"fcmp fast olt", - .@"fcmp fast one", - .@"fcmp fast ord", - .@"fcmp fast true", - .@"fcmp fast ueq", - .@"fcmp fast uge", - .@"fcmp fast ugt", - .@"fcmp fast ule", - .@"fcmp fast ult", - .@"fcmp fast une", - .@"fcmp fast uno", - .@"fcmp oeq", - .@"fcmp oge", - .@"fcmp ogt", - .@"fcmp ole", - .@"fcmp olt", - .@"fcmp one", - .@"fcmp ord", - .@"fcmp true", - .@"fcmp ueq", - .@"fcmp uge", - .@"fcmp ugt", - .@"fcmp ule", - .@"fcmp ult", - .@"fcmp une", - .@"fcmp uno", - .fdiv, - .@"fdiv fast", - .fmul, - .@"fmul fast", - .frem, - .@"frem fast", - .fsub, - .@"fsub fast", - .@"icmp eq", - .@"icmp ne", - .@"icmp sge", - .@"icmp sgt", - .@"icmp sle", - .@"icmp slt", - .@"icmp uge", - .@"icmp ugt", - .@"icmp ule", - .@"icmp ult", - .lshr, - .@"lshr exact", - .mul, - .@"mul nsw", - .@"mul nuw", - .@"mul nuw nsw", - .@"or", - .sdiv, - .@"sdiv exact", - .srem, - .shl, - .@"shl nsw", - .@"shl nuw", - .@"shl nuw nsw", - .sub, - .@"sub nsw", - .@"sub nuw", - .@"sub nuw nsw", - .udiv, - .@"udiv exact", - .urem, - .xor, - => |tag| { - const extra = function.extraData(Function.Instruction.Binary, instruction.data); - try writer.print(" %{} = {s} {%}, {}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.lhs.fmt(function_index, self), - extra.rhs.fmt(function_index, self), - }); - }, - .addrspacecast, - .bitcast, - .fpext, - .fptosi, - .fptoui, - .fptrunc, - .inttoptr, - .ptrtoint, - .sext, - .sitofp, - .trunc, - .uitofp, - .zext, - => |tag| { - const extra = function.extraData(Function.Instruction.Cast, instruction.data); - try writer.print(" %{} = {s} {%} to {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.val.fmt(function_index, self), - extra.type.fmt(self), - }); - }, - .alloca, - .@"alloca inalloca", - => |tag| { - const extra = function.extraData(Function.Instruction.Alloca, instruction.data); - try writer.print(" %{} = {s} {%}{,%}{, }{, }", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.type.fmt(self), - Value.fmt(switch (extra.len) { - .@"1" => .none, - else => extra.len, - }, function_index, self), - extra.info.alignment, - extra.info.addr_space, - }); - }, - .arg => unreachable, - .atomicrmw => |tag| { - const extra = - function.extraData(Function.Instruction.AtomicRmw, instruction.data); - try writer.print(" %{} = {s}{ } {s} {%}, {%}{ }{ }{, }", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, - @tagName(extra.info.atomic_rmw_operation), - extra.ptr.fmt(function_index, self), - extra.val.fmt(function_index, self), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, - }); - }, - .block => { - block_incoming_len = instruction.data; - const name = instruction_index.name(&function); - if (@intFromEnum(instruction_index) > params_len) - try writer.writeByte('\n'); - try writer.print("{}:\n", .{name.fmt(self)}); - continue; - }, - .br => |tag| { - const target: Function.Block.Index = @enumFromInt(instruction.data); - try writer.print(" {s} {%}", .{ - @tagName(tag), target.toInst(&function).fmt(function_index, self), - }); - }, - .br_cond => { - const extra = function.extraData(Function.Instruction.BrCond, instruction.data); - try writer.print(" br {%}, {%}, {%}", .{ - extra.cond.fmt(function_index, self), - extra.then.toInst(&function).fmt(function_index, self), - extra.@"else".toInst(&function).fmt(function_index, self), - }); - metadata_formatter.need_comma = true; - defer metadata_formatter.need_comma = undefined; - switch (extra.weights) { - .none => {}, - .unpredictable => try writer.writeAll("!unpredictable !{}"), - _ => try writer.print("{}", .{ - try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights)))), - }), - } - }, - .call, - .@"call fast", - .@"musttail call", - .@"musttail call fast", - .@"notail call", - .@"notail call fast", - .@"tail call", - .@"tail call fast", - => |tag| { - var extra = - function.extraDataTrail(Function.Instruction.Call, instruction.data); - const args = extra.trail.next(extra.data.args_len, Value, &function); - try writer.writeAll(" "); - const ret_ty = extra.data.ty.functionReturn(self); - switch (ret_ty) { - .void => {}, - else => try writer.print("%{} = ", .{ - instruction_index.name(&function).fmt(self), - }), - .none => unreachable, - } - try writer.print("{s}{}{}{} {%} {}(", .{ - @tagName(tag), - extra.data.info.call_conv, - extra.data.attributes.ret(self).fmt(self), - extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self), - switch (extra.data.ty.functionKind(self)) { - .normal => ret_ty, - .vararg => extra.data.ty, - }.fmt(self), - extra.data.callee.fmt(function_index, self), - }); - for (0.., args) |arg_index, arg| { - if (arg_index > 0) try writer.writeAll(", "); - metadata_formatter.need_comma = false; - defer metadata_formatter.need_comma = undefined; - try writer.print("{%}{}{}", .{ - arg.typeOf(function_index, self).fmt(self), - extra.data.attributes.param(arg_index, self).fmt(self), - try metadata_formatter.fmtLocal(" ", arg, function_index), - }); - } - try writer.writeByte(')'); - if (extra.data.info.has_op_bundle_cold) { - try writer.writeAll(" [ \"cold\"() ]"); - } - const call_function_attributes = extra.data.attributes.func(self); - if (call_function_attributes != .none) try writer.print(" #{d}", .{ - (try attribute_groups.getOrPutValue( - self.gpa, - call_function_attributes, - {}, - )).index, - }); - }, - .cmpxchg, - .@"cmpxchg weak", - => |tag| { - const extra = - function.extraData(Function.Instruction.CmpXchg, instruction.data); - try writer.print(" %{} = {s}{ } {%}, {%}, {%}{ }{ }{ }{, }", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, - extra.ptr.fmt(function_index, self), - extra.cmp.fmt(function_index, self), - extra.new.fmt(function_index, self), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.failure_ordering, - extra.info.alignment, - }); - }, - .extractelement => |tag| { - const extra = - function.extraData(Function.Instruction.ExtractElement, instruction.data); - try writer.print(" %{} = {s} {%}, {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.val.fmt(function_index, self), - extra.index.fmt(function_index, self), - }); - }, - .extractvalue => |tag| { - var extra = function.extraDataTrail( - Function.Instruction.ExtractValue, - instruction.data, - ); - const indices = extra.trail.next(extra.data.indices_len, u32, &function); - try writer.print(" %{} = {s} {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.data.val.fmt(function_index, self), - }); - for (indices) |index| try writer.print(", {d}", .{index}); - }, - .fence => |tag| { - const info: MemoryAccessInfo = @bitCast(instruction.data); - try writer.print(" {s}{ }{ }", .{ - @tagName(tag), - info.sync_scope, - info.success_ordering, - }); - }, - .fneg, - .@"fneg fast", - => |tag| { - const val: Value = @enumFromInt(instruction.data); - try writer.print(" %{} = {s} {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - val.fmt(function_index, self), - }); - }, - .getelementptr, - .@"getelementptr inbounds", - => |tag| { - var extra = function.extraDataTrail( - Function.Instruction.GetElementPtr, - instruction.data, - ); - const indices = extra.trail.next(extra.data.indices_len, Value, &function); - try writer.print(" %{} = {s} {%}, {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.data.type.fmt(self), - extra.data.base.fmt(function_index, self), - }); - for (indices) |index| try writer.print(", {%}", .{ - index.fmt(function_index, self), - }); - }, - .indirectbr => |tag| { - var extra = - function.extraDataTrail(Function.Instruction.IndirectBr, instruction.data); - const targets = - extra.trail.next(extra.data.targets_len, Function.Block.Index, &function); - try writer.print(" {s} {%}, [", .{ - @tagName(tag), - extra.data.addr.fmt(function_index, self), - }); - for (0.., targets) |target_index, target| { - if (target_index > 0) try writer.writeAll(", "); - try writer.print("{%}", .{ - target.toInst(&function).fmt(function_index, self), - }); - } - try writer.writeByte(']'); - }, - .insertelement => |tag| { - const extra = - function.extraData(Function.Instruction.InsertElement, instruction.data); - try writer.print(" %{} = {s} {%}, {%}, {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.val.fmt(function_index, self), - extra.elem.fmt(function_index, self), - extra.index.fmt(function_index, self), - }); - }, - .insertvalue => |tag| { - var extra = - function.extraDataTrail(Function.Instruction.InsertValue, instruction.data); - const indices = extra.trail.next(extra.data.indices_len, u32, &function); - try writer.print(" %{} = {s} {%}, {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.data.val.fmt(function_index, self), - extra.data.elem.fmt(function_index, self), - }); - for (indices) |index| try writer.print(", {d}", .{index}); - }, - .load, - .@"load atomic", - => |tag| { - const extra = function.extraData(Function.Instruction.Load, instruction.data); - try writer.print(" %{} = {s}{ } {%}, {%}{ }{ }{, }", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, - extra.type.fmt(self), - extra.ptr.fmt(function_index, self), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, - }); - }, - .phi, - .@"phi fast", - => |tag| { - var extra = function.extraDataTrail(Function.Instruction.Phi, instruction.data); - const vals = extra.trail.next(block_incoming_len, Value, &function); - const blocks = - extra.trail.next(block_incoming_len, Function.Block.Index, &function); - try writer.print(" %{} = {s} {%} ", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - vals[0].typeOf(function_index, self).fmt(self), - }); - for (0.., vals, blocks) |incoming_index, incoming_val, incoming_block| { - if (incoming_index > 0) try writer.writeAll(", "); - try writer.print("[ {}, {} ]", .{ - incoming_val.fmt(function_index, self), - incoming_block.toInst(&function).fmt(function_index, self), - }); - } - }, - .ret => |tag| { - const val: Value = @enumFromInt(instruction.data); - try writer.print(" {s} {%}", .{ - @tagName(tag), - val.fmt(function_index, self), - }); - }, - .@"ret void", - .@"unreachable", - => |tag| try writer.print(" {s}", .{@tagName(tag)}), - .select, - .@"select fast", - => |tag| { - const extra = function.extraData(Function.Instruction.Select, instruction.data); - try writer.print(" %{} = {s} {%}, {%}, {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.cond.fmt(function_index, self), - extra.lhs.fmt(function_index, self), - extra.rhs.fmt(function_index, self), - }); - }, - .shufflevector => |tag| { - const extra = - function.extraData(Function.Instruction.ShuffleVector, instruction.data); - try writer.print(" %{} = {s} {%}, {%}, {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.lhs.fmt(function_index, self), - extra.rhs.fmt(function_index, self), - extra.mask.fmt(function_index, self), - }); - }, - .store, - .@"store atomic", - => |tag| { - const extra = function.extraData(Function.Instruction.Store, instruction.data); - try writer.print(" {s}{ } {%}, {%}{ }{ }{, }", .{ - @tagName(tag), - extra.info.access_kind, - extra.val.fmt(function_index, self), - extra.ptr.fmt(function_index, self), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, - }); - }, - .@"switch" => |tag| { - var extra = - function.extraDataTrail(Function.Instruction.Switch, instruction.data); - const vals = extra.trail.next(extra.data.cases_len, Constant, &function); - const blocks = - extra.trail.next(extra.data.cases_len, Function.Block.Index, &function); - try writer.print(" {s} {%}, {%} [\n", .{ - @tagName(tag), - extra.data.val.fmt(function_index, self), - extra.data.default.toInst(&function).fmt(function_index, self), - }); - for (vals, blocks) |case_val, case_block| try writer.print( - " {%}, {%}\n", - .{ - case_val.fmt(self), - case_block.toInst(&function).fmt(function_index, self), - }, - ); - try writer.writeAll(" ]"); - metadata_formatter.need_comma = true; - defer metadata_formatter.need_comma = undefined; - switch (extra.data.weights) { - .none => {}, - .unpredictable => try writer.writeAll("!unpredictable !{}"), - _ => try writer.print("{}", .{ - try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights)))), - }), - } - }, - .va_arg => |tag| { - const extra = function.extraData(Function.Instruction.VaArg, instruction.data); - try writer.print(" %{} = {s} {%}, {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.list.fmt(function_index, self), - extra.type.fmt(self), - }); - }, - } - - if (maybe_dbg_index) |dbg_index| { - try writer.print(", !dbg !{}", .{dbg_index}); - } - try writer.writeByte('\n'); - } - try writer.writeByte('}'); - } - try writer.writeByte('\n'); - } - - if (attribute_groups.count() > 0) { - if (need_newline) try writer.writeByte('\n') else need_newline = true; - for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group| - try writer.print( - \\attributes #{d} = {{{#"} }} - \\ - , .{ attribute_group_index, attribute_group.fmt(self) }); - } - - if (self.metadata_named.count() > 0) { - if (need_newline) try writer.writeByte('\n') else need_newline = true; - for (self.metadata_named.keys(), self.metadata_named.values()) |name, data| { - const elements: []const Metadata = - @ptrCast(self.metadata_extra.items[data.index..][0..data.len]); - try writer.writeByte('!'); - try printEscapedString(name.slice(self), .quote_unless_valid_identifier, writer); - try writer.writeAll(" = !{"); - metadata_formatter.need_comma = false; - defer metadata_formatter.need_comma = undefined; - for (elements) |element| try writer.print("{}", .{try metadata_formatter.fmt("", element)}); - try writer.writeAll("}\n"); - } - } - - if (metadata_formatter.map.count() > 0) { - if (need_newline) try writer.writeByte('\n') else need_newline = true; - var metadata_index: usize = 0; - while (metadata_index < metadata_formatter.map.count()) : (metadata_index += 1) { - @setEvalBranchQuota(10_000); - try writer.print("!{} = ", .{metadata_index}); - metadata_formatter.need_comma = false; - defer metadata_formatter.need_comma = undefined; - - const key = metadata_formatter.map.keys()[metadata_index]; - const metadata_item = switch (key) { - .debug_location => |location| { - try metadata_formatter.specialized(.@"!", .DILocation, .{ - .line = location.line, - .column = location.column, - .scope = location.scope, - .inlinedAt = location.inlined_at, - .isImplicitCode = false, - }, writer); - continue; - }, - .metadata => |metadata| self.metadata_items.get(@intFromEnum(metadata)), - }; - - switch (metadata_item.tag) { - .none, .expression, .constant => unreachable, - .file => { - const extra = self.metadataExtraData(Metadata.File, metadata_item.data); - try metadata_formatter.specialized(.@"!", .DIFile, .{ - .filename = extra.filename, - .directory = extra.directory, - .checksumkind = null, - .checksum = null, - .source = null, - }, writer); - }, - .compile_unit, - .@"compile_unit optimized", - => |kind| { - const extra = self.metadataExtraData(Metadata.CompileUnit, metadata_item.data); - try metadata_formatter.specialized(.@"distinct !", .DICompileUnit, .{ - .language = .DW_LANG_C99, - .file = extra.file, - .producer = extra.producer, - .isOptimized = switch (kind) { - .compile_unit => false, - .@"compile_unit optimized" => true, - else => unreachable, - }, - .flags = null, - .runtimeVersion = 0, - .splitDebugFilename = null, - .emissionKind = .FullDebug, - .enums = extra.enums, - .retainedTypes = null, - .globals = extra.globals, - .imports = null, - .macros = null, - .dwoId = null, - .splitDebugInlining = false, - .debugInfoForProfiling = null, - .nameTableKind = null, - .rangesBaseAddress = null, - .sysroot = null, - .sdk = null, - }, writer); - }, - .subprogram, - .@"subprogram local", - .@"subprogram definition", - .@"subprogram local definition", - .@"subprogram optimized", - .@"subprogram optimized local", - .@"subprogram optimized definition", - .@"subprogram optimized local definition", - => |kind| { - const extra = self.metadataExtraData(Metadata.Subprogram, metadata_item.data); - try metadata_formatter.specialized(.@"distinct !", .DISubprogram, .{ - .name = extra.name, - .linkageName = extra.linkage_name, - .scope = extra.file, - .file = extra.file, - .line = extra.line, - .type = extra.ty, - .scopeLine = extra.scope_line, - .containingType = null, - .virtualIndex = null, - .thisAdjustment = null, - .flags = extra.di_flags, - .spFlags = @as(Metadata.Subprogram.DISPFlags, @bitCast(@as(u32, @as(u3, @intCast( - @intFromEnum(kind) - @intFromEnum(Metadata.Tag.subprogram), - ))) << 2)), - .unit = extra.compile_unit, - .templateParams = null, - .declaration = null, - .retainedNodes = null, - .thrownTypes = null, - .annotations = null, - .targetFuncName = null, - }, writer); - }, - .lexical_block => { - const extra = self.metadataExtraData(Metadata.LexicalBlock, metadata_item.data); - try metadata_formatter.specialized(.@"distinct !", .DILexicalBlock, .{ - .scope = extra.scope, - .file = extra.file, - .line = extra.line, - .column = extra.column, - }, writer); - }, - .location => { - const extra = self.metadataExtraData(Metadata.Location, metadata_item.data); - try metadata_formatter.specialized(.@"!", .DILocation, .{ - .line = extra.line, - .column = extra.column, - .scope = extra.scope, - .inlinedAt = extra.inlined_at, - .isImplicitCode = false, - }, writer); - }, - .basic_bool_type, - .basic_unsigned_type, - .basic_signed_type, - .basic_float_type, - => |kind| { - const extra = self.metadataExtraData(Metadata.BasicType, metadata_item.data); - try metadata_formatter.specialized(.@"!", .DIBasicType, .{ - .tag = null, - .name = switch (extra.name) { - .none => null, - else => extra.name, - }, - .size = extra.bitSize(), - .@"align" = null, - .encoding = @as(enum { - DW_ATE_boolean, - DW_ATE_unsigned, - DW_ATE_signed, - DW_ATE_float, - }, switch (kind) { - .basic_bool_type => .DW_ATE_boolean, - .basic_unsigned_type => .DW_ATE_unsigned, - .basic_signed_type => .DW_ATE_signed, - .basic_float_type => .DW_ATE_float, - else => unreachable, - }), - .flags = null, - }, writer); - }, - .composite_struct_type, - .composite_union_type, - .composite_enumeration_type, - .composite_array_type, - .composite_vector_type, - => |kind| { - const extra = self.metadataExtraData(Metadata.CompositeType, metadata_item.data); - try metadata_formatter.specialized(.@"!", .DICompositeType, .{ - .tag = @as(enum { - DW_TAG_structure_type, - DW_TAG_union_type, - DW_TAG_enumeration_type, - DW_TAG_array_type, - }, switch (kind) { - .composite_struct_type => .DW_TAG_structure_type, - .composite_union_type => .DW_TAG_union_type, - .composite_enumeration_type => .DW_TAG_enumeration_type, - .composite_array_type, .composite_vector_type => .DW_TAG_array_type, - else => unreachable, - }), - .name = switch (extra.name) { - .none => null, - else => extra.name, - }, - .scope = extra.scope, - .file = null, - .line = null, - .baseType = extra.underlying_type, - .size = extra.bitSize(), - .@"align" = extra.bitAlign(), - .offset = null, - .flags = null, - .elements = extra.fields_tuple, - .runtimeLang = null, - .vtableHolder = null, - .templateParams = null, - .identifier = null, - .discriminator = null, - .dataLocation = null, - .associated = null, - .allocated = null, - .rank = null, - .annotations = null, - }, writer); - }, - .derived_pointer_type, - .derived_member_type, - => |kind| { - const extra = self.metadataExtraData(Metadata.DerivedType, metadata_item.data); - try metadata_formatter.specialized(.@"!", .DIDerivedType, .{ - .tag = @as(enum { - DW_TAG_pointer_type, - DW_TAG_member, - }, switch (kind) { - .derived_pointer_type => .DW_TAG_pointer_type, - .derived_member_type => .DW_TAG_member, - else => unreachable, - }), - .name = switch (extra.name) { - .none => null, - else => extra.name, - }, - .scope = extra.scope, - .file = null, - .line = null, - .baseType = extra.underlying_type, - .size = extra.bitSize(), - .@"align" = extra.bitAlign(), - .offset = switch (extra.bitOffset()) { - 0 => null, - else => |bit_offset| bit_offset, - }, - .flags = null, - .extraData = null, - .dwarfAddressSpace = null, - .annotations = null, - }, writer); - }, - .subroutine_type => { - const extra = self.metadataExtraData(Metadata.SubroutineType, metadata_item.data); - try metadata_formatter.specialized(.@"!", .DISubroutineType, .{ - .flags = null, - .cc = null, - .types = extra.types_tuple, - }, writer); - }, - .enumerator_unsigned, - .enumerator_signed_positive, - .enumerator_signed_negative, - => |kind| { - const extra = self.metadataExtraData(Metadata.Enumerator, metadata_item.data); - - const ExpectedContents = extern struct { - const expected_limbs = @divExact(512, @bitSizeOf(std.math.big.Limb)); - string: [ - (std.math.big.int.Const{ - .limbs = &([1]std.math.big.Limb{ - std.math.maxInt(std.math.big.Limb), - } ** expected_limbs), - .positive = false, - }).sizeInBaseUpperBound(10) - ]u8, - limbs: [ - std.math.big.int.calcToStringLimbsBufferLen(expected_limbs, 10) - ]std.math.big.Limb, - }; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); - const allocator = stack.get(); - - const limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len]; - const bigint: std.math.big.int.Const = .{ - .limbs = limbs, - .positive = switch (kind) { - .enumerator_unsigned, - .enumerator_signed_positive, - => true, - .enumerator_signed_negative => false, - else => unreachable, - }, - }; - const str = try bigint.toStringAlloc(allocator, 10, undefined); - defer allocator.free(str); - - try metadata_formatter.specialized(.@"!", .DIEnumerator, .{ - .name = extra.name, - .value = str, - .isUnsigned = switch (kind) { - .enumerator_unsigned => true, - .enumerator_signed_positive, - .enumerator_signed_negative, - => false, - else => unreachable, - }, - }, writer); - }, - .subrange => { - const extra = self.metadataExtraData(Metadata.Subrange, metadata_item.data); - try metadata_formatter.specialized(.@"!", .DISubrange, .{ - .count = extra.count, - .lowerBound = extra.lower_bound, - .upperBound = null, - .stride = null, - }, writer); - }, - .tuple => { - var extra = self.metadataExtraDataTrail(Metadata.Tuple, metadata_item.data); - const elements = extra.trail.next(extra.data.elements_len, Metadata, self); - try writer.writeAll("!{"); - for (elements) |element| try writer.print("{[element]%}", .{ - .element = try metadata_formatter.fmt("", element), - }); - try writer.writeAll("}\n"); - }, - .str_tuple => { - var extra = self.metadataExtraDataTrail(Metadata.StrTuple, metadata_item.data); - const elements = extra.trail.next(extra.data.elements_len, Metadata, self); - try writer.print("!{{{[str]%}", .{ - .str = try metadata_formatter.fmt("", extra.data.str), - }); - for (elements) |element| try writer.print("{[element]%}", .{ - .element = try metadata_formatter.fmt("", element), - }); - try writer.writeAll("}\n"); - }, - .module_flag => { - const extra = self.metadataExtraData(Metadata.ModuleFlag, metadata_item.data); - try writer.print("!{{{[behavior]%}{[name]%}{[constant]%}}}\n", .{ - .behavior = try metadata_formatter.fmt("", extra.behavior), - .name = try metadata_formatter.fmt("", extra.name), - .constant = try metadata_formatter.fmt("", extra.constant), - }); - }, - .local_var => { - const extra = self.metadataExtraData(Metadata.LocalVar, metadata_item.data); - try metadata_formatter.specialized(.@"!", .DILocalVariable, .{ - .name = extra.name, - .arg = null, - .scope = extra.scope, - .file = extra.file, - .line = extra.line, - .type = extra.ty, - .flags = null, - .@"align" = null, - .annotations = null, - }, writer); - }, - .parameter => { - const extra = self.metadataExtraData(Metadata.Parameter, metadata_item.data); - try metadata_formatter.specialized(.@"!", .DILocalVariable, .{ - .name = extra.name, - .arg = extra.arg_no, - .scope = extra.scope, - .file = extra.file, - .line = extra.line, - .type = extra.ty, - .flags = null, - .@"align" = null, - .annotations = null, - }, writer); - }, - .global_var, - .@"global_var local", - => |kind| { - const extra = self.metadataExtraData(Metadata.GlobalVar, metadata_item.data); - try metadata_formatter.specialized(.@"distinct !", .DIGlobalVariable, .{ - .name = extra.name, - .linkageName = extra.linkage_name, - .scope = extra.scope, - .file = extra.file, - .line = extra.line, - .type = extra.ty, - .isLocal = switch (kind) { - .global_var => false, - .@"global_var local" => true, - else => unreachable, - }, - .isDefinition = true, - .declaration = null, - .templateParams = null, - .@"align" = null, - .annotations = null, - }, writer); - }, - .global_var_expression => { - const extra = - self.metadataExtraData(Metadata.GlobalVarExpression, metadata_item.data); - try metadata_formatter.specialized(.@"!", .DIGlobalVariableExpression, .{ - .@"var" = extra.variable, - .expr = extra.expression, - }, writer); - }, - } - } - } -} - -const NoExtra = struct {}; - -fn isValidIdentifier(id: []const u8) bool { - for (id, 0..) |byte, index| switch (byte) { - '$', '-', '.', 'A'...'Z', '_', 'a'...'z' => {}, - '0'...'9' => if (index == 0) return false, - else => return false, - }; - return true; -} - -const QuoteBehavior = enum { always_quote, quote_unless_valid_identifier }; -fn printEscapedString( - slice: []const u8, - quotes: QuoteBehavior, - writer: anytype, -) @TypeOf(writer).Error!void { - const need_quotes = switch (quotes) { - .always_quote => true, - .quote_unless_valid_identifier => !isValidIdentifier(slice), - }; - if (need_quotes) try writer.writeByte('"'); - for (slice) |byte| switch (byte) { - '\\' => try writer.writeAll("\\\\"), - ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try writer.writeByte(byte), - else => try writer.print("\\{X:0>2}", .{byte}), - }; - if (need_quotes) try writer.writeByte('"'); -} - -fn ensureUnusedGlobalCapacity(self: *Builder, name: StrtabString) Allocator.Error!void { - try self.strtab_string_map.ensureUnusedCapacity(self.gpa, 1); - if (name.slice(self)) |id| { - const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)}); - try self.strtab_string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); - } - try self.strtab_string_indices.ensureUnusedCapacity(self.gpa, 1); - try self.globals.ensureUnusedCapacity(self.gpa, 1); - try self.next_unique_global_id.ensureUnusedCapacity(self.gpa, 1); -} - -fn fnTypeAssumeCapacity( - self: *Builder, - ret: Type, - params: []const Type, - comptime kind: Type.Function.Kind, -) Type { - const tag: Type.Tag = switch (kind) { - .normal => .function, - .vararg => .vararg_function, - }; - const Key = struct { ret: Type, params: []const Type }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - var hasher = std.hash.Wyhash.init(comptime std.hash.uint32(@intFromEnum(tag))); - hasher.update(std.mem.asBytes(&key.ret)); - hasher.update(std.mem.sliceAsBytes(key.params)); - return @truncate(hasher.final()); - } - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - const rhs_data = ctx.builder.type_items.items[rhs_index]; - if (rhs_data.tag != tag) return false; - var rhs_extra = ctx.builder.typeExtraDataTrail(Type.Function, rhs_data.data); - const rhs_params = rhs_extra.trail.next(rhs_extra.data.params_len, Type, ctx.builder); - return lhs_key.ret == rhs_extra.data.ret and std.mem.eql(Type, lhs_key.params, rhs_params); - } - }; - const gop = self.type_map.getOrPutAssumeCapacityAdapted( - Key{ .ret = ret, .params = params }, - Adapter{ .builder = self }, - ); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.type_items.appendAssumeCapacity(.{ - .tag = tag, - .data = self.addTypeExtraAssumeCapacity(Type.Function{ - .ret = ret, - .params_len = @intCast(params.len), - }), - }); - self.type_extra.appendSliceAssumeCapacity(@ptrCast(params)); - } - return @enumFromInt(gop.index); -} - -fn intTypeAssumeCapacity(self: *Builder, bits: u24) Type { - assert(bits > 0); - const result = self.getOrPutTypeNoExtraAssumeCapacity(.{ .tag = .integer, .data = bits }); - return result.type; -} - -fn ptrTypeAssumeCapacity(self: *Builder, addr_space: AddrSpace) Type { - const result = self.getOrPutTypeNoExtraAssumeCapacity( - .{ .tag = .pointer, .data = @intFromEnum(addr_space) }, - ); - return result.type; -} - -fn vectorTypeAssumeCapacity( - self: *Builder, - comptime kind: Type.Vector.Kind, - len: u32, - child: Type, -) Type { - assert(child.isFloatingPoint() or child.isInteger(self) or child.isPointer(self)); - const tag: Type.Tag = switch (kind) { - .normal => .vector, - .scalable => .scalable_vector, - }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Type.Vector) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(tag)), - std.mem.asBytes(&key), - )); - } - pub fn eql(ctx: @This(), lhs_key: Type.Vector, _: void, rhs_index: usize) bool { - const rhs_data = ctx.builder.type_items.items[rhs_index]; - return rhs_data.tag == tag and - std.meta.eql(lhs_key, ctx.builder.typeExtraData(Type.Vector, rhs_data.data)); - } - }; - const data = Type.Vector{ .len = len, .child = child }; - const gop = self.type_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.type_items.appendAssumeCapacity(.{ - .tag = tag, - .data = self.addTypeExtraAssumeCapacity(data), - }); - } - return @enumFromInt(gop.index); -} - -fn arrayTypeAssumeCapacity(self: *Builder, len: u64, child: Type) Type { - if (std.math.cast(u32, len)) |small_len| { - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Type.Vector) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(Type.Tag.small_array)), - std.mem.asBytes(&key), - )); - } - pub fn eql(ctx: @This(), lhs_key: Type.Vector, _: void, rhs_index: usize) bool { - const rhs_data = ctx.builder.type_items.items[rhs_index]; - return rhs_data.tag == .small_array and - std.meta.eql(lhs_key, ctx.builder.typeExtraData(Type.Vector, rhs_data.data)); - } - }; - const data = Type.Vector{ .len = small_len, .child = child }; - const gop = self.type_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.type_items.appendAssumeCapacity(.{ - .tag = .small_array, - .data = self.addTypeExtraAssumeCapacity(data), - }); - } - return @enumFromInt(gop.index); - } else { - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Type.Array) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(Type.Tag.array)), - std.mem.asBytes(&key), - )); - } - pub fn eql(ctx: @This(), lhs_key: Type.Array, _: void, rhs_index: usize) bool { - const rhs_data = ctx.builder.type_items.items[rhs_index]; - return rhs_data.tag == .array and - std.meta.eql(lhs_key, ctx.builder.typeExtraData(Type.Array, rhs_data.data)); - } - }; - const data = Type.Array{ - .len_lo = @truncate(len), - .len_hi = @intCast(len >> 32), - .child = child, - }; - const gop = self.type_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.type_items.appendAssumeCapacity(.{ - .tag = .array, - .data = self.addTypeExtraAssumeCapacity(data), - }); - } - return @enumFromInt(gop.index); - } -} - -fn structTypeAssumeCapacity( - self: *Builder, - comptime kind: Type.Structure.Kind, - fields: []const Type, -) Type { - const tag: Type.Tag = switch (kind) { - .normal => .structure, - .@"packed" => .packed_structure, - }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: []const Type) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(tag)), - std.mem.sliceAsBytes(key), - )); - } - pub fn eql(ctx: @This(), lhs_key: []const Type, _: void, rhs_index: usize) bool { - const rhs_data = ctx.builder.type_items.items[rhs_index]; - if (rhs_data.tag != tag) return false; - var rhs_extra = ctx.builder.typeExtraDataTrail(Type.Structure, rhs_data.data); - const rhs_fields = rhs_extra.trail.next(rhs_extra.data.fields_len, Type, ctx.builder); - return std.mem.eql(Type, lhs_key, rhs_fields); - } - }; - const gop = self.type_map.getOrPutAssumeCapacityAdapted(fields, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.type_items.appendAssumeCapacity(.{ - .tag = tag, - .data = self.addTypeExtraAssumeCapacity(Type.Structure{ - .fields_len = @intCast(fields.len), - }), - }); - self.type_extra.appendSliceAssumeCapacity(@ptrCast(fields)); - } - return @enumFromInt(gop.index); -} - -fn opaqueTypeAssumeCapacity(self: *Builder, name: String) Type { - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: String) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(Type.Tag.named_structure)), - std.mem.asBytes(&key), - )); - } - pub fn eql(ctx: @This(), lhs_key: String, _: void, rhs_index: usize) bool { - const rhs_data = ctx.builder.type_items.items[rhs_index]; - return rhs_data.tag == .named_structure and - lhs_key == ctx.builder.typeExtraData(Type.NamedStructure, rhs_data.data).id; - } - }; - var id = name; - if (name == .empty) { - id = self.next_unnamed_type; - assert(id != .none); - self.next_unnamed_type = @enumFromInt(@intFromEnum(id) + 1); - } else assert(!name.isAnon()); - 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_items.appendAssumeCapacity(.{ - .tag = .named_structure, - .data = self.addTypeExtraAssumeCapacity(Type.NamedStructure{ - .id = id, - .body = .none, - }), - }); - const result: Type = @enumFromInt(gop.index); - type_gop.value_ptr.* = result; - 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.slice(self).?, unique_gop.value_ptr.* }); - unique_gop.value_ptr.* += 1; - } -} - -fn ensureUnusedTypeCapacity( - self: *Builder, - count: usize, - comptime Extra: type, - trail_len: usize, -) Allocator.Error!void { - try self.type_map.ensureUnusedCapacity(self.gpa, count); - try self.type_items.ensureUnusedCapacity(self.gpa, count); - try self.type_extra.ensureUnusedCapacity( - self.gpa, - count * (@typeInfo(Extra).@"struct".fields.len + trail_len), - ); -} - -fn getOrPutTypeNoExtraAssumeCapacity(self: *Builder, item: Type.Item) struct { new: bool, type: Type } { - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Type.Item) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(Type.Tag.simple)), - std.mem.asBytes(&key), - )); - } - pub fn eql(ctx: @This(), lhs_key: Type.Item, _: void, rhs_index: usize) bool { - const lhs_bits: u32 = @bitCast(lhs_key); - const rhs_bits: u32 = @bitCast(ctx.builder.type_items.items[rhs_index]); - return lhs_bits == rhs_bits; - } - }; - const gop = self.type_map.getOrPutAssumeCapacityAdapted(item, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.type_items.appendAssumeCapacity(item); - } - return .{ .new = !gop.found_existing, .type = @enumFromInt(gop.index) }; -} - -fn addTypeExtraAssumeCapacity(self: *Builder, extra: anytype) Type.Item.ExtraIndex { - const result: Type.Item.ExtraIndex = @intCast(self.type_extra.items.len); - inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| { - const value = @field(extra, field.name); - self.type_extra.appendAssumeCapacity(switch (field.type) { - u32 => value, - String, Type => @intFromEnum(value), - else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), - }); - } - return result; -} - -const TypeExtraDataTrail = struct { - index: Type.Item.ExtraIndex, - - fn nextMut(self: *TypeExtraDataTrail, len: u32, comptime Item: type, builder: *Builder) []Item { - const items: []Item = @ptrCast(builder.type_extra.items[self.index..][0..len]); - self.index += @intCast(len); - return items; - } - - fn next( - self: *TypeExtraDataTrail, - len: u32, - comptime Item: type, - builder: *const Builder, - ) []const Item { - const items: []const Item = @ptrCast(builder.type_extra.items[self.index..][0..len]); - self.index += @intCast(len); - return items; - } -}; - -fn typeExtraDataTrail( - self: *const Builder, - comptime T: type, - index: Type.Item.ExtraIndex, -) struct { data: T, trail: TypeExtraDataTrail } { - var result: T = undefined; - const fields = @typeInfo(T).@"struct".fields; - inline for (fields, self.type_extra.items[index..][0..fields.len]) |field, value| - @field(result, field.name) = switch (field.type) { - u32 => value, - String, Type => @enumFromInt(value), - else => @compileError("bad field type: " ++ @typeName(field.type)), - }; - return .{ - .data = result, - .trail = .{ .index = index + @as(Type.Item.ExtraIndex, @intCast(fields.len)) }, - }; -} - -fn typeExtraData(self: *const Builder, comptime T: type, index: Type.Item.ExtraIndex) T { - return self.typeExtraDataTrail(T, index).data; -} - -fn attrGeneric(self: *Builder, data: []const u32) Allocator.Error!u32 { - try self.attributes_map.ensureUnusedCapacity(self.gpa, 1); - try self.attributes_indices.ensureUnusedCapacity(self.gpa, 1); - try self.attributes_extra.ensureUnusedCapacity(self.gpa, data.len); - - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: []const u32) u32 { - return @truncate(std.hash.Wyhash.hash(1, std.mem.sliceAsBytes(key))); - } - pub fn eql(ctx: @This(), lhs_key: []const u32, _: void, rhs_index: usize) bool { - const start = ctx.builder.attributes_indices.items[rhs_index]; - const end = ctx.builder.attributes_indices.items[rhs_index + 1]; - return std.mem.eql(u32, lhs_key, ctx.builder.attributes_extra.items[start..end]); - } - }; - const gop = self.attributes_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); - if (!gop.found_existing) { - self.attributes_extra.appendSliceAssumeCapacity(data); - self.attributes_indices.appendAssumeCapacity(@intCast(self.attributes_extra.items.len)); - } - return @intCast(gop.index); -} - -fn bigIntConstAssumeCapacity( - self: *Builder, - ty: Type, - value: std.math.big.int.Const, -) Allocator.Error!Constant { - const type_item = self.type_items.items[@intFromEnum(ty)]; - assert(type_item.tag == .integer); - const bits = type_item.data; - - const ExpectedContents = [64 / @sizeOf(std.math.big.Limb)]std.math.big.Limb; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); - const allocator = stack.get(); - - var limbs: []std.math.big.Limb = &.{}; - defer allocator.free(limbs); - const canonical_value = if (value.fitsInTwosComp(.signed, bits)) value else canon: { - assert(value.fitsInTwosComp(.unsigned, bits)); - limbs = try allocator.alloc(std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(bits)); - var temp_value = std.math.big.int.Mutable.init(limbs, 0); - temp_value.truncate(value, .signed, bits); - break :canon temp_value.toConst(); - }; - assert(canonical_value.fitsInTwosComp(.signed, bits)); - - const ExtraPtr = *align(@alignOf(std.math.big.Limb)) Constant.Integer; - const Key = struct { tag: Constant.Tag, type: Type, limbs: []const std.math.big.Limb }; - const tag: Constant.Tag = switch (canonical_value.positive) { - true => .positive_integer, - false => .negative_integer, - }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - var hasher = std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(key.tag))); - hasher.update(std.mem.asBytes(&key.type)); - hasher.update(std.mem.sliceAsBytes(key.limbs)); - return @truncate(hasher.final()); - } - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - const rhs_extra: ExtraPtr = - @ptrCast(ctx.builder.constant_limbs.items[rhs_data..][0..Constant.Integer.limbs]); - const rhs_limbs = ctx.builder.constant_limbs - .items[rhs_data + Constant.Integer.limbs ..][0..rhs_extra.limbs_len]; - return lhs_key.type == rhs_extra.type and - std.mem.eql(std.math.big.Limb, lhs_key.limbs, rhs_limbs); - } - }; - - const gop = self.constant_map.getOrPutAssumeCapacityAdapted( - Key{ .tag = tag, .type = ty, .limbs = canonical_value.limbs }, - Adapter{ .builder = self }, - ); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = tag, - .data = @intCast(self.constant_limbs.items.len), - }); - const extra: ExtraPtr = - @ptrCast(self.constant_limbs.addManyAsArrayAssumeCapacity(Constant.Integer.limbs)); - extra.* = .{ .type = ty, .limbs_len = @intCast(canonical_value.limbs.len) }; - self.constant_limbs.appendSliceAssumeCapacity(canonical_value.limbs); - } - return @enumFromInt(gop.index); -} - -fn halfConstAssumeCapacity(self: *Builder, val: f16) Constant { - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .half, .data = @as(u16, @bitCast(val)) }, - ); - return result.constant; -} - -fn bfloatConstAssumeCapacity(self: *Builder, val: f32) Constant { - assert(@as(u16, @truncate(@as(u32, @bitCast(val)))) == 0); - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .bfloat, .data = @bitCast(val) }, - ); - return result.constant; -} - -fn floatConstAssumeCapacity(self: *Builder, val: f32) Constant { - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .float, .data = @bitCast(val) }, - ); - return result.constant; -} - -fn doubleConstAssumeCapacity(self: *Builder, val: f64) Constant { - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: f64) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(Constant.Tag.double)), - std.mem.asBytes(&key), - )); - } - pub fn eql(ctx: @This(), lhs_key: f64, _: void, rhs_index: usize) bool { - if (ctx.builder.constant_items.items(.tag)[rhs_index] != .double) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - const rhs_extra = ctx.builder.constantExtraData(Constant.Double, rhs_data); - return @as(u64, @bitCast(lhs_key)) == @as(u64, rhs_extra.hi) << 32 | rhs_extra.lo; - } - }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted(val, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = .double, - .data = self.addConstantExtraAssumeCapacity(Constant.Double{ - .lo = @truncate(@as(u64, @bitCast(val))), - .hi = @intCast(@as(u64, @bitCast(val)) >> 32), - }), - }); - } - return @enumFromInt(gop.index); -} - -fn fp128ConstAssumeCapacity(self: *Builder, val: f128) Constant { - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: f128) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(Constant.Tag.fp128)), - std.mem.asBytes(&key), - )); - } - pub fn eql(ctx: @This(), lhs_key: f128, _: void, rhs_index: usize) bool { - if (ctx.builder.constant_items.items(.tag)[rhs_index] != .fp128) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - const rhs_extra = ctx.builder.constantExtraData(Constant.Fp128, rhs_data); - return @as(u128, @bitCast(lhs_key)) == @as(u128, rhs_extra.hi_hi) << 96 | - @as(u128, rhs_extra.hi_lo) << 64 | @as(u128, rhs_extra.lo_hi) << 32 | rhs_extra.lo_lo; - } - }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted(val, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = .fp128, - .data = self.addConstantExtraAssumeCapacity(Constant.Fp128{ - .lo_lo = @truncate(@as(u128, @bitCast(val))), - .lo_hi = @truncate(@as(u128, @bitCast(val)) >> 32), - .hi_lo = @truncate(@as(u128, @bitCast(val)) >> 64), - .hi_hi = @intCast(@as(u128, @bitCast(val)) >> 96), - }), - }); - } - return @enumFromInt(gop.index); -} - -fn x86_fp80ConstAssumeCapacity(self: *Builder, val: f80) Constant { - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: f80) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(Constant.Tag.x86_fp80)), - std.mem.asBytes(&key)[0..10], - )); - } - pub fn eql(ctx: @This(), lhs_key: f80, _: void, rhs_index: usize) bool { - if (ctx.builder.constant_items.items(.tag)[rhs_index] != .x86_fp80) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - const rhs_extra = ctx.builder.constantExtraData(Constant.Fp80, rhs_data); - return @as(u80, @bitCast(lhs_key)) == @as(u80, rhs_extra.hi) << 64 | - @as(u80, rhs_extra.lo_hi) << 32 | rhs_extra.lo_lo; - } - }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted(val, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = .x86_fp80, - .data = self.addConstantExtraAssumeCapacity(Constant.Fp80{ - .lo_lo = @truncate(@as(u80, @bitCast(val))), - .lo_hi = @truncate(@as(u80, @bitCast(val)) >> 32), - .hi = @intCast(@as(u80, @bitCast(val)) >> 64), - }), - }); - } - return @enumFromInt(gop.index); -} - -fn ppc_fp128ConstAssumeCapacity(self: *Builder, val: [2]f64) Constant { - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: [2]f64) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(Constant.Tag.ppc_fp128)), - std.mem.asBytes(&key), - )); - } - pub fn eql(ctx: @This(), lhs_key: [2]f64, _: void, rhs_index: usize) bool { - if (ctx.builder.constant_items.items(.tag)[rhs_index] != .ppc_fp128) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - const rhs_extra = ctx.builder.constantExtraData(Constant.Fp128, rhs_data); - return @as(u64, @bitCast(lhs_key[0])) == @as(u64, rhs_extra.lo_hi) << 32 | rhs_extra.lo_lo and - @as(u64, @bitCast(lhs_key[1])) == @as(u64, rhs_extra.hi_hi) << 32 | rhs_extra.hi_lo; - } - }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted(val, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = .ppc_fp128, - .data = self.addConstantExtraAssumeCapacity(Constant.Fp128{ - .lo_lo = @truncate(@as(u64, @bitCast(val[0]))), - .lo_hi = @intCast(@as(u64, @bitCast(val[0])) >> 32), - .hi_lo = @truncate(@as(u64, @bitCast(val[1]))), - .hi_hi = @intCast(@as(u64, @bitCast(val[1])) >> 32), - }), - }); - } - return @enumFromInt(gop.index); -} - -fn nullConstAssumeCapacity(self: *Builder, ty: Type) Constant { - assert(self.type_items.items[@intFromEnum(ty)].tag == .pointer); - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .null, .data = @intFromEnum(ty) }, - ); - return result.constant; -} - -fn noneConstAssumeCapacity(self: *Builder, ty: Type) Constant { - assert(ty == .token); - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .none, .data = @intFromEnum(ty) }, - ); - return result.constant; -} - -fn structConstAssumeCapacity(self: *Builder, ty: Type, vals: []const Constant) Constant { - const type_item = self.type_items.items[@intFromEnum(ty)]; - var extra = self.typeExtraDataTrail(Type.Structure, switch (type_item.tag) { - .structure, .packed_structure => type_item.data, - .named_structure => data: { - const body_ty = self.typeExtraData(Type.NamedStructure, type_item.data).body; - const body_item = self.type_items.items[@intFromEnum(body_ty)]; - switch (body_item.tag) { - .structure, .packed_structure => break :data body_item.data, - else => unreachable, - } - }, - else => unreachable, - }); - const fields = extra.trail.next(extra.data.fields_len, Type, self); - for (fields, vals) |field, val| assert(field == val.typeOf(self)); - - for (vals) |val| { - if (!val.isZeroInit(self)) break; - } else return self.zeroInitConstAssumeCapacity(ty); - - const tag: Constant.Tag = switch (ty.unnamedTag(self)) { - .structure => .structure, - .packed_structure => .packed_structure, - else => unreachable, - }; - const result = self.getOrPutConstantAggregateAssumeCapacity(tag, ty, vals); - return result.constant; -} - -fn arrayConstAssumeCapacity(self: *Builder, ty: Type, vals: []const Constant) Constant { - const type_item = self.type_items.items[@intFromEnum(ty)]; - const type_extra: struct { len: u64, child: Type } = switch (type_item.tag) { - inline .small_array, .array => |kind| extra: { - const extra = self.typeExtraData(switch (kind) { - .small_array => Type.Vector, - .array => Type.Array, - else => unreachable, - }, type_item.data); - break :extra .{ .len = extra.length(), .child = extra.child }; - }, - else => unreachable, - }; - assert(type_extra.len == vals.len); - for (vals) |val| assert(type_extra.child == val.typeOf(self)); - - for (vals) |val| { - if (!val.isZeroInit(self)) break; - } else return self.zeroInitConstAssumeCapacity(ty); - - const result = self.getOrPutConstantAggregateAssumeCapacity(.array, ty, vals); - return result.constant; -} - -fn stringConstAssumeCapacity(self: *Builder, val: String) Constant { - const slice = val.slice(self).?; - const ty = self.arrayTypeAssumeCapacity(slice.len, .i8); - if (std.mem.allEqual(u8, slice, 0)) return self.zeroInitConstAssumeCapacity(ty); - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .string, .data = @intFromEnum(val) }, - ); - return result.constant; -} - -fn vectorConstAssumeCapacity(self: *Builder, ty: Type, vals: []const Constant) Constant { - assert(ty.isVector(self)); - assert(ty.vectorLen(self) == vals.len); - for (vals) |val| assert(ty.childType(self) == val.typeOf(self)); - - for (vals[1..]) |val| { - if (vals[0] != val) break; - } else return self.splatConstAssumeCapacity(ty, vals[0]); - for (vals) |val| { - if (!val.isZeroInit(self)) break; - } else return self.zeroInitConstAssumeCapacity(ty); - - const result = self.getOrPutConstantAggregateAssumeCapacity(.vector, ty, vals); - return result.constant; -} - -fn splatConstAssumeCapacity(self: *Builder, ty: Type, val: Constant) Constant { - assert(ty.scalarType(self) == val.typeOf(self)); - - if (!ty.isVector(self)) return val; - if (val.isZeroInit(self)) return self.zeroInitConstAssumeCapacity(ty); - - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Constant.Splat) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(Constant.Tag.splat)), - std.mem.asBytes(&key), - )); - } - pub fn eql(ctx: @This(), lhs_key: Constant.Splat, _: void, rhs_index: usize) bool { - if (ctx.builder.constant_items.items(.tag)[rhs_index] != .splat) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - const rhs_extra = ctx.builder.constantExtraData(Constant.Splat, rhs_data); - return std.meta.eql(lhs_key, rhs_extra); - } - }; - const data = Constant.Splat{ .type = ty, .value = val }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = .splat, - .data = self.addConstantExtraAssumeCapacity(data), - }); - } - return @enumFromInt(gop.index); -} - -fn zeroInitConstAssumeCapacity(self: *Builder, ty: Type) Constant { - switch (ty) { - inline .half, - .bfloat, - .float, - .double, - .fp128, - .x86_fp80, - => |tag| return @field(Builder, @tagName(tag) ++ "ConstAssumeCapacity")(self, 0.0), - .ppc_fp128 => return self.ppc_fp128ConstAssumeCapacity(.{ 0.0, 0.0 }), - .token => return .none, - .i1 => return .false, - else => switch (self.type_items.items[@intFromEnum(ty)].tag) { - .simple, - .function, - .vararg_function, - => unreachable, - .integer => { - var limbs: [std.math.big.int.calcLimbLen(0)]std.math.big.Limb = undefined; - const bigint = std.math.big.int.Mutable.init(&limbs, 0); - return self.bigIntConstAssumeCapacity(ty, bigint.toConst()) catch unreachable; - }, - .pointer => return self.nullConstAssumeCapacity(ty), - .target, - .vector, - .scalable_vector, - .small_array, - .array, - .structure, - .packed_structure, - .named_structure, - => {}, - }, - } - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .zeroinitializer, .data = @intFromEnum(ty) }, - ); - return result.constant; -} - -fn undefConstAssumeCapacity(self: *Builder, ty: Type) Constant { - switch (self.type_items.items[@intFromEnum(ty)].tag) { - .simple => switch (ty) { - .void, .label => unreachable, - else => {}, - }, - .function, .vararg_function => unreachable, - else => {}, - } - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .undef, .data = @intFromEnum(ty) }, - ); - return result.constant; -} - -fn poisonConstAssumeCapacity(self: *Builder, ty: Type) Constant { - switch (self.type_items.items[@intFromEnum(ty)].tag) { - .simple => switch (ty) { - .void, .label => unreachable, - else => {}, - }, - .function, .vararg_function => unreachable, - else => {}, - } - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .poison, .data = @intFromEnum(ty) }, - ); - return result.constant; -} - -fn blockAddrConstAssumeCapacity( - self: *Builder, - function: Function.Index, - block: Function.Block.Index, -) Constant { - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Constant.BlockAddress) u32 { - return @truncate(std.hash.Wyhash.hash( - comptime std.hash.uint32(@intFromEnum(Constant.Tag.blockaddress)), - std.mem.asBytes(&key), - )); - } - pub fn eql(ctx: @This(), lhs_key: Constant.BlockAddress, _: void, rhs_index: usize) bool { - if (ctx.builder.constant_items.items(.tag)[rhs_index] != .blockaddress) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - const rhs_extra = ctx.builder.constantExtraData(Constant.BlockAddress, rhs_data); - return std.meta.eql(lhs_key, rhs_extra); - } - }; - const data = Constant.BlockAddress{ .function = function, .block = block }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = .blockaddress, - .data = self.addConstantExtraAssumeCapacity(data), - }); - } - return @enumFromInt(gop.index); -} - -fn dsoLocalEquivalentConstAssumeCapacity(self: *Builder, function: Function.Index) Constant { - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .dso_local_equivalent, .data = @intFromEnum(function) }, - ); - return result.constant; -} - -fn noCfiConstAssumeCapacity(self: *Builder, function: Function.Index) Constant { - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .no_cfi, .data = @intFromEnum(function) }, - ); - return result.constant; -} - -fn convTag( - self: *Builder, - signedness: Constant.Cast.Signedness, - val_ty: Type, - ty: Type, -) Function.Instruction.Tag { - assert(val_ty != ty); - return switch (val_ty.scalarTag(self)) { - .simple => switch (ty.scalarTag(self)) { - .simple => switch (std.math.order(val_ty.scalarBits(self), ty.scalarBits(self))) { - .lt => .fpext, - .eq => unreachable, - .gt => .fptrunc, - }, - .integer => switch (signedness) { - .unsigned => .fptoui, - .signed => .fptosi, - .unneeded => unreachable, - }, - else => unreachable, - }, - .integer => switch (ty.scalarTag(self)) { - .simple => switch (signedness) { - .unsigned => .uitofp, - .signed => .sitofp, - .unneeded => unreachable, - }, - .integer => switch (std.math.order(val_ty.scalarBits(self), ty.scalarBits(self))) { - .lt => switch (signedness) { - .unsigned => .zext, - .signed => .sext, - .unneeded => unreachable, - }, - .eq => unreachable, - .gt => .trunc, - }, - .pointer => .inttoptr, - else => unreachable, - }, - .pointer => switch (ty.scalarTag(self)) { - .integer => .ptrtoint, - .pointer => .addrspacecast, - else => unreachable, - }, - else => unreachable, - }; -} - -fn convConstTag( - self: *Builder, - val_ty: Type, - ty: Type, -) Constant.Tag { - assert(val_ty != ty); - return switch (val_ty.scalarTag(self)) { - .integer => switch (ty.scalarTag(self)) { - .integer => switch (std.math.order(val_ty.scalarBits(self), ty.scalarBits(self))) { - .gt => .trunc, - else => unreachable, - }, - .pointer => .inttoptr, - else => unreachable, - }, - .pointer => switch (ty.scalarTag(self)) { - .integer => .ptrtoint, - .pointer => .addrspacecast, - else => unreachable, - }, - else => unreachable, - }; -} - -fn convConstAssumeCapacity( - self: *Builder, - val: Constant, - ty: Type, -) Constant { - const val_ty = val.typeOf(self); - if (val_ty == ty) return val; - return self.castConstAssumeCapacity(self.convConstTag(val_ty, ty), val, ty); -} - -fn castConstAssumeCapacity(self: *Builder, tag: Constant.Tag, val: Constant, ty: Type) Constant { - const Key = struct { tag: Constant.Tag, cast: Constant.Cast }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - return @truncate(std.hash.Wyhash.hash( - std.hash.uint32(@intFromEnum(key.tag)), - std.mem.asBytes(&key.cast), - )); - } - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - const rhs_extra = ctx.builder.constantExtraData(Constant.Cast, rhs_data); - return std.meta.eql(lhs_key.cast, rhs_extra); - } - }; - const data = Key{ .tag = tag, .cast = .{ .val = val, .type = ty } }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = tag, - .data = self.addConstantExtraAssumeCapacity(data.cast), - }); - } - return @enumFromInt(gop.index); -} - -fn gepConstAssumeCapacity( - self: *Builder, - comptime kind: Constant.GetElementPtr.Kind, - ty: Type, - base: Constant, - inrange: ?u16, - indices: []const Constant, -) Constant { - const tag: Constant.Tag = switch (kind) { - .normal => .getelementptr, - .inbounds => .@"getelementptr inbounds", - }; - const base_ty = base.typeOf(self); - const base_is_vector = base_ty.isVector(self); - - const VectorInfo = struct { - kind: Type.Vector.Kind, - len: u32, - - fn init(vector_ty: Type, builder: *const Builder) @This() { - return .{ .kind = vector_ty.vectorKind(builder), .len = vector_ty.vectorLen(builder) }; - } - }; - var vector_info: ?VectorInfo = if (base_is_vector) VectorInfo.init(base_ty, self) else null; - for (indices) |index| { - const index_ty = index.typeOf(self); - switch (index_ty.tag(self)) { - .integer => {}, - .vector, .scalable_vector => { - const index_info = VectorInfo.init(index_ty, self); - if (vector_info) |info| - assert(std.meta.eql(info, index_info)) - else - vector_info = index_info; - }, - else => unreachable, - } - } - if (!base_is_vector) if (vector_info) |info| switch (info.kind) { - inline else => |vector_kind| _ = self.vectorTypeAssumeCapacity(vector_kind, info.len, base_ty), - }; - - const Key = struct { - type: Type, - base: Constant, - inrange: Constant.GetElementPtr.InRangeIndex, - indices: []const Constant, - }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - var hasher = std.hash.Wyhash.init(comptime std.hash.uint32(@intFromEnum(tag))); - hasher.update(std.mem.asBytes(&key.type)); - hasher.update(std.mem.asBytes(&key.base)); - hasher.update(std.mem.asBytes(&key.inrange)); - hasher.update(std.mem.sliceAsBytes(key.indices)); - return @truncate(hasher.final()); - } - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - if (ctx.builder.constant_items.items(.tag)[rhs_index] != tag) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - var rhs_extra = ctx.builder.constantExtraDataTrail(Constant.GetElementPtr, rhs_data); - const rhs_indices = - rhs_extra.trail.next(rhs_extra.data.info.indices_len, Constant, ctx.builder); - return lhs_key.type == rhs_extra.data.type and lhs_key.base == rhs_extra.data.base and - lhs_key.inrange == rhs_extra.data.info.inrange and - std.mem.eql(Constant, lhs_key.indices, rhs_indices); - } - }; - const data = Key{ - .type = ty, - .base = base, - .inrange = if (inrange) |index| @enumFromInt(index) else .none, - .indices = indices, - }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = tag, - .data = self.addConstantExtraAssumeCapacity(Constant.GetElementPtr{ - .type = ty, - .base = base, - .info = .{ .indices_len = @intCast(indices.len), .inrange = data.inrange }, - }), - }); - self.constant_extra.appendSliceAssumeCapacity(@ptrCast(indices)); - } - return @enumFromInt(gop.index); -} - -fn binConstAssumeCapacity( - self: *Builder, - tag: Constant.Tag, - lhs: Constant, - rhs: Constant, -) Constant { - switch (tag) { - .add, - .@"add nsw", - .@"add nuw", - .sub, - .@"sub nsw", - .@"sub nuw", - .shl, - .xor, - => {}, - else => unreachable, - } - const Key = struct { tag: Constant.Tag, extra: Constant.Binary }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - return @truncate(std.hash.Wyhash.hash( - std.hash.uint32(@intFromEnum(key.tag)), - std.mem.asBytes(&key.extra), - )); - } - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - const rhs_extra = ctx.builder.constantExtraData(Constant.Binary, rhs_data); - return std.meta.eql(lhs_key.extra, rhs_extra); - } - }; - const data = Key{ .tag = tag, .extra = .{ .lhs = lhs, .rhs = rhs } }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = tag, - .data = self.addConstantExtraAssumeCapacity(data.extra), - }); - } - return @enumFromInt(gop.index); -} - -fn asmConstAssumeCapacity( - self: *Builder, - ty: Type, - info: Constant.Assembly.Info, - assembly: String, - constraints: String, -) Constant { - assert(ty.functionKind(self) == .normal); - - const Key = struct { tag: Constant.Tag, extra: Constant.Assembly }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - return @truncate(std.hash.Wyhash.hash( - std.hash.uint32(@intFromEnum(key.tag)), - std.mem.asBytes(&key.extra), - )); - } - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - const rhs_extra = ctx.builder.constantExtraData(Constant.Assembly, rhs_data); - return std.meta.eql(lhs_key.extra, rhs_extra); - } - }; - - const data = Key{ - .tag = @enumFromInt(@intFromEnum(Constant.Tag.@"asm") + @as(u4, @bitCast(info))), - .extra = .{ .type = ty, .assembly = assembly, .constraints = constraints }, - }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = data.tag, - .data = self.addConstantExtraAssumeCapacity(data.extra), - }); - } - return @enumFromInt(gop.index); -} - -fn ensureUnusedConstantCapacity( - self: *Builder, - count: usize, - comptime Extra: type, - trail_len: usize, -) Allocator.Error!void { - try self.constant_map.ensureUnusedCapacity(self.gpa, count); - try self.constant_items.ensureUnusedCapacity(self.gpa, count); - try self.constant_extra.ensureUnusedCapacity( - self.gpa, - count * (@typeInfo(Extra).@"struct".fields.len + trail_len), - ); -} - -fn getOrPutConstantNoExtraAssumeCapacity( - self: *Builder, - item: Constant.Item, -) struct { new: bool, constant: Constant } { - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Constant.Item) u32 { - return @truncate(std.hash.Wyhash.hash( - std.hash.uint32(@intFromEnum(key.tag)), - std.mem.asBytes(&key.data), - )); - } - pub fn eql(ctx: @This(), lhs_key: Constant.Item, _: void, rhs_index: usize) bool { - return std.meta.eql(lhs_key, ctx.builder.constant_items.get(rhs_index)); - } - }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted(item, Adapter{ .builder = self }); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(item); - } - return .{ .new = !gop.found_existing, .constant = @enumFromInt(gop.index) }; -} - -fn getOrPutConstantAggregateAssumeCapacity( - self: *Builder, - tag: Constant.Tag, - ty: Type, - vals: []const Constant, -) struct { new: bool, constant: Constant } { - switch (tag) { - .structure, .packed_structure, .array, .vector => {}, - else => unreachable, - } - const Key = struct { tag: Constant.Tag, type: Type, vals: []const Constant }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - var hasher = std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(key.tag))); - hasher.update(std.mem.asBytes(&key.type)); - hasher.update(std.mem.sliceAsBytes(key.vals)); - return @truncate(hasher.final()); - } - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; - const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; - var rhs_extra = ctx.builder.constantExtraDataTrail(Constant.Aggregate, rhs_data); - if (lhs_key.type != rhs_extra.data.type) return false; - const rhs_vals = rhs_extra.trail.next(@intCast(lhs_key.vals.len), Constant, ctx.builder); - return std.mem.eql(Constant, lhs_key.vals, rhs_vals); - } - }; - const gop = self.constant_map.getOrPutAssumeCapacityAdapted( - Key{ .tag = tag, .type = ty, .vals = vals }, - Adapter{ .builder = self }, - ); - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.constant_items.appendAssumeCapacity(.{ - .tag = tag, - .data = self.addConstantExtraAssumeCapacity(Constant.Aggregate{ .type = ty }), - }); - self.constant_extra.appendSliceAssumeCapacity(@ptrCast(vals)); - } - return .{ .new = !gop.found_existing, .constant = @enumFromInt(gop.index) }; -} - -fn addConstantExtraAssumeCapacity(self: *Builder, extra: anytype) Constant.Item.ExtraIndex { - const result: Constant.Item.ExtraIndex = @intCast(self.constant_extra.items.len); - inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| { - const value = @field(extra, field.name); - self.constant_extra.appendAssumeCapacity(switch (field.type) { - u32 => value, - String, Type, Constant, Function.Index, Function.Block.Index => @intFromEnum(value), - Constant.GetElementPtr.Info => @bitCast(value), - else => @compileError("bad field type: " ++ @typeName(field.type)), - }); - } - return result; -} - -const ConstantExtraDataTrail = struct { - index: Constant.Item.ExtraIndex, - - fn nextMut(self: *ConstantExtraDataTrail, len: u32, comptime Item: type, builder: *Builder) []Item { - const items: []Item = @ptrCast(builder.constant_extra.items[self.index..][0..len]); - self.index += @intCast(len); - return items; - } - - fn next( - self: *ConstantExtraDataTrail, - len: u32, - comptime Item: type, - builder: *const Builder, - ) []const Item { - const items: []const Item = @ptrCast(builder.constant_extra.items[self.index..][0..len]); - self.index += @intCast(len); - return items; - } -}; - -fn constantExtraDataTrail( - self: *const Builder, - comptime T: type, - index: Constant.Item.ExtraIndex, -) struct { data: T, trail: ConstantExtraDataTrail } { - var result: T = undefined; - const fields = @typeInfo(T).@"struct".fields; - inline for (fields, self.constant_extra.items[index..][0..fields.len]) |field, value| - @field(result, field.name) = switch (field.type) { - u32 => value, - String, Type, Constant, Function.Index, Function.Block.Index => @enumFromInt(value), - Constant.GetElementPtr.Info => @bitCast(value), - else => @compileError("bad field type: " ++ @typeName(field.type)), - }; - return .{ - .data = result, - .trail = .{ .index = index + @as(Constant.Item.ExtraIndex, @intCast(fields.len)) }, - }; -} - -fn constantExtraData(self: *const Builder, comptime T: type, index: Constant.Item.ExtraIndex) T { - return self.constantExtraDataTrail(T, index).data; -} - -fn ensureUnusedMetadataCapacity( - self: *Builder, - count: usize, - comptime Extra: type, - trail_len: usize, -) Allocator.Error!void { - try self.metadata_map.ensureUnusedCapacity(self.gpa, count); - try self.metadata_items.ensureUnusedCapacity(self.gpa, count); - try self.metadata_extra.ensureUnusedCapacity( - self.gpa, - count * (@typeInfo(Extra).@"struct".fields.len + trail_len), - ); -} - -fn addMetadataExtraAssumeCapacity(self: *Builder, extra: anytype) Metadata.Item.ExtraIndex { - const result: Metadata.Item.ExtraIndex = @intCast(self.metadata_extra.items.len); - inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| { - const value = @field(extra, field.name); - self.metadata_extra.appendAssumeCapacity(switch (field.type) { - u32 => value, - MetadataString, Metadata, Variable.Index, Value => @intFromEnum(value), - Metadata.DIFlags => @bitCast(value), - else => @compileError("bad field type: " ++ @typeName(field.type)), - }); - } - return result; -} - -const MetadataExtraDataTrail = struct { - index: Metadata.Item.ExtraIndex, - - fn nextMut(self: *MetadataExtraDataTrail, len: u32, comptime Item: type, builder: *Builder) []Item { - const items: []Item = @ptrCast(builder.metadata_extra.items[self.index..][0..len]); - self.index += @intCast(len); - return items; - } - - fn next( - self: *MetadataExtraDataTrail, - len: u32, - comptime Item: type, - builder: *const Builder, - ) []const Item { - const items: []const Item = @ptrCast(builder.metadata_extra.items[self.index..][0..len]); - self.index += @intCast(len); - return items; - } -}; - -fn metadataExtraDataTrail( - self: *const Builder, - comptime T: type, - index: Metadata.Item.ExtraIndex, -) struct { data: T, trail: MetadataExtraDataTrail } { - var result: T = undefined; - const fields = @typeInfo(T).@"struct".fields; - inline for (fields, self.metadata_extra.items[index..][0..fields.len]) |field, value| - @field(result, field.name) = switch (field.type) { - u32 => value, - MetadataString, Metadata, Variable.Index, Value => @enumFromInt(value), - Metadata.DIFlags => @bitCast(value), - else => @compileError("bad field type: " ++ @typeName(field.type)), - }; - return .{ - .data = result, - .trail = .{ .index = index + @as(Metadata.Item.ExtraIndex, @intCast(fields.len)) }, - }; -} - -fn metadataExtraData(self: *const Builder, comptime T: type, index: Metadata.Item.ExtraIndex) T { - return self.metadataExtraDataTrail(T, index).data; -} - -pub fn metadataString(self: *Builder, bytes: []const u8) Allocator.Error!MetadataString { - try self.metadata_string_bytes.ensureUnusedCapacity(self.gpa, bytes.len); - try self.metadata_string_indices.ensureUnusedCapacity(self.gpa, 1); - try self.metadata_string_map.ensureUnusedCapacity(self.gpa, 1); - - const gop = self.metadata_string_map.getOrPutAssumeCapacityAdapted( - bytes, - MetadataString.Adapter{ .builder = self }, - ); - if (!gop.found_existing) { - self.metadata_string_bytes.appendSliceAssumeCapacity(bytes); - self.metadata_string_indices.appendAssumeCapacity(@intCast(self.metadata_string_bytes.items.len)); - } - return @enumFromInt(gop.index); -} - -pub fn metadataStringFromStrtabString(self: *Builder, str: StrtabString) Allocator.Error!MetadataString { - if (str == .none or str == .empty) return MetadataString.none; - return try self.metadataString(str.slice(self).?); -} - -pub fn metadataStringFmt(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) Allocator.Error!MetadataString { - try self.metadata_string_map.ensureUnusedCapacity(self.gpa, 1); - try self.metadata_string_bytes.ensureUnusedCapacity(self.gpa, @intCast(std.fmt.count(fmt_str, fmt_args))); - try self.metadata_string_indices.ensureUnusedCapacity(self.gpa, 1); - return self.metadataStringFmtAssumeCapacity(fmt_str, fmt_args); -} - -pub fn metadataStringFmtAssumeCapacity(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) MetadataString { - self.metadata_string_bytes.writer(undefined).print(fmt_str, fmt_args) catch unreachable; - return self.trailingMetadataStringAssumeCapacity(); -} - -pub fn trailingMetadataString(self: *Builder) Allocator.Error!MetadataString { - try self.metadata_string_indices.ensureUnusedCapacity(self.gpa, 1); - try self.metadata_string_map.ensureUnusedCapacity(self.gpa, 1); - return self.trailingMetadataStringAssumeCapacity(); -} - -pub fn trailingMetadataStringAssumeCapacity(self: *Builder) MetadataString { - const start = self.metadata_string_indices.getLast(); - const bytes: []const u8 = self.metadata_string_bytes.items[start..]; - const gop = self.metadata_string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self }); - if (gop.found_existing) { - self.metadata_string_bytes.shrinkRetainingCapacity(start); - } else { - self.metadata_string_indices.appendAssumeCapacity(@intCast(self.metadata_string_bytes.items.len)); - } - return @enumFromInt(gop.index); -} - -pub fn metadataNamed(self: *Builder, name: MetadataString, operands: []const Metadata) Allocator.Error!void { - try self.metadata_extra.ensureUnusedCapacity(self.gpa, operands.len); - try self.metadata_named.ensureUnusedCapacity(self.gpa, 1); - self.metadataNamedAssumeCapacity(name, operands); -} - -fn metadataNone(self: *Builder) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, NoExtra, 0); - return self.metadataNoneAssumeCapacity(); -} - -pub fn debugFile( - self: *Builder, - filename: MetadataString, - directory: MetadataString, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.File, 0); - return self.debugFileAssumeCapacity(filename, directory); -} - -pub fn debugCompileUnit( - self: *Builder, - file: Metadata, - producer: MetadataString, - enums: Metadata, - globals: Metadata, - options: Metadata.CompileUnit.Options, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.CompileUnit, 0); - return self.debugCompileUnitAssumeCapacity(file, producer, enums, globals, options); -} - -pub fn debugSubprogram( - self: *Builder, - file: Metadata, - name: MetadataString, - linkage_name: MetadataString, - line: u32, - scope_line: u32, - ty: Metadata, - options: Metadata.Subprogram.Options, - compile_unit: Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.Subprogram, 0); - return self.debugSubprogramAssumeCapacity( - file, - name, - linkage_name, - line, - scope_line, - ty, - options, - compile_unit, - ); -} - -pub fn debugLexicalBlock(self: *Builder, scope: Metadata, file: Metadata, line: u32, column: u32) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.LexicalBlock, 0); - return self.debugLexicalBlockAssumeCapacity(scope, file, line, column); -} - -pub fn debugLocation(self: *Builder, line: u32, column: u32, scope: Metadata, inlined_at: Metadata) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.Location, 0); - return self.debugLocationAssumeCapacity(line, column, scope, inlined_at); -} - -pub fn debugBoolType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); - return self.debugBoolTypeAssumeCapacity(name, size_in_bits); -} - -pub fn debugUnsignedType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); - return self.debugUnsignedTypeAssumeCapacity(name, size_in_bits); -} - -pub fn debugSignedType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); - return self.debugSignedTypeAssumeCapacity(name, size_in_bits); -} - -pub fn debugFloatType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); - return self.debugFloatTypeAssumeCapacity(name, size_in_bits); -} - -pub fn debugForwardReference(self: *Builder) Allocator.Error!Metadata { - try self.metadata_forward_references.ensureUnusedCapacity(self.gpa, 1); - return self.debugForwardReferenceAssumeCapacity(); -} - -pub fn debugStructType( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - fields_tuple: Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); - return self.debugStructTypeAssumeCapacity( - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - fields_tuple, - ); -} - -pub fn debugUnionType( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - fields_tuple: Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); - return self.debugUnionTypeAssumeCapacity( - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - fields_tuple, - ); -} - -pub fn debugEnumerationType( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - fields_tuple: Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); - return self.debugEnumerationTypeAssumeCapacity( - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - fields_tuple, - ); -} - -pub fn debugArrayType( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - fields_tuple: Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); - return self.debugArrayTypeAssumeCapacity( - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - fields_tuple, - ); -} - -pub fn debugVectorType( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - fields_tuple: Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); - return self.debugVectorTypeAssumeCapacity( - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - fields_tuple, - ); -} - -pub fn debugPointerType( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - offset_in_bits: u64, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.DerivedType, 0); - return self.debugPointerTypeAssumeCapacity( - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - offset_in_bits, - ); -} - -pub fn debugMemberType( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - offset_in_bits: u64, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.DerivedType, 0); - return self.debugMemberTypeAssumeCapacity( - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - offset_in_bits, - ); -} - -pub fn debugSubroutineType( - self: *Builder, - types_tuple: Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.SubroutineType, 0); - return self.debugSubroutineTypeAssumeCapacity(types_tuple); -} - -pub fn debugEnumerator( - self: *Builder, - name: MetadataString, - unsigned: bool, - bit_width: u32, - value: std.math.big.int.Const, -) Allocator.Error!Metadata { - assert(!(unsigned and !value.positive)); - try self.ensureUnusedMetadataCapacity(1, Metadata.Enumerator, 0); - try self.metadata_limbs.ensureUnusedCapacity(self.gpa, value.limbs.len); - return self.debugEnumeratorAssumeCapacity(name, unsigned, bit_width, value); -} - -pub fn debugSubrange( - self: *Builder, - lower_bound: Metadata, - count: Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.Subrange, 0); - return self.debugSubrangeAssumeCapacity(lower_bound, count); -} - -pub fn debugExpression( - self: *Builder, - elements: []const u32, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.Expression, elements.len); - return self.debugExpressionAssumeCapacity(elements); -} - -pub fn metadataTuple( - self: *Builder, - elements: []const Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.Tuple, elements.len); - return self.metadataTupleAssumeCapacity(elements); -} - -pub fn strTuple( - self: *Builder, - str: MetadataString, - elements: []const Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.StrTuple, elements.len); - return self.strTupleAssumeCapacity(str, elements); -} - -pub fn metadataModuleFlag( - self: *Builder, - behavior: Metadata, - name: MetadataString, - constant: Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.ModuleFlag, 0); - return self.metadataModuleFlagAssumeCapacity(behavior, name, constant); -} - -pub fn debugLocalVar( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - ty: Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.LocalVar, 0); - return self.debugLocalVarAssumeCapacity(name, file, scope, line, ty); -} - -pub fn debugParameter( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - ty: Metadata, - arg_no: u32, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.Parameter, 0); - return self.debugParameterAssumeCapacity(name, file, scope, line, ty, arg_no); -} - -pub fn debugGlobalVar( - self: *Builder, - name: MetadataString, - linkage_name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - ty: Metadata, - variable: Variable.Index, - options: Metadata.GlobalVar.Options, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.GlobalVar, 0); - return self.debugGlobalVarAssumeCapacity( - name, - linkage_name, - file, - scope, - line, - ty, - variable, - options, - ); -} - -pub fn debugGlobalVarExpression( - self: *Builder, - variable: Metadata, - expression: Metadata, -) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, Metadata.GlobalVarExpression, 0); - return self.debugGlobalVarExpressionAssumeCapacity(variable, expression); -} - -pub fn metadataConstant(self: *Builder, value: Constant) Allocator.Error!Metadata { - try self.ensureUnusedMetadataCapacity(1, NoExtra, 0); - return self.metadataConstantAssumeCapacity(value); -} - -pub fn debugForwardReferenceSetType(self: *Builder, fwd_ref: Metadata, ty: Metadata) void { - assert( - @intFromEnum(fwd_ref) >= Metadata.first_forward_reference and - @intFromEnum(fwd_ref) <= Metadata.first_local_metadata, - ); - const index = @intFromEnum(fwd_ref) - Metadata.first_forward_reference; - self.metadata_forward_references.items[index] = ty; -} - -fn metadataSimpleAssumeCapacity(self: *Builder, tag: Metadata.Tag, value: anytype) Metadata { - const Key = struct { - tag: Metadata.Tag, - value: @TypeOf(value), - }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - var hasher = std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(key.tag))); - inline for (std.meta.fields(@TypeOf(value))) |field| { - hasher.update(std.mem.asBytes(&@field(key.value, field.name))); - } - return @truncate(hasher.final()); - } - - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - if (lhs_key.tag != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; - const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; - const rhs_extra = ctx.builder.metadataExtraData(@TypeOf(value), rhs_data); - return std.meta.eql(lhs_key.value, rhs_extra); - } - }; - - const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( - Key{ .tag = tag, .value = value }, - Adapter{ .builder = self }, - ); - - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.metadata_items.appendAssumeCapacity(.{ - .tag = tag, - .data = self.addMetadataExtraAssumeCapacity(value), - }); - } - return @enumFromInt(gop.index); -} - -fn metadataDistinctAssumeCapacity(self: *Builder, tag: Metadata.Tag, value: anytype) Metadata { - const Key = struct { tag: Metadata.Tag, index: Metadata }; - const Adapter = struct { - pub fn hash(_: @This(), key: Key) u32 { - return @truncate(std.hash.Wyhash.hash( - std.hash.uint32(@intFromEnum(key.tag)), - std.mem.asBytes(&key.index), - )); - } - - pub fn eql(_: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - return @intFromEnum(lhs_key.index) == rhs_index; - } - }; - - const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( - Key{ .tag = tag, .index = @enumFromInt(self.metadata_map.count()) }, - Adapter{}, - ); - - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.metadata_items.appendAssumeCapacity(.{ - .tag = tag, - .data = self.addMetadataExtraAssumeCapacity(value), - }); - } - return @enumFromInt(gop.index); -} - -fn metadataNamedAssumeCapacity(self: *Builder, name: MetadataString, operands: []const Metadata) void { - assert(name != .none); - const extra_index: u32 = @intCast(self.metadata_extra.items.len); - self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(operands)); - - const gop = self.metadata_named.getOrPutAssumeCapacity(name); - gop.value_ptr.* = .{ - .index = extra_index, - .len = @intCast(operands.len), - }; -} - -pub fn metadataNoneAssumeCapacity(self: *Builder) Metadata { - return self.metadataSimpleAssumeCapacity(.none, .{}); -} - -fn debugFileAssumeCapacity( - self: *Builder, - filename: MetadataString, - directory: MetadataString, -) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.file, Metadata.File{ - .filename = filename, - .directory = directory, - }); -} - -pub fn debugCompileUnitAssumeCapacity( - self: *Builder, - file: Metadata, - producer: MetadataString, - enums: Metadata, - globals: Metadata, - options: Metadata.CompileUnit.Options, -) Metadata { - assert(!self.strip); - return self.metadataDistinctAssumeCapacity( - if (options.optimized) .@"compile_unit optimized" else .compile_unit, - Metadata.CompileUnit{ - .file = file, - .producer = producer, - .enums = enums, - .globals = globals, - }, - ); -} - -fn debugSubprogramAssumeCapacity( - self: *Builder, - file: Metadata, - name: MetadataString, - linkage_name: MetadataString, - line: u32, - scope_line: u32, - ty: Metadata, - options: Metadata.Subprogram.Options, - compile_unit: Metadata, -) Metadata { - assert(!self.strip); - const tag: Metadata.Tag = @enumFromInt(@intFromEnum(Metadata.Tag.subprogram) + - @as(u3, @truncate(@as(u32, @bitCast(options.sp_flags)) >> 2))); - return self.metadataDistinctAssumeCapacity(tag, Metadata.Subprogram{ - .file = file, - .name = name, - .linkage_name = linkage_name, - .line = line, - .scope_line = scope_line, - .ty = ty, - .di_flags = options.di_flags, - .compile_unit = compile_unit, - }); -} - -fn debugLexicalBlockAssumeCapacity(self: *Builder, scope: Metadata, file: Metadata, line: u32, column: u32) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.lexical_block, Metadata.LexicalBlock{ - .scope = scope, - .file = file, - .line = line, - .column = column, - }); -} - -fn debugLocationAssumeCapacity(self: *Builder, line: u32, column: u32, scope: Metadata, inlined_at: Metadata) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.location, Metadata.Location{ - .line = line, - .column = column, - .scope = scope, - .inlined_at = inlined_at, - }); -} - -fn debugBoolTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.basic_bool_type, Metadata.BasicType{ - .name = name, - .size_in_bits_lo = @truncate(size_in_bits), - .size_in_bits_hi = @truncate(size_in_bits >> 32), - }); -} - -fn debugUnsignedTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.basic_unsigned_type, Metadata.BasicType{ - .name = name, - .size_in_bits_lo = @truncate(size_in_bits), - .size_in_bits_hi = @truncate(size_in_bits >> 32), - }); -} - -fn debugSignedTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.basic_signed_type, Metadata.BasicType{ - .name = name, - .size_in_bits_lo = @truncate(size_in_bits), - .size_in_bits_hi = @truncate(size_in_bits >> 32), - }); -} - -fn debugFloatTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.basic_float_type, Metadata.BasicType{ - .name = name, - .size_in_bits_lo = @truncate(size_in_bits), - .size_in_bits_hi = @truncate(size_in_bits >> 32), - }); -} - -fn debugForwardReferenceAssumeCapacity(self: *Builder) Metadata { - assert(!self.strip); - const index = Metadata.first_forward_reference + self.metadata_forward_references.items.len; - self.metadata_forward_references.appendAssumeCapacity(.none); - return @enumFromInt(index); -} - -fn debugStructTypeAssumeCapacity( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - fields_tuple: Metadata, -) Metadata { - assert(!self.strip); - return self.debugCompositeTypeAssumeCapacity( - .composite_struct_type, - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - fields_tuple, - ); -} - -fn debugUnionTypeAssumeCapacity( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - fields_tuple: Metadata, -) Metadata { - assert(!self.strip); - return self.debugCompositeTypeAssumeCapacity( - .composite_union_type, - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - fields_tuple, - ); -} - -fn debugEnumerationTypeAssumeCapacity( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - fields_tuple: Metadata, -) Metadata { - assert(!self.strip); - return self.debugCompositeTypeAssumeCapacity( - .composite_enumeration_type, - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - fields_tuple, - ); -} - -fn debugArrayTypeAssumeCapacity( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - fields_tuple: Metadata, -) Metadata { - assert(!self.strip); - return self.debugCompositeTypeAssumeCapacity( - .composite_array_type, - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - fields_tuple, - ); -} - -fn debugVectorTypeAssumeCapacity( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - fields_tuple: Metadata, -) Metadata { - assert(!self.strip); - return self.debugCompositeTypeAssumeCapacity( - .composite_vector_type, - name, - file, - scope, - line, - underlying_type, - size_in_bits, - align_in_bits, - fields_tuple, - ); -} - -fn debugCompositeTypeAssumeCapacity( - self: *Builder, - tag: Metadata.Tag, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - fields_tuple: Metadata, -) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(tag, Metadata.CompositeType{ - .name = name, - .file = file, - .scope = scope, - .line = line, - .underlying_type = underlying_type, - .size_in_bits_lo = @truncate(size_in_bits), - .size_in_bits_hi = @truncate(size_in_bits >> 32), - .align_in_bits_lo = @truncate(align_in_bits), - .align_in_bits_hi = @truncate(align_in_bits >> 32), - .fields_tuple = fields_tuple, - }); -} - -fn debugPointerTypeAssumeCapacity( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - offset_in_bits: u64, -) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.derived_pointer_type, Metadata.DerivedType{ - .name = name, - .file = file, - .scope = scope, - .line = line, - .underlying_type = underlying_type, - .size_in_bits_lo = @truncate(size_in_bits), - .size_in_bits_hi = @truncate(size_in_bits >> 32), - .align_in_bits_lo = @truncate(align_in_bits), - .align_in_bits_hi = @truncate(align_in_bits >> 32), - .offset_in_bits_lo = @truncate(offset_in_bits), - .offset_in_bits_hi = @truncate(offset_in_bits >> 32), - }); -} - -fn debugMemberTypeAssumeCapacity( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - underlying_type: Metadata, - size_in_bits: u64, - align_in_bits: u64, - offset_in_bits: u64, -) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.derived_member_type, Metadata.DerivedType{ - .name = name, - .file = file, - .scope = scope, - .line = line, - .underlying_type = underlying_type, - .size_in_bits_lo = @truncate(size_in_bits), - .size_in_bits_hi = @truncate(size_in_bits >> 32), - .align_in_bits_lo = @truncate(align_in_bits), - .align_in_bits_hi = @truncate(align_in_bits >> 32), - .offset_in_bits_lo = @truncate(offset_in_bits), - .offset_in_bits_hi = @truncate(offset_in_bits >> 32), - }); -} - -fn debugSubroutineTypeAssumeCapacity( - self: *Builder, - types_tuple: Metadata, -) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.subroutine_type, Metadata.SubroutineType{ - .types_tuple = types_tuple, - }); -} - -fn debugEnumeratorAssumeCapacity( - self: *Builder, - name: MetadataString, - unsigned: bool, - bit_width: u32, - value: std.math.big.int.Const, -) Metadata { - assert(!self.strip); - const Key = struct { - tag: Metadata.Tag, - name: MetadataString, - bit_width: u32, - value: std.math.big.int.Const, - }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - var hasher = std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(key.tag))); - hasher.update(std.mem.asBytes(&key.name)); - hasher.update(std.mem.asBytes(&key.bit_width)); - hasher.update(std.mem.sliceAsBytes(key.value.limbs)); - return @truncate(hasher.final()); - } - - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - if (lhs_key.tag != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; - const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; - const rhs_extra = ctx.builder.metadataExtraData(Metadata.Enumerator, rhs_data); - const limbs = ctx.builder.metadata_limbs - .items[rhs_extra.limbs_index..][0..rhs_extra.limbs_len]; - const rhs_value = std.math.big.int.Const{ - .limbs = limbs, - .positive = lhs_key.value.positive, - }; - return lhs_key.name == rhs_extra.name and - lhs_key.bit_width == rhs_extra.bit_width and - lhs_key.value.eql(rhs_value); - } - }; - - const tag: Metadata.Tag = if (unsigned) - .enumerator_unsigned - else if (value.positive) - .enumerator_signed_positive - else - .enumerator_signed_negative; - - assert(!(tag == .enumerator_unsigned and !value.positive)); - - const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( - Key{ - .tag = tag, - .name = name, - .bit_width = bit_width, - .value = value, - }, - Adapter{ .builder = self }, - ); - - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.metadata_items.appendAssumeCapacity(.{ - .tag = tag, - .data = self.addMetadataExtraAssumeCapacity(Metadata.Enumerator{ - .name = name, - .bit_width = bit_width, - .limbs_index = @intCast(self.metadata_limbs.items.len), - .limbs_len = @intCast(value.limbs.len), - }), - }); - self.metadata_limbs.appendSliceAssumeCapacity(value.limbs); - } - return @enumFromInt(gop.index); -} - -fn debugSubrangeAssumeCapacity( - self: *Builder, - lower_bound: Metadata, - count: Metadata, -) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.subrange, Metadata.Subrange{ - .lower_bound = lower_bound, - .count = count, - }); -} - -fn debugExpressionAssumeCapacity( - self: *Builder, - elements: []const u32, -) Metadata { - assert(!self.strip); - const Key = struct { - elements: []const u32, - }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - var hasher = comptime std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(Metadata.Tag.expression))); - hasher.update(std.mem.sliceAsBytes(key.elements)); - return @truncate(hasher.final()); - } - - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - if (Metadata.Tag.expression != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; - const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; - var rhs_extra = ctx.builder.metadataExtraDataTrail(Metadata.Expression, rhs_data); - return std.mem.eql( - u32, - lhs_key.elements, - rhs_extra.trail.next(rhs_extra.data.elements_len, u32, ctx.builder), - ); - } - }; - - const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( - Key{ .elements = elements }, - Adapter{ .builder = self }, - ); - - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.metadata_items.appendAssumeCapacity(.{ - .tag = .expression, - .data = self.addMetadataExtraAssumeCapacity(Metadata.Expression{ - .elements_len = @intCast(elements.len), - }), - }); - self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(elements)); - } - return @enumFromInt(gop.index); -} - -fn metadataTupleAssumeCapacity( - self: *Builder, - elements: []const Metadata, -) Metadata { - const Key = struct { - elements: []const Metadata, - }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - var hasher = comptime std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(Metadata.Tag.tuple))); - hasher.update(std.mem.sliceAsBytes(key.elements)); - return @truncate(hasher.final()); - } - - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - if (Metadata.Tag.tuple != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; - const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; - var rhs_extra = ctx.builder.metadataExtraDataTrail(Metadata.Tuple, rhs_data); - return std.mem.eql( - Metadata, - lhs_key.elements, - rhs_extra.trail.next(rhs_extra.data.elements_len, Metadata, ctx.builder), - ); - } - }; - - const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( - Key{ .elements = elements }, - Adapter{ .builder = self }, - ); - - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.metadata_items.appendAssumeCapacity(.{ - .tag = .tuple, - .data = self.addMetadataExtraAssumeCapacity(Metadata.Tuple{ - .elements_len = @intCast(elements.len), - }), - }); - self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(elements)); - } - return @enumFromInt(gop.index); -} - -fn strTupleAssumeCapacity( - self: *Builder, - str: MetadataString, - elements: []const Metadata, -) Metadata { - const Key = struct { - str: MetadataString, - elements: []const Metadata, - }; - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Key) u32 { - var hasher = comptime std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(Metadata.Tag.tuple))); - hasher.update(std.mem.sliceAsBytes(key.elements)); - return @truncate(hasher.final()); - } - - pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - if (.str_tuple != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; - const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; - var rhs_extra = ctx.builder.metadataExtraDataTrail(Metadata.StrTuple, rhs_data); - return rhs_extra.data.str == lhs_key.str and std.mem.eql( - Metadata, - lhs_key.elements, - rhs_extra.trail.next(rhs_extra.data.elements_len, Metadata, ctx.builder), - ); - } - }; - - const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( - Key{ .str = str, .elements = elements }, - Adapter{ .builder = self }, - ); - - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.metadata_items.appendAssumeCapacity(.{ - .tag = .str_tuple, - .data = self.addMetadataExtraAssumeCapacity(Metadata.StrTuple{ - .str = str, - .elements_len = @intCast(elements.len), - }), - }); - self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(elements)); - } - return @enumFromInt(gop.index); -} - -fn metadataModuleFlagAssumeCapacity( - self: *Builder, - behavior: Metadata, - name: MetadataString, - constant: Metadata, -) Metadata { - return self.metadataSimpleAssumeCapacity(.module_flag, Metadata.ModuleFlag{ - .behavior = behavior, - .name = name, - .constant = constant, - }); -} - -fn debugLocalVarAssumeCapacity( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - ty: Metadata, -) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.local_var, Metadata.LocalVar{ - .name = name, - .file = file, - .scope = scope, - .line = line, - .ty = ty, - }); -} - -fn debugParameterAssumeCapacity( - self: *Builder, - name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - ty: Metadata, - arg_no: u32, -) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.parameter, Metadata.Parameter{ - .name = name, - .file = file, - .scope = scope, - .line = line, - .ty = ty, - .arg_no = arg_no, - }); -} - -fn debugGlobalVarAssumeCapacity( - self: *Builder, - name: MetadataString, - linkage_name: MetadataString, - file: Metadata, - scope: Metadata, - line: u32, - ty: Metadata, - variable: Variable.Index, - options: Metadata.GlobalVar.Options, -) Metadata { - assert(!self.strip); - return self.metadataDistinctAssumeCapacity( - if (options.local) .@"global_var local" else .global_var, - Metadata.GlobalVar{ - .name = name, - .linkage_name = linkage_name, - .file = file, - .scope = scope, - .line = line, - .ty = ty, - .variable = variable, - }, - ); -} - -fn debugGlobalVarExpressionAssumeCapacity( - self: *Builder, - variable: Metadata, - expression: Metadata, -) Metadata { - assert(!self.strip); - return self.metadataSimpleAssumeCapacity(.global_var_expression, Metadata.GlobalVarExpression{ - .variable = variable, - .expression = expression, - }); -} - -fn metadataConstantAssumeCapacity(self: *Builder, constant: Constant) Metadata { - const Adapter = struct { - builder: *const Builder, - pub fn hash(_: @This(), key: Constant) u32 { - var hasher = comptime std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(Metadata.Tag.constant))); - hasher.update(std.mem.asBytes(&key)); - return @truncate(hasher.final()); - } - - pub fn eql(ctx: @This(), lhs_key: Constant, _: void, rhs_index: usize) bool { - if (Metadata.Tag.constant != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; - const rhs_data: Constant = @enumFromInt(ctx.builder.metadata_items.items(.data)[rhs_index]); - return rhs_data == lhs_key; - } - }; - - const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( - constant, - Adapter{ .builder = self }, - ); - - if (!gop.found_existing) { - gop.key_ptr.* = {}; - gop.value_ptr.* = {}; - self.metadata_items.appendAssumeCapacity(.{ - .tag = .constant, - .data = @intFromEnum(constant), - }); - } - return @enumFromInt(gop.index); -} - -pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]const u32 { - const BitcodeWriter = bitcode_writer.BitcodeWriter(&.{ Type, FunctionAttributes }); - var bitcode = BitcodeWriter.init(allocator, .{ - std.math.log2_int_ceil(usize, self.type_items.items.len), - std.math.log2_int_ceil(usize, 1 + self.function_attributes_set.count()), - }); - errdefer bitcode.deinit(); - - // Write LLVM IR magic - try bitcode.writeBits(ir.MAGIC, 32); - - var record: std.ArrayListUnmanaged(u64) = .empty; - defer record.deinit(self.gpa); - - // IDENTIFICATION_BLOCK - { - const Identification = ir.Identification; - var identification_block = try bitcode.enterTopBlock(Identification); - - const producer = try std.fmt.allocPrint(self.gpa, "zig {d}.{d}.{d}", .{ - build_options.semver.major, - build_options.semver.minor, - build_options.semver.patch, - }); - defer self.gpa.free(producer); - - try identification_block.writeAbbrev(Identification.Version{ .string = producer }); - try identification_block.writeAbbrev(Identification.Epoch{ .epoch = 0 }); - - try identification_block.end(); - } - - // MODULE_BLOCK - { - const Module = ir.Module; - var module_block = try bitcode.enterTopBlock(Module); - - try module_block.writeAbbrev(Module.Version{}); - - if (self.target_triple.slice(self)) |triple| { - try module_block.writeAbbrev(Module.String{ - .code = 2, - .string = triple, - }); - } - - if (self.data_layout.slice(self)) |data_layout| { - try module_block.writeAbbrev(Module.String{ - .code = 3, - .string = data_layout, - }); - } - - if (self.source_filename.slice(self)) |source_filename| { - try module_block.writeAbbrev(Module.String{ - .code = 16, - .string = source_filename, - }); - } - - if (self.module_asm.items.len != 0) { - try module_block.writeAbbrev(Module.String{ - .code = 4, - .string = self.module_asm.items, - }); - } - - // TYPE_BLOCK - { - var type_block = try module_block.enterSubBlock(ir.Type, true); - - try type_block.writeAbbrev(ir.Type.NumEntry{ .num = @intCast(self.type_items.items.len) }); - - for (self.type_items.items, 0..) |item, i| { - const ty: Type = @enumFromInt(i); - - switch (item.tag) { - .simple => try type_block.writeAbbrev(ir.Type.Simple{ .code = @truncate(item.data) }), - .integer => try type_block.writeAbbrev(ir.Type.Integer{ .width = item.data }), - .structure, - .packed_structure, - => |kind| { - const is_packed = switch (kind) { - .structure => false, - .packed_structure => true, - else => unreachable, - }; - var extra = self.typeExtraDataTrail(Type.Structure, item.data); - try type_block.writeAbbrev(ir.Type.StructAnon{ - .is_packed = is_packed, - .types = extra.trail.next(extra.data.fields_len, Type, self), - }); - }, - .named_structure => { - const extra = self.typeExtraData(Type.NamedStructure, item.data); - try type_block.writeAbbrev(ir.Type.StructName{ - .string = extra.id.slice(self).?, - }); - - switch (extra.body) { - .none => try type_block.writeAbbrev(ir.Type.Opaque{}), - else => { - const real_struct = self.type_items.items[@intFromEnum(extra.body)]; - const is_packed: bool = switch (real_struct.tag) { - .structure => false, - .packed_structure => true, - else => unreachable, - }; - - var real_extra = self.typeExtraDataTrail(Type.Structure, real_struct.data); - try type_block.writeAbbrev(ir.Type.StructNamed{ - .is_packed = is_packed, - .types = real_extra.trail.next(real_extra.data.fields_len, Type, self), - }); - }, - } - }, - .array, - .small_array, - => try type_block.writeAbbrev(ir.Type.Array{ - .len = ty.aggregateLen(self), - .child = ty.childType(self), - }), - .vector, - .scalable_vector, - => try type_block.writeAbbrev(ir.Type.Vector{ - .len = ty.aggregateLen(self), - .child = ty.childType(self), - }), - .pointer => try type_block.writeAbbrev(ir.Type.Pointer{ - .addr_space = ty.pointerAddrSpace(self), - }), - .target => { - var extra = self.typeExtraDataTrail(Type.Target, item.data); - try type_block.writeAbbrev(ir.Type.StructName{ - .string = extra.data.name.slice(self).?, - }); - - const types = extra.trail.next(extra.data.types_len, Type, self); - const ints = extra.trail.next(extra.data.ints_len, u32, self); - - try type_block.writeAbbrev(ir.Type.Target{ - .num_types = extra.data.types_len, - .types = types, - .ints = ints, - }); - }, - .function, .vararg_function => |kind| { - const is_vararg = switch (kind) { - .function => false, - .vararg_function => true, - else => unreachable, - }; - var extra = self.typeExtraDataTrail(Type.Function, item.data); - try type_block.writeAbbrev(ir.Type.Function{ - .is_vararg = is_vararg, - .return_type = extra.data.ret, - .param_types = extra.trail.next(extra.data.params_len, Type, self), - }); - }, - } - } - - try type_block.end(); - } - - var attributes_set: std.AutoArrayHashMapUnmanaged(struct { - attributes: Attributes, - index: u32, - }, void) = .{}; - defer attributes_set.deinit(self.gpa); - - // PARAMATTR_GROUP_BLOCK - { - const ParamattrGroup = ir.ParamattrGroup; - - var paramattr_group_block = try module_block.enterSubBlock(ParamattrGroup, true); - - for (self.function_attributes_set.keys()) |func_attributes| { - for (func_attributes.slice(self), 0..) |attributes, i| { - const attributes_slice = attributes.slice(self); - if (attributes_slice.len == 0) continue; - - const attr_gop = try attributes_set.getOrPut(self.gpa, .{ - .attributes = attributes, - .index = @intCast(i), - }); - - if (attr_gop.found_existing) continue; - - record.clearRetainingCapacity(); - try record.ensureUnusedCapacity(self.gpa, 2); - - record.appendAssumeCapacity(attr_gop.index); - record.appendAssumeCapacity(switch (i) { - 0 => 0xffffffff, - else => i - 1, - }); - - for (attributes_slice) |attr_index| { - const kind = attr_index.getKind(self); - switch (attr_index.toAttribute(self)) { - .zeroext, - .signext, - .inreg, - .@"noalias", - .nocapture, - .nofree, - .nest, - .returned, - .nonnull, - .swiftself, - .swiftasync, - .swifterror, - .immarg, - .noundef, - .allocalign, - .allocptr, - .readnone, - .readonly, - .writeonly, - .alwaysinline, - .builtin, - .cold, - .convergent, - .disable_sanitizer_information, - .fn_ret_thunk_extern, - .hot, - .inlinehint, - .jumptable, - .minsize, - .naked, - .nobuiltin, - .nocallback, - .noduplicate, - .noimplicitfloat, - .@"noinline", - .nomerge, - .nonlazybind, - .noprofile, - .skipprofile, - .noredzone, - .noreturn, - .norecurse, - .willreturn, - .nosync, - .nounwind, - .nosanitize_bounds, - .nosanitize_coverage, - .null_pointer_is_valid, - .optforfuzzing, - .optnone, - .optsize, - .returns_twice, - .safestack, - .sanitize_address, - .sanitize_memory, - .sanitize_thread, - .sanitize_hwaddress, - .sanitize_memtag, - .speculative_load_hardening, - .speculatable, - .ssp, - .sspstrong, - .sspreq, - .strictfp, - .nocf_check, - .shadowcallstack, - .mustprogress, - .no_sanitize_address, - .no_sanitize_hwaddress, - .sanitize_address_dyninit, - => { - try record.ensureUnusedCapacity(self.gpa, 2); - record.appendAssumeCapacity(0); - record.appendAssumeCapacity(@intFromEnum(kind)); - }, - .byval, - .byref, - .preallocated, - .inalloca, - .sret, - .elementtype, - => |ty| { - try record.ensureUnusedCapacity(self.gpa, 3); - record.appendAssumeCapacity(6); - record.appendAssumeCapacity(@intFromEnum(kind)); - record.appendAssumeCapacity(@intFromEnum(ty)); - }, - .@"align", - .alignstack, - => |alignment| { - try record.ensureUnusedCapacity(self.gpa, 3); - record.appendAssumeCapacity(1); - record.appendAssumeCapacity(@intFromEnum(kind)); - record.appendAssumeCapacity(alignment.toByteUnits() orelse 0); - }, - .dereferenceable, - .dereferenceable_or_null, - => |size| { - try record.ensureUnusedCapacity(self.gpa, 3); - record.appendAssumeCapacity(1); - record.appendAssumeCapacity(@intFromEnum(kind)); - record.appendAssumeCapacity(size); - }, - .nofpclass => |fpclass| { - try record.ensureUnusedCapacity(self.gpa, 3); - record.appendAssumeCapacity(1); - record.appendAssumeCapacity(@intFromEnum(kind)); - record.appendAssumeCapacity(@as(u32, @bitCast(fpclass))); - }, - .allockind => |allockind| { - try record.ensureUnusedCapacity(self.gpa, 3); - record.appendAssumeCapacity(1); - record.appendAssumeCapacity(@intFromEnum(kind)); - record.appendAssumeCapacity(@as(u32, @bitCast(allockind))); - }, - - .allocsize => |allocsize| { - try record.ensureUnusedCapacity(self.gpa, 3); - record.appendAssumeCapacity(1); - record.appendAssumeCapacity(@intFromEnum(kind)); - record.appendAssumeCapacity(@bitCast(allocsize.toLlvm())); - }, - .memory => |memory| { - try record.ensureUnusedCapacity(self.gpa, 3); - record.appendAssumeCapacity(1); - record.appendAssumeCapacity(@intFromEnum(kind)); - record.appendAssumeCapacity(@as(u32, @bitCast(memory))); - }, - .uwtable => |uwtable| if (uwtable != .none) { - try record.ensureUnusedCapacity(self.gpa, 3); - record.appendAssumeCapacity(1); - record.appendAssumeCapacity(@intFromEnum(kind)); - record.appendAssumeCapacity(@intFromEnum(uwtable)); - }, - .vscale_range => |vscale_range| { - try record.ensureUnusedCapacity(self.gpa, 3); - record.appendAssumeCapacity(1); - record.appendAssumeCapacity(@intFromEnum(kind)); - record.appendAssumeCapacity(@bitCast(vscale_range.toLlvm())); - }, - .string => |string_attr| { - const string_attr_kind_slice = string_attr.kind.slice(self).?; - const string_attr_value_slice = if (string_attr.value != .none) - string_attr.value.slice(self).? - else - null; - - try record.ensureUnusedCapacity( - self.gpa, - 2 + string_attr_kind_slice.len + if (string_attr_value_slice) |slice| slice.len + 1 else 0, - ); - record.appendAssumeCapacity(if (string_attr.value == .none) 3 else 4); - for (string_attr.kind.slice(self).?) |c| { - record.appendAssumeCapacity(c); - } - record.appendAssumeCapacity(0); - if (string_attr_value_slice) |slice| { - for (slice) |c| { - record.appendAssumeCapacity(c); - } - record.appendAssumeCapacity(0); - } - }, - .none => unreachable, - } - } - - try paramattr_group_block.writeUnabbrev(3, record.items); - } - } - - try paramattr_group_block.end(); - } - - // PARAMATTR_BLOCK - { - const Paramattr = ir.Paramattr; - var paramattr_block = try module_block.enterSubBlock(Paramattr, true); - - for (self.function_attributes_set.keys()) |func_attributes| { - const func_attributes_slice = func_attributes.slice(self); - record.clearRetainingCapacity(); - try record.ensureUnusedCapacity(self.gpa, func_attributes_slice.len); - for (func_attributes_slice, 0..) |attributes, i| { - const attributes_slice = attributes.slice(self); - if (attributes_slice.len == 0) continue; - - const group_index = attributes_set.getIndex(.{ - .attributes = attributes, - .index = @intCast(i), - }).?; - record.appendAssumeCapacity(@intCast(group_index)); - } - - try paramattr_block.writeAbbrev(Paramattr.Entry{ .group_indices = record.items }); - } - - try paramattr_block.end(); - } - - var globals: std.AutoArrayHashMapUnmanaged(Global.Index, void) = .empty; - defer globals.deinit(self.gpa); - try globals.ensureUnusedCapacity( - self.gpa, - self.variables.items.len + - self.functions.items.len + - self.aliases.items.len, - ); - - for (self.variables.items) |variable| { - if (variable.global.getReplacement(self) != .none) continue; - - globals.putAssumeCapacity(variable.global, {}); - } - - for (self.functions.items) |function| { - if (function.global.getReplacement(self) != .none) continue; - - globals.putAssumeCapacity(function.global, {}); - } - - for (self.aliases.items) |alias| { - if (alias.global.getReplacement(self) != .none) continue; - - globals.putAssumeCapacity(alias.global, {}); - } - - const ConstantAdapter = struct { - const ConstantAdapter = @This(); - builder: *const Builder, - globals: *const std.AutoArrayHashMapUnmanaged(Global.Index, void), - - pub fn get(adapter: @This(), param: anytype, comptime field_name: []const u8) @TypeOf(param) { - _ = field_name; - return switch (@TypeOf(param)) { - Constant => @enumFromInt(adapter.getConstantIndex(param)), - else => param, - }; - } - - pub fn getConstantIndex(adapter: ConstantAdapter, constant: Constant) u32 { - return switch (constant.unwrap()) { - .constant => |c| c + adapter.numGlobals(), - .global => |global| @intCast(adapter.globals.getIndex(global.unwrap(adapter.builder)).?), - }; - } - - pub fn numConstants(adapter: ConstantAdapter) u32 { - return @intCast(adapter.globals.count() + adapter.builder.constant_items.len); - } - - pub fn numGlobals(adapter: ConstantAdapter) u32 { - return @intCast(adapter.globals.count()); - } - }; - - const constant_adapter = ConstantAdapter{ - .builder = self, - .globals = &globals, - }; - - // Globals - { - var section_map: std.AutoArrayHashMapUnmanaged(String, void) = .empty; - defer section_map.deinit(self.gpa); - try section_map.ensureUnusedCapacity(self.gpa, globals.count()); - - for (self.variables.items) |variable| { - if (variable.global.getReplacement(self) != .none) continue; - - const section = blk: { - if (variable.section == .none) break :blk 0; - const gop = section_map.getOrPutAssumeCapacity(variable.section); - if (!gop.found_existing) { - try module_block.writeAbbrev(Module.String{ - .code = 5, - .string = variable.section.slice(self).?, - }); - } - break :blk gop.index + 1; - }; - - const initid = if (variable.init == .no_init) - 0 - else - (constant_adapter.getConstantIndex(variable.init) + 1); - - const strtab = variable.global.strtab(self); - - const global = variable.global.ptrConst(self); - try module_block.writeAbbrev(Module.Variable{ - .strtab_offset = strtab.offset, - .strtab_size = strtab.size, - .type_index = global.type, - .is_const = .{ - .is_const = switch (variable.mutability) { - .global => false, - .constant => true, - }, - .addr_space = global.addr_space, - }, - .initid = initid, - .linkage = global.linkage, - .alignment = variable.alignment.toLlvm(), - .section = section, - .visibility = global.visibility, - .thread_local = variable.thread_local, - .unnamed_addr = global.unnamed_addr, - .externally_initialized = global.externally_initialized, - .dllstorageclass = global.dll_storage_class, - .preemption = global.preemption, - }); - } - - for (self.functions.items) |func| { - if (func.global.getReplacement(self) != .none) continue; - - const section = blk: { - if (func.section == .none) break :blk 0; - const gop = section_map.getOrPutAssumeCapacity(func.section); - if (!gop.found_existing) { - try module_block.writeAbbrev(Module.String{ - .code = 5, - .string = func.section.slice(self).?, - }); - } - break :blk gop.index + 1; - }; - - const paramattr_index = if (self.function_attributes_set.getIndex(func.attributes)) |index| - index + 1 - else - 0; - - const strtab = func.global.strtab(self); - - const global = func.global.ptrConst(self); - try module_block.writeAbbrev(Module.Function{ - .strtab_offset = strtab.offset, - .strtab_size = strtab.size, - .type_index = global.type, - .call_conv = func.call_conv, - .is_proto = func.instructions.len == 0, - .linkage = global.linkage, - .paramattr = paramattr_index, - .alignment = func.alignment.toLlvm(), - .section = section, - .visibility = global.visibility, - .unnamed_addr = global.unnamed_addr, - .dllstorageclass = global.dll_storage_class, - .preemption = global.preemption, - .addr_space = global.addr_space, - }); - } - - for (self.aliases.items) |alias| { - if (alias.global.getReplacement(self) != .none) continue; - - const strtab = alias.global.strtab(self); - - const global = alias.global.ptrConst(self); - try module_block.writeAbbrev(Module.Alias{ - .strtab_offset = strtab.offset, - .strtab_size = strtab.size, - .type_index = global.type, - .addr_space = global.addr_space, - .aliasee = constant_adapter.getConstantIndex(alias.aliasee), - .linkage = global.linkage, - .visibility = global.visibility, - .thread_local = alias.thread_local, - .unnamed_addr = global.unnamed_addr, - .dllstorageclass = global.dll_storage_class, - .preemption = global.preemption, - }); - } - } - - // CONSTANTS_BLOCK - { - const Constants = ir.Constants; - var constants_block = try module_block.enterSubBlock(Constants, true); - - var current_type: Type = .none; - const tags = self.constant_items.items(.tag); - const datas = self.constant_items.items(.data); - for (0..self.constant_items.len) |index| { - record.clearRetainingCapacity(); - const constant: Constant = @enumFromInt(index); - const constant_type = constant.typeOf(self); - if (constant_type != current_type) { - try constants_block.writeAbbrev(Constants.SetType{ .type_id = constant_type }); - current_type = constant_type; - } - const data = datas[index]; - switch (tags[index]) { - .null, - .zeroinitializer, - .none, - => try constants_block.writeAbbrev(Constants.Null{}), - .undef => try constants_block.writeAbbrev(Constants.Undef{}), - .poison => try constants_block.writeAbbrev(Constants.Poison{}), - .positive_integer, - .negative_integer, - => |tag| { - const extra: *align(@alignOf(std.math.big.Limb)) Constant.Integer = - @ptrCast(self.constant_limbs.items[data..][0..Constant.Integer.limbs]); - const bigint: std.math.big.int.Const = .{ - .limbs = self.constant_limbs - .items[data + Constant.Integer.limbs ..][0..extra.limbs_len], - .positive = switch (tag) { - .positive_integer => true, - .negative_integer => false, - else => unreachable, - }, - }; - const bit_count = extra.type.scalarBits(self); - const val: i64 = if (bit_count <= 64) - bigint.toInt(i64) catch unreachable - else if (bigint.toInt(u64)) |val| - @bitCast(val) - else |_| { - const limbs = try record.addManyAsSlice( - self.gpa, - std.math.divCeil(u24, bit_count, 64) catch unreachable, - ); - bigint.writeTwosComplement(std.mem.sliceAsBytes(limbs), .little); - for (limbs) |*limb| { - const val = std.mem.littleToNative(i64, @bitCast(limb.*)); - limb.* = @bitCast(if (val >= 0) - val << 1 | 0 - else - -%val << 1 | 1); - } - try constants_block.writeUnabbrev(5, record.items); - continue; - }; - try constants_block.writeAbbrev(Constants.Integer{ - .value = @bitCast(if (val >= 0) - val << 1 | 0 - else - -%val << 1 | 1), - }); - }, - .half, - .bfloat, - => try constants_block.writeAbbrev(Constants.Half{ .value = @truncate(data) }), - .float => try constants_block.writeAbbrev(Constants.Float{ .value = data }), - .double => { - const extra = self.constantExtraData(Constant.Double, data); - try constants_block.writeAbbrev(Constants.Double{ - .value = (@as(u64, extra.hi) << 32) | extra.lo, - }); - }, - .x86_fp80 => { - const extra = self.constantExtraData(Constant.Fp80, data); - try constants_block.writeAbbrev(Constants.Fp80{ - .hi = @as(u64, extra.hi) << 48 | @as(u64, extra.lo_hi) << 16 | - extra.lo_lo >> 16, - .lo = @truncate(extra.lo_lo), - }); - }, - .fp128, - .ppc_fp128, - => { - const extra = self.constantExtraData(Constant.Fp128, data); - try constants_block.writeAbbrev(Constants.Fp128{ - .lo = @as(u64, extra.lo_hi) << 32 | @as(u64, extra.lo_lo), - .hi = @as(u64, extra.hi_hi) << 32 | @as(u64, extra.hi_lo), - }); - }, - .array, - .vector, - .structure, - .packed_structure, - => { - var extra = self.constantExtraDataTrail(Constant.Aggregate, data); - const len: u32 = @intCast(extra.data.type.aggregateLen(self)); - const values = extra.trail.next(len, Constant, self); - - try constants_block.writeAbbrevAdapted( - Constants.Aggregate{ .values = values }, - constant_adapter, - ); - }, - .splat => { - const ConstantsWriter = @TypeOf(constants_block); - const extra = self.constantExtraData(Constant.Splat, data); - const vector_len = extra.type.vectorLen(self); - const c = constant_adapter.getConstantIndex(extra.value); - - try bitcode.writeBits( - ConstantsWriter.abbrevId(Constants.Aggregate), - ConstantsWriter.abbrev_len, - ); - try bitcode.writeVBR(vector_len, 6); - for (0..vector_len) |_| { - try bitcode.writeBits(c, Constants.Aggregate.ops[1].array_fixed); - } - }, - .string => { - const str: String = @enumFromInt(data); - if (str == .none) { - try constants_block.writeAbbrev(Constants.Null{}); - } else { - const slice = str.slice(self).?; - if (slice.len > 0 and slice[slice.len - 1] == 0) - try constants_block.writeAbbrev(Constants.CString{ .string = slice[0 .. slice.len - 1] }) - else - try constants_block.writeAbbrev(Constants.String{ .string = slice }); - } - }, - .bitcast, - .inttoptr, - .ptrtoint, - .addrspacecast, - .trunc, - => |tag| { - const extra = self.constantExtraData(Constant.Cast, data); - try constants_block.writeAbbrevAdapted(Constants.Cast{ - .type_index = extra.type, - .val = extra.val, - .opcode = tag.toCastOpcode(), - }, constant_adapter); - }, - .add, - .@"add nsw", - .@"add nuw", - .sub, - .@"sub nsw", - .@"sub nuw", - .shl, - .xor, - => |tag| { - const extra = self.constantExtraData(Constant.Binary, data); - try constants_block.writeAbbrevAdapted(Constants.Binary{ - .opcode = tag.toBinaryOpcode(), - .lhs = extra.lhs, - .rhs = extra.rhs, - }, constant_adapter); - }, - .getelementptr, - .@"getelementptr inbounds", - => |tag| { - var extra = self.constantExtraDataTrail(Constant.GetElementPtr, data); - const indices = extra.trail.next(extra.data.info.indices_len, Constant, self); - try record.ensureUnusedCapacity(self.gpa, 1 + 2 + 2 * indices.len); - - record.appendAssumeCapacity(@intFromEnum(extra.data.type)); - - record.appendAssumeCapacity(@intFromEnum(extra.data.base.typeOf(self))); - record.appendAssumeCapacity(constant_adapter.getConstantIndex(extra.data.base)); - - for (indices) |i| { - record.appendAssumeCapacity(@intFromEnum(i.typeOf(self))); - record.appendAssumeCapacity(constant_adapter.getConstantIndex(i)); - } - - try constants_block.writeUnabbrev(switch (tag) { - .getelementptr => 12, - .@"getelementptr inbounds" => 20, - else => unreachable, - }, record.items); - }, - .@"asm", - .@"asm sideeffect", - .@"asm alignstack", - .@"asm sideeffect alignstack", - .@"asm inteldialect", - .@"asm sideeffect inteldialect", - .@"asm alignstack inteldialect", - .@"asm sideeffect alignstack inteldialect", - .@"asm unwind", - .@"asm sideeffect unwind", - .@"asm alignstack unwind", - .@"asm sideeffect alignstack unwind", - .@"asm inteldialect unwind", - .@"asm sideeffect inteldialect unwind", - .@"asm alignstack inteldialect unwind", - .@"asm sideeffect alignstack inteldialect unwind", - => |tag| { - const extra = self.constantExtraData(Constant.Assembly, data); - - const assembly_slice = extra.assembly.slice(self).?; - const constraints_slice = extra.constraints.slice(self).?; - - try record.ensureUnusedCapacity(self.gpa, 4 + assembly_slice.len + constraints_slice.len); - - record.appendAssumeCapacity(@intFromEnum(extra.type)); - record.appendAssumeCapacity(switch (tag) { - .@"asm" => 0, - .@"asm sideeffect" => 0b0001, - .@"asm sideeffect alignstack" => 0b0011, - .@"asm sideeffect inteldialect" => 0b0101, - .@"asm sideeffect alignstack inteldialect" => 0b0111, - .@"asm sideeffect unwind" => 0b1001, - .@"asm sideeffect alignstack unwind" => 0b1011, - .@"asm sideeffect inteldialect unwind" => 0b1101, - .@"asm sideeffect alignstack inteldialect unwind" => 0b1111, - .@"asm alignstack" => 0b0010, - .@"asm inteldialect" => 0b0100, - .@"asm alignstack inteldialect" => 0b0110, - .@"asm unwind" => 0b1000, - .@"asm alignstack unwind" => 0b1010, - .@"asm inteldialect unwind" => 0b1100, - .@"asm alignstack inteldialect unwind" => 0b1110, - else => unreachable, - }); - - record.appendAssumeCapacity(assembly_slice.len); - for (assembly_slice) |c| record.appendAssumeCapacity(c); - - record.appendAssumeCapacity(constraints_slice.len); - for (constraints_slice) |c| record.appendAssumeCapacity(c); - - try constants_block.writeUnabbrev(30, record.items); - }, - .blockaddress => { - const extra = self.constantExtraData(Constant.BlockAddress, data); - try constants_block.writeAbbrev(Constants.BlockAddress{ - .type_id = extra.function.typeOf(self), - .function = constant_adapter.getConstantIndex(extra.function.toConst(self)), - .block = @intFromEnum(extra.block), - }); - }, - .dso_local_equivalent, - .no_cfi, - => |tag| { - const function: Function.Index = @enumFromInt(data); - try constants_block.writeAbbrev(Constants.DsoLocalEquivalentOrNoCfi{ - .code = switch (tag) { - .dso_local_equivalent => 27, - .no_cfi => 29, - else => unreachable, - }, - .type_id = function.typeOf(self), - .function = constant_adapter.getConstantIndex(function.toConst(self)), - }); - }, - } - } - - try constants_block.end(); - } - - // METADATA_KIND_BLOCK - { - const MetadataKindBlock = ir.MetadataKindBlock; - var metadata_kind_block = try module_block.enterSubBlock(MetadataKindBlock, true); - - inline for (@typeInfo(ir.FixedMetadataKind).@"enum".fields) |field| { - // don't include `dbg` in stripped functions - if (!(self.strip and std.mem.eql(u8, field.name, "dbg"))) { - try metadata_kind_block.writeAbbrev(MetadataKindBlock.Kind{ - .id = field.value, - .name = field.name, - }); - } - } - - try metadata_kind_block.end(); - } - - const MetadataAdapter = struct { - builder: *const Builder, - constant_adapter: ConstantAdapter, - - pub fn init( - builder: *const Builder, - const_adapter: ConstantAdapter, - ) @This() { - return .{ - .builder = builder, - .constant_adapter = const_adapter, - }; - } - - pub fn get(adapter: @This(), value: anytype, comptime field_name: []const u8) @TypeOf(value) { - _ = field_name; - const Ty = @TypeOf(value); - return switch (Ty) { - Metadata => @enumFromInt(adapter.getMetadataIndex(value)), - MetadataString => @enumFromInt(adapter.getMetadataStringIndex(value)), - Constant => @enumFromInt(adapter.constant_adapter.getConstantIndex(value)), - else => value, - }; - } - - pub fn getMetadataIndex(adapter: @This(), metadata: Metadata) u32 { - if (metadata == .none) return 0; - return @intCast(adapter.builder.metadata_string_map.count() + - @intFromEnum(metadata.unwrap(adapter.builder)) - 1); - } - - pub fn getMetadataStringIndex(_: @This(), metadata_string: MetadataString) u32 { - return @intFromEnum(metadata_string); - } - }; - - const metadata_adapter = MetadataAdapter.init(self, constant_adapter); - - // METADATA_BLOCK - { - const MetadataBlock = ir.MetadataBlock; - var metadata_block = try module_block.enterSubBlock(MetadataBlock, true); - - const MetadataBlockWriter = @TypeOf(metadata_block); - - // Emit all MetadataStrings - if (self.metadata_string_map.count() > 1) { - const strings_offset, const strings_size = blk: { - var strings_offset: u32 = 0; - var strings_size: u32 = 0; - for (1..self.metadata_string_map.count()) |metadata_string_index| { - const metadata_string: MetadataString = @enumFromInt(metadata_string_index); - const slice = metadata_string.slice(self); - strings_offset += bitcode.bitsVBR(@as(u32, @intCast(slice.len)), 6); - strings_size += @intCast(slice.len * 8); - } - break :blk .{ - std.mem.alignForward(u32, strings_offset, 32) / 8, - std.mem.alignForward(u32, strings_size, 32) / 8, - }; - }; - - try bitcode.writeBits( - comptime MetadataBlockWriter.abbrevId(MetadataBlock.Strings), - MetadataBlockWriter.abbrev_len, - ); - - try bitcode.writeVBR(@as(u32, @intCast(self.metadata_string_map.count() - 1)), 6); - try bitcode.writeVBR(strings_offset, 6); - - try bitcode.writeVBR(strings_size + strings_offset, 6); - - try bitcode.alignTo32(); - - for (1..self.metadata_string_map.count()) |metadata_string_index| { - const metadata_string: MetadataString = @enumFromInt(metadata_string_index); - const slice = metadata_string.slice(self); - try bitcode.writeVBR(@as(u32, @intCast(slice.len)), 6); - } - - try bitcode.writeBlob(self.metadata_string_bytes.items); - } - - for ( - self.metadata_items.items(.tag)[1..], - self.metadata_items.items(.data)[1..], - ) |tag, data| { - record.clearRetainingCapacity(); - switch (tag) { - .none => unreachable, - .file => { - const extra = self.metadataExtraData(Metadata.File, data); - - try metadata_block.writeAbbrevAdapted(MetadataBlock.File{ - .filename = extra.filename, - .directory = extra.directory, - }, metadata_adapter); - }, - .compile_unit, - .@"compile_unit optimized", - => |kind| { - const extra = self.metadataExtraData(Metadata.CompileUnit, data); - try metadata_block.writeAbbrevAdapted(MetadataBlock.CompileUnit{ - .file = extra.file, - .producer = extra.producer, - .is_optimized = switch (kind) { - .compile_unit => false, - .@"compile_unit optimized" => true, - else => unreachable, - }, - .enums = extra.enums, - .globals = extra.globals, - }, metadata_adapter); - }, - .subprogram, - .@"subprogram local", - .@"subprogram definition", - .@"subprogram local definition", - .@"subprogram optimized", - .@"subprogram optimized local", - .@"subprogram optimized definition", - .@"subprogram optimized local definition", - => |kind| { - const extra = self.metadataExtraData(Metadata.Subprogram, data); - - try metadata_block.writeAbbrevAdapted(MetadataBlock.Subprogram{ - .scope = extra.file, - .name = extra.name, - .linkage_name = extra.linkage_name, - .file = extra.file, - .line = extra.line, - .ty = extra.ty, - .scope_line = extra.scope_line, - .sp_flags = @bitCast(@as(u32, @as(u3, @intCast( - @intFromEnum(kind) - @intFromEnum(Metadata.Tag.subprogram), - ))) << 2), - .flags = extra.di_flags, - .compile_unit = extra.compile_unit, - }, metadata_adapter); - }, - .lexical_block => { - const extra = self.metadataExtraData(Metadata.LexicalBlock, data); - try metadata_block.writeAbbrevAdapted(MetadataBlock.LexicalBlock{ - .scope = extra.scope, - .file = extra.file, - .line = extra.line, - .column = extra.column, - }, metadata_adapter); - }, - .location => { - const extra = self.metadataExtraData(Metadata.Location, data); - assert(extra.scope != .none); - try metadata_block.writeAbbrev(MetadataBlock.Location{ - .line = extra.line, - .column = extra.column, - .scope = metadata_adapter.getMetadataIndex(extra.scope) - 1, - .inlined_at = @enumFromInt(metadata_adapter.getMetadataIndex(extra.inlined_at)), - }); - }, - .basic_bool_type, - .basic_unsigned_type, - .basic_signed_type, - .basic_float_type, - => |kind| { - const extra = self.metadataExtraData(Metadata.BasicType, data); - try metadata_block.writeAbbrevAdapted(MetadataBlock.BasicType{ - .name = extra.name, - .size_in_bits = extra.bitSize(), - .encoding = switch (kind) { - .basic_bool_type => DW.ATE.boolean, - .basic_unsigned_type => DW.ATE.unsigned, - .basic_signed_type => DW.ATE.signed, - .basic_float_type => DW.ATE.float, - else => unreachable, - }, - }, metadata_adapter); - }, - .composite_struct_type, - .composite_union_type, - .composite_enumeration_type, - .composite_array_type, - .composite_vector_type, - => |kind| { - const extra = self.metadataExtraData(Metadata.CompositeType, data); - - try metadata_block.writeAbbrevAdapted(MetadataBlock.CompositeType{ - .tag = switch (kind) { - .composite_struct_type => DW.TAG.structure_type, - .composite_union_type => DW.TAG.union_type, - .composite_enumeration_type => DW.TAG.enumeration_type, - .composite_array_type, .composite_vector_type => DW.TAG.array_type, - else => unreachable, - }, - .name = extra.name, - .file = extra.file, - .line = extra.line, - .scope = extra.scope, - .underlying_type = extra.underlying_type, - .size_in_bits = extra.bitSize(), - .align_in_bits = extra.bitAlign(), - .flags = if (kind == .composite_vector_type) .{ .Vector = true } else .{}, - .elements = extra.fields_tuple, - }, metadata_adapter); - }, - .derived_pointer_type, - .derived_member_type, - => |kind| { - const extra = self.metadataExtraData(Metadata.DerivedType, data); - try metadata_block.writeAbbrevAdapted(MetadataBlock.DerivedType{ - .tag = switch (kind) { - .derived_pointer_type => DW.TAG.pointer_type, - .derived_member_type => DW.TAG.member, - else => unreachable, - }, - .name = extra.name, - .file = extra.file, - .line = extra.line, - .scope = extra.scope, - .underlying_type = extra.underlying_type, - .size_in_bits = extra.bitSize(), - .align_in_bits = extra.bitAlign(), - .offset_in_bits = extra.bitOffset(), - }, metadata_adapter); - }, - .subroutine_type => { - const extra = self.metadataExtraData(Metadata.SubroutineType, data); - - try metadata_block.writeAbbrevAdapted(MetadataBlock.SubroutineType{ - .types = extra.types_tuple, - }, metadata_adapter); - }, - .enumerator_unsigned, - .enumerator_signed_positive, - .enumerator_signed_negative, - => |kind| { - const extra = self.metadataExtraData(Metadata.Enumerator, data); - const bigint: std.math.big.int.Const = .{ - .limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len], - .positive = switch (kind) { - .enumerator_unsigned, - .enumerator_signed_positive, - => true, - .enumerator_signed_negative => false, - else => unreachable, - }, - }; - const flags: MetadataBlock.Enumerator.Flags = .{ - .unsigned = switch (kind) { - .enumerator_unsigned => true, - .enumerator_signed_positive, - .enumerator_signed_negative, - => false, - else => unreachable, - }, - }; - const val: i64 = if (bigint.toInt(i64)) |val| - val - else |_| if (bigint.toInt(u64)) |val| - @bitCast(val) - else |_| { - const limbs_len = std.math.divCeil(u32, extra.bit_width, 64) catch unreachable; - try record.ensureTotalCapacity(self.gpa, 3 + limbs_len); - record.appendAssumeCapacity(@as( - @typeInfo(MetadataBlock.Enumerator.Flags).@"struct".backing_integer.?, - @bitCast(flags), - )); - record.appendAssumeCapacity(extra.bit_width); - record.appendAssumeCapacity(metadata_adapter.getMetadataStringIndex(extra.name)); - const limbs = record.addManyAsSliceAssumeCapacity(limbs_len); - bigint.writeTwosComplement(std.mem.sliceAsBytes(limbs), .little); - for (limbs) |*limb| { - const val = std.mem.littleToNative(i64, @bitCast(limb.*)); - limb.* = @bitCast(if (val >= 0) - val << 1 | 0 - else - -%val << 1 | 1); - } - try metadata_block.writeUnabbrev(@intFromEnum(MetadataBlock.Enumerator.id), record.items); - continue; - }; - try metadata_block.writeAbbrevAdapted(MetadataBlock.Enumerator{ - .flags = flags, - .bit_width = extra.bit_width, - .name = extra.name, - .value = @bitCast(if (val >= 0) - val << 1 | 0 - else - -%val << 1 | 1), - }, metadata_adapter); - }, - .subrange => { - const extra = self.metadataExtraData(Metadata.Subrange, data); - - try metadata_block.writeAbbrevAdapted(MetadataBlock.Subrange{ - .count = extra.count, - .lower_bound = extra.lower_bound, - }, metadata_adapter); - }, - .expression => { - var extra = self.metadataExtraDataTrail(Metadata.Expression, data); - - const elements = extra.trail.next(extra.data.elements_len, u32, self); - - try metadata_block.writeAbbrevAdapted(MetadataBlock.Expression{ - .elements = elements, - }, metadata_adapter); - }, - .tuple => { - var extra = self.metadataExtraDataTrail(Metadata.Tuple, data); - - const elements = extra.trail.next(extra.data.elements_len, Metadata, self); - - try metadata_block.writeAbbrevAdapted(MetadataBlock.Node{ - .elements = elements, - }, metadata_adapter); - }, - .str_tuple => { - var extra = self.metadataExtraDataTrail(Metadata.StrTuple, data); - - const elements = extra.trail.next(extra.data.elements_len, Metadata, self); - - const all_elems = try self.gpa.alloc(Metadata, elements.len + 1); - defer self.gpa.free(all_elems); - all_elems[0] = @enumFromInt(metadata_adapter.getMetadataStringIndex(extra.data.str)); - for (elements, all_elems[1..]) |elem, *out_elem| { - out_elem.* = @enumFromInt(metadata_adapter.getMetadataIndex(elem)); - } - - try metadata_block.writeAbbrev(MetadataBlock.Node{ - .elements = all_elems, - }); - }, - .module_flag => { - const extra = self.metadataExtraData(Metadata.ModuleFlag, data); - try metadata_block.writeAbbrev(MetadataBlock.Node{ - .elements = &.{ - @enumFromInt(metadata_adapter.getMetadataIndex(extra.behavior)), - @enumFromInt(metadata_adapter.getMetadataStringIndex(extra.name)), - @enumFromInt(metadata_adapter.getMetadataIndex(extra.constant)), - }, - }); - }, - .local_var => { - const extra = self.metadataExtraData(Metadata.LocalVar, data); - try metadata_block.writeAbbrevAdapted(MetadataBlock.LocalVar{ - .scope = extra.scope, - .name = extra.name, - .file = extra.file, - .line = extra.line, - .ty = extra.ty, - }, metadata_adapter); - }, - .parameter => { - const extra = self.metadataExtraData(Metadata.Parameter, data); - try metadata_block.writeAbbrevAdapted(MetadataBlock.Parameter{ - .scope = extra.scope, - .name = extra.name, - .file = extra.file, - .line = extra.line, - .ty = extra.ty, - .arg = extra.arg_no, - }, metadata_adapter); - }, - .global_var, - .@"global_var local", - => |kind| { - const extra = self.metadataExtraData(Metadata.GlobalVar, data); - try metadata_block.writeAbbrevAdapted(MetadataBlock.GlobalVar{ - .scope = extra.scope, - .name = extra.name, - .linkage_name = extra.linkage_name, - .file = extra.file, - .line = extra.line, - .ty = extra.ty, - .local = kind == .@"global_var local", - }, metadata_adapter); - }, - .global_var_expression => { - const extra = self.metadataExtraData(Metadata.GlobalVarExpression, data); - try metadata_block.writeAbbrevAdapted(MetadataBlock.GlobalVarExpression{ - .variable = extra.variable, - .expression = extra.expression, - }, metadata_adapter); - }, - .constant => { - const constant: Constant = @enumFromInt(data); - try metadata_block.writeAbbrevAdapted(MetadataBlock.Constant{ - .ty = constant.typeOf(self), - .constant = constant, - }, metadata_adapter); - }, - } - } - - // Write named metadata - for (self.metadata_named.keys(), self.metadata_named.values()) |name, operands| { - const slice = name.slice(self); - try metadata_block.writeAbbrev(MetadataBlock.Name{ - .name = slice, - }); - - const elements = self.metadata_extra.items[operands.index..][0..operands.len]; - for (elements) |*e| { - e.* = metadata_adapter.getMetadataIndex(@enumFromInt(e.*)) - 1; - } - - try metadata_block.writeAbbrev(MetadataBlock.NamedNode{ - .elements = @ptrCast(elements), - }); - } - - // Write global attached metadata - { - for (globals.keys()) |global| { - const global_ptr = global.ptrConst(self); - if (global_ptr.dbg == .none) continue; - - switch (global_ptr.kind) { - .function => |f| if (f.ptrConst(self).instructions.len != 0) continue, - else => {}, - } - - try metadata_block.writeAbbrev(MetadataBlock.GlobalDeclAttachment{ - .value = @enumFromInt(constant_adapter.getConstantIndex(global.toConst())), - .kind = .dbg, - .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(global_ptr.dbg) - 1), - }); - } - } - - try metadata_block.end(); - } - - // OPERAND_BUNDLE_TAGS_BLOCK - { - const OperandBundleTags = ir.OperandBundleTags; - var operand_bundle_tags_block = try module_block.enterSubBlock(OperandBundleTags, true); - - try operand_bundle_tags_block.writeAbbrev(OperandBundleTags.OperandBundleTag{ - .tag = "cold", - }); - - try operand_bundle_tags_block.end(); - } - - // Block info - { - const BlockInfo = ir.BlockInfo; - var block_info_block = try module_block.enterSubBlock(BlockInfo, true); - - try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.FunctionBlock.id}); - inline for (ir.FunctionBlock.abbrevs) |abbrev| { - try block_info_block.defineAbbrev(&abbrev.ops); - } - - try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.FunctionValueSymbolTable.id}); - inline for (ir.FunctionValueSymbolTable.abbrevs) |abbrev| { - try block_info_block.defineAbbrev(&abbrev.ops); - } - - try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.FunctionMetadataBlock.id}); - inline for (ir.FunctionMetadataBlock.abbrevs) |abbrev| { - try block_info_block.defineAbbrev(&abbrev.ops); - } - - try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.MetadataAttachmentBlock.id}); - inline for (ir.MetadataAttachmentBlock.abbrevs) |abbrev| { - try block_info_block.defineAbbrev(&abbrev.ops); - } - - try block_info_block.end(); - } - - // FUNCTION_BLOCKS - { - const FunctionAdapter = struct { - constant_adapter: ConstantAdapter, - metadata_adapter: MetadataAdapter, - func: *const Function, - instruction_index: Function.Instruction.Index, - - pub fn get(adapter: @This(), value: anytype, comptime field_name: []const u8) @TypeOf(value) { - _ = field_name; - const Ty = @TypeOf(value); - return switch (Ty) { - Value => @enumFromInt(adapter.getOffsetValueIndex(value)), - Constant => @enumFromInt(adapter.getOffsetConstantIndex(value)), - FunctionAttributes => @enumFromInt(switch (value) { - .none => 0, - else => 1 + adapter.constant_adapter.builder.function_attributes_set.getIndex(value).?, - }), - else => value, - }; - } - - pub fn getValueIndex(adapter: @This(), value: Value) u32 { - return @intCast(switch (value.unwrap()) { - .instruction => |instruction| instruction.valueIndex(adapter.func) + adapter.firstInstr(), - .constant => |constant| adapter.constant_adapter.getConstantIndex(constant), - .metadata => |metadata| { - const real_metadata = metadata.unwrap(adapter.metadata_adapter.builder); - if (@intFromEnum(real_metadata) < Metadata.first_local_metadata) - return adapter.metadata_adapter.getMetadataIndex(real_metadata) - 1; - - return @intCast(@intFromEnum(metadata) - - Metadata.first_local_metadata + - adapter.metadata_adapter.builder.metadata_string_map.count() - 1 + - adapter.metadata_adapter.builder.metadata_map.count() - 1); - }, - }); - } - - pub fn getOffsetValueIndex(adapter: @This(), value: Value) u32 { - return adapter.offset() -% adapter.getValueIndex(value); - } - - pub fn getOffsetValueSignedIndex(adapter: @This(), value: Value) i32 { - const signed_offset: i32 = @intCast(adapter.offset()); - const signed_value: i32 = @intCast(adapter.getValueIndex(value)); - return signed_offset - signed_value; - } - - pub fn getOffsetConstantIndex(adapter: @This(), constant: Constant) u32 { - return adapter.offset() - adapter.constant_adapter.getConstantIndex(constant); - } - - pub fn offset(adapter: @This()) u32 { - return adapter.instruction_index.valueIndex(adapter.func) + adapter.firstInstr(); - } - - fn firstInstr(adapter: @This()) u32 { - return adapter.constant_adapter.numConstants(); - } - }; - - for (self.functions.items, 0..) |func, func_index| { - const FunctionBlock = ir.FunctionBlock; - if (func.global.getReplacement(self) != .none) continue; - - if (func.instructions.len == 0) continue; - - var function_block = try module_block.enterSubBlock(FunctionBlock, false); - - try function_block.writeAbbrev(FunctionBlock.DeclareBlocks{ .num_blocks = func.blocks.len }); - - var adapter: FunctionAdapter = .{ - .constant_adapter = constant_adapter, - .metadata_adapter = metadata_adapter, - .func = &func, - .instruction_index = @enumFromInt(0), - }; - - // Emit function level metadata block - if (!func.strip and func.debug_values.len > 0) { - const MetadataBlock = ir.FunctionMetadataBlock; - var metadata_block = try function_block.enterSubBlock(MetadataBlock, false); - - for (func.debug_values) |value| { - try metadata_block.writeAbbrev(MetadataBlock.Value{ - .ty = value.typeOf(@enumFromInt(func_index), self), - .value = @enumFromInt(adapter.getValueIndex(value.toValue())), - }); - } - - try metadata_block.end(); - } - - const tags = func.instructions.items(.tag); - const datas = func.instructions.items(.data); - - var has_location = false; - - var block_incoming_len: u32 = undefined; - for (tags, datas, 0..) |tag, data, instr_index| { - adapter.instruction_index = @enumFromInt(instr_index); - record.clearRetainingCapacity(); - - switch (tag) { - .arg => continue, - .block => { - block_incoming_len = data; - continue; - }, - .@"unreachable" => try function_block.writeAbbrev(FunctionBlock.Unreachable{}), - .call, - .@"musttail call", - .@"notail call", - .@"tail call", - => |kind| { - var extra = func.extraDataTrail(Function.Instruction.Call, data); - - if (extra.data.info.has_op_bundle_cold) { - try function_block.writeAbbrev(FunctionBlock.ColdOperandBundle{}); - } - - const call_conv = extra.data.info.call_conv; - const args = extra.trail.next(extra.data.args_len, Value, &func); - try function_block.writeAbbrevAdapted(FunctionBlock.Call{ - .attributes = extra.data.attributes, - .call_type = switch (kind) { - .call => .{ .call_conv = call_conv }, - .@"tail call" => .{ .tail = true, .call_conv = call_conv }, - .@"musttail call" => .{ .must_tail = true, .call_conv = call_conv }, - .@"notail call" => .{ .no_tail = true, .call_conv = call_conv }, - else => unreachable, - }, - .type_id = extra.data.ty, - .callee = extra.data.callee, - .args = args, - }, adapter); - }, - .@"call fast", - .@"musttail call fast", - .@"notail call fast", - .@"tail call fast", - => |kind| { - var extra = func.extraDataTrail(Function.Instruction.Call, data); - - if (extra.data.info.has_op_bundle_cold) { - try function_block.writeAbbrev(FunctionBlock.ColdOperandBundle{}); - } - - const call_conv = extra.data.info.call_conv; - const args = extra.trail.next(extra.data.args_len, Value, &func); - try function_block.writeAbbrevAdapted(FunctionBlock.CallFast{ - .attributes = extra.data.attributes, - .call_type = switch (kind) { - .@"call fast" => .{ .call_conv = call_conv }, - .@"tail call fast" => .{ .tail = true, .call_conv = call_conv }, - .@"musttail call fast" => .{ .must_tail = true, .call_conv = call_conv }, - .@"notail call fast" => .{ .no_tail = true, .call_conv = call_conv }, - else => unreachable, - }, - .fast_math = FastMath.fast, - .type_id = extra.data.ty, - .callee = extra.data.callee, - .args = args, - }, adapter); - }, - .add, - .@"and", - .fadd, - .fdiv, - .fmul, - .mul, - .frem, - .fsub, - .sdiv, - .sub, - .udiv, - .xor, - .shl, - .lshr, - .@"or", - .urem, - .srem, - .ashr, - => |kind| { - const extra = func.extraData(Function.Instruction.Binary, data); - try function_block.writeAbbrev(FunctionBlock.Binary{ - .opcode = kind.toBinaryOpcode(), - .lhs = adapter.getOffsetValueIndex(extra.lhs), - .rhs = adapter.getOffsetValueIndex(extra.rhs), - }); - }, - .@"sdiv exact", - .@"udiv exact", - .@"lshr exact", - .@"ashr exact", - => |kind| { - const extra = func.extraData(Function.Instruction.Binary, data); - try function_block.writeAbbrev(FunctionBlock.BinaryExact{ - .opcode = kind.toBinaryOpcode(), - .lhs = adapter.getOffsetValueIndex(extra.lhs), - .rhs = adapter.getOffsetValueIndex(extra.rhs), - }); - }, - .@"add nsw", - .@"add nuw", - .@"add nuw nsw", - .@"mul nsw", - .@"mul nuw", - .@"mul nuw nsw", - .@"sub nsw", - .@"sub nuw", - .@"sub nuw nsw", - .@"shl nsw", - .@"shl nuw", - .@"shl nuw nsw", - => |kind| { - const extra = func.extraData(Function.Instruction.Binary, data); - try function_block.writeAbbrev(FunctionBlock.BinaryNoWrap{ - .opcode = kind.toBinaryOpcode(), - .lhs = adapter.getOffsetValueIndex(extra.lhs), - .rhs = adapter.getOffsetValueIndex(extra.rhs), - .flags = switch (kind) { - .@"add nsw", - .@"mul nsw", - .@"sub nsw", - .@"shl nsw", - => .{ .no_unsigned_wrap = false, .no_signed_wrap = true }, - .@"add nuw", - .@"mul nuw", - .@"sub nuw", - .@"shl nuw", - => .{ .no_unsigned_wrap = true, .no_signed_wrap = false }, - .@"add nuw nsw", - .@"mul nuw nsw", - .@"sub nuw nsw", - .@"shl nuw nsw", - => .{ .no_unsigned_wrap = true, .no_signed_wrap = true }, - else => unreachable, - }, - }); - }, - .@"fadd fast", - .@"fdiv fast", - .@"fmul fast", - .@"frem fast", - .@"fsub fast", - => |kind| { - const extra = func.extraData(Function.Instruction.Binary, data); - try function_block.writeAbbrev(FunctionBlock.BinaryFast{ - .opcode = kind.toBinaryOpcode(), - .lhs = adapter.getOffsetValueIndex(extra.lhs), - .rhs = adapter.getOffsetValueIndex(extra.rhs), - .fast_math = FastMath.fast, - }); - }, - .alloca, - .@"alloca inalloca", - => |kind| { - const extra = func.extraData(Function.Instruction.Alloca, data); - const alignment = extra.info.alignment.toLlvm(); - try function_block.writeAbbrev(FunctionBlock.Alloca{ - .inst_type = extra.type, - .len_type = extra.len.typeOf(@enumFromInt(func_index), self), - .len_value = adapter.getValueIndex(extra.len), - .flags = .{ - .align_lower = @truncate(alignment), - .inalloca = kind == .@"alloca inalloca", - .explicit_type = true, - .swift_error = false, - .align_upper = @truncate(alignment << 5), - }, - }); - }, - .bitcast, - .inttoptr, - .ptrtoint, - .fptosi, - .fptoui, - .sitofp, - .uitofp, - .addrspacecast, - .fptrunc, - .trunc, - .fpext, - .sext, - .zext, - => |kind| { - const extra = func.extraData(Function.Instruction.Cast, data); - try function_block.writeAbbrev(FunctionBlock.Cast{ - .val = adapter.getOffsetValueIndex(extra.val), - .type_index = extra.type, - .opcode = kind.toCastOpcode(), - }); - }, - .@"fcmp false", - .@"fcmp oeq", - .@"fcmp oge", - .@"fcmp ogt", - .@"fcmp ole", - .@"fcmp olt", - .@"fcmp one", - .@"fcmp ord", - .@"fcmp true", - .@"fcmp ueq", - .@"fcmp uge", - .@"fcmp ugt", - .@"fcmp ule", - .@"fcmp ult", - .@"fcmp une", - .@"fcmp uno", - .@"icmp eq", - .@"icmp ne", - .@"icmp sge", - .@"icmp sgt", - .@"icmp sle", - .@"icmp slt", - .@"icmp uge", - .@"icmp ugt", - .@"icmp ule", - .@"icmp ult", - => |kind| { - const extra = func.extraData(Function.Instruction.Binary, data); - try function_block.writeAbbrev(FunctionBlock.Cmp{ - .lhs = adapter.getOffsetValueIndex(extra.lhs), - .rhs = adapter.getOffsetValueIndex(extra.rhs), - .pred = kind.toCmpPredicate(), - }); - }, - .@"fcmp fast false", - .@"fcmp fast oeq", - .@"fcmp fast oge", - .@"fcmp fast ogt", - .@"fcmp fast ole", - .@"fcmp fast olt", - .@"fcmp fast one", - .@"fcmp fast ord", - .@"fcmp fast true", - .@"fcmp fast ueq", - .@"fcmp fast uge", - .@"fcmp fast ugt", - .@"fcmp fast ule", - .@"fcmp fast ult", - .@"fcmp fast une", - .@"fcmp fast uno", - => |kind| { - const extra = func.extraData(Function.Instruction.Binary, data); - try function_block.writeAbbrev(FunctionBlock.CmpFast{ - .lhs = adapter.getOffsetValueIndex(extra.lhs), - .rhs = adapter.getOffsetValueIndex(extra.rhs), - .pred = kind.toCmpPredicate(), - .fast_math = FastMath.fast, - }); - }, - .fneg => try function_block.writeAbbrev(FunctionBlock.FNeg{ - .val = adapter.getOffsetValueIndex(@enumFromInt(data)), - }), - .@"fneg fast" => try function_block.writeAbbrev(FunctionBlock.FNegFast{ - .val = adapter.getOffsetValueIndex(@enumFromInt(data)), - .fast_math = FastMath.fast, - }), - .extractvalue => { - var extra = func.extraDataTrail(Function.Instruction.ExtractValue, data); - const indices = extra.trail.next(extra.data.indices_len, u32, &func); - try function_block.writeAbbrev(FunctionBlock.ExtractValue{ - .val = adapter.getOffsetValueIndex(extra.data.val), - .indices = indices, - }); - }, - .extractelement => { - const extra = func.extraData(Function.Instruction.ExtractElement, data); - try function_block.writeAbbrev(FunctionBlock.ExtractElement{ - .val = adapter.getOffsetValueIndex(extra.val), - .index = adapter.getOffsetValueIndex(extra.index), - }); - }, - .indirectbr => { - var extra = - func.extraDataTrail(Function.Instruction.IndirectBr, datas[instr_index]); - const targets = - extra.trail.next(extra.data.targets_len, Function.Block.Index, &func); - try function_block.writeAbbrevAdapted( - FunctionBlock.IndirectBr{ - .ty = extra.data.addr.typeOf(@enumFromInt(func_index), self), - .addr = extra.data.addr, - .targets = targets, - }, - adapter, - ); - }, - .insertelement => { - const extra = func.extraData(Function.Instruction.InsertElement, data); - try function_block.writeAbbrev(FunctionBlock.InsertElement{ - .val = adapter.getOffsetValueIndex(extra.val), - .elem = adapter.getOffsetValueIndex(extra.elem), - .index = adapter.getOffsetValueIndex(extra.index), - }); - }, - .insertvalue => { - var extra = func.extraDataTrail(Function.Instruction.InsertValue, datas[instr_index]); - const indices = extra.trail.next(extra.data.indices_len, u32, &func); - try function_block.writeAbbrev(FunctionBlock.InsertValue{ - .val = adapter.getOffsetValueIndex(extra.data.val), - .elem = adapter.getOffsetValueIndex(extra.data.elem), - .indices = indices, - }); - }, - .select => { - const extra = func.extraData(Function.Instruction.Select, data); - try function_block.writeAbbrev(FunctionBlock.Select{ - .lhs = adapter.getOffsetValueIndex(extra.lhs), - .rhs = adapter.getOffsetValueIndex(extra.rhs), - .cond = adapter.getOffsetValueIndex(extra.cond), - }); - }, - .@"select fast" => { - const extra = func.extraData(Function.Instruction.Select, data); - try function_block.writeAbbrev(FunctionBlock.SelectFast{ - .lhs = adapter.getOffsetValueIndex(extra.lhs), - .rhs = adapter.getOffsetValueIndex(extra.rhs), - .cond = adapter.getOffsetValueIndex(extra.cond), - .fast_math = FastMath.fast, - }); - }, - .shufflevector => { - const extra = func.extraData(Function.Instruction.ShuffleVector, data); - try function_block.writeAbbrev(FunctionBlock.ShuffleVector{ - .lhs = adapter.getOffsetValueIndex(extra.lhs), - .rhs = adapter.getOffsetValueIndex(extra.rhs), - .mask = adapter.getOffsetValueIndex(extra.mask), - }); - }, - .getelementptr, - .@"getelementptr inbounds", - => |kind| { - var extra = func.extraDataTrail(Function.Instruction.GetElementPtr, data); - const indices = extra.trail.next(extra.data.indices_len, Value, &func); - try function_block.writeAbbrevAdapted( - FunctionBlock.GetElementPtr{ - .is_inbounds = kind == .@"getelementptr inbounds", - .type_index = extra.data.type, - .base = extra.data.base, - .indices = indices, - }, - adapter, - ); - }, - .load => { - const extra = func.extraData(Function.Instruction.Load, data); - try function_block.writeAbbrev(FunctionBlock.Load{ - .ptr = adapter.getOffsetValueIndex(extra.ptr), - .ty = extra.type, - .alignment = extra.info.alignment.toLlvm(), - .is_volatile = extra.info.access_kind == .@"volatile", - }); - }, - .@"load atomic" => { - const extra = func.extraData(Function.Instruction.Load, data); - try function_block.writeAbbrev(FunctionBlock.LoadAtomic{ - .ptr = adapter.getOffsetValueIndex(extra.ptr), - .ty = extra.type, - .alignment = extra.info.alignment.toLlvm(), - .is_volatile = extra.info.access_kind == .@"volatile", - .success_ordering = extra.info.success_ordering, - .sync_scope = extra.info.sync_scope, - }); - }, - .store => { - const extra = func.extraData(Function.Instruction.Store, data); - try function_block.writeAbbrev(FunctionBlock.Store{ - .ptr = adapter.getOffsetValueIndex(extra.ptr), - .val = adapter.getOffsetValueIndex(extra.val), - .alignment = extra.info.alignment.toLlvm(), - .is_volatile = extra.info.access_kind == .@"volatile", - }); - }, - .@"store atomic" => { - const extra = func.extraData(Function.Instruction.Store, data); - try function_block.writeAbbrev(FunctionBlock.StoreAtomic{ - .ptr = adapter.getOffsetValueIndex(extra.ptr), - .val = adapter.getOffsetValueIndex(extra.val), - .alignment = extra.info.alignment.toLlvm(), - .is_volatile = extra.info.access_kind == .@"volatile", - .success_ordering = extra.info.success_ordering, - .sync_scope = extra.info.sync_scope, - }); - }, - .br => { - try function_block.writeAbbrev(FunctionBlock.BrUnconditional{ - .block = data, - }); - }, - .br_cond => { - const extra = func.extraData(Function.Instruction.BrCond, data); - try function_block.writeAbbrev(FunctionBlock.BrConditional{ - .then_block = @intFromEnum(extra.then), - .else_block = @intFromEnum(extra.@"else"), - .condition = adapter.getOffsetValueIndex(extra.cond), - }); - }, - .@"switch" => { - var extra = func.extraDataTrail(Function.Instruction.Switch, data); - - try record.ensureUnusedCapacity(self.gpa, 3 + extra.data.cases_len * 2); - - // Conditional type - record.appendAssumeCapacity(@intFromEnum(extra.data.val.typeOf(@enumFromInt(func_index), self))); - - // Conditional - record.appendAssumeCapacity(adapter.getOffsetValueIndex(extra.data.val)); - - // Default block - record.appendAssumeCapacity(@intFromEnum(extra.data.default)); - - const vals = extra.trail.next(extra.data.cases_len, Constant, &func); - const blocks = extra.trail.next(extra.data.cases_len, Function.Block.Index, &func); - for (vals, blocks) |val, block| { - record.appendAssumeCapacity(adapter.constant_adapter.getConstantIndex(val)); - record.appendAssumeCapacity(@intFromEnum(block)); - } - - try function_block.writeUnabbrev(12, record.items); - }, - .va_arg => { - const extra = func.extraData(Function.Instruction.VaArg, data); - try function_block.writeAbbrev(FunctionBlock.VaArg{ - .list_type = extra.list.typeOf(@enumFromInt(func_index), self), - .list = adapter.getOffsetValueIndex(extra.list), - .type = extra.type, - }); - }, - .phi, - .@"phi fast", - => |kind| { - var extra = func.extraDataTrail(Function.Instruction.Phi, data); - const vals = extra.trail.next(block_incoming_len, Value, &func); - const blocks = extra.trail.next(block_incoming_len, Function.Block.Index, &func); - - try record.ensureUnusedCapacity( - self.gpa, - 1 + block_incoming_len * 2 + @intFromBool(kind == .@"phi fast"), - ); - - record.appendAssumeCapacity(@intFromEnum(extra.data.type)); - - for (vals, blocks) |val, block| { - const offset_value = adapter.getOffsetValueSignedIndex(val); - const abs_value: u32 = @intCast(@abs(offset_value)); - const signed_vbr = if (offset_value > 0) abs_value << 1 else ((abs_value << 1) | 1); - record.appendAssumeCapacity(signed_vbr); - record.appendAssumeCapacity(@intFromEnum(block)); - } - - if (kind == .@"phi fast") record.appendAssumeCapacity(@as(u8, @bitCast(FastMath{}))); - - try function_block.writeUnabbrev(16, record.items); - }, - .ret => try function_block.writeAbbrev(FunctionBlock.Ret{ - .val = adapter.getOffsetValueIndex(@enumFromInt(data)), - }), - .@"ret void" => try function_block.writeAbbrev(FunctionBlock.RetVoid{}), - .atomicrmw => { - const extra = func.extraData(Function.Instruction.AtomicRmw, data); - try function_block.writeAbbrev(FunctionBlock.AtomicRmw{ - .ptr = adapter.getOffsetValueIndex(extra.ptr), - .val = adapter.getOffsetValueIndex(extra.val), - .operation = extra.info.atomic_rmw_operation, - .is_volatile = extra.info.access_kind == .@"volatile", - .success_ordering = extra.info.success_ordering, - .sync_scope = extra.info.sync_scope, - .alignment = extra.info.alignment.toLlvm(), - }); - }, - .cmpxchg, - .@"cmpxchg weak", - => |kind| { - const extra = func.extraData(Function.Instruction.CmpXchg, data); - - try function_block.writeAbbrev(FunctionBlock.CmpXchg{ - .ptr = adapter.getOffsetValueIndex(extra.ptr), - .cmp = adapter.getOffsetValueIndex(extra.cmp), - .new = adapter.getOffsetValueIndex(extra.new), - .is_volatile = extra.info.access_kind == .@"volatile", - .success_ordering = extra.info.success_ordering, - .sync_scope = extra.info.sync_scope, - .failure_ordering = extra.info.failure_ordering, - .is_weak = kind == .@"cmpxchg weak", - .alignment = extra.info.alignment.toLlvm(), - }); - }, - .fence => { - const info: MemoryAccessInfo = @bitCast(data); - try function_block.writeAbbrev(FunctionBlock.Fence{ - .ordering = info.success_ordering, - .sync_scope = info.sync_scope, - }); - }, - } - - if (!func.strip) { - if (func.debug_locations.get(adapter.instruction_index)) |debug_location| { - switch (debug_location) { - .no_location => has_location = false, - .location => |location| { - try function_block.writeAbbrev(FunctionBlock.DebugLoc{ - .line = location.line, - .column = location.column, - .scope = @enumFromInt(metadata_adapter.getMetadataIndex(location.scope)), - .inlined_at = @enumFromInt(metadata_adapter.getMetadataIndex(location.inlined_at)), - }); - has_location = true; - }, - } - } else if (has_location) { - try function_block.writeAbbrev(FunctionBlock.DebugLocAgain{}); - } - } - } - - // VALUE_SYMTAB - if (!func.strip) { - const ValueSymbolTable = ir.FunctionValueSymbolTable; - - var value_symtab_block = try function_block.enterSubBlock(ValueSymbolTable, false); - - for (func.blocks, 0..) |block, block_index| { - const name = block.instruction.name(&func); - - if (name == .none or name == .empty) continue; - - try value_symtab_block.writeAbbrev(ValueSymbolTable.BlockEntry{ - .value_id = @intCast(block_index), - .string = name.slice(self).?, - }); - } - - // TODO: Emit non block entries if the builder ever starts assigning names to non blocks - - try value_symtab_block.end(); - } - - // METADATA_ATTACHMENT_BLOCK - { - const MetadataAttachmentBlock = ir.MetadataAttachmentBlock; - var metadata_attach_block = try function_block.enterSubBlock(MetadataAttachmentBlock, false); - - dbg: { - if (func.strip) break :dbg; - const dbg = func.global.ptrConst(self).dbg; - if (dbg == .none) break :dbg; - try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentGlobalSingle{ - .kind = .dbg, - .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(dbg) - 1), - }); - } - - var instr_index: u32 = 0; - for (func.instructions.items(.tag), func.instructions.items(.data)) |instr_tag, data| switch (instr_tag) { - .arg, .block => {}, // not an actual instruction - else => { - instr_index += 1; - }, - .br_cond, .@"switch" => { - const weights = switch (instr_tag) { - .br_cond => func.extraData(Function.Instruction.BrCond, data).weights, - .@"switch" => func.extraData(Function.Instruction.Switch, data).weights, - else => unreachable, - }; - switch (weights) { - .none => {}, - .unpredictable => try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentInstructionSingle{ - .inst = instr_index, - .kind = .unpredictable, - .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(.empty_tuple) - 1), - }), - _ => try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentInstructionSingle{ - .inst = instr_index, - .kind = .prof, - .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(@enumFromInt(@intFromEnum(weights))) - 1), - }), - } - instr_index += 1; - }, - }; - - try metadata_attach_block.end(); - } - - try function_block.end(); - } - } - - try module_block.end(); - } - - // STRTAB_BLOCK - { - const Strtab = ir.Strtab; - var strtab_block = try bitcode.enterTopBlock(Strtab); - - try strtab_block.writeAbbrev(Strtab.Blob{ .blob = self.strtab_string_bytes.items }); - - try strtab_block.end(); - } - - return bitcode.toOwnedSlice(); -} - -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; -const bitcode_writer = @import("bitcode_writer.zig"); -const build_options = @import("build_options"); -const Builder = @This(); -const builtin = @import("builtin"); -const DW = std.dwarf; -const ir = @import("ir.zig"); -const log = std.log.scoped(.llvm); -const std = @import("std"); diff --git a/src/codegen/llvm/bitcode_writer.zig b/src/codegen/llvm/bitcode_writer.zig deleted file mode 100644 index 049a15fe179c22054b00ee98b94097a51fdb6886..0000000000000000000000000000000000000000 --- a/src/codegen/llvm/bitcode_writer.zig +++ /dev/null @@ -1,433 +0,0 @@ -const std = @import("std"); - -pub const AbbrevOp = union(enum) { - literal: u32, // 0 - fixed: u16, // 1 - fixed_runtime: type, // 1 - vbr: u16, // 2 - char6: void, // 4 - blob: void, // 5 - array_fixed: u16, // 3, 1 - array_fixed_runtime: type, // 3, 1 - array_vbr: u16, // 3, 2 - array_char6: void, // 3, 4 -}; - -pub const Error = error{OutOfMemory}; - -pub fn BitcodeWriter(comptime types: []const type) type { - return struct { - const BcWriter = @This(); - - buffer: std.ArrayList(u32), - bit_buffer: u32 = 0, - bit_count: u5 = 0, - - widths: [types.len]u16, - - pub fn getTypeWidth(self: BcWriter, comptime Type: type) u16 { - return self.widths[comptime std.mem.indexOfScalar(type, types, Type).?]; - } - - pub fn init(allocator: std.mem.Allocator, widths: [types.len]u16) BcWriter { - return .{ - .buffer = std.ArrayList(u32).init(allocator), - .widths = widths, - }; - } - - pub fn deinit(self: BcWriter) void { - self.buffer.deinit(); - } - - pub fn toOwnedSlice(self: *BcWriter) Error![]const u32 { - std.debug.assert(self.bit_count == 0); - return self.buffer.toOwnedSlice(); - } - - pub fn length(self: BcWriter) usize { - std.debug.assert(self.bit_count == 0); - return self.buffer.items.len; - } - - pub fn writeBits(self: *BcWriter, value: anytype, bits: u16) Error!void { - if (bits == 0) return; - - var in_buffer = bufValue(value, 32); - var in_bits = bits; - - // Store input bits in buffer if they fit otherwise store as many as possible and flush - if (self.bit_count > 0) { - const bits_remaining = 31 - self.bit_count + 1; - const n: u5 = @intCast(@min(bits_remaining, in_bits)); - const v = @as(u32, @truncate(in_buffer)) << self.bit_count; - self.bit_buffer |= v; - in_buffer >>= n; - - self.bit_count +%= n; - in_bits -= n; - - if (self.bit_count != 0) return; - try self.buffer.append(self.bit_buffer); - self.bit_buffer = 0; - } - - // Write 32-bit chunks of input bits - while (in_bits >= 32) { - try self.buffer.append(@truncate(in_buffer)); - - in_buffer >>= 31; - in_buffer >>= 1; - in_bits -= 32; - } - - // Store remaining input bits in buffer - if (in_bits > 0) { - self.bit_count = @intCast(in_bits); - self.bit_buffer = @truncate(in_buffer); - } - } - - pub fn writeVBR(self: *BcWriter, value: anytype, comptime vbr_bits: usize) Error!void { - comptime { - std.debug.assert(vbr_bits > 1); - if (@bitSizeOf(@TypeOf(value)) > 64) @compileError("Unsupported VBR block type: " ++ @typeName(@TypeOf(value))); - } - - var in_buffer = bufValue(value, vbr_bits); - - const continue_bit = @as(@TypeOf(in_buffer), 1) << @intCast(vbr_bits - 1); - const mask = continue_bit - 1; - - // If input is larger than one VBR block can store - // then store vbr_bits - 1 bits and a continue bit - while (in_buffer > mask) { - try self.writeBits(in_buffer & mask | continue_bit, vbr_bits); - in_buffer >>= @intCast(vbr_bits - 1); - } - - // Store remaining bits - try self.writeBits(in_buffer, vbr_bits); - } - - pub fn bitsVBR(_: *const BcWriter, value: anytype, comptime vbr_bits: usize) u16 { - comptime { - std.debug.assert(vbr_bits > 1); - if (@bitSizeOf(@TypeOf(value)) > 64) @compileError("Unsupported VBR block type: " ++ @typeName(@TypeOf(value))); - } - - var bits: u16 = 0; - - var in_buffer = bufValue(value, vbr_bits); - - const continue_bit = @as(@TypeOf(in_buffer), 1) << @intCast(vbr_bits - 1); - const mask = continue_bit - 1; - - // If input is larger than one VBR block can store - // then store vbr_bits - 1 bits and a continue bit - while (in_buffer > mask) { - bits += @intCast(vbr_bits); - in_buffer >>= @intCast(vbr_bits - 1); - } - - // Store remaining bits - bits += @intCast(vbr_bits); - return bits; - } - - pub fn write6BitChar(self: *BcWriter, c: u8) Error!void { - try self.writeBits(charTo6Bit(c), 6); - } - - pub fn writeBlob(self: *BcWriter, blob: []const u8) Error!void { - const blob_word_size = std.mem.alignForward(usize, blob.len, 4); - try self.buffer.ensureUnusedCapacity(blob_word_size + 1); - self.alignTo32() catch unreachable; - - const slice = self.buffer.addManyAsSliceAssumeCapacity(blob_word_size / 4); - const slice_bytes = std.mem.sliceAsBytes(slice); - @memcpy(slice_bytes[0..blob.len], blob); - @memset(slice_bytes[blob.len..], 0); - } - - pub fn alignTo32(self: *BcWriter) Error!void { - if (self.bit_count == 0) return; - - try self.buffer.append(self.bit_buffer); - self.bit_buffer = 0; - self.bit_count = 0; - } - - pub fn enterTopBlock(self: *BcWriter, comptime SubBlock: type) Error!BlockWriter(SubBlock) { - return BlockWriter(SubBlock).init(self, 2, true); - } - - fn BlockWriter(comptime Block: type) type { - return struct { - const Self = @This(); - - // The minimum abbrev id length based on the number of abbrevs present in the block - pub const abbrev_len = std.math.log2_int_ceil( - u6, - 4 + (if (@hasDecl(Block, "abbrevs")) Block.abbrevs.len else 0), - ); - - start: usize, - bitcode: *BcWriter, - - pub fn init(bitcode: *BcWriter, comptime parent_abbrev_len: u6, comptime define_abbrevs: bool) Error!Self { - try bitcode.writeBits(1, parent_abbrev_len); - try bitcode.writeVBR(Block.id, 8); - try bitcode.writeVBR(abbrev_len, 4); - try bitcode.alignTo32(); - - // We store the index of the block size and store a dummy value as the number of words in the block - const start = bitcode.length(); - try bitcode.writeBits(0, 32); - - var self = Self{ - .start = start, - .bitcode = bitcode, - }; - - // Predefine all block abbrevs - if (define_abbrevs) { - inline for (Block.abbrevs) |Abbrev| { - try self.defineAbbrev(&Abbrev.ops); - } - } - - return self; - } - - pub fn enterSubBlock(self: Self, comptime SubBlock: type, comptime define_abbrevs: bool) Error!BlockWriter(SubBlock) { - return BlockWriter(SubBlock).init(self.bitcode, abbrev_len, define_abbrevs); - } - - pub fn end(self: *Self) Error!void { - try self.bitcode.writeBits(0, abbrev_len); - try self.bitcode.alignTo32(); - - // Set the number of words in the block at the start of the block - self.bitcode.buffer.items[self.start] = @truncate(self.bitcode.length() - self.start - 1); - } - - pub fn writeUnabbrev(self: *Self, code: u32, values: []const u64) Error!void { - try self.bitcode.writeBits(3, abbrev_len); - try self.bitcode.writeVBR(code, 6); - try self.bitcode.writeVBR(values.len, 6); - for (values) |val| { - try self.bitcode.writeVBR(val, 6); - } - } - - pub fn writeAbbrev(self: *Self, params: anytype) Error!void { - return self.writeAbbrevAdapted(params, struct { - pub fn get(_: @This(), param: anytype, comptime _: []const u8) @TypeOf(param) { - return param; - } - }{}); - } - - pub fn abbrevId(comptime Abbrev: type) u32 { - inline for (Block.abbrevs, 0..) |abbrev, i| { - if (Abbrev == abbrev) return i + 4; - } - - @compileError("Unknown abbrev: " ++ @typeName(Abbrev)); - } - - pub fn writeAbbrevAdapted( - self: *Self, - params: anytype, - adapter: anytype, - ) Error!void { - const Abbrev = @TypeOf(params); - - try self.bitcode.writeBits(comptime abbrevId(Abbrev), abbrev_len); - - const fields = std.meta.fields(Abbrev); - - // This abbreviation might only contain literals - if (fields.len == 0) return; - - comptime var field_index: usize = 0; - inline for (Abbrev.ops) |ty| { - const field_name = fields[field_index].name; - const param = @field(params, field_name); - - switch (ty) { - .literal => continue, - .fixed => |len| try self.bitcode.writeBits(adapter.get(param, field_name), len), - .fixed_runtime => |width_ty| try self.bitcode.writeBits( - adapter.get(param, field_name), - self.bitcode.getTypeWidth(width_ty), - ), - .vbr => |len| try self.bitcode.writeVBR(adapter.get(param, field_name), len), - .char6 => try self.bitcode.write6BitChar(adapter.get(param, field_name)), - .blob => { - try self.bitcode.writeVBR(param.len, 6); - try self.bitcode.writeBlob(param); - }, - .array_fixed => |len| { - try self.bitcode.writeVBR(param.len, 6); - for (param) |x| { - try self.bitcode.writeBits(adapter.get(x, field_name), len); - } - }, - .array_fixed_runtime => |width_ty| { - try self.bitcode.writeVBR(param.len, 6); - for (param) |x| { - try self.bitcode.writeBits( - adapter.get(x, field_name), - self.bitcode.getTypeWidth(width_ty), - ); - } - }, - .array_vbr => |len| { - try self.bitcode.writeVBR(param.len, 6); - for (param) |x| { - try self.bitcode.writeVBR(adapter.get(x, field_name), len); - } - }, - .array_char6 => { - try self.bitcode.writeVBR(param.len, 6); - for (param) |x| { - try self.bitcode.write6BitChar(adapter.get(x, field_name)); - } - }, - } - field_index += 1; - if (field_index == fields.len) break; - } - } - - pub fn defineAbbrev(self: *Self, comptime ops: []const AbbrevOp) Error!void { - const bitcode = self.bitcode; - try bitcode.writeBits(2, abbrev_len); - - // ops.len is not accurate because arrays are actually two ops - try bitcode.writeVBR(blk: { - var count: usize = 0; - inline for (ops) |op| { - count += switch (op) { - .literal, .fixed, .fixed_runtime, .vbr, .char6, .blob => 1, - .array_fixed, .array_fixed_runtime, .array_vbr, .array_char6 => 2, - }; - } - break :blk count; - }, 5); - - inline for (ops) |op| { - switch (op) { - .literal => |value| { - try bitcode.writeBits(1, 1); - try bitcode.writeVBR(value, 8); - }, - .fixed => |width| { - try bitcode.writeBits(0, 1); - try bitcode.writeBits(1, 3); - try bitcode.writeVBR(width, 5); - }, - .fixed_runtime => |width_ty| { - try bitcode.writeBits(0, 1); - try bitcode.writeBits(1, 3); - try bitcode.writeVBR(bitcode.getTypeWidth(width_ty), 5); - }, - .vbr => |width| { - try bitcode.writeBits(0, 1); - try bitcode.writeBits(2, 3); - try bitcode.writeVBR(width, 5); - }, - .char6 => { - try bitcode.writeBits(0, 1); - try bitcode.writeBits(4, 3); - }, - .blob => { - try bitcode.writeBits(0, 1); - try bitcode.writeBits(5, 3); - }, - .array_fixed => |width| { - // Array op - try bitcode.writeBits(0, 1); - try bitcode.writeBits(3, 3); - - // Fixed or VBR op - try bitcode.writeBits(0, 1); - try bitcode.writeBits(1, 3); - try bitcode.writeVBR(width, 5); - }, - .array_fixed_runtime => |width_ty| { - // Array op - try bitcode.writeBits(0, 1); - try bitcode.writeBits(3, 3); - - // Fixed or VBR op - try bitcode.writeBits(0, 1); - try bitcode.writeBits(1, 3); - try bitcode.writeVBR(bitcode.getTypeWidth(width_ty), 5); - }, - .array_vbr => |width| { - // Array op - try bitcode.writeBits(0, 1); - try bitcode.writeBits(3, 3); - - // Fixed or VBR op - try bitcode.writeBits(0, 1); - try bitcode.writeBits(2, 3); - try bitcode.writeVBR(width, 5); - }, - .array_char6 => { - // Array op - try bitcode.writeBits(0, 1); - try bitcode.writeBits(3, 3); - - // Char6 op - try bitcode.writeBits(0, 1); - try bitcode.writeBits(4, 3); - }, - } - } - } - }; - } - }; -} - -fn charTo6Bit(c: u8) u8 { - return switch (c) { - 'a'...'z' => c - 'a', - 'A'...'Z' => c - 'A' + 26, - '0'...'9' => c - '0' + 52, - '.' => 62, - '_' => 63, - else => @panic("Failed to encode byte as 6-bit char"), - }; -} - -fn BufType(comptime T: type, comptime min_len: usize) type { - return std.meta.Int(.unsigned, @max(min_len, @bitSizeOf(switch (@typeInfo(T)) { - .comptime_int => u32, - .int => |info| if (info.signedness == .unsigned) - T - else - @compileError("Unsupported type: " ++ @typeName(T)), - .@"enum" => |info| info.tag_type, - .bool => u1, - .@"struct" => |info| switch (info.layout) { - .auto, .@"extern" => @compileError("Unsupported type: " ++ @typeName(T)), - .@"packed" => std.meta.Int(.unsigned, @bitSizeOf(T)), - }, - else => @compileError("Unsupported type: " ++ @typeName(T)), - }))); -} - -fn bufValue(value: anytype, comptime min_len: usize) BufType(@TypeOf(value), min_len) { - return switch (@typeInfo(@TypeOf(value))) { - .comptime_int, .int => @intCast(value), - .@"enum" => @intFromEnum(value), - .bool => @intFromBool(value), - .@"struct" => @intCast(@as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(value))), @bitCast(value))), - else => unreachable, - }; -} diff --git a/src/codegen/llvm/ir.zig b/src/codegen/llvm/ir.zig deleted file mode 100644 index 271e87c9959c3b84487eaeda5c5190adfe611535..0000000000000000000000000000000000000000 --- a/src/codegen/llvm/ir.zig +++ /dev/null @@ -1,1862 +0,0 @@ -const std = @import("std"); -const Builder = @import("Builder.zig"); -const bitcode_writer = @import("bitcode_writer.zig"); - -const AbbrevOp = bitcode_writer.AbbrevOp; - -pub const MAGIC: u32 = 0xdec04342; - -const ValueAbbrev = AbbrevOp{ .vbr = 6 }; -const ValueArrayAbbrev = AbbrevOp{ .array_vbr = 6 }; - -const ConstantAbbrev = AbbrevOp{ .vbr = 6 }; -const ConstantArrayAbbrev = AbbrevOp{ .array_vbr = 6 }; - -const MetadataAbbrev = AbbrevOp{ .vbr = 16 }; -const MetadataArrayAbbrev = AbbrevOp{ .array_vbr = 16 }; - -const LineAbbrev = AbbrevOp{ .vbr = 8 }; -const ColumnAbbrev = AbbrevOp{ .vbr = 8 }; - -const BlockAbbrev = AbbrevOp{ .vbr = 6 }; -const BlockArrayAbbrev = AbbrevOp{ .array_vbr = 6 }; - -/// Unused tags are commented out so that they are omitted in the generated -/// bitcode, which scans over this enum using reflection. -pub const FixedMetadataKind = enum(u8) { - dbg = 0, - //tbaa = 1, - prof = 2, - //fpmath = 3, - //range = 4, - //@"tbaa.struct" = 5, - //@"invariant.load" = 6, - //@"alias.scope" = 7, - //@"noalias" = 8, - //nontemporal = 9, - //@"llvm.mem.parallel_loop_access" = 10, - //nonnull = 11, - //dereferenceable = 12, - //dereferenceable_or_null = 13, - //@"make.implicit" = 14, - unpredictable = 15, - //@"invariant.group" = 16, - //@"align" = 17, - //@"llvm.loop" = 18, - //type = 19, - //section_prefix = 20, - //absolute_symbol = 21, - //associated = 22, - //callees = 23, - //irr_loop = 24, - //@"llvm.access.group" = 25, - //callback = 26, - //@"llvm.preserve.access.index" = 27, - //vcall_visibility = 28, - //noundef = 29, - //annotation = 30, - //nosanitize = 31, - //func_sanitize = 32, - //exclude = 33, - //memprof = 34, - //callsite = 35, - //kcfi_type = 36, - //pcsections = 37, - //DIAssignID = 38, - //@"coro.outside.frame" = 39, -}; - -pub const MetadataCode = enum(u8) { - /// MDSTRING: [values] - STRING_OLD = 1, - /// VALUE: [type num, value num] - VALUE = 2, - /// NODE: [n x md num] - NODE = 3, - /// STRING: [values] - NAME = 4, - /// DISTINCT_NODE: [n x md num] - DISTINCT_NODE = 5, - /// [n x [id, name]] - KIND = 6, - /// [distinct, line, col, scope, inlined-at?] - LOCATION = 7, - /// OLD_NODE: [n x (type num, value num)] - OLD_NODE = 8, - /// OLD_FN_NODE: [n x (type num, value num)] - OLD_FN_NODE = 9, - /// NAMED_NODE: [n x mdnodes] - NAMED_NODE = 10, - /// [m x [value, [n x [id, mdnode]]] - ATTACHMENT = 11, - /// [distinct, tag, vers, header, n x md num] - GENERIC_DEBUG = 12, - /// [distinct, count, lo] - SUBRANGE = 13, - /// [isUnsigned|distinct, value, name] - ENUMERATOR = 14, - /// [distinct, tag, name, size, align, enc] - BASIC_TYPE = 15, - /// [distinct, filename, directory, checksumkind, checksum] - FILE = 16, - /// [distinct, ...] - DERIVED_TYPE = 17, - /// [distinct, ...] - COMPOSITE_TYPE = 18, - /// [distinct, flags, types, cc] - SUBROUTINE_TYPE = 19, - /// [distinct, ...] - COMPILE_UNIT = 20, - /// [distinct, ...] - SUBPROGRAM = 21, - /// [distinct, scope, file, line, column] - LEXICAL_BLOCK = 22, - ///[distinct, scope, file, discriminator] - LEXICAL_BLOCK_FILE = 23, - /// [distinct, scope, file, name, line, exportSymbols] - NAMESPACE = 24, - /// [distinct, scope, name, type, ...] - TEMPLATE_TYPE = 25, - /// [distinct, scope, name, type, value, ...] - TEMPLATE_VALUE = 26, - /// [distinct, ...] - GLOBAL_VAR = 27, - /// [distinct, ...] - LOCAL_VAR = 28, - /// [distinct, n x element] - EXPRESSION = 29, - /// [distinct, name, file, line, ...] - OBJC_PROPERTY = 30, - /// [distinct, tag, scope, entity, line, name] - IMPORTED_ENTITY = 31, - /// [distinct, scope, name, ...] - MODULE = 32, - /// [distinct, macinfo, line, name, value] - MACRO = 33, - /// [distinct, macinfo, line, file, ...] - MACRO_FILE = 34, - /// [count, offset] blob([lengths][chars]) - STRINGS = 35, - /// [valueid, n x [id, mdnode]] - GLOBAL_DECL_ATTACHMENT = 36, - /// [distinct, var, expr] - GLOBAL_VAR_EXPR = 37, - /// [offset] - INDEX_OFFSET = 38, - /// [bitpos] - INDEX = 39, - /// [distinct, scope, name, file, line] - LABEL = 40, - /// [distinct, name, size, align,...] - STRING_TYPE = 41, - /// [distinct, scope, name, variable,...] - COMMON_BLOCK = 44, - /// [distinct, count, lo, up, stride] - GENERIC_SUBRANGE = 45, - /// [n x [type num, value num]] - ARG_LIST = 46, - /// [distinct, ...] - ASSIGN_ID = 47, -}; - -pub const Identification = struct { - pub const id = 13; - - pub const abbrevs = [_]type{ - Version, - Epoch, - }; - - pub const Version = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 1 }, - .{ .array_fixed = 8 }, - }; - string: []const u8, - }; - - pub const Epoch = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 2 }, - .{ .vbr = 6 }, - }; - epoch: u32, - }; -}; - -pub const Module = struct { - pub const id = 8; - - pub const abbrevs = [_]type{ - Version, - String, - Variable, - Function, - Alias, - }; - - pub const Version = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 1 }, - .{ .literal = 2 }, - }; - }; - - pub const String = struct { - pub const ops = [_]AbbrevOp{ - .{ .vbr = 4 }, - .{ .array_fixed = 8 }, - }; - code: u16, - string: []const u8, - }; - - pub const Variable = struct { - const AddrSpaceAndIsConst = packed struct { - is_const: bool, - one: u1 = 1, - addr_space: Builder.AddrSpace, - }; - - pub const ops = [_]AbbrevOp{ - .{ .literal = 7 }, // Code - .{ .vbr = 16 }, // strtab_offset - .{ .vbr = 16 }, // strtab_size - .{ .fixed_runtime = Builder.Type }, - .{ .fixed = @bitSizeOf(AddrSpaceAndIsConst) }, // isconst - ConstantAbbrev, // initid - .{ .fixed = @bitSizeOf(Builder.Linkage) }, - .{ .fixed = @bitSizeOf(Builder.Alignment) }, - .{ .vbr = 16 }, // section - .{ .fixed = @bitSizeOf(Builder.Visibility) }, - .{ .fixed = @bitSizeOf(Builder.ThreadLocal) }, // threadlocal - .{ .fixed = @bitSizeOf(Builder.UnnamedAddr) }, - .{ .fixed = @bitSizeOf(Builder.ExternallyInitialized) }, - .{ .fixed = @bitSizeOf(Builder.DllStorageClass) }, - .{ .literal = 0 }, // comdat - .{ .literal = 0 }, // attributes - .{ .fixed = @bitSizeOf(Builder.Preemption) }, - }; - strtab_offset: usize, - strtab_size: usize, - type_index: Builder.Type, - is_const: AddrSpaceAndIsConst, - initid: u32, - linkage: Builder.Linkage, - alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), - section: usize, - visibility: Builder.Visibility, - thread_local: Builder.ThreadLocal, - unnamed_addr: Builder.UnnamedAddr, - externally_initialized: Builder.ExternallyInitialized, - dllstorageclass: Builder.DllStorageClass, - preemption: Builder.Preemption, - }; - - pub const Function = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 8 }, // Code - .{ .vbr = 16 }, // strtab_offset - .{ .vbr = 16 }, // strtab_size - .{ .fixed_runtime = Builder.Type }, - .{ .fixed = @bitSizeOf(Builder.CallConv) }, - .{ .fixed = 1 }, // isproto - .{ .fixed = @bitSizeOf(Builder.Linkage) }, - .{ .vbr = 16 }, // paramattr - .{ .fixed = @bitSizeOf(Builder.Alignment) }, - .{ .vbr = 16 }, // section - .{ .fixed = @bitSizeOf(Builder.Visibility) }, - .{ .literal = 0 }, // gc - .{ .fixed = @bitSizeOf(Builder.UnnamedAddr) }, - .{ .literal = 0 }, // prologuedata - .{ .fixed = @bitSizeOf(Builder.DllStorageClass) }, - .{ .literal = 0 }, // comdat - .{ .literal = 0 }, // prefixdata - .{ .literal = 0 }, // personalityfn - .{ .fixed = @bitSizeOf(Builder.Preemption) }, - .{ .fixed = @bitSizeOf(Builder.AddrSpace) }, - }; - strtab_offset: usize, - strtab_size: usize, - type_index: Builder.Type, - call_conv: Builder.CallConv, - is_proto: bool, - linkage: Builder.Linkage, - paramattr: usize, - alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), - section: usize, - visibility: Builder.Visibility, - unnamed_addr: Builder.UnnamedAddr, - dllstorageclass: Builder.DllStorageClass, - preemption: Builder.Preemption, - addr_space: Builder.AddrSpace, - }; - - pub const Alias = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 14 }, // Code - .{ .vbr = 16 }, // strtab_offset - .{ .vbr = 16 }, // strtab_size - .{ .fixed_runtime = Builder.Type }, - .{ .fixed = @bitSizeOf(Builder.AddrSpace) }, - ConstantAbbrev, // aliasee val - .{ .fixed = @bitSizeOf(Builder.Linkage) }, - .{ .fixed = @bitSizeOf(Builder.Visibility) }, - .{ .fixed = @bitSizeOf(Builder.DllStorageClass) }, - .{ .fixed = @bitSizeOf(Builder.ThreadLocal) }, - .{ .fixed = @bitSizeOf(Builder.UnnamedAddr) }, - .{ .fixed = @bitSizeOf(Builder.Preemption) }, - }; - strtab_offset: usize, - strtab_size: usize, - type_index: Builder.Type, - addr_space: Builder.AddrSpace, - aliasee: u32, - linkage: Builder.Linkage, - visibility: Builder.Visibility, - dllstorageclass: Builder.DllStorageClass, - thread_local: Builder.ThreadLocal, - unnamed_addr: Builder.UnnamedAddr, - preemption: Builder.Preemption, - }; -}; - -pub const BlockInfo = struct { - pub const id = 0; - - pub const set_block_id = 1; - - pub const abbrevs = [_]type{}; -}; - -pub const Type = struct { - pub const id = 17; - - pub const abbrevs = [_]type{ - NumEntry, - Simple, - Opaque, - Integer, - StructAnon, - StructNamed, - StructName, - Array, - Vector, - Pointer, - Target, - Function, - }; - - pub const NumEntry = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 1 }, - .{ .fixed = 32 }, - }; - num: u32, - }; - - pub const Simple = struct { - pub const ops = [_]AbbrevOp{ - .{ .vbr = 4 }, - }; - code: u5, - }; - - pub const Opaque = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 6 }, - .{ .literal = 0 }, - }; - }; - - pub const Integer = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 7 }, - .{ .fixed = 28 }, - }; - width: u28, - }; - - pub const StructAnon = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 18 }, - .{ .fixed = 1 }, - .{ .array_fixed_runtime = Builder.Type }, - }; - is_packed: bool, - types: []const Builder.Type, - }; - - pub const StructNamed = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 20 }, - .{ .fixed = 1 }, - .{ .array_fixed_runtime = Builder.Type }, - }; - is_packed: bool, - types: []const Builder.Type, - }; - - pub const StructName = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 19 }, - .{ .array_fixed = 8 }, - }; - string: []const u8, - }; - - pub const Array = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 11 }, - .{ .vbr = 16 }, - .{ .fixed_runtime = Builder.Type }, - }; - len: u64, - child: Builder.Type, - }; - - pub const Vector = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 12 }, - .{ .vbr = 16 }, - .{ .fixed_runtime = Builder.Type }, - }; - len: u64, - child: Builder.Type, - }; - - pub const Pointer = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 25 }, - .{ .vbr = 4 }, - }; - addr_space: Builder.AddrSpace, - }; - - pub const Target = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 26 }, - .{ .vbr = 4 }, - .{ .array_fixed_runtime = Builder.Type }, - .{ .array_fixed = 32 }, - }; - num_types: u32, - types: []const Builder.Type, - ints: []const u32, - }; - - pub const Function = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 21 }, - .{ .fixed = 1 }, - .{ .fixed_runtime = Builder.Type }, - .{ .array_fixed_runtime = Builder.Type }, - }; - is_vararg: bool, - return_type: Builder.Type, - param_types: []const Builder.Type, - }; -}; - -pub const Paramattr = struct { - pub const id = 9; - - pub const abbrevs = [_]type{ - Entry, - }; - - pub const Entry = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 2 }, - .{ .array_vbr = 8 }, - }; - group_indices: []const u64, - }; -}; - -pub const ParamattrGroup = struct { - pub const id = 10; - - pub const abbrevs = [_]type{}; -}; - -pub const Constants = struct { - pub const id = 11; - - pub const abbrevs = [_]type{ - SetType, - Null, - Undef, - Poison, - Integer, - Half, - Float, - Double, - Fp80, - Fp128, - Aggregate, - String, - CString, - Cast, - Binary, - Cmp, - ExtractElement, - InsertElement, - ShuffleVector, - ShuffleVectorEx, - BlockAddress, - DsoLocalEquivalentOrNoCfi, - }; - - pub const SetType = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 1 }, - .{ .fixed_runtime = Builder.Type }, - }; - type_id: Builder.Type, - }; - - pub const Null = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 2 }, - }; - }; - - pub const Undef = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 3 }, - }; - }; - - pub const Poison = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 26 }, - }; - }; - - pub const Integer = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 4 }, - .{ .vbr = 16 }, - }; - value: u64, - }; - - pub const Half = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 6 }, - .{ .fixed = 16 }, - }; - value: u16, - }; - - pub const Float = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 6 }, - .{ .fixed = 32 }, - }; - value: u32, - }; - - pub const Double = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 6 }, - .{ .vbr = 6 }, - }; - value: u64, - }; - - pub const Fp80 = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 6 }, - .{ .vbr = 6 }, - .{ .vbr = 6 }, - }; - hi: u64, - lo: u16, - }; - - pub const Fp128 = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 6 }, - .{ .vbr = 6 }, - .{ .vbr = 6 }, - }; - lo: u64, - hi: u64, - }; - - pub const Aggregate = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 7 }, - .{ .array_fixed = 32 }, - }; - values: []const Builder.Constant, - }; - - pub const String = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 8 }, - .{ .array_fixed = 8 }, - }; - string: []const u8, - }; - - pub const CString = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 9 }, - .{ .array_fixed = 8 }, - }; - string: []const u8, - }; - - pub const Cast = struct { - const CastOpcode = Builder.CastOpcode; - pub const ops = [_]AbbrevOp{ - .{ .literal = 11 }, - .{ .fixed = @bitSizeOf(CastOpcode) }, - .{ .fixed_runtime = Builder.Type }, - ConstantAbbrev, - }; - - opcode: CastOpcode, - type_index: Builder.Type, - val: Builder.Constant, - }; - - pub const Binary = struct { - const BinaryOpcode = Builder.BinaryOpcode; - pub const ops = [_]AbbrevOp{ - .{ .literal = 10 }, - .{ .fixed = @bitSizeOf(BinaryOpcode) }, - ConstantAbbrev, - ConstantAbbrev, - }; - - opcode: BinaryOpcode, - lhs: Builder.Constant, - rhs: Builder.Constant, - }; - - pub const Cmp = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 17 }, - .{ .fixed_runtime = Builder.Type }, - ConstantAbbrev, - ConstantAbbrev, - .{ .vbr = 6 }, - }; - - ty: Builder.Type, - lhs: Builder.Constant, - rhs: Builder.Constant, - pred: u32, - }; - - pub const ExtractElement = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 14 }, - .{ .fixed_runtime = Builder.Type }, - ConstantAbbrev, - .{ .fixed_runtime = Builder.Type }, - ConstantAbbrev, - }; - - val_type: Builder.Type, - val: Builder.Constant, - index_type: Builder.Type, - index: Builder.Constant, - }; - - pub const InsertElement = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 15 }, - ConstantAbbrev, - ConstantAbbrev, - .{ .fixed_runtime = Builder.Type }, - ConstantAbbrev, - }; - - val: Builder.Constant, - elem: Builder.Constant, - index_type: Builder.Type, - index: Builder.Constant, - }; - - pub const ShuffleVector = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 16 }, - ValueAbbrev, - ValueAbbrev, - ValueAbbrev, - }; - - lhs: Builder.Constant, - rhs: Builder.Constant, - mask: Builder.Constant, - }; - - pub const ShuffleVectorEx = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 19 }, - .{ .fixed_runtime = Builder.Type }, - ValueAbbrev, - ValueAbbrev, - ValueAbbrev, - }; - - ty: Builder.Type, - lhs: Builder.Constant, - rhs: Builder.Constant, - mask: Builder.Constant, - }; - - pub const BlockAddress = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 21 }, - .{ .fixed_runtime = Builder.Type }, - ConstantAbbrev, - BlockAbbrev, - }; - type_id: Builder.Type, - function: u32, - block: u32, - }; - - pub const DsoLocalEquivalentOrNoCfi = struct { - pub const ops = [_]AbbrevOp{ - .{ .fixed = 5 }, - .{ .fixed_runtime = Builder.Type }, - ConstantAbbrev, - }; - code: u5, - type_id: Builder.Type, - function: u32, - }; -}; - -pub const MetadataKindBlock = struct { - pub const id = 22; - - pub const abbrevs = [_]type{ - Kind, - }; - - pub const Kind = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 6 }, - .{ .vbr = 4 }, - .{ .array_fixed = 8 }, - }; - id: u32, - name: []const u8, - }; -}; - -pub const MetadataAttachmentBlock = struct { - pub const id = 16; - - pub const abbrevs = [_]type{ - AttachmentGlobalSingle, - AttachmentInstructionSingle, - }; - - pub const AttachmentGlobalSingle = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.ATTACHMENT) }, - .{ .fixed = 1 }, - MetadataAbbrev, - }; - kind: FixedMetadataKind, - metadata: Builder.Metadata, - }; - - pub const AttachmentInstructionSingle = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.ATTACHMENT) }, - ValueAbbrev, - .{ .fixed = 5 }, - MetadataAbbrev, - }; - inst: u32, - kind: FixedMetadataKind, - metadata: Builder.Metadata, - }; -}; - -pub const MetadataBlock = struct { - pub const id = 15; - - pub const abbrevs = [_]type{ - Strings, - File, - CompileUnit, - Subprogram, - LexicalBlock, - Location, - BasicType, - CompositeType, - DerivedType, - SubroutineType, - Enumerator, - Subrange, - Expression, - Node, - LocalVar, - Parameter, - GlobalVar, - GlobalVarExpression, - Constant, - Name, - NamedNode, - GlobalDeclAttachment, - }; - - pub const Strings = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.STRINGS) }, - .{ .vbr = 6 }, - .{ .vbr = 6 }, - .blob, - }; - num_strings: u32, - strings_offset: u32, - blob: []const u8, - }; - - pub const File = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.FILE) }, - .{ .literal = 0 }, // is distinct - MetadataAbbrev, // filename - MetadataAbbrev, // directory - .{ .literal = 0 }, // checksum - .{ .literal = 0 }, // checksum - }; - - filename: Builder.MetadataString, - directory: Builder.MetadataString, - }; - - pub const CompileUnit = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.COMPILE_UNIT) }, - .{ .literal = 1 }, // is distinct - .{ .literal = std.dwarf.LANG.C99 }, // source language - MetadataAbbrev, // file - MetadataAbbrev, // producer - .{ .fixed = 1 }, // isOptimized - .{ .literal = 0 }, // raw flags - .{ .literal = 0 }, // runtime version - .{ .literal = 0 }, // split debug file name - .{ .literal = 1 }, // emission kind - MetadataAbbrev, // enums - .{ .literal = 0 }, // retained types - .{ .literal = 0 }, // subprograms - MetadataAbbrev, // globals - .{ .literal = 0 }, // imported entities - .{ .literal = 0 }, // DWO ID - .{ .literal = 0 }, // macros - .{ .literal = 0 }, // split debug inlining - .{ .literal = 0 }, // debug info profiling - .{ .literal = 0 }, // name table kind - .{ .literal = 0 }, // ranges base address - .{ .literal = 0 }, // raw sysroot - .{ .literal = 0 }, // raw SDK - }; - - file: Builder.Metadata, - producer: Builder.MetadataString, - is_optimized: bool, - enums: Builder.Metadata, - globals: Builder.Metadata, - }; - - pub const Subprogram = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.SUBPROGRAM) }, - .{ .literal = 0b111 }, // is distinct | has sp flags | has flags - MetadataAbbrev, // scope - MetadataAbbrev, // name - MetadataAbbrev, // linkage name - MetadataAbbrev, // file - LineAbbrev, // line - MetadataAbbrev, // type - LineAbbrev, // scope line - .{ .literal = 0 }, // containing type - .{ .fixed = 32 }, // sp flags - .{ .literal = 0 }, // virtual index - .{ .fixed = 32 }, // flags - MetadataAbbrev, // compile unit - .{ .literal = 0 }, // template params - .{ .literal = 0 }, // declaration - .{ .literal = 0 }, // retained nodes - .{ .literal = 0 }, // this adjustment - .{ .literal = 0 }, // thrown types - .{ .literal = 0 }, // annotations - .{ .literal = 0 }, // target function name - }; - - scope: Builder.Metadata, - name: Builder.MetadataString, - linkage_name: Builder.MetadataString, - file: Builder.Metadata, - line: u32, - ty: Builder.Metadata, - scope_line: u32, - sp_flags: Builder.Metadata.Subprogram.DISPFlags, - flags: Builder.Metadata.DIFlags, - compile_unit: Builder.Metadata, - }; - - pub const LexicalBlock = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.LEXICAL_BLOCK) }, - .{ .literal = 0 }, // is distinct - MetadataAbbrev, // scope - MetadataAbbrev, // file - LineAbbrev, // line - ColumnAbbrev, // column - }; - - scope: Builder.Metadata, - file: Builder.Metadata, - line: u32, - column: u32, - }; - - pub const Location = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.LOCATION) }, - .{ .literal = 0 }, // is distinct - LineAbbrev, // line - ColumnAbbrev, // column - MetadataAbbrev, // scope - MetadataAbbrev, // inlined at - .{ .literal = 0 }, // is implicit code - }; - - line: u32, - column: u32, - scope: u32, - inlined_at: Builder.Metadata, - }; - - pub const BasicType = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.BASIC_TYPE) }, - .{ .literal = 0 }, // is distinct - .{ .literal = std.dwarf.TAG.base_type }, // tag - MetadataAbbrev, // name - .{ .vbr = 6 }, // size in bits - .{ .literal = 0 }, // align in bits - .{ .vbr = 8 }, // encoding - .{ .literal = 0 }, // flags - }; - - name: Builder.MetadataString, - size_in_bits: u64, - encoding: u32, - }; - - pub const CompositeType = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.COMPOSITE_TYPE) }, - .{ .literal = 0 | 0x2 }, // is distinct | is not used in old type ref - .{ .fixed = 32 }, // tag - MetadataAbbrev, // name - MetadataAbbrev, // file - LineAbbrev, // line - MetadataAbbrev, // scope - MetadataAbbrev, // underlying type - .{ .vbr = 6 }, // size in bits - .{ .vbr = 6 }, // align in bits - .{ .literal = 0 }, // offset in bits - .{ .fixed = 32 }, // flags - MetadataAbbrev, // elements - .{ .literal = 0 }, // runtime lang - .{ .literal = 0 }, // vtable holder - .{ .literal = 0 }, // template params - .{ .literal = 0 }, // raw id - .{ .literal = 0 }, // discriminator - .{ .literal = 0 }, // data location - .{ .literal = 0 }, // associated - .{ .literal = 0 }, // allocated - .{ .literal = 0 }, // rank - .{ .literal = 0 }, // annotations - }; - - tag: u32, - name: Builder.MetadataString, - file: Builder.Metadata, - line: u32, - scope: Builder.Metadata, - underlying_type: Builder.Metadata, - size_in_bits: u64, - align_in_bits: u64, - flags: Builder.Metadata.DIFlags, - elements: Builder.Metadata, - }; - - pub const DerivedType = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.DERIVED_TYPE) }, - .{ .literal = 0 }, // is distinct - .{ .fixed = 32 }, // tag - MetadataAbbrev, // name - MetadataAbbrev, // file - LineAbbrev, // line - MetadataAbbrev, // scope - MetadataAbbrev, // underlying type - .{ .vbr = 6 }, // size in bits - .{ .vbr = 6 }, // align in bits - .{ .vbr = 6 }, // offset in bits - .{ .literal = 0 }, // flags - .{ .literal = 0 }, // extra data - }; - - tag: u32, - name: Builder.MetadataString, - file: Builder.Metadata, - line: u32, - scope: Builder.Metadata, - underlying_type: Builder.Metadata, - size_in_bits: u64, - align_in_bits: u64, - offset_in_bits: u64, - }; - - pub const SubroutineType = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.SUBROUTINE_TYPE) }, - .{ .literal = 0 | 0x2 }, // is distinct | has no old type refs - .{ .literal = 0 }, // flags - MetadataAbbrev, // types - .{ .literal = 0 }, // cc - }; - - types: Builder.Metadata, - }; - - pub const Enumerator = struct { - pub const id: MetadataCode = .ENUMERATOR; - - pub const Flags = packed struct(u3) { - distinct: bool = false, - unsigned: bool, - bigint: bool = true, - }; - - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(Enumerator.id) }, - .{ .fixed = @bitSizeOf(Flags) }, // flags - .{ .vbr = 6 }, // bit width - MetadataAbbrev, // name - .{ .vbr = 16 }, // integer value - }; - - flags: Flags, - bit_width: u32, - name: Builder.MetadataString, - value: u64, - }; - - pub const Subrange = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.SUBRANGE) }, - .{ .literal = 0b10 }, // is distinct | version - MetadataAbbrev, // count - MetadataAbbrev, // lower bound - .{ .literal = 0 }, // upper bound - .{ .literal = 0 }, // stride - }; - - count: Builder.Metadata, - lower_bound: Builder.Metadata, - }; - - pub const Expression = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.EXPRESSION) }, - .{ .literal = 0 | (3 << 1) }, // is distinct | version - MetadataArrayAbbrev, // elements - }; - - elements: []const u32, - }; - - pub const Node = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.NODE) }, - MetadataArrayAbbrev, // elements - }; - - elements: []const Builder.Metadata, - }; - - pub const LocalVar = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.LOCAL_VAR) }, - .{ .literal = 0b10 }, // is distinct | has alignment - MetadataAbbrev, // scope - MetadataAbbrev, // name - MetadataAbbrev, // file - LineAbbrev, // line - MetadataAbbrev, // type - .{ .literal = 0 }, // arg - .{ .literal = 0 }, // flags - .{ .literal = 0 }, // align bits - .{ .literal = 0 }, // annotations - }; - - scope: Builder.Metadata, - name: Builder.MetadataString, - file: Builder.Metadata, - line: u32, - ty: Builder.Metadata, - }; - - pub const Parameter = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.LOCAL_VAR) }, - .{ .literal = 0b10 }, // is distinct | has alignment - MetadataAbbrev, // scope - MetadataAbbrev, // name - MetadataAbbrev, // file - LineAbbrev, // line - MetadataAbbrev, // type - .{ .vbr = 4 }, // arg - .{ .literal = 0 }, // flags - .{ .literal = 0 }, // align bits - .{ .literal = 0 }, // annotations - }; - - scope: Builder.Metadata, - name: Builder.MetadataString, - file: Builder.Metadata, - line: u32, - ty: Builder.Metadata, - arg: u32, - }; - - pub const GlobalVar = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.GLOBAL_VAR) }, - .{ .literal = 0b101 }, // is distinct | version - MetadataAbbrev, // scope - MetadataAbbrev, // name - MetadataAbbrev, // linkage name - MetadataAbbrev, // file - LineAbbrev, // line - MetadataAbbrev, // type - .{ .fixed = 1 }, // local - .{ .literal = 1 }, // defined - .{ .literal = 0 }, // static data members declaration - .{ .literal = 0 }, // template params - .{ .literal = 0 }, // align in bits - .{ .literal = 0 }, // annotations - }; - - scope: Builder.Metadata, - name: Builder.MetadataString, - linkage_name: Builder.MetadataString, - file: Builder.Metadata, - line: u32, - ty: Builder.Metadata, - local: bool, - }; - - pub const GlobalVarExpression = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.GLOBAL_VAR_EXPR) }, - .{ .literal = 0 }, // is distinct - MetadataAbbrev, // variable - MetadataAbbrev, // expression - }; - - variable: Builder.Metadata, - expression: Builder.Metadata, - }; - - pub const Constant = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.VALUE) }, - MetadataAbbrev, // type - MetadataAbbrev, // value - }; - - ty: Builder.Type, - constant: Builder.Constant, - }; - - pub const Name = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.NAME) }, - .{ .array_fixed = 8 }, // name - }; - - name: []const u8, - }; - - pub const NamedNode = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.NAMED_NODE) }, - MetadataArrayAbbrev, // elements - }; - - elements: []const Builder.Metadata, - }; - - pub const GlobalDeclAttachment = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = @intFromEnum(MetadataCode.GLOBAL_DECL_ATTACHMENT) }, - ValueAbbrev, // value id - .{ .fixed = 1 }, // kind - MetadataAbbrev, // elements - }; - - value: Builder.Constant, - kind: FixedMetadataKind, - metadata: Builder.Metadata, - }; -}; - -pub const OperandBundleTags = struct { - pub const id = 21; - - pub const abbrevs = [_]type{OperandBundleTag}; - - pub const OperandBundleTag = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 1 }, - .array_char6, - }; - tag: []const u8, - }; -}; - -pub const FunctionMetadataBlock = struct { - pub const id = 15; - - pub const abbrevs = [_]type{ - Value, - }; - - pub const Value = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 2 }, - .{ .fixed = 32 }, // variable - .{ .fixed = 32 }, // expression - }; - - ty: Builder.Type, - value: Builder.Value, - }; -}; - -pub const FunctionBlock = struct { - pub const id = 12; - - pub const abbrevs = [_]type{ - DeclareBlocks, - Call, - CallFast, - FNeg, - FNegFast, - Binary, - BinaryNoWrap, - BinaryExact, - BinaryFast, - Cmp, - CmpFast, - Select, - SelectFast, - Cast, - Alloca, - GetElementPtr, - ExtractValue, - InsertValue, - ExtractElement, - InsertElement, - ShuffleVector, - RetVoid, - Ret, - Unreachable, - Load, - LoadAtomic, - Store, - StoreAtomic, - BrUnconditional, - BrConditional, - VaArg, - AtomicRmw, - CmpXchg, - Fence, - DebugLoc, - DebugLocAgain, - ColdOperandBundle, - IndirectBr, - }; - - pub const DeclareBlocks = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 1 }, - .{ .vbr = 8 }, - }; - num_blocks: usize, - }; - - pub const Call = struct { - pub const CallType = packed struct(u17) { - tail: bool = false, - call_conv: Builder.CallConv, - reserved: u3 = 0, - must_tail: bool = false, - // We always use the explicit type version as that is what LLVM does - explicit_type: bool = true, - no_tail: bool = false, - }; - pub const ops = [_]AbbrevOp{ - .{ .literal = 34 }, - .{ .fixed_runtime = Builder.FunctionAttributes }, - .{ .fixed = @bitSizeOf(CallType) }, - .{ .fixed_runtime = Builder.Type }, - ValueAbbrev, // Callee - ValueArrayAbbrev, // Args - }; - - attributes: Builder.FunctionAttributes, - call_type: CallType, - type_id: Builder.Type, - callee: Builder.Value, - args: []const Builder.Value, - }; - - pub const CallFast = struct { - const CallType = packed struct(u18) { - tail: bool = false, - call_conv: Builder.CallConv, - reserved: u3 = 0, - must_tail: bool = false, - // We always use the explicit type version as that is what LLVM does - explicit_type: bool = true, - no_tail: bool = false, - fast: bool = true, - }; - - pub const ops = [_]AbbrevOp{ - .{ .literal = 34 }, - .{ .fixed_runtime = Builder.FunctionAttributes }, - .{ .fixed = @bitSizeOf(CallType) }, - .{ .fixed = @bitSizeOf(Builder.FastMath) }, - .{ .fixed_runtime = Builder.Type }, - ValueAbbrev, // Callee - ValueArrayAbbrev, // Args - }; - - attributes: Builder.FunctionAttributes, - call_type: CallType, - fast_math: Builder.FastMath, - type_id: Builder.Type, - callee: Builder.Value, - args: []const Builder.Value, - }; - - pub const FNeg = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 56 }, - ValueAbbrev, - .{ .literal = 0 }, - }; - - val: u32, - }; - - pub const FNegFast = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 56 }, - ValueAbbrev, - .{ .literal = 0 }, - .{ .fixed = @bitSizeOf(Builder.FastMath) }, - }; - - val: u32, - fast_math: Builder.FastMath, - }; - - pub const Binary = struct { - const BinaryOpcode = Builder.BinaryOpcode; - pub const ops = [_]AbbrevOp{ - .{ .literal = 2 }, - ValueAbbrev, - ValueAbbrev, - .{ .fixed = @bitSizeOf(BinaryOpcode) }, - }; - - lhs: u32, - rhs: u32, - opcode: BinaryOpcode, - }; - - pub const BinaryNoWrap = struct { - const BinaryOpcode = Builder.BinaryOpcode; - pub const ops = [_]AbbrevOp{ - .{ .literal = 2 }, - ValueAbbrev, - ValueAbbrev, - .{ .fixed = @bitSizeOf(BinaryOpcode) }, - .{ .fixed = 2 }, - }; - - lhs: u32, - rhs: u32, - opcode: BinaryOpcode, - flags: packed struct(u2) { - no_unsigned_wrap: bool, - no_signed_wrap: bool, - }, - }; - - pub const BinaryExact = struct { - const BinaryOpcode = Builder.BinaryOpcode; - pub const ops = [_]AbbrevOp{ - .{ .literal = 2 }, - ValueAbbrev, - ValueAbbrev, - .{ .fixed = @bitSizeOf(BinaryOpcode) }, - .{ .literal = 1 }, - }; - - lhs: u32, - rhs: u32, - opcode: BinaryOpcode, - }; - - pub const BinaryFast = struct { - const BinaryOpcode = Builder.BinaryOpcode; - pub const ops = [_]AbbrevOp{ - .{ .literal = 2 }, - ValueAbbrev, - ValueAbbrev, - .{ .fixed = @bitSizeOf(BinaryOpcode) }, - .{ .fixed = @bitSizeOf(Builder.FastMath) }, - }; - - lhs: u32, - rhs: u32, - opcode: BinaryOpcode, - fast_math: Builder.FastMath, - }; - - pub const Cmp = struct { - const CmpPredicate = Builder.CmpPredicate; - pub const ops = [_]AbbrevOp{ - .{ .literal = 28 }, - ValueAbbrev, - ValueAbbrev, - .{ .fixed = @bitSizeOf(CmpPredicate) }, - }; - - lhs: u32, - rhs: u32, - pred: CmpPredicate, - }; - - pub const CmpFast = struct { - const CmpPredicate = Builder.CmpPredicate; - pub const ops = [_]AbbrevOp{ - .{ .literal = 28 }, - ValueAbbrev, - ValueAbbrev, - .{ .fixed = @bitSizeOf(CmpPredicate) }, - .{ .fixed = @bitSizeOf(Builder.FastMath) }, - }; - - lhs: u32, - rhs: u32, - pred: CmpPredicate, - fast_math: Builder.FastMath, - }; - - pub const Select = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 29 }, - ValueAbbrev, - ValueAbbrev, - ValueAbbrev, - }; - - lhs: u32, - rhs: u32, - cond: u32, - }; - - pub const SelectFast = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 29 }, - ValueAbbrev, - ValueAbbrev, - ValueAbbrev, - .{ .fixed = @bitSizeOf(Builder.FastMath) }, - }; - - lhs: u32, - rhs: u32, - cond: u32, - fast_math: Builder.FastMath, - }; - - pub const Cast = struct { - const CastOpcode = Builder.CastOpcode; - pub const ops = [_]AbbrevOp{ - .{ .literal = 3 }, - ValueAbbrev, - .{ .fixed_runtime = Builder.Type }, - .{ .fixed = @bitSizeOf(CastOpcode) }, - }; - - val: u32, - type_index: Builder.Type, - opcode: CastOpcode, - }; - - pub const Alloca = struct { - pub const Flags = packed struct(u11) { - align_lower: u5, - inalloca: bool, - explicit_type: bool, - swift_error: bool, - align_upper: u3, - }; - pub const ops = [_]AbbrevOp{ - .{ .literal = 19 }, - .{ .fixed_runtime = Builder.Type }, - .{ .fixed_runtime = Builder.Type }, - ValueAbbrev, - .{ .fixed = @bitSizeOf(Flags) }, - }; - - inst_type: Builder.Type, - len_type: Builder.Type, - len_value: u32, - flags: Flags, - }; - - pub const RetVoid = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 10 }, - }; - }; - - pub const Ret = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 10 }, - ValueAbbrev, - }; - val: u32, - }; - - pub const GetElementPtr = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 43 }, - .{ .fixed = 1 }, - .{ .fixed_runtime = Builder.Type }, - ValueAbbrev, - ValueArrayAbbrev, - }; - - is_inbounds: bool, - type_index: Builder.Type, - base: Builder.Value, - indices: []const Builder.Value, - }; - - pub const ExtractValue = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 26 }, - ValueAbbrev, - ValueArrayAbbrev, - }; - - val: u32, - indices: []const u32, - }; - - pub const InsertValue = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 27 }, - ValueAbbrev, - ValueAbbrev, - ValueArrayAbbrev, - }; - - val: u32, - elem: u32, - indices: []const u32, - }; - - pub const ExtractElement = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 6 }, - ValueAbbrev, - ValueAbbrev, - }; - - val: u32, - index: u32, - }; - - pub const InsertElement = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 7 }, - ValueAbbrev, - ValueAbbrev, - ValueAbbrev, - }; - - val: u32, - elem: u32, - index: u32, - }; - - pub const ShuffleVector = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 8 }, - ValueAbbrev, - ValueAbbrev, - ValueAbbrev, - }; - - lhs: u32, - rhs: u32, - mask: u32, - }; - - pub const Unreachable = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 15 }, - }; - }; - - pub const Load = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 20 }, - ValueAbbrev, - .{ .fixed_runtime = Builder.Type }, - .{ .fixed = @bitSizeOf(Builder.Alignment) }, - .{ .fixed = 1 }, - }; - ptr: u32, - ty: Builder.Type, - alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), - is_volatile: bool, - }; - - pub const LoadAtomic = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 41 }, - ValueAbbrev, - .{ .fixed_runtime = Builder.Type }, - .{ .fixed = @bitSizeOf(Builder.Alignment) }, - .{ .fixed = 1 }, - .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, - .{ .fixed = @bitSizeOf(Builder.SyncScope) }, - }; - ptr: u32, - ty: Builder.Type, - alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), - is_volatile: bool, - success_ordering: Builder.AtomicOrdering, - sync_scope: Builder.SyncScope, - }; - - pub const Store = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 44 }, - ValueAbbrev, - ValueAbbrev, - .{ .fixed = @bitSizeOf(Builder.Alignment) }, - .{ .fixed = 1 }, - }; - ptr: u32, - val: u32, - alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), - is_volatile: bool, - }; - - pub const StoreAtomic = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 45 }, - ValueAbbrev, - ValueAbbrev, - .{ .fixed = @bitSizeOf(Builder.Alignment) }, - .{ .fixed = 1 }, - .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, - .{ .fixed = @bitSizeOf(Builder.SyncScope) }, - }; - ptr: u32, - val: u32, - alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), - is_volatile: bool, - success_ordering: Builder.AtomicOrdering, - sync_scope: Builder.SyncScope, - }; - - pub const BrUnconditional = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 11 }, - BlockAbbrev, - }; - block: u32, - }; - - pub const BrConditional = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 11 }, - BlockAbbrev, - BlockAbbrev, - BlockAbbrev, - }; - then_block: u32, - else_block: u32, - condition: u32, - }; - - pub const VaArg = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 23 }, - .{ .fixed_runtime = Builder.Type }, - ValueAbbrev, - .{ .fixed_runtime = Builder.Type }, - }; - list_type: Builder.Type, - list: u32, - type: Builder.Type, - }; - - pub const AtomicRmw = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 59 }, - ValueAbbrev, - ValueAbbrev, - .{ .fixed = @bitSizeOf(Builder.Function.Instruction.AtomicRmw.Operation) }, - .{ .fixed = 1 }, - .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, - .{ .fixed = @bitSizeOf(Builder.SyncScope) }, - .{ .fixed = @bitSizeOf(Builder.Alignment) }, - }; - ptr: u32, - val: u32, - operation: Builder.Function.Instruction.AtomicRmw.Operation, - is_volatile: bool, - success_ordering: Builder.AtomicOrdering, - sync_scope: Builder.SyncScope, - alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), - }; - - pub const CmpXchg = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 46 }, - ValueAbbrev, - ValueAbbrev, - ValueAbbrev, - .{ .fixed = 1 }, - .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, - .{ .fixed = @bitSizeOf(Builder.SyncScope) }, - .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, - .{ .fixed = 1 }, - .{ .fixed = @bitSizeOf(Builder.Alignment) }, - }; - ptr: u32, - cmp: u32, - new: u32, - is_volatile: bool, - success_ordering: Builder.AtomicOrdering, - sync_scope: Builder.SyncScope, - failure_ordering: Builder.AtomicOrdering, - is_weak: bool, - alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), - }; - - pub const Fence = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 36 }, - .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, - .{ .fixed = @bitSizeOf(Builder.SyncScope) }, - }; - ordering: Builder.AtomicOrdering, - sync_scope: Builder.SyncScope, - }; - - pub const DebugLoc = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 35 }, - LineAbbrev, - ColumnAbbrev, - MetadataAbbrev, - MetadataAbbrev, - .{ .literal = 0 }, - }; - line: u32, - column: u32, - scope: Builder.Metadata, - inlined_at: Builder.Metadata, - }; - - pub const DebugLocAgain = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 33 }, - }; - }; - - pub const ColdOperandBundle = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 55 }, - .{ .literal = 0 }, - }; - }; - - pub const IndirectBr = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 31 }, - .{ .fixed_runtime = Builder.Type }, - ValueAbbrev, - BlockArrayAbbrev, - }; - ty: Builder.Type, - addr: Builder.Value, - targets: []const Builder.Function.Block.Index, - }; -}; - -pub const FunctionValueSymbolTable = struct { - pub const id = 14; - - pub const abbrevs = [_]type{ - BlockEntry, - }; - - pub const BlockEntry = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 2 }, - ValueAbbrev, - .{ .array_fixed = 8 }, - }; - value_id: u32, - string: []const u8, - }; -}; - -pub const Strtab = struct { - pub const id = 23; - - pub const abbrevs = [_]type{Blob}; - - pub const Blob = struct { - pub const ops = [_]AbbrevOp{ - .{ .literal = 1 }, - .blob, - }; - blob: []const u8, - }; -};