authorgravatar for liljaanton2001@gmail.comantlilja <liljaanton2001@gmail.com> 2023-08-13 15:55:55+02:00
committergravatar for liljaanton2001@gmail.comantlilja <liljaanton2001@gmail.com> 2024-02-21 16:24:59+01:00
log239680616522c1908afb8935e5f8e644a9115403
tree2c41433f281bec283ec37631099db5919029cf1b
parent4653fc4bb4b101ca6a937ad97a6691515cae0fb7

Add LLVM bitcode writer


1 files changed, 425 insertions(+), 0 deletions(-)

src/codegen/llvm/bitcode_writer.zig created+425
...@@ -0,0 +1,425 @@
1const std = @import("std");
2
3pub const AbbrevOp = union(enum) {
4 literal: u32, // 0
5 fixed: u16, // 1
6 fixed_runtime: type, // 1
7 vbr: u16, // 2
8 char6: void, // 4
9 blob: void, // 5
10 array_fixed: u16, // 3, 1
11 array_fixed_runtime: type, // 3, 1
12 array_vbr: u16, // 3, 2
13 array_char6: void, // 3, 4
14};
15
16pub const Error = error{OutOfMemory};
17
18pub fn BitcodeWriter(comptime types: []const type) type {
19 return struct {
20 const BcWriter = @This();
21
22 buffer: std.ArrayList(u32),
23 bit_buffer: u32 = 0,
24 bit_count: u5 = 0,
25
26 widths: []const u16,
27
28 pub fn getTypeIndex(comptime ty: type) usize {
29 inline for (types, 0..) |t, i| {
30 if (t == ty) return i;
31 }
32 unreachable;
33 }
34
35 pub fn init(allocator: std.mem.Allocator, widths: []const u16) BcWriter {
36 std.debug.assert(widths.len == types.len);
37 return .{
38 .buffer = std.ArrayList(u32).init(allocator),
39 .widths = widths,
40 };
41 }
42
43 pub fn deinit(self: BcWriter) void {
44 self.buffer.deinit();
45 }
46
47 pub fn toSlice(self: BcWriter) []const u32 {
48 std.debug.assert(self.bit_count == 0);
49 return self.buffer.items;
50 }
51
52 pub fn length(self: BcWriter) usize {
53 std.debug.assert(self.bit_count == 0);
54 return self.buffer.items.len;
55 }
56
57 pub fn writeBits(self: *BcWriter, value: anytype, bits: u16) Error!void {
58 if (bits == 0) return;
59
60 var in_buffer = bufValue(value, 32);
61 var in_bits = bits;
62
63 // Store input bits in buffer if they fit otherwise store as many as possible and flush
64 if (self.bit_count > 0) {
65 const bits_remaining = 31 - self.bit_count + 1;
66 const n: u5 = @intCast(@min(bits_remaining, in_bits));
67 const v = @as(u32, @truncate(in_buffer)) << self.bit_count;
68 self.bit_buffer |= v;
69 in_buffer >>= n;
70
71 self.bit_count +%= n;
72 in_bits -= n;
73
74 if (self.bit_count != 0) return;
75 try self.buffer.append(self.bit_buffer);
76 self.bit_buffer = 0;
77 }
78
79 // Write 32-bit chunks of input bits
80 while (in_bits >= 32) {
81 try self.buffer.append(@truncate(in_buffer));
82
83 in_buffer >>= 31;
84 in_buffer >>= 1;
85 in_bits -= 32;
86 }
87
88 // Store remaining input bits in buffer
89 if (in_bits > 0) {
90 self.bit_count = @intCast(in_bits);
91 self.bit_buffer = @truncate(in_buffer);
92 }
93 }
94
95 pub fn writeVBR(self: *BcWriter, value: anytype, comptime vbr_bits: usize) Error!void {
96 comptime {
97 std.debug.assert(vbr_bits > 1);
98 if (@bitSizeOf(@TypeOf(value)) > 64) @compileError("Unsupported VBR block type: " ++ @typeName(@TypeOf(value)));
99 }
100
101 var in_buffer = bufValue(value, vbr_bits);
102
103 const continue_bit = @as(@TypeOf(in_buffer), 1) << @intCast(vbr_bits - 1);
104 const mask = continue_bit - 1;
105
106 // If input is larger than one VBR block can store
107 // then store vbr_bits - 1 bits and a continue bit
108 while (in_buffer > mask) {
109 try self.writeBits(in_buffer & mask | continue_bit, vbr_bits);
110 in_buffer >>= @intCast(vbr_bits - 1);
111 }
112
113 // Store remaining bits
114 try self.writeBits(in_buffer, vbr_bits);
115 }
116
117 pub fn bitsVBR(_: *const BcWriter, value: anytype, comptime vbr_bits: usize) u16 {
118 comptime {
119 std.debug.assert(vbr_bits > 1);
120 if (@bitSizeOf(@TypeOf(value)) > 64) @compileError("Unsupported VBR block type: " ++ @typeName(@TypeOf(value)));
121 }
122
123 var bits: u16 = 0;
124
125 var in_buffer = bufValue(value, vbr_bits);
126
127 const continue_bit = @as(@TypeOf(in_buffer), 1) << @intCast(vbr_bits - 1);
128 const mask = continue_bit - 1;
129
130 // If input is larger than one VBR block can store
131 // then store vbr_bits - 1 bits and a continue bit
132 while (in_buffer > mask) {
133 bits += @intCast(vbr_bits);
134 in_buffer >>= @intCast(vbr_bits - 1);
135 }
136
137 // Store remaining bits
138 bits += @intCast(vbr_bits);
139 return bits;
140 }
141
142 pub fn write6BitChar(self: *BcWriter, c: u8) Error!void {
143 try self.writeBits(charTo6Bit(c), 6);
144 }
145
146 pub fn alignTo32(self: *BcWriter) Error!void {
147 if (self.bit_count == 0) return;
148
149 try self.buffer.append(self.bit_buffer);
150 self.bit_buffer = 0;
151 self.bit_count = 0;
152 }
153
154 pub fn enterTopBlock(self: *BcWriter, comptime SubBlock: type) Error!BlockWriter(SubBlock) {
155 return BlockWriter(SubBlock).init(self, 2);
156 }
157
158 fn BlockWriter(comptime Block: type) type {
159 return struct {
160 const Self = @This();
161
162 // The minimum abbrev id length based on the number of abbrevs present in the block
163 pub const abbrev_len = std.math.log2_int_ceil(
164 u6,
165 4 + (if (@hasDecl(Block, "abbrevs")) Block.abbrevs.len else 0),
166 );
167
168 start: usize,
169 bitcode: *BcWriter,
170
171 pub fn init(bitcode: *BcWriter, comptime parent_abbrev_len: u6) Error!Self {
172 try bitcode.writeBits(1, parent_abbrev_len);
173 try bitcode.writeVBR(Block.id, 8);
174 try bitcode.writeVBR(abbrev_len, 4);
175 try bitcode.alignTo32();
176
177 // We store the index of the block size and store a dummy value as the number of words in the block
178 const start = bitcode.length();
179 try bitcode.writeBits(0, 32);
180
181 // Predefine all block abbrevs
182 inline for (Block.abbrevs) |Abbrev| {
183 try defineAbbrev(bitcode, &Abbrev.ops);
184 }
185
186 return .{
187 .start = start,
188 .bitcode = bitcode,
189 };
190 }
191
192 pub fn enterSubBlock(self: Self, comptime SubBlock: type) Error!BlockWriter(SubBlock) {
193 return BlockWriter(SubBlock).init(self.bitcode, abbrev_len);
194 }
195
196 pub fn end(self: *Self) Error!void {
197 try self.bitcode.writeBits(0, abbrev_len);
198 try self.bitcode.alignTo32();
199
200 // Set the number of words in the block at the start of the block
201 self.bitcode.buffer.items[self.start] = @truncate(self.bitcode.length() - self.start - 1);
202 }
203
204 pub fn writeUnabbrev(self: *Self, code: u32, values: []const u64) Error!void {
205 try self.bitcode.writeBits(3, abbrev_len);
206 try self.bitcode.writeVBR(code, 6);
207 try self.bitcode.writeVBR(values.len, 6);
208 for (values) |val| {
209 try self.bitcode.writeVBR(val, 6);
210 }
211 }
212
213 pub fn writeAbbrev(self: *Self, params: anytype) Error!void {
214 return self.writeAbbrevAdapted(params, struct {
215 pub fn get(_: @This(), param: anytype, comptime _: []const u8) @TypeOf(param) {
216 return param;
217 }
218 }{});
219 }
220
221 pub fn abbrevId(comptime Abbrev: type) u32 {
222 inline for (Block.abbrevs, 0..) |abbrev, i| {
223 if (Abbrev == abbrev) return i + 4;
224 }
225
226 @compileError("Unknown abbrev: " ++ @typeName(Abbrev));
227 }
228
229 pub fn writeAbbrevAdapted(
230 self: *Self,
231 params: anytype,
232 adapter: anytype,
233 ) Error!void {
234 const Abbrev = @TypeOf(params);
235
236 try self.bitcode.writeBits(comptime abbrevId(Abbrev), abbrev_len);
237
238 const fields = std.meta.fields(Abbrev);
239
240 // This abbreviation might only contain literals
241 if (fields.len == 0) return;
242
243 comptime var field_index: usize = 0;
244 inline for (Abbrev.ops) |ty| {
245 const field_name = fields[field_index].name;
246 const param = @field(params, field_name);
247
248 switch (ty) {
249 .literal => continue,
250 .fixed => |len| try self.bitcode.writeBits(adapter.get(param, field_name), len),
251 .fixed_runtime => |width_ty| try self.bitcode.writeBits(
252 adapter.get(param, field_name),
253 self.bitcode.widths[getTypeIndex(width_ty)],
254 ),
255 .vbr => |len| try self.bitcode.writeVBR(adapter.get(param, field_name), len),
256 .char6 => try self.bitcode.write6BitChar(adapter.get(param, field_name)),
257 .blob => {
258 try self.bitcode.writeVBR(param.len, 6);
259 try self.bitcode.alignTo32();
260 for (param) |x| {
261 try self.bitcode.writeBits(x, 8);
262 }
263 try self.bitcode.alignTo32();
264 },
265 .array_fixed => |len| {
266 try self.bitcode.writeVBR(param.len, 6);
267 for (param) |x| {
268 try self.bitcode.writeBits(adapter.get(x, field_name), len);
269 }
270 },
271 .array_fixed_runtime => |width_ty| {
272 try self.bitcode.writeVBR(param.len, 6);
273 for (param) |x| {
274 try self.bitcode.writeBits(
275 adapter.get(x, field_name),
276 self.bitcode.widths[getTypeIndex(width_ty)],
277 );
278 }
279 },
280 .array_vbr => |len| {
281 try self.bitcode.writeVBR(param.len, 6);
282 for (param) |x| {
283 try self.bitcode.writeVBR(adapter.get(x, field_name), len);
284 }
285 },
286 .array_char6 => {
287 try self.bitcode.writeVBR(param.len, 6);
288 for (param) |x| {
289 try self.bitcode.write6BitChar(adapter.get(x, field_name));
290 }
291 },
292 }
293 field_index += 1;
294 if (field_index == fields.len) break;
295 }
296 }
297
298 fn defineAbbrev(bitcode: *BcWriter, comptime ops: []const AbbrevOp) Error!void {
299 try bitcode.writeBits(2, abbrev_len);
300
301 // ops.len is not accurate because arrays are actually two ops
302 try bitcode.writeVBR(blk: {
303 var count: usize = 0;
304 inline for (ops) |op| {
305 count += switch (op) {
306 .literal, .fixed, .fixed_runtime, .vbr, .char6, .blob => 1,
307 .array_fixed, .array_fixed_runtime, .array_vbr, .array_char6 => 2,
308 };
309 }
310 break :blk count;
311 }, 5);
312
313 inline for (ops) |op| {
314 switch (op) {
315 .literal => |value| {
316 try bitcode.writeBits(1, 1);
317 try bitcode.writeVBR(value, 8);
318 },
319 .fixed => |width| {
320 try bitcode.writeBits(0, 1);
321 try bitcode.writeBits(1, 3);
322 try bitcode.writeVBR(width, 5);
323 },
324 .fixed_runtime => |width_ty| {
325 try bitcode.writeBits(0, 1);
326 try bitcode.writeBits(1, 3);
327 try bitcode.writeVBR(bitcode.widths[getTypeIndex(width_ty)], 5);
328 },
329 .vbr => |width| {
330 try bitcode.writeBits(0, 1);
331 try bitcode.writeBits(2, 3);
332 try bitcode.writeVBR(width, 5);
333 },
334 .char6 => {
335 try bitcode.writeBits(0, 1);
336 try bitcode.writeBits(4, 3);
337 },
338 .blob => {
339 try bitcode.writeBits(0, 1);
340 try bitcode.writeBits(5, 3);
341 },
342 .array_fixed => |width| {
343 // Array op
344 try bitcode.writeBits(0, 1);
345 try bitcode.writeBits(3, 3);
346
347 // Fixed or VBR op
348 try bitcode.writeBits(0, 1);
349 try bitcode.writeBits(1, 3);
350 try bitcode.writeVBR(width, 5);
351 },
352 .array_fixed_runtime => |width_ty| {
353 // Array op
354 try bitcode.writeBits(0, 1);
355 try bitcode.writeBits(3, 3);
356
357 // Fixed or VBR op
358 try bitcode.writeBits(0, 1);
359 try bitcode.writeBits(1, 3);
360 try bitcode.writeVBR(bitcode.widths[getTypeIndex(width_ty)], 5);
361 },
362 .array_vbr => |width| {
363 // Array op
364 try bitcode.writeBits(0, 1);
365 try bitcode.writeBits(3, 3);
366
367 // Fixed or VBR op
368 try bitcode.writeBits(0, 1);
369 try bitcode.writeBits(2, 3);
370 try bitcode.writeVBR(width, 5);
371 },
372 .array_char6 => {
373 // Array op
374 try bitcode.writeBits(0, 1);
375 try bitcode.writeBits(3, 3);
376
377 // Char6 op
378 try bitcode.writeBits(0, 1);
379 try bitcode.writeBits(4, 3);
380 },
381 }
382 }
383 }
384 };
385 }
386 };
387}
388
389fn charTo6Bit(c: u8) u8 {
390 return switch (c) {
391 'a'...'z' => c - 'a',
392 'A'...'Z' => c - 'A' + 26,
393 '0'...'9' => c - '0' + 52,
394 '.' => 62,
395 '_' => 63,
396 else => @panic("Failed to encode byte as 6-bit char"),
397 };
398}
399
400fn BufType(comptime T: type, comptime min_len: usize) type {
401 return std.meta.Int(.unsigned, @max(min_len, @bitSizeOf(switch (@typeInfo(T)) {
402 .ComptimeInt => u32,
403 .Int => |info| if (info.signedness == .unsigned)
404 T
405 else
406 @compileError("Unsupported type: " ++ @typeName(T)),
407 .Enum => |info| info.tag_type,
408 .Bool => u1,
409 .Struct => |info| switch (info.layout) {
410 .Auto, .Extern => @compileError("Unsupported type: " ++ @typeName(T)),
411 .Packed => std.meta.Int(.unsigned, @bitSizeOf(T)),
412 },
413 else => @compileError("Unsupported type: " ++ @typeName(T)),
414 })));
415}
416
417fn bufValue(value: anytype, comptime min_len: usize) BufType(@TypeOf(value), min_len) {
418 return switch (@typeInfo(@TypeOf(value))) {
419 .ComptimeInt, .Int => @intCast(value),
420 .Enum => @intFromEnum(value),
421 .Bool => @intFromBool(value),
422 .Struct => @intCast(@as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(value))), @bitCast(value))),
423 else => unreachable,
424 };
425}