feature. See also
. The project being documented here (as the example) is the Zig library itself.
Builder.toBitcode
pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitcode_writer.Error![]const u32
File
Code
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();
try bitcode.writeBits(ir.MAGIC, 32);
var record: std.ArrayList(u64) = .empty;
defer record.deinit(self.gpa);
{
const IdentificationBlock = ir.IdentificationBlock;
var identification_block = try bitcode.enterTopBlock(IdentificationBlock);
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(IdentificationBlock.Version{ .string = producer_str });
try identification_block.writeAbbrev(IdentificationBlock.Epoch{ .epoch = 0 });
try identification_block.end();
}
{
const ModuleBlock = ir.ModuleBlock;
var module_block = try bitcode.enterTopBlock(ModuleBlock);
try module_block.writeAbbrev(ModuleBlock.Version{});
if (self.target_triple.slice(self)) |triple| {
try module_block.writeAbbrev(ModuleBlock.String{
.code = 2,
.string = triple,
});
}
if (self.data_layout.slice(self)) |data_layout| {
try module_block.writeAbbrev(ModuleBlock.String{
.code = 3,
.string = data_layout,
});
}
if (self.source_filename.slice(self)) |source_filename| {
try module_block.writeAbbrev(ModuleBlock.String{
.code = 16,
.string = source_filename,
});
}
if (self.module_asm.items.len != 0) {
try module_block.writeAbbrev(ModuleBlock.String{
.code = 4,
.string = self.module_asm.items,
});
}
{
const TypeBlock = ir.ModuleBlock.TypeBlock;
var type_block = try module_block.enterSubBlock(TypeBlock, true);
try type_block.writeAbbrev(TypeBlock.NumEntry{ .num = @intCast(self.type_items.items.len) });
for (self.type_items.items, 0..) |item, i| {
const ty: Type = @fromBackingInt(@intCast(i));
switch (item.tag) {
.simple => try type_block.writeAbbrev(TypeBlock.Simple{ .code = @fromBackingInt(@intCast(item.data)) }),
.integer => try type_block.writeAbbrev(TypeBlock.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(TypeBlock.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(TypeBlock.StructName{
.string = extra.id.slice(self).?,
});
switch (extra.body) {
.none => try type_block.writeAbbrev(TypeBlock.Opaque{}),
else => {
const real_struct = self.type_items.items[@backingInt(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(TypeBlock.StructNamed{
.is_packed = is_packed,
.types = real_extra.trail.next(real_extra.data.fields_len, Type, self),
});
},
}
},
.array,
.small_array,
=> try type_block.writeAbbrev(TypeBlock.Array{
.len = ty.aggregateLen(self),
.child = ty.childType(self),
}),
.vector,
.scalable_vector,
=> try type_block.writeAbbrev(TypeBlock.Vector{
.len = ty.aggregateLen(self),
.child = ty.childType(self),
}),
.pointer => try type_block.writeAbbrev(TypeBlock.Pointer{
.addr_space = ty.pointerAddrSpace(self),
}),
.target => {
var extra = self.typeExtraDataTrail(Type.Target, item.data);
try type_block.writeAbbrev(TypeBlock.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(TypeBlock.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(TypeBlock.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.array_hash_map.Auto(struct {
attributes: Attributes,
index: u32,
}, void) = .{};
defer attributes_set.deinit(self.gpa);
{
const ParamattrGroupBlock = ir.ModuleBlock.ParamattrGroupBlock;
var paramattr_group_block = try module_block.enterSubBlock(ParamattrGroupBlock, 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(@backingInt(kind));
},
.byval,
.byref,
.preallocated,
.inalloca,
.sret,
.elementtype,
=> |ty| {
try record.ensureUnusedCapacity(self.gpa, 3);
record.appendAssumeCapacity(6);
record.appendAssumeCapacity(@backingInt(kind));
record.appendAssumeCapacity(@backingInt(ty));
},
.@"align",
.alignstack,
=> |alignment| {
try record.ensureUnusedCapacity(self.gpa, 3);
record.appendAssumeCapacity(1);
record.appendAssumeCapacity(@backingInt(kind));
record.appendAssumeCapacity(alignment.resolve(self).toByteUnits() orelse 0);
},
.dereferenceable,
.dereferenceable_or_null,
=> |size| {
try record.ensureUnusedCapacity(self.gpa, 3);
record.appendAssumeCapacity(1);
record.appendAssumeCapacity(@backingInt(kind));
record.appendAssumeCapacity(size);
},
.nofpclass => |fpclass| {
try record.ensureUnusedCapacity(self.gpa, 3);
record.appendAssumeCapacity(1);
record.appendAssumeCapacity(@backingInt(kind));
record.appendAssumeCapacity(@as(u32, @bitCast(fpclass)));
},
.allockind => |allockind| {
try record.ensureUnusedCapacity(self.gpa, 3);
record.appendAssumeCapacity(1);
record.appendAssumeCapacity(@backingInt(kind));
record.appendAssumeCapacity(@as(u32, @bitCast(allockind)));
},
.allocsize => |allocsize| {
try record.ensureUnusedCapacity(self.gpa, 3);
record.appendAssumeCapacity(1);
record.appendAssumeCapacity(@backingInt(kind));
record.appendAssumeCapacity(@bitCast(allocsize.toLlvm()));
},
.memory => |memory| {
try record.ensureUnusedCapacity(self.gpa, 3);
record.appendAssumeCapacity(1);
record.appendAssumeCapacity(@backingInt(kind));
record.appendAssumeCapacity(@as(u32, @bitCast(memory)));
},
.uwtable => |uwtable| if (uwtable != .none) {
try record.ensureUnusedCapacity(self.gpa, 3);
record.appendAssumeCapacity(1);
record.appendAssumeCapacity(@backingInt(kind));
record.appendAssumeCapacity(@backingInt(uwtable));
},
.vscale_range => |vscale_range| {
try record.ensureUnusedCapacity(self.gpa, 3);
record.appendAssumeCapacity(1);
record.appendAssumeCapacity(@backingInt(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();
}
{
const ParamattrBlock = ir.ModuleBlock.ParamattrBlock;
var paramattr_block = try module_block.enterSubBlock(ParamattrBlock, 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(ParamattrBlock.Entry{ .group_indices = record.items });
}
try paramattr_block.end();
}
var globals: std.array_hash_map.Auto(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, 0..) |variable, variable_i| {
switch (variable.global.ptrConst(self).kind) {
.variable => |v| if (@backingInt(v) != variable_i) continue,
else => continue,
}
globals.putAssumeCapacity(variable.global, {});
}
for (self.functions.items, 0..) |function, function_i| {
switch (function.global.ptrConst(self).kind) {
.function => |f| if (@backingInt(f) != function_i) continue,
else => continue,
}
globals.putAssumeCapacity(function.global, {});
}
for (self.aliases.items, 0..) |alias, alias_i| {
switch (alias.global.ptrConst(self).kind) {
.alias => |a| if (@backingInt(a) != alias_i) continue,
else => continue,
}
globals.putAssumeCapacity(alias.global, {});
}
const ConstantAdapter = struct {
builder: *const Builder,
globals: *const std.array_hash_map.Auto(Global.Index, void),
pub fn get(adapter: @This(), param: anytype) switch (@TypeOf(param)) {
Constant => u32,
else => |Param| Param,
} {
return switch (@TypeOf(param)) {
Constant => adapter.getConstantIndex(param),
else => param,
};
}
pub fn getConstantIndex(adapter: @This(), 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: @This()) u32 {
return @intCast(adapter.globals.count() + adapter.builder.constant_items.len);
}
pub fn numGlobals(adapter: @This()) u32 {
return @intCast(adapter.globals.count());
}
};
const constant_adapter: ConstantAdapter = .{ .builder = self, .globals = &globals };
{
var section_map: std.array_hash_map.Auto(String, void) = .empty;
defer section_map.deinit(self.gpa);
try section_map.ensureUnusedCapacity(self.gpa, globals.count());
for (self.variables.items, 0..) |variable, variable_i| {
switch (variable.global.ptrConst(self).kind) {
.variable => |v| if (@backingInt(v) != variable_i) continue,
else => 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(ModuleBlock.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(ModuleBlock.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, 0..) |func, func_i| {
switch (func.global.ptrConst(self).kind) {
.function => |f| if (@backingInt(f) != func_i) continue,
else => 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(ModuleBlock.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(ModuleBlock.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, 0..) |alias, alias_i| {
switch (alias.global.ptrConst(self).kind) {
.alias => |a| if (@backingInt(a) != alias_i) continue,
else => continue,
}
const strtab = alias.global.strtab(self);
const global = alias.global.ptrConst(self);
assert(global.addr_space == alias.aliasee.typeOf(self).pointerAddrSpace(self));
try module_block.writeAbbrev(ModuleBlock.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,
});
}
}
{
const ConstantsBlock = ir.ModuleBlock.ConstantsBlock;
var constants_block = try module_block.enterSubBlock(ConstantsBlock, 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 = @fromBackingInt(@intCast(index));
const constant_type = constant.typeOf(self);
if (constant_type != current_type) {
try constants_block.writeAbbrev(ConstantsBlock.SetType{ .type_id = constant_type });
current_type = constant_type;
}
const data = datas[index];
switch (tags[index]) {
.null,
.zeroinitializer,
.none,
=> try constants_block.writeAbbrev(ConstantsBlock.Null{}),
.undef => try constants_block.writeAbbrev(ConstantsBlock.Undef{}),
.poison => try constants_block.writeAbbrev(ConstantsBlock.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(u63)) |val|
@bitCast(@as(u64, 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(ConstantsBlock.Integer{
.value = @bitCast(if (val >= 0)
val << 1 | 0
else
-%val << 1 | 1),
});
},
.half,
.bfloat,
=> try constants_block.writeAbbrev(ConstantsBlock.Half{ .value = @truncate(data) }),
.float => try constants_block.writeAbbrev(ConstantsBlock.Float{ .value = data }),
.double => {
const extra = self.constantExtraData(Constant.Double, data);
try constants_block.writeAbbrev(ConstantsBlock.Double{
.value = (@as(u64, extra.hi) << 32) | extra.lo,
});
},
.x86_fp80 => {
const extra = self.constantExtraData(Constant.Fp80, data);
try constants_block.writeAbbrev(ConstantsBlock.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(ConstantsBlock.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(
ConstantsBlock.Aggregate{ .values = values },
constant_adapter,
);
},
.splat => {
const ConstantsBlockWriter = @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(
ConstantsBlockWriter.abbrevId(ConstantsBlock.Aggregate),
ConstantsBlockWriter.abbrev_len,
);
try bitcode.writeVbr(vector_len, 6);
for (0..vector_len) |_| {
try bitcode.writeBits(c, ConstantsBlock.Aggregate.ops[1].array_fixed);
}
},
.string => {
const str: String = @fromBackingInt(@intCast(data));
if (str == .none) {
try constants_block.writeAbbrev(ConstantsBlock.Null{});
} else {
const slice = str.slice(self).?;
if (slice.len > 0 and slice[slice.len - 1] == 0)
try constants_block.writeAbbrev(ConstantsBlock.CString{ .string = slice[0 .. slice.len - 1] })
else
try constants_block.writeAbbrev(ConstantsBlock.String{ .string = slice });
}
},
.bitcast,
.inttoptr,
.ptrtoint,
.addrspacecast,
.trunc,
=> |tag| {
const extra = self.constantExtraData(Constant.Cast, data);
try constants_block.writeAbbrevAdapted(ConstantsBlock.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(ConstantsBlock.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(@backingInt(extra.data.type));
record.appendAssumeCapacity(@backingInt(extra.data.base.typeOf(self)));
record.appendAssumeCapacity(constant_adapter.getConstantIndex(extra.data.base));
for (indices) |i| {
record.appendAssumeCapacity(@backingInt(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(@backingInt(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(ConstantsBlock.BlockAddress{
.type_id = extra.function.typeOf(self),
.function = constant_adapter.getConstantIndex(extra.function.toConst(self)),
.block = @backingInt(extra.block),
});
},
.dso_local_equivalent,
.no_cfi,
=> |tag| {
const function: Function.Index = @fromBackingInt(@intCast(data));
try constants_block.writeAbbrev(ConstantsBlock.DsoLocalEquivalentOrNoCfi{
.code = switch (tag) {
.dso_local_equivalent => .DSO_LOCAL_EQUIVALENT,
.no_cfi => .NO_CFI_VALUE,
else => unreachable,
},
.type_id = function.typeOf(self),
.function = constant_adapter.getConstantIndex(function.toConst(self)),
});
},
}
}
try constants_block.end();
}
{
const MetadataKindBlock = ir.ModuleBlock.MetadataKindBlock;
var metadata_kind_block = try module_block.enterSubBlock(MetadataKindBlock, true);
const info = @typeInfo(ir.FixedMetadataKind).@"enum";
inline for (info.field_names, info.field_values) |field_name, field_value| {
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 {
constant_adapter: ConstantAdapter,
pub fn get(adapter: @This(), param: anytype) switch (@TypeOf(param)) {
Metadata, Metadata.Optional, Metadata.String, Metadata.String.Optional, Constant => u32,
else => |Result| Result,
} {
return switch (@TypeOf(param)) {
Metadata => adapter.getMetadataIndex(param),
Metadata.Optional => adapter.getOptionalMetadataIndex(param),
Metadata.String => adapter.getMetadataIndex(param.toMetadata()),
Metadata.String.Optional => adapter.getOptionalMetadataIndex(param.toMetadata()),
Constant => adapter.constant_adapter.getConstantIndex(param),
else => param,
};
}
pub fn getMetadataIndex(adapter: @This(), metadata: Metadata) u32 {
const builder = adapter.constant_adapter.builder;
const unwrapped_metadata = metadata.unwrap(builder);
return switch (unwrapped_metadata.kind) {
.string => unwrapped_metadata.index,
.node => @intCast(builder.metadata_string_map.count() + unwrapped_metadata.index),
.forward, .local => unreachable,
};
}
pub fn getOptionalMetadataIndex(adapter: @This(), metadata: Metadata.Optional) u32 {
return if (metadata.unwrap()) |m| 1 + adapter.getMetadataIndex(m) else 0;
}
};
const metadata_adapter: MetadataAdapter = .{ .constant_adapter = constant_adapter };
{
const MetadataBlock = ir.ModuleBlock.MetadataBlock;
var metadata_block = try module_block.enterSubBlock(MetadataBlock, true);
const MetadataBlockWriter = @TypeOf(metadata_block);
const strings_len: u32 = @intCast(self.metadata_string_map.count());
if (strings_len > 0) {
const string_bytes_offset = string_bytes_offset: {
var string_bytes_bit_offset: u32 = 0;
for (
self.metadata_string_indices.items[0..strings_len],
self.metadata_string_indices.items[1..],
) |start, end| string_bytes_bit_offset += BitcodeWriter.bitsVbr(end - start, 6);
break :string_bytes_offset @divExact(
std.mem.alignForward(u32, string_bytes_bit_offset, 32),
8,
);
};
const string_bytes_len =
std.mem.alignForward(u32, @intCast(self.metadata_string_bytes.items.len), 4);
try bitcode.writeBits(
comptime MetadataBlockWriter.abbrevId(MetadataBlock.Strings),
MetadataBlockWriter.abbrev_len,
);
try bitcode.writeVbr(strings_len, 6);
try bitcode.writeVbr(string_bytes_offset, 6);
try bitcode.writeVbr(string_bytes_offset + string_bytes_len, 6);
try bitcode.alignTo32();
for (
self.metadata_string_indices.items[0..strings_len],
self.metadata_string_indices.items[1..],
) |start, end| try bitcode.writeVbr(end - start, 6);
try bitcode.writeBlob(self.metadata_string_bytes.items);
}
for (self.metadata_items.items(.tag), self.metadata_items.items(.data)) |tag, data| {
record.clearRetainingCapacity();
switch (tag) {
.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(
@backingInt(kind) - @backingInt(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);
try metadata_block.writeAbbrevAdapted(MetadataBlock.Location{
.line = extra.line,
.column = extra.column,
.scope = extra.scope,
.inlined_at = extra.inlined_at,
}, metadata_adapter);
},
.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,
.derived_typedef_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,
.derived_typedef_type => DW.TAG.typedef,
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.getOptionalMetadataIndex(extra.name.toMetadata()));
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(@backingInt(MetadataBlock.Code.ENUMERATOR), 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.Optional, self);
try metadata_block.writeAbbrevAdapted(MetadataBlock.Node{
.elements = elements,
}, metadata_adapter);
},
.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 = @fromBackingInt(@intCast(data));
try metadata_block.writeAbbrevAdapted(MetadataBlock.Constant{
.ty = constant.typeOf(self),
.constant = constant,
}, metadata_adapter);
},
}
}
for (self.metadata_named.keys(), self.metadata_named.values()) |name, operands| {
try metadata_block.writeAbbrev(MetadataBlock.Name{ .name = name.slice(self).? });
try metadata_block.writeAbbrevAdapted(MetadataBlock.NamedNode{
.elements = @ptrCast(self.metadata_extra.items[operands.index..][0..operands.len]),
}, metadata_adapter);
}
{
for (globals.keys()) |global_index| {
const global = global_index.ptrConst(self);
if (global.dbg.unwrap()) |dbg| {
switch (global.kind) {
.function => |f| if (f.ptrConst(self).instructions.len != 0) continue,
else => {},
}
try metadata_block.writeAbbrevAdapted(MetadataBlock.GlobalDeclAttachment{
.value = global_index.toConst(),
.kind = .dbg,
.metadata = dbg,
}, metadata_adapter);
}
}
}
try metadata_block.end();
}
{
const OperandBundleTagsBlock = ir.ModuleBlock.OperandBundleTagsBlock;
var operand_bundle_tags_block = try module_block.enterSubBlock(OperandBundleTagsBlock, true);
try operand_bundle_tags_block.writeAbbrev(OperandBundleTagsBlock.OperandBundleTag{
.tag = "cold",
});
try operand_bundle_tags_block.end();
}
{
const BlockInfoBlock = ir.BlockInfoBlock;
var block_info_block = try module_block.enterSubBlock(BlockInfoBlock, true);
try block_info_block.writeUnabbrev(BlockInfoBlock.set_block_id, &.{
@backingInt(ir.ModuleBlock.FunctionBlock.id),
});
inline for (ir.ModuleBlock.FunctionBlock.abbrevs) |abbrev| {
try block_info_block.defineAbbrev(&abbrev.ops);
}
try block_info_block.writeUnabbrev(BlockInfoBlock.set_block_id, &.{
@backingInt(ir.ModuleBlock.FunctionBlock.ValueSymtabBlock.id),
});
inline for (ir.ModuleBlock.FunctionBlock.ValueSymtabBlock.abbrevs) |abbrev| {
try block_info_block.defineAbbrev(&abbrev.ops);
}
try block_info_block.writeUnabbrev(BlockInfoBlock.set_block_id, &.{
@backingInt(ir.ModuleBlock.FunctionBlock.MetadataBlock.id),
});
inline for (ir.ModuleBlock.FunctionBlock.MetadataBlock.abbrevs) |abbrev| {
try block_info_block.defineAbbrev(&abbrev.ops);
}
try block_info_block.writeUnabbrev(BlockInfoBlock.set_block_id, &.{
@backingInt(ir.ModuleBlock.FunctionBlock.MetadataAttachmentBlock.id),
});
inline for (ir.ModuleBlock.FunctionBlock.MetadataAttachmentBlock.abbrevs) |abbrev| {
try block_info_block.defineAbbrev(&abbrev.ops);
}
try block_info_block.end();
}
{
const FunctionAdapter = struct {
metadata_adapter: MetadataAdapter,
func: *const Function,
instruction_index: Function.Instruction.Index,
pub fn get(adapter: @This(), param: anytype) switch (@TypeOf(param)) {
Value, Constant, FunctionAttributes => u32,
else => |Result| Result,
} {
return switch (@TypeOf(param)) {
Value => adapter.getOffsetValueIndex(param),
Constant => adapter.getOffsetConstantIndex(param),
FunctionAttributes => switch (param) {
.none => 0,
else => @intCast(1 + adapter.metadata_adapter.constant_adapter.builder
.function_attributes_set.getIndex(param).?),
},
else => param,
};
}
pub fn getValueIndex(adapter: @This(), value: Value) u32 {
return @intCast(switch (value.unwrap()) {
.instruction => |instruction| instruction.valueIndex(adapter.func) + adapter.firstInstr(),
.constant => |constant| adapter.metadata_adapter.constant_adapter.getConstantIndex(constant),
.metadata => |metadata| {
const builder = adapter.metadata_adapter.constant_adapter.builder;
const unwrapped_metadata = metadata.unwrap(builder);
return switch (unwrapped_metadata.kind) {
.string, .node => adapter.metadata_adapter.getMetadataIndex(unwrapped_metadata),
.forward => unreachable,
.local => @intCast(builder.metadata_string_map.count() +
builder.metadata_map.count() +
unwrapped_metadata.index),
};
},
});
}
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.metadata_adapter.constant_adapter.numConstants();
}
};
for (self.functions.items, 0..) |func, func_index| {
switch (func.global.ptrConst(self).kind) {
.function => |f| if (@backingInt(f) != func_index) continue,
else => continue,
}
const FunctionBlock = ir.ModuleBlock.FunctionBlock;
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 = .{
.metadata_adapter = metadata_adapter,
.func = &func,
.instruction_index = @fromBackingInt(@intCast(0)),
};
if (!func.strip and func.debug_values.len > 0) {
const MetadataBlock = ir.ModuleBlock.FunctionBlock.MetadataBlock;
var metadata_block = try function_block.enterSubBlock(MetadataBlock, false);
for (func.debug_values) |value| {
try metadata_block.writeAbbrev(MetadataBlock.Value{
.ty = value.typeOf(@fromBackingInt(@intCast(func_index)), self),
.value = @fromBackingInt(@intCast(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 = @fromBackingInt(@intCast(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(@fromBackingInt(@intCast(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(),
});
},
.@"trunc nuw",
.@"trunc nsw",
.@"trunc nuw nsw",
=> |kind| {
const extra = func.extraData(Function.Instruction.Cast, data);
try function_block.writeAbbrev(FunctionBlock.TruncNoWrap{
.val = adapter.getOffsetValueIndex(extra.val),
.type_index = extra.type,
.flags = .{
.no_unsigned_wrap = kind == .@"trunc nuw" or kind == .@"trunc nuw nsw",
.no_signed_wrap = kind == .@"trunc nsw" or kind == .@"trunc nuw nsw",
},
});
},
.@"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(@fromBackingInt(@intCast(data))),
}),
.@"fneg fast" => try function_block.writeAbbrev(FunctionBlock.FNegFast{
.val = adapter.getOffsetValueIndex(@fromBackingInt(@intCast(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(@fromBackingInt(@intCast(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 = @backingInt(extra.then),
.else_block = @backingInt(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);
record.appendAssumeCapacity(@backingInt(extra.data.val.typeOf(@fromBackingInt(@intCast(func_index)), self)));
record.appendAssumeCapacity(adapter.getOffsetValueIndex(extra.data.val));
record.appendAssumeCapacity(@backingInt(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.metadata_adapter.constant_adapter.getConstantIndex(val));
record.appendAssumeCapacity(@backingInt(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(@fromBackingInt(@intCast(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(@backingInt(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(@backingInt(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(@fromBackingInt(@intCast(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.writeAbbrevAdapted(FunctionBlock.DebugLoc{
.line = location.line,
.column = location.column,
.scope = location.scope,
.inlined_at = location.inlined_at,
}, metadata_adapter);
has_location = true;
},
}
} else if (has_location) {
try function_block.writeAbbrev(FunctionBlock.DebugLocAgain{});
}
}
}
if (!func.strip) {
const ValueSymtabBlock = ir.ModuleBlock.FunctionBlock.ValueSymtabBlock;
var value_symtab_block = try function_block.enterSubBlock(ValueSymtabBlock, 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(ValueSymtabBlock.BlockEntry{
.value_id = @intCast(block_index),
.string = name.slice(self).?,
});
}
try value_symtab_block.end();
}
{
const MetadataAttachmentBlock = ir.ModuleBlock.FunctionBlock.MetadataAttachmentBlock;
var metadata_attach_block = try function_block.enterSubBlock(MetadataAttachmentBlock, false);
if (func.global.ptrConst(self).dbg.unwrap()) |dbg| {
try metadata_attach_block.writeAbbrevAdapted(MetadataAttachmentBlock.AttachmentGlobalSingle{
.kind = .dbg,
.metadata = dbg,
}, metadata_adapter);
}
var instr_index: u32 = 0;
for (func.instructions.items(.tag), func.instructions.items(.data)) |instr_tag, data| switch (instr_tag) {
.arg, .block => {},
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.writeAbbrevAdapted(MetadataAttachmentBlock.AttachmentInstructionSingle{
.inst = instr_index,
.kind = .unpredictable,
.metadata = .empty_tuple,
}, metadata_adapter),
_ => try metadata_attach_block.writeAbbrevAdapted(MetadataAttachmentBlock.AttachmentInstructionSingle{
.inst = instr_index,
.kind = .prof,
.metadata = weights.toMetadata(),
}, metadata_adapter),
}
instr_index += 1;
},
};
try metadata_attach_block.end();
}
try function_block.end();
}
}
try module_block.end();
}
{
const Strtab = ir.StrtabBlock;
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();
}