master
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const assert = std.debug.assert;
4const BigIntConst = std.math.big.int.Const;
5const BigIntMutable = std.math.big.int.Mutable;
6const Hash = std.hash.Wyhash;
7const Limb = std.math.big.Limb;
8
9const Interner = @This();
10
11map: std.AutoArrayHashMapUnmanaged(void, void) = .empty,
12items: std.MultiArrayList(struct {
13 tag: Tag,
14 data: u32,
15}) = .empty,
16extra: std.ArrayList(u32) = .empty,
17limbs: std.ArrayList(Limb) = .empty,
18strings: std.ArrayList(u8) = .empty,
19
20const KeyAdapter = struct {
21 interner: *const Interner,
22
23 pub fn eql(adapter: KeyAdapter, a: Key, b_void: void, b_map_index: usize) bool {
24 _ = b_void;
25 return adapter.interner.get(@as(Ref, @enumFromInt(b_map_index))).eql(a);
26 }
27
28 pub fn hash(adapter: KeyAdapter, a: Key) u32 {
29 _ = adapter;
30 return a.hash();
31 }
32};
33
34pub const Key = union(enum) {
35 int_ty: u16,
36 float_ty: u16,
37 complex_ty: u16,
38 ptr_ty,
39 noreturn_ty,
40 void_ty,
41 func_ty,
42 array_ty: struct {
43 len: u64,
44 child: Ref,
45 },
46 vector_ty: struct {
47 len: u32,
48 child: Ref,
49 },
50 record_ty: []const Ref,
51 /// May not be zero
52 null,
53 int: union(enum) {
54 u64: u64,
55 i64: i64,
56 big_int: BigIntConst,
57
58 pub fn toBigInt(repr: @This(), space: *Tag.Int.BigIntSpace) BigIntConst {
59 return switch (repr) {
60 .big_int => |x| x,
61 inline .u64, .i64 => |x| BigIntMutable.init(&space.limbs, x).toConst(),
62 };
63 }
64 },
65 float: Float,
66 complex: Complex,
67 bytes: []const u8,
68 pointer: Pointer,
69
70 pub const Float = union(enum) {
71 f16: f16,
72 f32: f32,
73 f64: f64,
74 f80: f80,
75 f128: f128,
76 };
77 pub const Complex = union(enum) {
78 cf16: [2]f16,
79 cf32: [2]f32,
80 cf64: [2]f64,
81 cf80: [2]f80,
82 cf128: [2]f128,
83 };
84 pub const Pointer = struct {
85 /// NodeIndex of decl or compound literal whose address we are offsetting from
86 node: u32,
87 /// Offset in bytes
88 offset: Ref,
89 };
90
91 pub fn hash(key: Key) u32 {
92 var hasher = Hash.init(0);
93 const tag = std.meta.activeTag(key);
94 std.hash.autoHash(&hasher, tag);
95 switch (key) {
96 .bytes => |bytes| {
97 hasher.update(bytes);
98 },
99 .record_ty => |elems| for (elems) |elem| {
100 std.hash.autoHash(&hasher, elem);
101 },
102 .float => |repr| switch (repr) {
103 inline else => |data| std.hash.autoHash(
104 &hasher,
105 @as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(data))), @bitCast(data)),
106 ),
107 },
108 .complex => |repr| switch (repr) {
109 inline else => |data| std.hash.autoHash(
110 &hasher,
111 @as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(data))), @bitCast(data)),
112 ),
113 },
114 .int => |repr| {
115 var space: Tag.Int.BigIntSpace = undefined;
116 const big = repr.toBigInt(&space);
117 std.hash.autoHash(&hasher, big.positive);
118 for (big.limbs) |limb| std.hash.autoHash(&hasher, limb);
119 },
120 inline else => |info| {
121 std.hash.autoHash(&hasher, info);
122 },
123 }
124 return @truncate(hasher.final());
125 }
126
127 pub fn eql(a: Key, b: Key) bool {
128 const KeyTag = std.meta.Tag(Key);
129 const a_tag: KeyTag = a;
130 const b_tag: KeyTag = b;
131 if (a_tag != b_tag) return false;
132 switch (a) {
133 .record_ty => |a_elems| {
134 const b_elems = b.record_ty;
135 if (a_elems.len != b_elems.len) return false;
136 for (a_elems, b_elems) |a_elem, b_elem| {
137 if (a_elem != b_elem) return false;
138 }
139 return true;
140 },
141 .bytes => |a_bytes| {
142 const b_bytes = b.bytes;
143 return std.mem.eql(u8, a_bytes, b_bytes);
144 },
145 .int => |a_repr| {
146 var a_space: Tag.Int.BigIntSpace = undefined;
147 const a_big = a_repr.toBigInt(&a_space);
148 var b_space: Tag.Int.BigIntSpace = undefined;
149 const b_big = b.int.toBigInt(&b_space);
150
151 return a_big.eql(b_big);
152 },
153 inline else => |a_info, tag| {
154 const b_info = @field(b, @tagName(tag));
155 return std.meta.eql(a_info, b_info);
156 },
157 }
158 }
159
160 fn toRef(key: Key) ?Ref {
161 switch (key) {
162 .int_ty => |bits| switch (bits) {
163 1 => return .i1,
164 8 => return .i8,
165 16 => return .i16,
166 32 => return .i32,
167 64 => return .i64,
168 128 => return .i128,
169 else => {},
170 },
171 .float_ty => |bits| switch (bits) {
172 16 => return .f16,
173 32 => return .f32,
174 64 => return .f64,
175 80 => return .f80,
176 128 => return .f128,
177 else => unreachable,
178 },
179 .complex_ty => |bits| switch (bits) {
180 16 => return .cf16,
181 32 => return .cf32,
182 64 => return .cf64,
183 80 => return .cf80,
184 128 => return .cf128,
185 else => unreachable,
186 },
187 .ptr_ty => return .ptr,
188 .func_ty => return .func,
189 .noreturn_ty => return .noreturn,
190 .void_ty => return .void,
191 .int => |repr| {
192 var space: Tag.Int.BigIntSpace = undefined;
193 const big = repr.toBigInt(&space);
194 if (big.eqlZero()) return .zero;
195 const big_one = BigIntConst{ .limbs = &.{1}, .positive = true };
196 if (big.eql(big_one)) return .one;
197 },
198 .float => |repr| switch (repr) {
199 inline else => |data| {
200 if (std.math.isPositiveZero(data)) return .zero;
201 if (data == 1) return .one;
202 },
203 },
204 .null => return .null,
205 else => {},
206 }
207 return null;
208 }
209
210 pub fn toBigInt(key: Key, space: *Tag.Int.BigIntSpace) BigIntConst {
211 return key.int.toBigInt(space);
212 }
213};
214
215pub const Ref = enum(u32) {
216 const max = std.math.maxInt(u32);
217
218 ptr = max - 1,
219 noreturn = max - 2,
220 void = max - 3,
221 i1 = max - 4,
222 i8 = max - 5,
223 i16 = max - 6,
224 i32 = max - 7,
225 i64 = max - 8,
226 i128 = max - 9,
227 f16 = max - 10,
228 f32 = max - 11,
229 f64 = max - 12,
230 f80 = max - 13,
231 f128 = max - 14,
232 func = max - 15,
233 zero = max - 16,
234 one = max - 17,
235 null = max - 18,
236 cf16 = max - 19,
237 cf32 = max - 20,
238 cf64 = max - 21,
239 cf80 = max - 22,
240 cf128 = max - 23,
241 _,
242};
243
244pub const OptRef = enum(u32) {
245 const max = std.math.maxInt(u32);
246
247 none = max - 0,
248 ptr = max - 1,
249 noreturn = max - 2,
250 void = max - 3,
251 i1 = max - 4,
252 i8 = max - 5,
253 i16 = max - 6,
254 i32 = max - 7,
255 i64 = max - 8,
256 i128 = max - 9,
257 f16 = max - 10,
258 f32 = max - 11,
259 f64 = max - 12,
260 f80 = max - 13,
261 f128 = max - 14,
262 func = max - 15,
263 zero = max - 16,
264 one = max - 17,
265 null = max - 18,
266 cf16 = max - 19,
267 cf32 = max - 20,
268 cf64 = max - 21,
269 cf80 = max - 22,
270 cf128 = max - 23,
271 _,
272};
273
274pub const Tag = enum(u8) {
275 /// `data` is `u16`
276 int_ty,
277 /// `data` is `u16`
278 float_ty,
279 /// `data` is `u16`
280 complex_ty,
281 /// `data` is index to `Array`
282 array_ty,
283 /// `data` is index to `Vector`
284 vector_ty,
285 /// `data` is `u32`
286 u32,
287 /// `data` is `i32`
288 i32,
289 /// `data` is `Int`
290 int_positive,
291 /// `data` is `Int`
292 int_negative,
293 /// `data` is `f16`
294 f16,
295 /// `data` is `f32`
296 f32,
297 /// `data` is `F64`
298 f64,
299 /// `data` is `F80`
300 f80,
301 /// `data` is `F128`
302 f128,
303 /// `data` is `CF16`
304 cf16,
305 /// `data` is `CF32`
306 cf32,
307 /// `data` is `CF64`
308 cf64,
309 /// `data` is `CF80`
310 cf80,
311 /// `data` is `CF128`
312 cf128,
313 /// `data` is `Bytes`
314 bytes,
315 /// `data` is `Record`
316 record_ty,
317 /// `data` is Pointer
318 pointer,
319
320 pub const Array = struct {
321 len0: u32,
322 len1: u32,
323 child: Ref,
324
325 pub fn getLen(a: Array) u64 {
326 return (PackedU64{
327 .a = a.len0,
328 .b = a.len1,
329 }).get();
330 }
331 };
332
333 pub const Vector = struct {
334 len: u32,
335 child: Ref,
336 };
337
338 pub const Pointer = struct {
339 node: u32,
340 offset: Ref,
341 };
342
343 pub const Int = struct {
344 limbs_index: u32,
345 limbs_len: u32,
346
347 /// Big enough to fit any non-BigInt value
348 pub const BigIntSpace = struct {
349 /// The +1 is headroom so that operations such as incrementing once
350 /// or decrementing once are possible without using an allocator.
351 limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
352 };
353 };
354
355 pub const F64 = struct {
356 piece0: u32,
357 piece1: u32,
358
359 pub fn get(self: F64) f64 {
360 const int_bits = @as(u64, self.piece0) | (@as(u64, self.piece1) << 32);
361 return @bitCast(int_bits);
362 }
363
364 fn pack(val: f64) F64 {
365 const bits = @as(u64, @bitCast(val));
366 return .{
367 .piece0 = @as(u32, @truncate(bits)),
368 .piece1 = @as(u32, @truncate(bits >> 32)),
369 };
370 }
371 };
372
373 pub const F80 = struct {
374 piece0: u32,
375 piece1: u32,
376 piece2: u32, // u16 part, top bits
377
378 pub fn get(self: F80) f80 {
379 const int_bits = @as(u80, self.piece0) |
380 (@as(u80, self.piece1) << 32) |
381 (@as(u80, self.piece2) << 64);
382 return @bitCast(int_bits);
383 }
384
385 fn pack(val: f80) F80 {
386 const bits = @as(u80, @bitCast(val));
387 return .{
388 .piece0 = @as(u32, @truncate(bits)),
389 .piece1 = @as(u32, @truncate(bits >> 32)),
390 .piece2 = @as(u16, @truncate(bits >> 64)),
391 };
392 }
393 };
394
395 pub const F128 = struct {
396 piece0: u32,
397 piece1: u32,
398 piece2: u32,
399 piece3: u32,
400
401 pub fn get(self: F128) f128 {
402 const int_bits = @as(u128, self.piece0) |
403 (@as(u128, self.piece1) << 32) |
404 (@as(u128, self.piece2) << 64) |
405 (@as(u128, self.piece3) << 96);
406 return @bitCast(int_bits);
407 }
408
409 fn pack(val: f128) F128 {
410 const bits = @as(u128, @bitCast(val));
411 return .{
412 .piece0 = @as(u32, @truncate(bits)),
413 .piece1 = @as(u32, @truncate(bits >> 32)),
414 .piece2 = @as(u32, @truncate(bits >> 64)),
415 .piece3 = @as(u32, @truncate(bits >> 96)),
416 };
417 }
418 };
419
420 pub const CF16 = struct {
421 piece0: u32,
422
423 pub fn get(self: CF16) [2]f16 {
424 const real: f16 = @bitCast(@as(u16, @truncate(self.piece0 >> 16)));
425 const imag: f16 = @bitCast(@as(u16, @truncate(self.piece0)));
426 return .{
427 real,
428 imag,
429 };
430 }
431
432 fn pack(val: [2]f16) CF16 {
433 const real: u16 = @bitCast(val[0]);
434 const imag: u16 = @bitCast(val[1]);
435 return .{
436 .piece0 = (@as(u32, real) << 16) | @as(u32, imag),
437 };
438 }
439 };
440
441 pub const CF32 = struct {
442 piece0: u32,
443 piece1: u32,
444
445 pub fn get(self: CF32) [2]f32 {
446 return .{
447 @bitCast(self.piece0),
448 @bitCast(self.piece1),
449 };
450 }
451
452 fn pack(val: [2]f32) CF32 {
453 return .{
454 .piece0 = @bitCast(val[0]),
455 .piece1 = @bitCast(val[1]),
456 };
457 }
458 };
459
460 pub const CF64 = struct {
461 piece0: u32,
462 piece1: u32,
463 piece2: u32,
464 piece3: u32,
465
466 pub fn get(self: CF64) [2]f64 {
467 return .{
468 (F64{ .piece0 = self.piece0, .piece1 = self.piece1 }).get(),
469 (F64{ .piece0 = self.piece2, .piece1 = self.piece3 }).get(),
470 };
471 }
472
473 fn pack(val: [2]f64) CF64 {
474 const real = F64.pack(val[0]);
475 const imag = F64.pack(val[1]);
476 return .{
477 .piece0 = real.piece0,
478 .piece1 = real.piece1,
479 .piece2 = imag.piece0,
480 .piece3 = imag.piece1,
481 };
482 }
483 };
484
485 /// TODO pack into 5 pieces
486 pub const CF80 = struct {
487 piece0: u32,
488 piece1: u32,
489 piece2: u32, // u16 part, top bits
490 piece3: u32,
491 piece4: u32,
492 piece5: u32, // u16 part, top bits
493
494 pub fn get(self: CF80) [2]f80 {
495 return .{
496 (F80{ .piece0 = self.piece0, .piece1 = self.piece1, .piece2 = self.piece2 }).get(),
497 (F80{ .piece0 = self.piece3, .piece1 = self.piece4, .piece2 = self.piece5 }).get(),
498 };
499 }
500
501 fn pack(val: [2]f80) CF80 {
502 const real = F80.pack(val[0]);
503 const imag = F80.pack(val[1]);
504 return .{
505 .piece0 = real.piece0,
506 .piece1 = real.piece1,
507 .piece2 = real.piece2,
508 .piece3 = imag.piece0,
509 .piece4 = imag.piece1,
510 .piece5 = imag.piece2,
511 };
512 }
513 };
514
515 pub const CF128 = struct {
516 piece0: u32,
517 piece1: u32,
518 piece2: u32,
519 piece3: u32,
520 piece4: u32,
521 piece5: u32,
522 piece6: u32,
523 piece7: u32,
524
525 pub fn get(self: CF128) [2]f128 {
526 return .{
527 (F128{ .piece0 = self.piece0, .piece1 = self.piece1, .piece2 = self.piece2, .piece3 = self.piece3 }).get(),
528 (F128{ .piece0 = self.piece4, .piece1 = self.piece5, .piece2 = self.piece6, .piece3 = self.piece7 }).get(),
529 };
530 }
531
532 fn pack(val: [2]f128) CF128 {
533 const real = F128.pack(val[0]);
534 const imag = F128.pack(val[1]);
535 return .{
536 .piece0 = real.piece0,
537 .piece1 = real.piece1,
538 .piece2 = real.piece2,
539 .piece3 = real.piece3,
540 .piece4 = imag.piece0,
541 .piece5 = imag.piece1,
542 .piece6 = imag.piece2,
543 .piece7 = imag.piece3,
544 };
545 }
546 };
547
548 pub const Bytes = struct {
549 strings_index: u32,
550 len: u32,
551 };
552
553 pub const Record = struct {
554 elements_len: u32,
555 // trailing
556 // [elements_len]Ref
557 };
558};
559
560pub const PackedU64 = packed struct(u64) {
561 a: u32,
562 b: u32,
563
564 pub fn get(x: PackedU64) u64 {
565 return @bitCast(x);
566 }
567
568 pub fn init(x: u64) PackedU64 {
569 return @bitCast(x);
570 }
571};
572
573pub fn deinit(i: *Interner, gpa: Allocator) void {
574 i.map.deinit(gpa);
575 i.items.deinit(gpa);
576 i.extra.deinit(gpa);
577 i.limbs.deinit(gpa);
578 i.strings.deinit(gpa);
579}
580
581pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref {
582 if (key.toRef()) |some| return some;
583 const adapter: KeyAdapter = .{ .interner = i };
584 const gop = try i.map.getOrPutAdapted(gpa, key, adapter);
585 if (gop.found_existing) return @enumFromInt(gop.index);
586 try i.items.ensureUnusedCapacity(gpa, 1);
587
588 switch (key) {
589 .int_ty => |bits| {
590 i.items.appendAssumeCapacity(.{
591 .tag = .int_ty,
592 .data = bits,
593 });
594 },
595 .float_ty => |bits| {
596 i.items.appendAssumeCapacity(.{
597 .tag = .float_ty,
598 .data = bits,
599 });
600 },
601 .complex_ty => |bits| {
602 i.items.appendAssumeCapacity(.{
603 .tag = .complex_ty,
604 .data = bits,
605 });
606 },
607 .array_ty => |info| {
608 const split_len = PackedU64.init(info.len);
609 i.items.appendAssumeCapacity(.{
610 .tag = .array_ty,
611 .data = try i.addExtra(gpa, Tag.Array{
612 .len0 = split_len.a,
613 .len1 = split_len.b,
614 .child = info.child,
615 }),
616 });
617 },
618 .vector_ty => |info| {
619 i.items.appendAssumeCapacity(.{
620 .tag = .vector_ty,
621 .data = try i.addExtra(gpa, Tag.Vector{
622 .len = info.len,
623 .child = info.child,
624 }),
625 });
626 },
627 .pointer => |info| {
628 i.items.appendAssumeCapacity(.{
629 .tag = .pointer,
630 .data = try i.addExtra(gpa, Tag.Pointer{
631 .node = info.node,
632 .offset = info.offset,
633 }),
634 });
635 },
636 .int => |repr| int: {
637 var space: Tag.Int.BigIntSpace = undefined;
638 const big = repr.toBigInt(&space);
639 switch (repr) {
640 .u64 => |data| if (std.math.cast(u32, data)) |small| {
641 i.items.appendAssumeCapacity(.{
642 .tag = .u32,
643 .data = small,
644 });
645 break :int;
646 },
647 .i64 => |data| if (std.math.cast(i32, data)) |small| {
648 i.items.appendAssumeCapacity(.{
649 .tag = .i32,
650 .data = @bitCast(small),
651 });
652 break :int;
653 },
654 .big_int => |data| {
655 if (data.fitsInTwosComp(.unsigned, 32)) {
656 i.items.appendAssumeCapacity(.{
657 .tag = .u32,
658 .data = data.toInt(u32) catch unreachable,
659 });
660 break :int;
661 } else if (data.fitsInTwosComp(.signed, 32)) {
662 i.items.appendAssumeCapacity(.{
663 .tag = .i32,
664 .data = @bitCast(data.toInt(i32) catch unreachable),
665 });
666 break :int;
667 }
668 },
669 }
670 const limbs_index: u32 = @intCast(i.limbs.items.len);
671 try i.limbs.appendSlice(gpa, big.limbs);
672 i.items.appendAssumeCapacity(.{
673 .tag = if (big.positive) .int_positive else .int_negative,
674 .data = try i.addExtra(gpa, Tag.Int{
675 .limbs_index = limbs_index,
676 .limbs_len = @intCast(big.limbs.len),
677 }),
678 });
679 },
680 .float => |repr| switch (repr) {
681 .f16 => |data| i.items.appendAssumeCapacity(.{
682 .tag = .f16,
683 .data = @as(u16, @bitCast(data)),
684 }),
685 .f32 => |data| i.items.appendAssumeCapacity(.{
686 .tag = .f32,
687 .data = @as(u32, @bitCast(data)),
688 }),
689 .f64 => |data| i.items.appendAssumeCapacity(.{
690 .tag = .f64,
691 .data = try i.addExtra(gpa, Tag.F64.pack(data)),
692 }),
693 .f80 => |data| i.items.appendAssumeCapacity(.{
694 .tag = .f80,
695 .data = try i.addExtra(gpa, Tag.F80.pack(data)),
696 }),
697 .f128 => |data| i.items.appendAssumeCapacity(.{
698 .tag = .f128,
699 .data = try i.addExtra(gpa, Tag.F128.pack(data)),
700 }),
701 },
702 .complex => |repr| switch (repr) {
703 .cf16 => |data| i.items.appendAssumeCapacity(.{
704 .tag = .cf16,
705 .data = try i.addExtra(gpa, Tag.CF16.pack(data)),
706 }),
707 .cf32 => |data| i.items.appendAssumeCapacity(.{
708 .tag = .cf32,
709 .data = try i.addExtra(gpa, Tag.CF32.pack(data)),
710 }),
711 .cf64 => |data| i.items.appendAssumeCapacity(.{
712 .tag = .cf64,
713 .data = try i.addExtra(gpa, Tag.CF64.pack(data)),
714 }),
715 .cf80 => |data| i.items.appendAssumeCapacity(.{
716 .tag = .cf80,
717 .data = try i.addExtra(gpa, Tag.CF80.pack(data)),
718 }),
719 .cf128 => |data| i.items.appendAssumeCapacity(.{
720 .tag = .cf128,
721 .data = try i.addExtra(gpa, Tag.CF128.pack(data)),
722 }),
723 },
724 .bytes => |bytes| {
725 const strings_index: u32 = @intCast(i.strings.items.len);
726 try i.strings.appendSlice(gpa, bytes);
727 i.items.appendAssumeCapacity(.{
728 .tag = .bytes,
729 .data = try i.addExtra(gpa, Tag.Bytes{
730 .strings_index = strings_index,
731 .len = @intCast(bytes.len),
732 }),
733 });
734 },
735 .record_ty => |elems| {
736 try i.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.Record).@"struct".fields.len +
737 elems.len);
738 i.items.appendAssumeCapacity(.{
739 .tag = .record_ty,
740 .data = i.addExtraAssumeCapacity(Tag.Record{
741 .elements_len = @intCast(elems.len),
742 }),
743 });
744 i.extra.appendSliceAssumeCapacity(@ptrCast(elems));
745 },
746 .ptr_ty,
747 .noreturn_ty,
748 .void_ty,
749 .func_ty,
750 .null,
751 => unreachable,
752 }
753
754 return @enumFromInt(gop.index);
755}
756
757fn addExtra(i: *Interner, gpa: Allocator, extra: anytype) Allocator.Error!u32 {
758 const fields = @typeInfo(@TypeOf(extra)).@"struct".fields;
759 try i.extra.ensureUnusedCapacity(gpa, fields.len);
760 return i.addExtraAssumeCapacity(extra);
761}
762
763fn addExtraAssumeCapacity(i: *Interner, extra: anytype) u32 {
764 const result = @as(u32, @intCast(i.extra.items.len));
765 inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| {
766 i.extra.appendAssumeCapacity(switch (field.type) {
767 Ref => @intFromEnum(@field(extra, field.name)),
768 u32 => @field(extra, field.name),
769 else => @compileError("bad field type: " ++ @typeName(field.type)),
770 });
771 }
772 return result;
773}
774
775pub fn get(i: *const Interner, ref: Ref) Key {
776 switch (ref) {
777 .ptr => return .ptr_ty,
778 .func => return .func_ty,
779 .noreturn => return .noreturn_ty,
780 .void => return .void_ty,
781 .i1 => return .{ .int_ty = 1 },
782 .i8 => return .{ .int_ty = 8 },
783 .i16 => return .{ .int_ty = 16 },
784 .i32 => return .{ .int_ty = 32 },
785 .i64 => return .{ .int_ty = 64 },
786 .i128 => return .{ .int_ty = 128 },
787 .f16 => return .{ .float_ty = 16 },
788 .f32 => return .{ .float_ty = 32 },
789 .f64 => return .{ .float_ty = 64 },
790 .f80 => return .{ .float_ty = 80 },
791 .f128 => return .{ .float_ty = 128 },
792 .zero => return .{ .int = .{ .u64 = 0 } },
793 .one => return .{ .int = .{ .u64 = 1 } },
794 .null => return .null,
795 .cf16 => return .{ .complex_ty = 16 },
796 .cf32 => return .{ .complex_ty = 32 },
797 .cf64 => return .{ .complex_ty = 64 },
798 .cf80 => return .{ .complex_ty = 80 },
799 else => {},
800 }
801
802 const item = i.items.get(@intFromEnum(ref));
803 const data = item.data;
804 return switch (item.tag) {
805 .int_ty => .{ .int_ty = @intCast(data) },
806 .float_ty => .{ .float_ty = @intCast(data) },
807 .complex_ty => .{ .complex_ty = @intCast(data) },
808 .array_ty => {
809 const array_ty = i.extraData(Tag.Array, data);
810 return .{ .array_ty = .{
811 .len = array_ty.getLen(),
812 .child = array_ty.child,
813 } };
814 },
815 .vector_ty => {
816 const vector_ty = i.extraData(Tag.Vector, data);
817 return .{ .vector_ty = .{
818 .len = vector_ty.len,
819 .child = vector_ty.child,
820 } };
821 },
822 .pointer => {
823 const pointer = i.extraData(Tag.Pointer, data);
824 return .{ .pointer = .{
825 .node = pointer.node,
826 .offset = pointer.offset,
827 } };
828 },
829 .u32 => .{ .int = .{ .u64 = data } },
830 .i32 => .{ .int = .{ .i64 = @as(i32, @bitCast(data)) } },
831 .int_positive, .int_negative => {
832 const int_info = i.extraData(Tag.Int, data);
833 const limbs = i.limbs.items[int_info.limbs_index..][0..int_info.limbs_len];
834 return .{ .int = .{
835 .big_int = .{
836 .positive = item.tag == .int_positive,
837 .limbs = limbs,
838 },
839 } };
840 },
841 .f16 => .{ .float = .{ .f16 = @bitCast(@as(u16, @intCast(data))) } },
842 .f32 => .{ .float = .{ .f32 = @bitCast(data) } },
843 .f64 => {
844 const float = i.extraData(Tag.F64, data);
845 return .{ .float = .{ .f64 = float.get() } };
846 },
847 .f80 => {
848 const float = i.extraData(Tag.F80, data);
849 return .{ .float = .{ .f80 = float.get() } };
850 },
851 .f128 => {
852 const float = i.extraData(Tag.F128, data);
853 return .{ .float = .{ .f128 = float.get() } };
854 },
855 .cf16 => {
856 const components = i.extraData(Tag.CF16, data);
857 return .{ .complex = .{ .cf16 = components.get() } };
858 },
859 .cf32 => {
860 const components = i.extraData(Tag.CF32, data);
861 return .{ .complex = .{ .cf32 = components.get() } };
862 },
863 .cf64 => {
864 const components = i.extraData(Tag.CF64, data);
865 return .{ .complex = .{ .cf64 = components.get() } };
866 },
867 .cf80 => {
868 const components = i.extraData(Tag.CF80, data);
869 return .{ .complex = .{ .cf80 = components.get() } };
870 },
871 .cf128 => {
872 const components = i.extraData(Tag.CF128, data);
873 return .{ .complex = .{ .cf128 = components.get() } };
874 },
875 .bytes => {
876 const bytes = i.extraData(Tag.Bytes, data);
877 return .{ .bytes = i.strings.items[bytes.strings_index..][0..bytes.len] };
878 },
879 .record_ty => {
880 const extra = i.extraDataTrail(Tag.Record, data);
881 return .{
882 .record_ty = @ptrCast(i.extra.items[extra.end..][0..extra.data.elements_len]),
883 };
884 },
885 };
886}
887
888fn extraData(i: *const Interner, comptime T: type, index: usize) T {
889 return i.extraDataTrail(T, index).data;
890}
891
892fn extraDataTrail(i: *const Interner, comptime T: type, index: usize) struct { data: T, end: u32 } {
893 var result: T = undefined;
894 const fields = @typeInfo(T).@"struct".fields;
895 inline for (fields, 0..) |field, field_i| {
896 const int32 = i.extra.items[field_i + index];
897 @field(result, field.name) = switch (field.type) {
898 Ref => @enumFromInt(int32),
899 u32 => int32,
900 else => @compileError("bad field type: " ++ @typeName(field.type)),
901 };
902 }
903 return .{
904 .data = result,
905 .end = @intCast(index + fields.len),
906 };
907}