Source
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();
}