Function toBitcode [src]

Prototype

pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitcode_writer.Error![]const u32

Parameters

self: *Builderallocator: Allocatorproducer: Producer

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