authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-02-20 21:31:57-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-02-20 23:48:36-05:00
logd8fada6b6325e07015fddd68bb4c6369a66f23f3
tree64d74490a26b80b9b53f7c292aca1aa4682dd0d8
parentdc1f50e505105cabe1ed53951ca612778d6019ee

CBE: add CType interning


5 files changed, 1504 insertions(+), 7 deletions(-)

CMakeLists.txt+1
......@@ -569,6 +569,7 @@ set(ZIG_STAGE2_SOURCES
569569 "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"
570570 "${CMAKE_SOURCE_DIR}/src/codegen.zig"
571571 "${CMAKE_SOURCE_DIR}/src/codegen/c.zig"
572 "${CMAKE_SOURCE_DIR}/src/codegen/c/type.zig"
572573 "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig"
573574 "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig"
574575 "${CMAKE_SOURCE_DIR}/src/glibc.zig"
src/Compilation.zig+4-3
......@@ -3266,8 +3266,8 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
32663266 const decl_emit_h = emit_h.declPtr(decl_index);
32673267 const fwd_decl = &decl_emit_h.fwd_decl;
32683268 fwd_decl.shrinkRetainingCapacity(0);
3269 var typedefs_arena = std.heap.ArenaAllocator.init(gpa);
3270 defer typedefs_arena.deinit();
3269 var ctypes_arena = std.heap.ArenaAllocator.init(gpa);
3270 defer ctypes_arena.deinit();
32713271
32723272 var dg: c_codegen.DeclGen = .{
32733273 .gpa = gpa,
......@@ -3276,8 +3276,9 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
32763276 .decl_index = decl_index,
32773277 .decl = decl,
32783278 .fwd_decl = fwd_decl.toManaged(gpa),
3279 .ctypes = .{},
32793280 .typedefs = c_codegen.TypedefMap.initContext(gpa, .{ .mod = module }),
3280 .typedefs_arena = typedefs_arena.allocator(),
3281 .typedefs_arena = ctypes_arena.allocator(),
32813282 };
32823283 defer {
32833284 for (dg.typedefs.values()) |typedef| {
src/codegen/c.zig+12-1
......@@ -27,6 +27,8 @@ const Mutability = enum { Const, ConstArgument, Mut };
2727const BigIntLimb = std.math.big.Limb;
2828const BigInt = std.math.big.int;
2929
30pub const CType = @import("c/type.zig").CType;
31
3032pub const CValue = union(enum) {
3133 none: void,
3234 local: LocalIndex,
......@@ -446,7 +448,8 @@ pub const Function = struct {
446448 return f.object.dg.fmtIntLiteral(ty, val);
447449 }
448450
449 pub fn deinit(f: *Function, gpa: mem.Allocator) void {
451 pub fn deinit(f: *Function) void {
452 const gpa = f.object.dg.gpa;
450453 f.allocs.deinit(gpa);
451454 f.locals.deinit(gpa);
452455 for (f.free_locals_stack.items) |*free_locals| {
......@@ -460,6 +463,7 @@ pub const Function = struct {
460463 gpa.free(typedef.rendered);
461464 }
462465 f.object.dg.typedefs.deinit();
466 f.object.dg.ctypes.deinit(gpa);
463467 f.object.dg.fwd_decl.deinit();
464468 f.arena.deinit();
465469 }
......@@ -487,6 +491,7 @@ pub const DeclGen = struct {
487491 decl_index: Decl.Index,
488492 fwd_decl: std.ArrayList(u8),
489493 error_msg: ?*Module.ErrorMsg,
494 ctypes: CType.Store,
490495 /// The key of this map is Type which has references to typedefs_arena.
491496 typedefs: TypedefMap,
492497 typedefs_arena: std.mem.Allocator,
......@@ -1949,6 +1954,10 @@ pub const DeclGen = struct {
19491954 return name;
19501955 }
19511956
1957 fn typeToCType(dg: *DeclGen, ty: Type) !CType {
1958 return dg.ctypes.typeToCType(dg.gpa, ty, dg.module);
1959 }
1960
19521961 /// Renders a type as a single identifier, generating intermediate typedefs
19531962 /// if necessary.
19541963 ///
......@@ -1967,6 +1976,8 @@ pub const DeclGen = struct {
19671976 t: Type,
19681977 kind: TypedefKind,
19691978 ) error{ OutOfMemory, AnalysisFail }!void {
1979 _ = try dg.typeToCType(t);
1980
19701981 const target = dg.module.getTarget();
19711982
19721983 switch (t.zigTypeTag()) {
src/codegen/c/type.zig created+1457
......@@ -0,0 +1,1457 @@
1const std = @import("std");
2const cstr = std.cstr;
3const mem = std.mem;
4const Allocator = mem.Allocator;
5const assert = std.debug.assert;
6const autoHash = std.hash.autoHash;
7const Target = std.Target;
8
9const Module = @import("../../Module.zig");
10const Type = @import("../../type.zig").Type;
11
12pub const CType = extern union {
13 /// If the tag value is less than Tag.no_payload_count, then no pointer
14 /// dereference is needed.
15 tag_if_small_enough: Tag,
16 ptr_otherwise: *const Payload,
17
18 pub fn initTag(small_tag: Tag) CType {
19 assert(!small_tag.hasPayload());
20 return .{ .tag_if_small_enough = small_tag };
21 }
22
23 pub fn initPayload(pl: anytype) CType {
24 const T = @typeInfo(@TypeOf(pl)).Pointer.child;
25 return switch (pl.base.tag) {
26 inline else => |t| if (comptime t.hasPayload() and t.Type() == T) .{
27 .ptr_otherwise = &pl.base,
28 } else unreachable,
29 };
30 }
31
32 pub fn hasPayload(self: CType) bool {
33 return self.tag_if_small_enough.hasPayload();
34 }
35
36 pub fn tag(self: CType) Tag {
37 return if (self.hasPayload()) self.ptr_otherwise.tag else self.tag_if_small_enough;
38 }
39
40 pub fn cast(self: CType, comptime T: type) ?*const T {
41 if (!self.hasPayload()) return null;
42 const pl = self.ptr_otherwise;
43 return switch (pl.tag) {
44 inline else => |t| if (comptime t.hasPayload() and t.Type() == T)
45 @fieldParentPtr(T, "base", pl)
46 else
47 null,
48 };
49 }
50
51 pub fn castTag(self: CType, comptime t: Tag) ?*const t.Type() {
52 return if (self.tag() == t) @fieldParentPtr(t.Type(), "base", self.ptr_otherwise) else null;
53 }
54
55 pub const Tag = enum(usize) {
56 // The first section of this enum are tags that require no payload.
57 void,
58
59 // C basic types
60 char,
61
62 @"signed char",
63 short,
64 int,
65 long,
66 @"long long",
67
68 _Bool,
69 @"unsigned char",
70 @"unsigned short",
71 @"unsigned int",
72 @"unsigned long",
73 @"unsigned long long",
74
75 float,
76 double,
77 @"long double",
78
79 // C header types
80 bool, // stdbool.h
81 size_t, // stddef.h
82 ptrdiff_t, // stddef.h
83
84 // zig.h types
85 zig_u8,
86 zig_i8,
87 zig_u16,
88 zig_i16,
89 zig_u32,
90 zig_i32,
91 zig_u64,
92 zig_i64,
93 zig_u128,
94 zig_i128,
95 zig_f16,
96 zig_f32,
97 zig_f64,
98 zig_f80,
99 zig_f128,
100
101 // After this, the tag requires a payload.
102 pointer,
103 pointer_const,
104 pointer_volatile,
105 pointer_const_volatile,
106 array,
107 vector,
108 fwd_struct,
109 fwd_union,
110 anon_struct,
111 packed_anon_struct,
112 @"struct",
113 @"union",
114 packed_struct,
115 packed_union,
116 function,
117 varargs_function,
118
119 pub const last_no_payload_tag = Tag.zig_f128;
120 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
121
122 pub fn hasPayload(self: Tag) bool {
123 return @enumToInt(self) >= no_payload_count;
124 }
125
126 pub fn toIndex(self: Tag) Index {
127 assert(!self.hasPayload());
128 return @intCast(Index, @enumToInt(self));
129 }
130
131 pub fn Type(comptime self: Tag) type {
132 return switch (self) {
133 .void,
134 .char,
135 .@"signed char",
136 .short,
137 .int,
138 .long,
139 .@"long long",
140 ._Bool,
141 .@"unsigned char",
142 .@"unsigned short",
143 .@"unsigned int",
144 .@"unsigned long",
145 .@"unsigned long long",
146 .float,
147 .double,
148 .@"long double",
149 .bool,
150 .size_t,
151 .ptrdiff_t,
152 .zig_u8,
153 .zig_i8,
154 .zig_u16,
155 .zig_i16,
156 .zig_u32,
157 .zig_i32,
158 .zig_u64,
159 .zig_i64,
160 .zig_u128,
161 .zig_i128,
162 .zig_f16,
163 .zig_f32,
164 .zig_f64,
165 .zig_f80,
166 .zig_f128,
167 => @compileError("Type Tag " ++ @tagName(self) ++ " has no payload"),
168
169 .pointer,
170 .pointer_const,
171 .pointer_volatile,
172 .pointer_const_volatile,
173 => Payload.Child,
174
175 .array,
176 .vector,
177 => Payload.Sequence,
178
179 .fwd_struct,
180 .fwd_union,
181 => Payload.FwdDecl,
182
183 .anon_struct,
184 .packed_anon_struct,
185 => Payload.Fields,
186
187 .@"struct",
188 .@"union",
189 .packed_struct,
190 .packed_union,
191 => Payload.Aggregate,
192
193 .function,
194 .varargs_function,
195 => Payload.Function,
196 };
197 }
198 };
199
200 pub const Payload = struct {
201 tag: Tag,
202
203 pub const Child = struct {
204 base: Payload,
205 data: Index,
206 };
207
208 pub const Sequence = struct {
209 base: Payload,
210 data: struct {
211 len: u64,
212 elem_type: Index,
213 },
214 };
215
216 pub const FwdDecl = struct {
217 base: Payload,
218 data: Module.Decl.Index,
219 };
220
221 pub const Fields = struct {
222 base: Payload,
223 data: Data,
224
225 const Data = []const Field;
226 const Field = struct {
227 name: [*:0]const u8,
228 type: Index,
229 alignas: u32,
230 };
231 };
232
233 pub const Aggregate = struct {
234 base: Payload,
235 data: struct {
236 fields: Fields.Data,
237 fwd_decl: Index,
238 },
239 };
240
241 pub const Function = struct {
242 base: Payload,
243 data: struct {
244 return_type: Index,
245 param_types: []const Index,
246 },
247 };
248 };
249
250 pub const Index = u32;
251 pub const Store = struct {
252 arena: std.heap.ArenaAllocator.State = .{},
253 set: Set = .{},
254
255 const Set = struct {
256 const Map = std.ArrayHashMapUnmanaged(CType, void, HashContext32, true);
257
258 map: Map = .{},
259
260 fn indexToCType(self: Set, index: Index) CType {
261 if (index < Tag.no_payload_count) return initTag(@intToEnum(Tag, index));
262 return self.map.keys()[index - Tag.no_payload_count];
263 }
264
265 fn indexToHash(self: Set, index: Index) Map.Hash {
266 if (index < Tag.no_payload_count) return self.indexToCType(index).hash(self);
267 return self.map.entries.items(.hash)[index - Tag.no_payload_count];
268 }
269
270 fn typeToIndex(self: Set, ty: Type, target: Target, kind: Kind) ?Index {
271 const lookup = Convert.Lookup{ .imm = .{ .set = &self, .target = target } };
272
273 var convert: Convert = undefined;
274 convert.initType(ty, kind, lookup) catch unreachable;
275
276 const t = convert.tag();
277 if (!t.hasPayload()) return t.toIndex();
278
279 return if (self.map.getIndexAdapted(
280 ty,
281 TypeAdapter32{ .kind = kind, .lookup = lookup, .convert = &convert },
282 )) |idx| @intCast(Index, Tag.no_payload_count + idx) else null;
283 }
284 };
285
286 const Promoted = struct {
287 arena: std.heap.ArenaAllocator,
288 set: Set,
289
290 fn gpa(self: *Promoted) Allocator {
291 return self.arena.child_allocator;
292 }
293
294 fn cTypeToIndex(self: *Promoted, cty: CType) Allocator.Error!Index {
295 const t = cty.tag();
296 if (@enumToInt(t) < Tag.no_payload_count) return @intCast(Index, @enumToInt(t));
297
298 const gop = try self.set.map.getOrPutContext(self.gpa(), cty, .{ .store = &self.set });
299 if (!gop.found_existing) gop.key_ptr.* = cty;
300 if (std.debug.runtime_safety) {
301 const key = self.set.map.entries.items(.key)[gop.index];
302 assert(key.eql(cty));
303 assert(cty.hash(self.set) == key.hash(self.set));
304 }
305 return @intCast(Index, Tag.no_payload_count + gop.index);
306 }
307
308 fn typeToIndex(self: *Promoted, ty: Type, mod: *Module, kind: Kind) Allocator.Error!Index {
309 const lookup = Convert.Lookup{ .mut = .{ .promoted = self, .mod = mod } };
310
311 var convert: Convert = undefined;
312 try convert.initType(ty, kind, lookup);
313
314 const t = convert.tag();
315 if (!t.hasPayload()) return t.toIndex();
316
317 const gop = try self.set.map.getOrPutContextAdapted(
318 self.gpa(),
319 ty,
320 TypeAdapter32{ .kind = kind, .lookup = lookup.freeze(), .convert = &convert },
321 .{ .store = &self.set },
322 );
323 if (!gop.found_existing) {
324 errdefer _ = self.set.map.pop();
325 gop.key_ptr.* = try createFromConvert(self, ty, lookup.getTarget(), kind, convert);
326 }
327 if (std.debug.runtime_safety) {
328 const adapter = TypeAdapter64{
329 .kind = kind,
330 .lookup = lookup.freeze(),
331 .convert = &convert,
332 };
333 const key = self.set.map.entries.items(.key)[gop.index];
334 assert(adapter.eql(ty, key));
335 assert(adapter.hash(ty) == key.hash(self.set));
336 }
337 return @intCast(Index, Tag.no_payload_count + gop.index);
338 }
339 };
340
341 fn promote(self: Store, gpa: Allocator) Promoted {
342 return .{ .arena = self.arena.promote(gpa), .set = self.set };
343 }
344
345 fn demote(self: *Store, promoted: Promoted) void {
346 self.arena = promoted.arena.state;
347 self.set = promoted.set;
348 }
349
350 pub fn indexToCType(self: Store, index: Index) CType {
351 return self.set.indexToCType(index);
352 }
353
354 pub fn cTypeToIndex(self: *Store, gpa: Allocator, cty: CType) !Index {
355 var promoted = self.promote(gpa);
356 defer self.demote(promoted);
357 return promoted.cTypeToIndex(cty);
358 }
359
360 pub fn typeToCType(self: *Store, gpa: Allocator, ty: Type, mod: *Module) !CType {
361 const idx = try self.typeToIndex(gpa, ty, mod);
362 return self.indexToCType(idx);
363 }
364
365 pub fn typeToIndex(self: *Store, gpa: Allocator, ty: Type, mod: *Module) !Index {
366 var promoted = self.promote(gpa);
367 defer self.demote(promoted);
368 return promoted.typeToIndex(ty, mod, .complete);
369 }
370
371 pub fn clearRetainingCapacity(self: *Store, gpa: Allocator) void {
372 var promoted = self.promote(gpa);
373 defer self.demote(promoted);
374 promoted.set.map.clearRetainingCapacity();
375 _ = promoted.arena.reset(.retain_capacity);
376 }
377
378 pub fn shrinkToFit(self: *Store, gpa: Allocator) void {
379 self.map.shrinkAndFree(gpa, self.map.entries.len);
380 }
381
382 pub fn shrinkAndFree(self: *Store, gpa: Allocator) void {
383 var promoted = self.promote(gpa);
384 defer self.demote(promoted);
385 promoted.set.map.clearAndFree(gpa);
386 _ = promoted.arena.reset(.free_all);
387 }
388
389 pub fn move(self: *Store) Store {
390 const moved = self.*;
391 self.* = .{};
392 return moved;
393 }
394
395 pub fn deinit(self: *Store, gpa: Allocator) void {
396 var promoted = self.promote(gpa);
397 promoted.set.map.deinit(gpa);
398 _ = promoted.arena.deinit();
399 self.* = undefined;
400 }
401 };
402
403 pub fn eql(lhs: CType, rhs: CType) bool {
404 // As a shortcut, if the small tags / addresses match, we're done.
405 if (lhs.tag_if_small_enough == rhs.tag_if_small_enough) return true;
406
407 const lhs_tag = lhs.tag();
408 const rhs_tag = rhs.tag();
409 if (lhs_tag != rhs_tag) return false;
410
411 return switch (lhs_tag) {
412 .void,
413 .char,
414 .@"signed char",
415 .short,
416 .int,
417 .long,
418 .@"long long",
419 ._Bool,
420 .@"unsigned char",
421 .@"unsigned short",
422 .@"unsigned int",
423 .@"unsigned long",
424 .@"unsigned long long",
425 .float,
426 .double,
427 .@"long double",
428 .bool,
429 .size_t,
430 .ptrdiff_t,
431 .zig_u8,
432 .zig_i8,
433 .zig_u16,
434 .zig_i16,
435 .zig_u32,
436 .zig_i32,
437 .zig_u64,
438 .zig_i64,
439 .zig_u128,
440 .zig_i128,
441 .zig_f16,
442 .zig_f32,
443 .zig_f64,
444 .zig_f80,
445 .zig_f128,
446 => false,
447
448 .pointer,
449 .pointer_const,
450 .pointer_volatile,
451 .pointer_const_volatile,
452 => lhs.cast(Payload.Child).?.data == rhs.cast(Payload.Child).?.data,
453
454 .array,
455 .vector,
456 => std.meta.eql(lhs.cast(Payload.Sequence).?.data, rhs.cast(Payload.Sequence).?.data),
457
458 .fwd_struct,
459 .fwd_union,
460 => lhs.cast(Payload.FwdDecl).?.data == rhs.cast(Payload.FwdDecl).?.data,
461
462 .anon_struct,
463 .packed_anon_struct,
464 => {
465 const lhs_data = lhs.cast(Payload.Fields).?.data;
466 const rhs_data = rhs.cast(Payload.Fields).?.data;
467 if (lhs_data.len != rhs_data.len) return false;
468 for (lhs_data, rhs_data) |lhs_field, rhs_field| {
469 if (lhs_field.type != rhs_field.type) return false;
470 if (lhs_field.alignas != rhs_field.alignas) return false;
471 if (cstr.cmp(lhs_field.name, rhs_field.name) != 0) return false;
472 }
473 return true;
474 },
475
476 .@"struct",
477 .@"union",
478 .packed_struct,
479 .packed_union,
480 => std.meta.eql(
481 lhs.cast(Payload.Aggregate).?.data.fwd_decl,
482 rhs.cast(Payload.Aggregate).?.data.fwd_decl,
483 ),
484
485 .function,
486 .varargs_function,
487 => {
488 const lhs_data = lhs.cast(Payload.Function).?.data;
489 const rhs_data = rhs.cast(Payload.Function).?.data;
490 if (lhs_data.return_type != rhs_data.return_type) return false;
491 if (lhs_data.param_types.len != rhs_data.param_types.len) return false;
492 for (lhs_data.param_types, rhs_data.param_types) |lhs_param_cty, rhs_param_cty| {
493 if (lhs_param_cty != rhs_param_cty) return false;
494 }
495 return true;
496 },
497 };
498 }
499
500 pub fn hash(self: CType, store: Store.Set) u64 {
501 var hasher = std.hash.Wyhash.init(0);
502 self.updateHasher(&hasher, store);
503 return hasher.final();
504 }
505
506 pub fn updateHasher(self: CType, hasher: anytype, store: Store.Set) void {
507 const t = self.tag();
508 autoHash(hasher, t);
509 switch (t) {
510 .void,
511 .char,
512 .@"signed char",
513 .short,
514 .int,
515 .long,
516 .@"long long",
517 ._Bool,
518 .@"unsigned char",
519 .@"unsigned short",
520 .@"unsigned int",
521 .@"unsigned long",
522 .@"unsigned long long",
523 .float,
524 .double,
525 .@"long double",
526 .bool,
527 .size_t,
528 .ptrdiff_t,
529 .zig_u8,
530 .zig_i8,
531 .zig_u16,
532 .zig_i16,
533 .zig_u32,
534 .zig_i32,
535 .zig_u64,
536 .zig_i64,
537 .zig_u128,
538 .zig_i128,
539 .zig_f16,
540 .zig_f32,
541 .zig_f64,
542 .zig_f80,
543 .zig_f128,
544 => {},
545
546 .pointer,
547 .pointer_const,
548 .pointer_volatile,
549 .pointer_const_volatile,
550 => store.indexToCType(self.cast(Payload.Child).?.data).updateHasher(hasher, store),
551
552 .array,
553 .vector,
554 => {
555 const data = self.cast(Payload.Sequence).?.data;
556 autoHash(hasher, data.len);
557 store.indexToCType(data.elem_type).updateHasher(hasher, store);
558 },
559
560 .fwd_struct,
561 .fwd_union,
562 => autoHash(hasher, self.cast(Payload.FwdDecl).?.data),
563
564 .anon_struct,
565 .packed_anon_struct,
566 => for (self.cast(Payload.Fields).?.data) |field| {
567 store.indexToCType(field.type).updateHasher(hasher, store);
568 hasher.update(mem.span(field.name));
569 autoHash(hasher, field.alignas);
570 },
571
572 .@"struct",
573 .@"union",
574 .packed_struct,
575 .packed_union,
576 => store.indexToCType(self.cast(Payload.Aggregate).?.data.fwd_decl)
577 .updateHasher(hasher, store),
578
579 .function,
580 .varargs_function,
581 => {
582 const data = self.cast(Payload.Function).?.data;
583 store.indexToCType(data.return_type).updateHasher(hasher, store);
584 for (data.param_types) |param_ty| {
585 store.indexToCType(param_ty).updateHasher(hasher, store);
586 }
587 },
588 }
589 }
590
591 pub const Kind = enum { forward, complete, global, parameter };
592
593 const Convert = struct {
594 storage: union {
595 none: void,
596 child: Payload.Child,
597 seq: Payload.Sequence,
598 fwd: Payload.FwdDecl,
599 anon: struct {
600 fields: [2]Payload.Fields.Field,
601 pl: Payload.Fields,
602 },
603 agg: Payload.Aggregate,
604 },
605 value: union(enum) {
606 tag: Tag,
607 cty: CType,
608 },
609
610 pub fn init(self: *@This(), t: Tag) void {
611 self.* = if (t.hasPayload()) .{
612 .storage = .{ .none = {} },
613 .value = .{ .tag = t },
614 } else .{
615 .storage = .{ .none = {} },
616 .value = .{ .cty = initTag(t) },
617 };
618 }
619
620 pub fn tag(self: @This()) Tag {
621 return switch (self.value) {
622 .tag => |t| t,
623 .cty => |c| c.tag(),
624 };
625 }
626
627 fn tagFromIntInfo(signedness: std.builtin.Signedness, bits: u16) Tag {
628 return switch (bits) {
629 0 => .void,
630 1...8 => switch (signedness) {
631 .unsigned => .zig_u8,
632 .signed => .zig_i8,
633 },
634 9...16 => switch (signedness) {
635 .unsigned => .zig_u16,
636 .signed => .zig_i16,
637 },
638 17...32 => switch (signedness) {
639 .unsigned => .zig_u32,
640 .signed => .zig_i32,
641 },
642 33...64 => switch (signedness) {
643 .unsigned => .zig_u64,
644 .signed => .zig_i64,
645 },
646 65...128 => switch (signedness) {
647 .unsigned => .zig_u128,
648 .signed => .zig_i128,
649 },
650 else => .array,
651 };
652 }
653
654 pub const Lookup = union(enum) {
655 fail: Target,
656 imm: struct {
657 set: *const Store.Set,
658 target: Target,
659 },
660 mut: struct {
661 promoted: *Store.Promoted,
662 mod: *Module,
663 },
664
665 pub fn isMutable(self: @This()) bool {
666 return switch (self) {
667 .fail, .imm => false,
668 .mut => true,
669 };
670 }
671
672 pub fn getTarget(self: @This()) Target {
673 return switch (self) {
674 .fail => |target| target,
675 .imm => |imm| imm.target,
676 .mut => |mut| mut.mod.getTarget(),
677 };
678 }
679
680 pub fn getSet(self: @This()) ?*const Store.Set {
681 return switch (self) {
682 .fail => null,
683 .imm => |imm| imm.set,
684 .mut => |mut| &mut.promoted.set,
685 };
686 }
687
688 pub fn typeToIndex(self: @This(), ty: Type, kind: Kind) !?Index {
689 return switch (self) {
690 .fail => null,
691 .imm => |imm| imm.set.typeToIndex(ty, imm.target, kind),
692 .mut => |mut| try mut.promoted.typeToIndex(ty, mut.mod, kind),
693 };
694 }
695
696 pub fn indexToCType(self: @This(), index: Index) ?CType {
697 return if (self.getSet()) |set| set.indexToCType(index) else null;
698 }
699
700 pub fn freeze(self: @This()) @This() {
701 return switch (self) {
702 .fail, .imm => self,
703 .mut => |mut| .{ .imm = .{ .set = &mut.promoted.set, .target = self.getTarget() } },
704 };
705 }
706 };
707
708 pub fn initType(self: *@This(), ty: Type, kind: Kind, lookup: Lookup) !void {
709 const target = lookup.getTarget();
710
711 self.* = undefined;
712 if (!ty.isFnOrHasRuntimeBitsIgnoreComptime())
713 self.init(.void)
714 else if (ty.isAbiInt()) switch (ty.tag()) {
715 .usize => self.init(.size_t),
716 .isize => self.init(.ptrdiff_t),
717 .c_short => self.init(.short),
718 .c_ushort => self.init(.@"unsigned short"),
719 .c_int => self.init(.int),
720 .c_uint => self.init(.@"unsigned int"),
721 .c_long => self.init(.long),
722 .c_ulong => self.init(.@"unsigned long"),
723 .c_longlong => self.init(.@"long long"),
724 .c_ulonglong => self.init(.@"unsigned long long"),
725 else => {
726 const info = ty.intInfo(target);
727 const t = tagFromIntInfo(info.signedness, info.bits);
728 switch (t) {
729 .void => unreachable,
730 else => self.init(t),
731 .array => {
732 const abi_size = ty.abiSize(target);
733 const abi_align = ty.abiAlignment(target);
734 self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{
735 .len = @divExact(abi_size, abi_align),
736 .elem_type = tagFromIntInfo(
737 .unsigned,
738 @intCast(u16, abi_align * 8),
739 ).toIndex(),
740 } } };
741 self.value = .{ .cty = initPayload(&self.storage.seq) };
742 },
743 }
744 },
745 } else switch (ty.zigTypeTag()) {
746 .Frame => unreachable,
747 .AnyFrame => unreachable,
748
749 .Int,
750 .Enum,
751 .ErrorSet,
752 .Type,
753 .Void,
754 .NoReturn,
755 .ComptimeFloat,
756 .ComptimeInt,
757 .Undefined,
758 .Null,
759 .EnumLiteral,
760 => unreachable,
761
762 .Bool => self.init(.bool),
763
764 .Float => self.init(switch (ty.tag()) {
765 .f16 => .zig_f16,
766 .f32 => .zig_f32,
767 .f64 => .zig_f64,
768 .f80 => .zig_f80,
769 .f128 => .zig_f128,
770 .c_longdouble => .@"long double",
771 else => unreachable,
772 }),
773
774 .Pointer => switch (ty.ptrSize()) {
775 .Slice => {
776 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
777 const ptr_ty = ty.slicePtrFieldType(&buf);
778 if (try lookup.typeToIndex(ptr_ty, kind)) |ptr_idx| {
779 self.storage = .{ .anon = .{ .fields = .{
780 .{
781 .name = "ptr",
782 .type = ptr_idx,
783 .alignas = ptr_ty.abiAlignment(target),
784 },
785 .{
786 .name = "len",
787 .type = Tag.size_t.toIndex(),
788 .alignas = Type.usize.abiAlignment(target),
789 },
790 }, .pl = undefined } };
791 self.storage.anon.pl = .{
792 .base = .{ .tag = .anon_struct },
793 .data = self.storage.anon.fields[0..2],
794 };
795 self.value = .{ .cty = initPayload(&self.storage.anon.pl) };
796 } else self.init(.anon_struct);
797 },
798
799 .One, .Many, .C => {
800 const t: Tag = switch (ty.isVolatilePtr()) {
801 false => switch (ty.isConstPtr()) {
802 false => .pointer,
803 true => .pointer_const,
804 },
805 true => switch (ty.isConstPtr()) {
806 false => .pointer_volatile,
807 true => .pointer_const_volatile,
808 },
809 };
810 if (try lookup.typeToIndex(ty.childType(), .forward)) |child_idx| {
811 self.storage = .{ .child = .{ .base = .{ .tag = t }, .data = child_idx } };
812 self.value = .{ .cty = initPayload(&self.storage.child) };
813 } else self.init(t);
814 },
815 },
816
817 .Struct, .Union => |zig_tag| if (ty.isTupleOrAnonStruct()) {
818 if (lookup.isMutable()) {
819 for (0..ty.structFieldCount()) |field_i| {
820 if (ty.structFieldIsComptime(field_i)) continue;
821 _ = try lookup.typeToIndex(ty.structFieldType(field_i), switch (kind) {
822 .forward, .complete, .parameter => .complete,
823 .global => .global,
824 });
825 }
826 }
827 self.init(.anon_struct);
828 } else {
829 const is_struct = zig_tag == .Struct or ty.unionTagTypeSafety() != null;
830 switch (kind) {
831 .forward => {
832 self.storage = .{ .fwd = .{
833 .base = .{ .tag = if (is_struct) .fwd_struct else .fwd_union },
834 .data = ty.getOwnerDecl(),
835 } };
836 self.value = .{ .cty = initPayload(&self.storage.fwd) };
837 },
838 else => {
839 if (lookup.isMutable()) {
840 for (0..switch (zig_tag) {
841 .Struct => ty.structFieldCount(),
842 .Union => ty.cast(Type.Payload.Union).?.data.fields.count(),
843 else => unreachable,
844 }) |field_i| {
845 if (zig_tag == .Struct and ty.structFieldIsComptime(field_i))
846 continue;
847 _ = try lookup.typeToIndex(
848 ty.structFieldType(field_i),
849 switch (kind) {
850 .forward => unreachable,
851 .complete, .parameter => .complete,
852 .global => .global,
853 },
854 );
855 }
856 _ = try lookup.typeToIndex(ty, .forward);
857 }
858 self.init(if (is_struct) .@"struct" else .@"union");
859 },
860 }
861 },
862
863 .Array, .Vector => |zig_tag| {
864 const t: Tag = switch (zig_tag) {
865 .Array => .array,
866 .Vector => .vector,
867 else => unreachable,
868 };
869 if (try lookup.typeToIndex(ty.childType(), kind)) |child_idx| {
870 self.storage = .{ .seq = .{ .base = .{ .tag = t }, .data = .{
871 .len = ty.arrayLenIncludingSentinel(),
872 .elem_type = child_idx,
873 } } };
874 self.value = .{ .cty = initPayload(&self.storage.seq) };
875 } else self.init(t);
876 },
877
878 .Optional => {
879 var buf: Type.Payload.ElemType = undefined;
880 const payload_ty = ty.optionalChild(&buf);
881 if (payload_ty.hasRuntimeBitsIgnoreComptime()) {
882 if (ty.optionalReprIsPayload())
883 try self.initType(payload_ty, kind, lookup)
884 else if (try lookup.typeToIndex(payload_ty, kind)) |payload_idx| {
885 self.storage = .{ .anon = .{ .fields = .{
886 .{
887 .name = "payload",
888 .type = payload_idx,
889 .alignas = payload_ty.abiAlignment(target),
890 },
891 .{
892 .name = "is_null",
893 .type = Tag.bool.toIndex(),
894 .alignas = Type.bool.abiAlignment(target),
895 },
896 }, .pl = undefined } };
897 self.storage.anon.pl = .{
898 .base = .{ .tag = .anon_struct },
899 .data = self.storage.anon.fields[0..2],
900 };
901 self.value = .{ .cty = initPayload(&self.storage.anon.pl) };
902 } else self.init(.anon_struct);
903 } else self.init(.bool);
904 },
905
906 .ErrorUnion => {
907 const payload_ty = ty.errorUnionPayload();
908 if (try lookup.typeToIndex(payload_ty, switch (kind) {
909 .forward, .complete, .parameter => .complete,
910 .global => .global,
911 })) |payload_idx| {
912 const error_ty = ty.errorUnionSet();
913 if (payload_idx == Tag.void.toIndex())
914 try self.initType(error_ty, kind, lookup)
915 else if (try lookup.typeToIndex(error_ty, kind)) |error_idx| {
916 self.storage = .{ .anon = .{ .fields = .{
917 .{
918 .name = "payload",
919 .type = payload_idx,
920 .alignas = payload_ty.abiAlignment(target),
921 },
922 .{
923 .name = "error",
924 .type = error_idx,
925 .alignas = error_ty.abiAlignment(target),
926 },
927 }, .pl = undefined } };
928 self.storage.anon.pl = .{
929 .base = .{ .tag = .anon_struct },
930 .data = self.storage.anon.fields[0..2],
931 };
932 self.value = .{ .cty = initPayload(&self.storage.anon.pl) };
933 } else self.init(.anon_struct);
934 } else self.init(.anon_struct);
935 },
936
937 .Opaque => switch (ty.tag()) {
938 .anyopaque => self.init(.void),
939 .@"opaque" => {
940 self.storage = .{ .fwd = .{
941 .base = .{ .tag = .fwd_struct },
942 .data = ty.getOwnerDecl(),
943 } };
944 self.value = .{ .cty = initPayload(&self.storage.fwd) };
945 },
946 else => unreachable,
947 },
948
949 .Fn => {
950 const info = ty.fnInfo();
951 if (lookup.isMutable()) {
952 _ = try lookup.typeToIndex(info.return_type, switch (kind) {
953 .forward => .forward,
954 .complete, .parameter, .global => .complete,
955 });
956 for (info.param_types, 0..) |param_ty, param_i| {
957 if (info.paramIsComptime(param_i)) continue;
958 _ = try lookup.typeToIndex(param_ty, switch (kind) {
959 .forward => .forward,
960 .complete, .parameter, .global => unreachable,
961 });
962 }
963 }
964 self.init(if (info.is_var_args) .varargs_function else .function);
965 },
966 }
967 }
968 };
969
970 fn copyFields(arena: Allocator, fields: Payload.Fields.Data) !Payload.Fields.Data {
971 const new_fields = try arena.dupe(Payload.Fields.Field, fields);
972 for (new_fields) |*new_field| {
973 new_field.name = try arena.dupeZ(u8, mem.span(new_field.name));
974 new_field.type = new_field.type;
975 }
976 return new_fields;
977 }
978
979 pub fn copy(self: CType, arena: Allocator) !CType {
980 switch (self.tag()) {
981 .void,
982 .char,
983 .@"signed char",
984 .short,
985 .int,
986 .long,
987 .@"long long",
988 ._Bool,
989 .@"unsigned char",
990 .@"unsigned short",
991 .@"unsigned int",
992 .@"unsigned long",
993 .@"unsigned long long",
994 .float,
995 .double,
996 .@"long double",
997 .bool,
998 .size_t,
999 .ptrdiff_t,
1000 .zig_u8,
1001 .zig_i8,
1002 .zig_u16,
1003 .zig_i16,
1004 .zig_u32,
1005 .zig_i32,
1006 .zig_u64,
1007 .zig_i64,
1008 .zig_u128,
1009 .zig_i128,
1010 .zig_f16,
1011 .zig_f32,
1012 .zig_f64,
1013 .zig_f80,
1014 .zig_f128,
1015 => return self,
1016
1017 .pointer,
1018 .pointer_const,
1019 .pointer_volatile,
1020 .pointer_const_volatile,
1021 => {
1022 const pl = self.cast(Payload.Child).?;
1023 const new_pl = try arena.create(Payload.Child);
1024 new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = pl.data };
1025 return initPayload(new_pl);
1026 },
1027
1028 .array,
1029 .vector,
1030 => {
1031 const pl = self.cast(Payload.Sequence).?;
1032 const new_pl = try arena.create(Payload.Sequence);
1033 new_pl.* = .{
1034 .base = .{ .tag = pl.base.tag },
1035 .data = .{ .len = pl.data.len, .elem_type = pl.data.elem_type },
1036 };
1037 return initPayload(new_pl);
1038 },
1039
1040 .fwd_struct,
1041 .fwd_union,
1042 => {
1043 const pl = self.cast(Payload.FwdDecl).?;
1044 const new_pl = try arena.create(Payload.FwdDecl);
1045 new_pl.* = .{
1046 .base = .{ .tag = pl.base.tag },
1047 .data = pl.data,
1048 };
1049 return initPayload(new_pl);
1050 },
1051
1052 .anon_struct,
1053 .packed_anon_struct,
1054 => {
1055 const pl = self.cast(Payload.Fields).?;
1056 const new_pl = try arena.create(Payload.Fields);
1057 new_pl.* = .{
1058 .base = .{ .tag = pl.base.tag },
1059 .data = try copyFields(arena, pl.data),
1060 };
1061 return initPayload(new_pl);
1062 },
1063
1064 .@"struct",
1065 .@"union",
1066 .packed_struct,
1067 .packed_union,
1068 => {
1069 const pl = self.cast(Payload.Aggregate).?;
1070 const new_pl = try arena.create(Payload.Aggregate);
1071 new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{
1072 .fields = try copyFields(arena, pl.data.fields),
1073 .fwd_decl = pl.data.fwd_decl,
1074 } };
1075 return initPayload(new_pl);
1076 },
1077
1078 .function,
1079 .varargs_function,
1080 => {
1081 const pl = self.cast(Payload.Function).?;
1082 const new_pl = try arena.create(Payload.Function);
1083 new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{
1084 .return_type = pl.data.return_type,
1085 .param_types = try arena.dupe(Index, pl.data.param_types),
1086 } };
1087 return initPayload(new_pl);
1088 },
1089 }
1090 }
1091
1092 fn createFromType(store: *Store.Promoted, ty: Type, target: Target, kind: Kind) !CType {
1093 var convert: Convert = undefined;
1094 try convert.initType(ty, kind, .{ .imm = .{ .set = &store.set, .target = target } });
1095 return createFromConvert(store, ty, target, kind, &convert);
1096 }
1097
1098 fn createFromConvert(
1099 store: *Store.Promoted,
1100 ty: Type,
1101 target: Target,
1102 kind: Kind,
1103 convert: Convert,
1104 ) !CType {
1105 const arena = store.arena.allocator();
1106 switch (convert.value) {
1107 .cty => |c| return c.copy(arena),
1108 .tag => |t| switch (t) {
1109 .anon_struct,
1110 .packed_anon_struct,
1111 .@"struct",
1112 .@"union",
1113 .packed_struct,
1114 .packed_union,
1115 => switch (ty.zigTypeTag()) {
1116 .Struct => {
1117 const fields_len = ty.structFieldCount();
1118
1119 var c_fields_len: usize = 0;
1120 for (0..fields_len) |field_i| {
1121 if (ty.structFieldIsComptime(field_i)) continue;
1122 c_fields_len += 1;
1123 }
1124
1125 const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len);
1126 var c_field_i: usize = 0;
1127 for (0..fields_len) |field_i| {
1128 if (ty.structFieldIsComptime(field_i)) continue;
1129
1130 fields_pl[c_field_i] = .{
1131 .name = try if (ty.isSimpleTuple())
1132 std.fmt.allocPrintZ(arena, "f{}", .{field_i})
1133 else
1134 arena.dupeZ(u8, ty.structFieldName(field_i)),
1135 .type = store.set.typeToIndex(
1136 ty.structFieldType(field_i),
1137 target,
1138 switch (kind) {
1139 .forward, .complete, .parameter => .complete,
1140 .global => .global,
1141 },
1142 ).?,
1143 .alignas = ty.structFieldAlign(field_i, target),
1144 };
1145 c_field_i += 1;
1146 }
1147
1148 if (ty.isTupleOrAnonStruct()) {
1149 const anon_pl = try arena.create(Payload.Fields);
1150 anon_pl.* = .{ .base = .{ .tag = .anon_struct }, .data = fields_pl };
1151 return initPayload(anon_pl);
1152 }
1153
1154 const struct_pl = try arena.create(Payload.Aggregate);
1155 struct_pl.* = .{ .base = .{ .tag = t }, .data = .{
1156 .fields = fields_pl,
1157 .fwd_decl = store.set.typeToIndex(ty, target, .forward).?,
1158 } };
1159 return initPayload(struct_pl);
1160 },
1161
1162 .Union => {
1163 const fields = ty.unionFields();
1164 const fields_len = fields.count();
1165
1166 var c_fields_len: usize = 0;
1167 for (0..fields_len) |field_i| {
1168 const field_ty = ty.structFieldType(field_i);
1169 if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue;
1170 c_fields_len += 1;
1171 }
1172
1173 const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len);
1174 var field_i: usize = 0;
1175 var c_field_i: usize = 0;
1176 var field_it = fields.iterator();
1177 while (field_it.next()) |field| {
1178 defer field_i += 1;
1179 if (!field.value_ptr.ty.hasRuntimeBitsIgnoreComptime()) continue;
1180
1181 fields_pl[c_field_i] = .{
1182 .name = try arena.dupeZ(u8, field.key_ptr.*),
1183 .type = store.set.typeToIndex(field.value_ptr.ty, target, switch (kind) {
1184 .forward => unreachable,
1185 .complete, .parameter => .complete,
1186 .global => .global,
1187 }).?,
1188 .alignas = ty.structFieldAlign(field_i, target),
1189 };
1190 c_field_i += 1;
1191 }
1192
1193 const union_pl = try arena.create(Payload.Aggregate);
1194 union_pl.* = .{ .base = .{ .tag = t }, .data = .{
1195 .fields = fields_pl,
1196 .fwd_decl = store.set.typeToIndex(ty, target, .forward).?,
1197 } };
1198 return initPayload(union_pl);
1199 },
1200
1201 else => unreachable,
1202 },
1203
1204 .function,
1205 .varargs_function,
1206 => {
1207 const info = ty.fnInfo();
1208 const recurse_kind: Kind = switch (kind) {
1209 .forward => .forward,
1210 .complete, .parameter, .global => unreachable,
1211 };
1212
1213 var c_params_len: usize = 0;
1214 for (0..info.param_types.len) |param_i| {
1215 if (info.paramIsComptime(param_i)) continue;
1216 c_params_len += 1;
1217 }
1218
1219 const params_pl = try arena.alloc(Index, c_params_len);
1220 var c_param_i: usize = 0;
1221 for (info.param_types, 0..) |param_ty, param_i| {
1222 if (info.paramIsComptime(param_i)) continue;
1223 params_pl[c_param_i] = store.set.typeToIndex(param_ty, target, recurse_kind).?;
1224 c_param_i += 1;
1225 }
1226
1227 const fn_pl = try arena.create(Payload.Function);
1228 fn_pl.* = .{ .base = .{ .tag = t }, .data = .{
1229 .return_type = store.set.typeToIndex(info.return_type, target, recurse_kind).?,
1230 .param_types = params_pl,
1231 } };
1232 return initPayload(fn_pl);
1233 },
1234
1235 else => unreachable,
1236 },
1237 }
1238 }
1239
1240 pub const HashContext64 = struct {
1241 store: *const Store.Set,
1242
1243 pub fn hash(_: @This(), cty: CType) u64 {
1244 return cty.hash();
1245 }
1246 pub fn eql(_: @This(), lhs: CType, rhs: CType) bool {
1247 return lhs.eql(rhs);
1248 }
1249 };
1250
1251 pub const HashContext32 = struct {
1252 store: *const Store.Set,
1253
1254 pub fn hash(self: @This(), cty: CType) u32 {
1255 return @truncate(u32, cty.hash(self.store.*));
1256 }
1257 pub fn eql(_: @This(), lhs: CType, rhs: CType, _: usize) bool {
1258 return lhs.eql(rhs);
1259 }
1260 };
1261
1262 pub const TypeAdapter64 = struct {
1263 kind: Kind,
1264 lookup: Convert.Lookup,
1265 convert: *const Convert,
1266
1267 fn eqlRecurse(self: @This(), ty: Type, cty: Index, kind: Kind) bool {
1268 assert(!self.lookup.isMutable());
1269
1270 var convert: Convert = undefined;
1271 convert.initType(ty, kind, self.lookup) catch unreachable;
1272
1273 const self_recurse = @This(){ .kind = kind, .lookup = self.lookup, .convert = &convert };
1274 return self_recurse.eql(ty, self.lookup.indexToCType(cty).?);
1275 }
1276
1277 pub fn eql(self: @This(), ty: Type, cty: CType) bool {
1278 switch (self.convert.value) {
1279 .cty => |c| return c.eql(cty),
1280 .tag => |t| {
1281 if (t != cty.tag()) return false;
1282
1283 const target = self.lookup.getTarget();
1284 switch (t) {
1285 .anon_struct,
1286 .packed_anon_struct,
1287 => {
1288 if (!ty.isTupleOrAnonStruct()) return false;
1289
1290 var name_buf: [
1291 std.fmt.count("f{}", .{std.math.maxInt(usize)})
1292 ]u8 = undefined;
1293 const c_fields = cty.cast(Payload.Fields).?.data;
1294
1295 var c_field_i: usize = 0;
1296 for (0..ty.structFieldCount()) |field_i| {
1297 if (ty.structFieldIsComptime(field_i)) continue;
1298
1299 const c_field = &c_fields[c_field_i];
1300 c_field_i += 1;
1301
1302 if (!self.eqlRecurse(
1303 ty.structFieldType(field_i),
1304 c_field.type,
1305 switch (self.kind) {
1306 .forward, .complete, .parameter => .complete,
1307 .global => .global,
1308 },
1309 ) or !mem.eql(
1310 u8,
1311 if (ty.isSimpleTuple())
1312 std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable
1313 else
1314 ty.structFieldName(field_i),
1315 mem.span(c_field.name),
1316 ) or ty.structFieldAlign(field_i, target) != c_field.alignas)
1317 return false;
1318 }
1319 return true;
1320 },
1321
1322 .@"struct",
1323 .@"union",
1324 .packed_struct,
1325 .packed_union,
1326 => return self.eqlRecurse(
1327 ty,
1328 cty.cast(Payload.Aggregate).?.data.fwd_decl,
1329 .forward,
1330 ),
1331
1332 .function,
1333 .varargs_function,
1334 => {
1335 if (ty.zigTypeTag() != .Fn) return false;
1336
1337 const info = ty.fnInfo();
1338 const data = cty.cast(Payload.Function).?.data;
1339 const recurse_kind: Kind = switch (self.kind) {
1340 .forward => .forward,
1341 .complete, .parameter, .global => unreachable,
1342 };
1343
1344 if (info.param_types.len != data.param_types.len or
1345 !self.eqlRecurse(info.return_type, data.return_type, recurse_kind))
1346 return false;
1347 for (info.param_types, data.param_types, 0..) |param_ty, param_cty, param_i| {
1348 if (info.paramIsComptime(param_i)) continue;
1349 if (!self.eqlRecurse(param_ty, param_cty, recurse_kind))
1350 return false;
1351 }
1352 return true;
1353 },
1354
1355 else => unreachable,
1356 }
1357 },
1358 }
1359 }
1360
1361 pub fn hash(self: @This(), ty: Type) u64 {
1362 var hasher = std.hash.Wyhash.init(0);
1363 self.updateHasher(&hasher, ty);
1364 return hasher.final();
1365 }
1366
1367 fn updateHasherRecurse(self: @This(), hasher: anytype, ty: Type, kind: Kind) void {
1368 assert(!self.lookup.isMutable());
1369
1370 var convert: Convert = undefined;
1371 convert.initType(ty, kind, self.lookup) catch unreachable;
1372
1373 const self_recurse = @This(){ .kind = kind, .lookup = self.lookup, .convert = &convert };
1374 self_recurse.updateHasher(hasher, ty);
1375 }
1376
1377 pub fn updateHasher(self: @This(), hasher: anytype, ty: Type) void {
1378 switch (self.convert.value) {
1379 .cty => |c| return c.updateHasher(hasher, self.lookup.getSet().?.*),
1380 .tag => |t| {
1381 autoHash(hasher, t);
1382
1383 const target = self.lookup.getTarget();
1384 switch (t) {
1385 .anon_struct,
1386 .packed_anon_struct,
1387 => {
1388 var name_buf: [
1389 std.fmt.count("f{}", .{std.math.maxInt(usize)})
1390 ]u8 = undefined;
1391 for (0..ty.structFieldCount()) |field_i| {
1392 if (ty.structFieldIsComptime(field_i)) continue;
1393
1394 self.updateHasherRecurse(
1395 hasher,
1396 ty.structFieldType(field_i),
1397 switch (self.kind) {
1398 .forward, .complete, .parameter => .complete,
1399 .global => .global,
1400 },
1401 );
1402 hasher.update(if (ty.isSimpleTuple())
1403 std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable
1404 else
1405 ty.structFieldName(field_i));
1406 autoHash(hasher, ty.structFieldAlign(field_i, target));
1407 }
1408 },
1409
1410 .@"struct",
1411 .@"union",
1412 .packed_struct,
1413 .packed_union,
1414 => self.updateHasherRecurse(hasher, ty, .forward),
1415
1416 .function,
1417 .varargs_function,
1418 => {
1419 const info = ty.fnInfo();
1420 const recurse_kind: Kind = switch (self.kind) {
1421 .forward => .forward,
1422 .complete, .parameter, .global => unreachable,
1423 };
1424
1425 self.updateHasherRecurse(hasher, info.return_type, recurse_kind);
1426 for (info.param_types, 0..) |param_ty, param_i| {
1427 if (info.paramIsComptime(param_i)) continue;
1428 self.updateHasherRecurse(hasher, param_ty, recurse_kind);
1429 }
1430 },
1431
1432 else => unreachable,
1433 }
1434 },
1435 }
1436 }
1437 };
1438
1439 pub const TypeAdapter32 = struct {
1440 kind: Kind,
1441 lookup: Convert.Lookup,
1442 convert: *const Convert,
1443
1444 fn to64(self: @This()) TypeAdapter64 {
1445 return .{ .kind = self.kind, .lookup = self.lookup, .convert = self.convert };
1446 }
1447
1448 pub fn eql(self: @This(), ty: Type, cty: CType, cty_index: usize) bool {
1449 _ = cty_index;
1450 return self.to64().eql(ty, cty);
1451 }
1452
1453 pub fn hash(self: @This(), ty: Type) u32 {
1454 return @truncate(u32, self.to64().hash(ty));
1455 }
1456 };
1457};
src/link/C.zig+30-3
......@@ -30,6 +30,7 @@ arena: std.heap.ArenaAllocator,
3030const DeclBlock = struct {
3131 code: std.ArrayListUnmanaged(u8) = .{},
3232 fwd_decl: std.ArrayListUnmanaged(u8) = .{},
33 ctypes: codegen.CType.Store = .{},
3334 /// Each Decl stores a mapping of Zig Types to corresponding C types, for every
3435 /// Zig Type used by the Decl. In flush(), we iterate over each Decl
3536 /// and emit the typedef code for all types, making sure to not emit the same thing twice.
......@@ -37,12 +38,13 @@ const DeclBlock = struct {
3738 typedefs: codegen.TypedefMap.Unmanaged = .{},
3839
3940 fn deinit(db: *DeclBlock, gpa: Allocator) void {
40 db.code.deinit(gpa);
41 db.fwd_decl.deinit(gpa);
4241 for (db.typedefs.values()) |typedef| {
4342 gpa.free(typedef.rendered);
4443 }
4544 db.typedefs.deinit(gpa);
45 db.ctypes.deinit(gpa);
46 db.fwd_decl.deinit(gpa);
47 db.code.deinit(gpa);
4648 db.* = undefined;
4749 }
4850};
......@@ -105,9 +107,11 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes
105107 gop.value_ptr.* = .{};
106108 }
107109 const fwd_decl = &gop.value_ptr.fwd_decl;
110 const ctypes = &gop.value_ptr.ctypes;
108111 const typedefs = &gop.value_ptr.typedefs;
109112 const code = &gop.value_ptr.code;
110113 fwd_decl.shrinkRetainingCapacity(0);
114 ctypes.clearRetainingCapacity(module.gpa);
111115 for (typedefs.values()) |typedef| {
112116 module.gpa.free(typedef.rendered);
113117 }
......@@ -127,6 +131,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes
127131 .decl_index = decl_index,
128132 .decl = module.declPtr(decl_index),
129133 .fwd_decl = fwd_decl.toManaged(module.gpa),
134 .ctypes = ctypes.*,
130135 .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }),
131136 .typedefs_arena = self.arena.allocator(),
132137 },
......@@ -137,7 +142,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes
137142 };
138143
139144 function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() };
140 defer function.deinit(module.gpa);
145 defer function.deinit();
141146
142147 codegen.genFunc(&function) catch |err| switch (err) {
143148 error.AnalysisFail => {
......@@ -148,6 +153,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes
148153 };
149154
150155 fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged();
156 ctypes.* = function.object.dg.ctypes.move();
151157 typedefs.* = function.object.dg.typedefs.unmanaged;
152158 function.object.dg.typedefs.unmanaged = .{};
153159 code.* = function.object.code.moveToUnmanaged();
......@@ -155,6 +161,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes
155161 // Free excess allocated memory for this Decl.
156162 fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len);
157163 code.shrinkAndFree(module.gpa, code.items.len);
164 ctypes.shrinkAndFree(module.gpa);
158165}
159166
160167pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !void {
......@@ -166,9 +173,11 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
166173 gop.value_ptr.* = .{};
167174 }
168175 const fwd_decl = &gop.value_ptr.fwd_decl;
176 const ctypes = &gop.value_ptr.ctypes;
169177 const typedefs = &gop.value_ptr.typedefs;
170178 const code = &gop.value_ptr.code;
171179 fwd_decl.shrinkRetainingCapacity(0);
180 ctypes.clearRetainingCapacity(module.gpa);
172181 for (typedefs.values()) |value| {
173182 module.gpa.free(value.rendered);
174183 }
......@@ -185,6 +194,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
185194 .decl_index = decl_index,
186195 .decl = decl,
187196 .fwd_decl = fwd_decl.toManaged(module.gpa),
197 .ctypes = ctypes.*,
188198 .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }),
189199 .typedefs_arena = self.arena.allocator(),
190200 },
......@@ -198,6 +208,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
198208 module.gpa.free(typedef.rendered);
199209 }
200210 object.dg.typedefs.deinit();
211 object.dg.ctypes.deinit(object.dg.gpa);
201212 object.dg.fwd_decl.deinit();
202213 }
203214
......@@ -210,6 +221,8 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
210221 };
211222
212223 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
224 ctypes.* = object.dg.ctypes;
225 object.dg.ctypes = .{};
213226 typedefs.* = object.dg.typedefs.unmanaged;
214227 object.dg.typedefs.unmanaged = .{};
215228 code.* = object.code.moveToUnmanaged();
......@@ -217,6 +230,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
217230 // Free excess allocated memory for this Decl.
218231 fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len);
219232 code.shrinkAndFree(module.gpa, code.items.len);
233 ctypes.shrinkAndFree(module.gpa);
220234}
221235
222236pub fn updateDeclLineNumber(self: *C, module: *Module, decl_index: Module.Decl.Index) !void {
......@@ -326,6 +340,8 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node)
326340const Flush = struct {
327341 err_decls: DeclBlock = .{},
328342 remaining_decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void) = .{},
343
344 ctypes: CTypes = .{},
329345 typedefs: Typedefs = .{},
330346 typedef_buf: std.ArrayListUnmanaged(u8) = .{},
331347 asm_buf: std.ArrayListUnmanaged(u8) = .{},
......@@ -334,6 +350,13 @@ const Flush = struct {
334350 /// Keeps track of the total bytes of `all_buffers`.
335351 file_size: u64 = 0,
336352
353 const CTypes = std.ArrayHashMapUnmanaged(
354 codegen.CType,
355 void,
356 codegen.CType.HashContext32,
357 true,
358 );
359
337360 const Typedefs = std.HashMapUnmanaged(
338361 Type,
339362 void,
......@@ -351,6 +374,7 @@ const Flush = struct {
351374 f.all_buffers.deinit(gpa);
352375 f.typedef_buf.deinit(gpa);
353376 f.typedefs.deinit(gpa);
377 f.ctypes.deinit(gpa);
354378 f.remaining_decls.deinit(gpa);
355379 f.err_decls.deinit(gpa);
356380 }
......@@ -383,6 +407,7 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void {
383407 const module = self.base.options.module.?;
384408
385409 const fwd_decl = &f.err_decls.fwd_decl;
410 const ctypes = &f.err_decls.ctypes;
386411 const typedefs = &f.err_decls.typedefs;
387412 const code = &f.err_decls.code;
388413
......@@ -394,6 +419,7 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void {
394419 .decl_index = undefined,
395420 .decl = undefined,
396421 .fwd_decl = fwd_decl.toManaged(module.gpa),
422 .ctypes = ctypes.*,
397423 .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }),
398424 .typedefs_arena = self.arena.allocator(),
399425 },
......@@ -403,6 +429,7 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void {
403429 object.indent_writer = .{ .underlying_writer = object.code.writer() };
404430 defer {
405431 object.code.deinit();
432 object.dg.ctypes.deinit(module.gpa);
406433 for (object.dg.typedefs.values()) |typedef| {
407434 module.gpa.free(typedef.rendered);
408435 }