struct bitcode_writer [src]

Alias for std.zig.llvm.bitcode_writer

Members

Source

const std = @import("../../std.zig"); pub const AbbrevOp = union(enum) { literal: u32, // 0 fixed: u16, // 1 fixed_runtime: type, // 1 vbr: u16, // 2 char6: void, // 4 blob: void, // 5 array_fixed: u16, // 3, 1 array_fixed_runtime: type, // 3, 1 array_vbr: u16, // 3, 2 array_char6: void, // 3, 4 }; pub const Error = error{OutOfMemory}; pub fn BitcodeWriter(comptime types: []const type) type { return struct { const BcWriter = @This(); buffer: std.ArrayList(u32), bit_buffer: u32 = 0, bit_count: u5 = 0, widths: [types.len]u16, pub fn getTypeWidth(self: BcWriter, comptime Type: type) u16 { return self.widths[comptime std.mem.indexOfScalar(type, types, Type).?]; } pub fn init(allocator: std.mem.Allocator, widths: [types.len]u16) BcWriter { return .{ .buffer = std.ArrayList(u32).init(allocator), .widths = widths, }; } pub fn deinit(self: BcWriter) void { self.buffer.deinit(); } pub fn toOwnedSlice(self: *BcWriter) Error![]const u32 { std.debug.assert(self.bit_count == 0); return self.buffer.toOwnedSlice(); } pub fn length(self: BcWriter) usize { std.debug.assert(self.bit_count == 0); return self.buffer.items.len; } pub fn writeBits(self: *BcWriter, value: anytype, bits: u16) Error!void { if (bits == 0) return; var in_buffer = bufValue(value, 32); var in_bits = bits; // Store input bits in buffer if they fit otherwise store as many as possible and flush if (self.bit_count > 0) { const bits_remaining = 31 - self.bit_count + 1; const n: u5 = @intCast(@min(bits_remaining, in_bits)); const v = @as(u32, @truncate(in_buffer)) << self.bit_count; self.bit_buffer |= v; in_buffer >>= n; self.bit_count +%= n; in_bits -= n; if (self.bit_count != 0) return; try self.buffer.append(std.mem.nativeToLittle(u32, self.bit_buffer)); self.bit_buffer = 0; } // Write 32-bit chunks of input bits while (in_bits >= 32) { try self.buffer.append(std.mem.nativeToLittle(u32, @truncate(in_buffer))); in_buffer >>= 31; in_buffer >>= 1; in_bits -= 32; } // Store remaining input bits in buffer if (in_bits > 0) { self.bit_count = @intCast(in_bits); self.bit_buffer = @truncate(in_buffer); } } pub fn writeVBR(self: *BcWriter, value: anytype, comptime vbr_bits: usize) Error!void { comptime { std.debug.assert(vbr_bits > 1); if (@bitSizeOf(@TypeOf(value)) > 64) @compileError("Unsupported VBR block type: " ++ @typeName(@TypeOf(value))); } var in_buffer = bufValue(value, vbr_bits); const continue_bit = @as(@TypeOf(in_buffer), 1) << @intCast(vbr_bits - 1); const mask = continue_bit - 1; // If input is larger than one VBR block can store // then store vbr_bits - 1 bits and a continue bit while (in_buffer > mask) { try self.writeBits(in_buffer & mask | continue_bit, vbr_bits); in_buffer >>= @intCast(vbr_bits - 1); } // Store remaining bits try self.writeBits(in_buffer, vbr_bits); } pub fn bitsVBR(_: *const BcWriter, value: anytype, comptime vbr_bits: usize) u16 { comptime { std.debug.assert(vbr_bits > 1); if (@bitSizeOf(@TypeOf(value)) > 64) @compileError("Unsupported VBR block type: " ++ @typeName(@TypeOf(value))); } var bits: u16 = 0; var in_buffer = bufValue(value, vbr_bits); const continue_bit = @as(@TypeOf(in_buffer), 1) << @intCast(vbr_bits - 1); const mask = continue_bit - 1; // If input is larger than one VBR block can store // then store vbr_bits - 1 bits and a continue bit while (in_buffer > mask) { bits += @intCast(vbr_bits); in_buffer >>= @intCast(vbr_bits - 1); } // Store remaining bits bits += @intCast(vbr_bits); return bits; } pub fn write6BitChar(self: *BcWriter, c: u8) Error!void { try self.writeBits(charTo6Bit(c), 6); } pub fn writeBlob(self: *BcWriter, blob: []const u8) Error!void { const blob_word_size = std.mem.alignForward(usize, blob.len, 4); try self.buffer.ensureUnusedCapacity(blob_word_size + 1); self.alignTo32() catch unreachable; const slice = self.buffer.addManyAsSliceAssumeCapacity(blob_word_size / 4); const slice_bytes = std.mem.sliceAsBytes(slice); @memcpy(slice_bytes[0..blob.len], blob); @memset(slice_bytes[blob.len..], 0); } pub fn alignTo32(self: *BcWriter) Error!void { if (self.bit_count == 0) return; try self.buffer.append(std.mem.nativeToLittle(u32, self.bit_buffer)); self.bit_buffer = 0; self.bit_count = 0; } pub fn enterTopBlock(self: *BcWriter, comptime SubBlock: type) Error!BlockWriter(SubBlock) { return BlockWriter(SubBlock).init(self, 2, true); } fn BlockWriter(comptime Block: type) type { return struct { const Self = @This(); // The minimum abbrev id length based on the number of abbrevs present in the block pub const abbrev_len = std.math.log2_int_ceil( u6, 4 + (if (@hasDecl(Block, "abbrevs")) Block.abbrevs.len else 0), ); start: usize, bitcode: *BcWriter, pub fn init(bitcode: *BcWriter, comptime parent_abbrev_len: u6, comptime define_abbrevs: bool) Error!Self { try bitcode.writeBits(1, parent_abbrev_len); try bitcode.writeVBR(Block.id, 8); try bitcode.writeVBR(abbrev_len, 4); try bitcode.alignTo32(); // We store the index of the block size and store a dummy value as the number of words in the block const start = bitcode.length(); try bitcode.writeBits(0, 32); var self = Self{ .start = start, .bitcode = bitcode, }; // Predefine all block abbrevs if (define_abbrevs) { inline for (Block.abbrevs) |Abbrev| { try self.defineAbbrev(&Abbrev.ops); } } return self; } pub fn enterSubBlock(self: Self, comptime SubBlock: type, comptime define_abbrevs: bool) Error!BlockWriter(SubBlock) { return BlockWriter(SubBlock).init(self.bitcode, abbrev_len, define_abbrevs); } pub fn end(self: *Self) Error!void { try self.bitcode.writeBits(0, abbrev_len); try self.bitcode.alignTo32(); // Set the number of words in the block at the start of the block self.bitcode.buffer.items[self.start] = std.mem.nativeToLittle(u32, @truncate(self.bitcode.length() - self.start - 1)); } pub fn writeUnabbrev(self: *Self, code: u32, values: []const u64) Error!void { try self.bitcode.writeBits(3, abbrev_len); try self.bitcode.writeVBR(code, 6); try self.bitcode.writeVBR(values.len, 6); for (values) |val| { try self.bitcode.writeVBR(val, 6); } } pub fn writeAbbrev(self: *Self, params: anytype) Error!void { return self.writeAbbrevAdapted(params, struct { pub fn get(_: @This(), param: anytype, comptime _: []const u8) @TypeOf(param) { return param; } }{}); } pub fn abbrevId(comptime Abbrev: type) u32 { inline for (Block.abbrevs, 0..) |abbrev, i| { if (Abbrev == abbrev) return i + 4; } @compileError("Unknown abbrev: " ++ @typeName(Abbrev)); } pub fn writeAbbrevAdapted( self: *Self, params: anytype, adapter: anytype, ) Error!void { const Abbrev = @TypeOf(params); try self.bitcode.writeBits(comptime abbrevId(Abbrev), abbrev_len); const fields = std.meta.fields(Abbrev); // This abbreviation might only contain literals if (fields.len == 0) return; comptime var field_index: usize = 0; inline for (Abbrev.ops) |ty| { const field_name = fields[field_index].name; const param = @field(params, field_name); switch (ty) { .literal => continue, .fixed => |len| try self.bitcode.writeBits(adapter.get(param, field_name), len), .fixed_runtime => |width_ty| try self.bitcode.writeBits( adapter.get(param, field_name), self.bitcode.getTypeWidth(width_ty), ), .vbr => |len| try self.bitcode.writeVBR(adapter.get(param, field_name), len), .char6 => try self.bitcode.write6BitChar(adapter.get(param, field_name)), .blob => { try self.bitcode.writeVBR(param.len, 6); try self.bitcode.writeBlob(param); }, .array_fixed => |len| { try self.bitcode.writeVBR(param.len, 6); for (param) |x| { try self.bitcode.writeBits(adapter.get(x, field_name), len); } }, .array_fixed_runtime => |width_ty| { try self.bitcode.writeVBR(param.len, 6); for (param) |x| { try self.bitcode.writeBits( adapter.get(x, field_name), self.bitcode.getTypeWidth(width_ty), ); } }, .array_vbr => |len| { try self.bitcode.writeVBR(param.len, 6); for (param) |x| { try self.bitcode.writeVBR(adapter.get(x, field_name), len); } }, .array_char6 => { try self.bitcode.writeVBR(param.len, 6); for (param) |x| { try self.bitcode.write6BitChar(adapter.get(x, field_name)); } }, } field_index += 1; if (field_index == fields.len) break; } } pub fn defineAbbrev(self: *Self, comptime ops: []const AbbrevOp) Error!void { const bitcode = self.bitcode; try bitcode.writeBits(2, abbrev_len); // ops.len is not accurate because arrays are actually two ops try bitcode.writeVBR(blk: { var count: usize = 0; inline for (ops) |op| { count += switch (op) { .literal, .fixed, .fixed_runtime, .vbr, .char6, .blob => 1, .array_fixed, .array_fixed_runtime, .array_vbr, .array_char6 => 2, }; } break :blk count; }, 5); inline for (ops) |op| { switch (op) { .literal => |value| { try bitcode.writeBits(1, 1); try bitcode.writeVBR(value, 8); }, .fixed => |width| { try bitcode.writeBits(0, 1); try bitcode.writeBits(1, 3); try bitcode.writeVBR(width, 5); }, .fixed_runtime => |width_ty| { try bitcode.writeBits(0, 1); try bitcode.writeBits(1, 3); try bitcode.writeVBR(bitcode.getTypeWidth(width_ty), 5); }, .vbr => |width| { try bitcode.writeBits(0, 1); try bitcode.writeBits(2, 3); try bitcode.writeVBR(width, 5); }, .char6 => { try bitcode.writeBits(0, 1); try bitcode.writeBits(4, 3); }, .blob => { try bitcode.writeBits(0, 1); try bitcode.writeBits(5, 3); }, .array_fixed => |width| { // Array op try bitcode.writeBits(0, 1); try bitcode.writeBits(3, 3); // Fixed or VBR op try bitcode.writeBits(0, 1); try bitcode.writeBits(1, 3); try bitcode.writeVBR(width, 5); }, .array_fixed_runtime => |width_ty| { // Array op try bitcode.writeBits(0, 1); try bitcode.writeBits(3, 3); // Fixed or VBR op try bitcode.writeBits(0, 1); try bitcode.writeBits(1, 3); try bitcode.writeVBR(bitcode.getTypeWidth(width_ty), 5); }, .array_vbr => |width| { // Array op try bitcode.writeBits(0, 1); try bitcode.writeBits(3, 3); // Fixed or VBR op try bitcode.writeBits(0, 1); try bitcode.writeBits(2, 3); try bitcode.writeVBR(width, 5); }, .array_char6 => { // Array op try bitcode.writeBits(0, 1); try bitcode.writeBits(3, 3); // Char6 op try bitcode.writeBits(0, 1); try bitcode.writeBits(4, 3); }, } } } }; } }; } fn charTo6Bit(c: u8) u8 { return switch (c) { 'a'...'z' => c - 'a', 'A'...'Z' => c - 'A' + 26, '0'...'9' => c - '0' + 52, '.' => 62, '_' => 63, else => @panic("Failed to encode byte as 6-bit char"), }; } fn BufType(comptime T: type, comptime min_len: usize) type { return std.meta.Int(.unsigned, @max(min_len, @bitSizeOf(switch (@typeInfo(T)) { .comptime_int => u32, .int => |info| if (info.signedness == .unsigned) T else @compileError("Unsupported type: " ++ @typeName(T)), .@"enum" => |info| info.tag_type, .bool => u1, .@"struct" => |info| switch (info.layout) { .auto, .@"extern" => @compileError("Unsupported type: " ++ @typeName(T)), .@"packed" => std.meta.Int(.unsigned, @bitSizeOf(T)), }, else => @compileError("Unsupported type: " ++ @typeName(T)), }))); } fn bufValue(value: anytype, comptime min_len: usize) BufType(@TypeOf(value), min_len) { return switch (@typeInfo(@TypeOf(value))) { .comptime_int, .int => @intCast(value), .@"enum" => @intFromEnum(value), .bool => @intFromBool(value), .@"struct" => @intCast(@as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(value))), @bitCast(value))), else => unreachable, }; }