1//! High-level Intermediate Representation is an instruction list format that
2//! describes a source file in a manner effective for Semantic Analysis to
3//! process. The root instruction of every Hir is a `.module` that contains the
4//! root declarations. Each declaration's value contains a list of instructions.
5//! In contrast to the nested nature of syntax trees, Hir represents nested
6//! expressions as flat instruction sequences.
7//!
8//! Hir for a given file has no dependencies on it's imports, meaning it can be
9//! computed in parallel and then cached when not changed.
10//!
11//! A helper program exists to visualize Hir: './build transform -- ./file.ix'
12//! Alternatively, `ix build` has the `--dump-ir` option.
13
14insts: Inst.List.Slice,
15string_bytes: []const u8,
16extra: ExtraStore,
17report: Report,
18
19pub const ExtraStore = dod.ExtraStore(.hir, .@"const");
20pub const EncodedData = dod.EncodedData(Tag, u64);
21pub const ExtraIndex = ExtraStore.Index;
22pub const Dump = @import("Hir.Dump.zig");
23
24pub const Tag = enum(u8) {
25 /// Either the top level module or a `module` keyword. Contains a list of `.declarations`
26 /// Every analyzed module gets a `Comp.Namespace`
27 module,
28 /// Smallest unit of re-compilation. Every analyzed declaration gets a `Comp.Decl`
29 declaration,
30 /// Top-level memory location
31 variable_declaration,
32 /// Test declaration
33 test_declaration,
34 /// Retrieves a declaration value via it's name. Since declarations are
35 /// analyzed in isolation of each other, all references to instructions
36 /// outside of the current declaration must be in the form of this.
37 load_decl,
38 /// Blocks are a list of instructions. The block will end with a `.break`,
39 /// which will assign the block a return value.
40 block,
41 /// Ends a block (current or any parent) with a value.
42 /// `return` expressions are lowered to this.
43 @"break",
44 /// Creates a memory cell in the current scope with a starting value.
45 memory,
46 /// Re-assign a memory cell with a new ref.
47 store,
48 /// Re-assign a global variable decl with a new ref.
49 store_decl,
50 /// Re-assign a field with a new ref
51 store_field,
52 /// Performs a type coercion, such as in variable declarations.
53 /// The resulting Air Ref will be guaranteed to be the coerced type.
54 coercion,
55 /// Load a parameter
56 /// if data value is maxInt(u32), the function return type is used
57 param,
58 /// Asserts the operand is not void. Used for discard statements.
59 discard,
60 /// Asserts the operand is `void`. Used for undiscarded expressions.
61 ensure_void,
62
63 // TODO: Rename to `.branch`
64 /// Given a boolean condition, pick one of two branches. This is used to
65 /// implement `if` statements as well as boolean `and` / `or` operators.
66 @"if",
67 /// Given a block instruction, evaluate to a Value without generating
68 /// any runtime instructions.
69 @"comptime",
70
71 /// A function expression, with or without captures
72 func,
73
74 // The following instructions are used to implement `Transform.ResultType`
75 result_type_argument,
76 result_type_struct_field,
77 result_type_tuple_field,
78 result_type_array_value,
79
80 /// Numeric constant.
81 number,
82 /// String literal constant.
83 string,
84
85 /// Constructs a unique struct type.
86 struct_decl,
87 /// Constructs an array type
88 array_type,
89
90 struct_init,
91 array_init,
92
93 tuple_init,
94 tuple_type,
95
96 add,
97 sub,
98 mul,
99 div,
100 rem,
101 pow,
102 @"and",
103 @"or",
104 bit_and,
105 bit_or,
106 bit_xor,
107 shl,
108 shr,
109 eq,
110 ne,
111 lt,
112 le,
113 gt,
114 ge,
115
116 negate,
117 bool_not,
118 bit_not,
119 assert,
120 field,
121 index,
122 call,
123 type_of,
124 /// Implements the `@extern` builtin
125 @"extern",
126
127 /// Implements the `@jsNew` builtin
128 js_new,
129 /// Implements the `@import` builtin
130 import,
131 /// Implements the `@fetch` builtin
132 fetch,
133 /// Implements the `@compileError` builtin
134 compile_error,
135
136 pub fn Data(comptime tag: Tag) type {
137 return switch (tag) {
138 .module, .block, .@"comptime" => PlNode(IndexList),
139
140 .declaration,
141 .variable_declaration,
142 .test_declaration,
143 => PlNode(Decl),
144
145 .coercion => PlNode(Coercion),
146 .@"break" => PlNode(Break),
147 .number => PlNode(Float),
148 .string => PlNode(SlicedString),
149 .call => PlNode(Call),
150 .@"if" => PlNode(If),
151 .store => PlNode(Store),
152 .store_decl => PlNode(StoreVarDecl),
153 .store_field => PlNode(StoreField),
154 .js_new => PlNode(JsNew),
155 .struct_decl => PlNode(StructDecl),
156 .struct_init => PlNode(StructInit),
157 .array_init => PlNode(ArrayInit),
158 .tuple_type => PlNode(TupleType),
159 .tuple_init => PlNode(TupleInit),
160 .func => PlNode(Func),
161
162 .load_decl => StrNode,
163 .param => DataNode,
164
165 .result_type_argument => ResultTypeArgument,
166 .result_type_struct_field => ResultTypeStructField,
167 .result_type_tuple_field => ResultTypeTupleField,
168 .result_type_array_value => ResultTypeArrayValue,
169
170 // One operand instructions
171 .memory,
172 .discard,
173 .ensure_void,
174 .negate,
175 .bool_not,
176 .bit_not,
177 .@"extern",
178 .import,
179 .fetch,
180 .compile_error,
181 .type_of,
182 .array_type,
183 .assert,
184 => RefNode,
185
186 // Two operand instructions
187 .field,
188 .index,
189 .add,
190 .sub,
191 .mul,
192 .div,
193 .rem,
194 .pow,
195 .@"and",
196 .@"or",
197 .bit_and,
198 .bit_or,
199 .bit_xor,
200 .shl,
201 .shr,
202 .eq,
203 .ne,
204 .lt,
205 .le,
206 .gt,
207 .ge,
208 => PlNode(Bin),
209 };
210 }
211
212 pub fn airTag(comptime tag: Tag) Air.Inst.Tag {
213 return @field(Air.Inst.Tag, @tagName(tag));
214 }
215};
216
217pub fn PlNode(comptime Payload: type) type {
218 return struct {
219 pub const T = Payload;
220
221 src_node: NodeOffset,
222 extra_index: ExtraIndex(Payload),
223 };
224}
225
226pub const RefNode = struct {
227 src_node: NodeOffset,
228 operand: Inst.Ref,
229};
230
231pub const StrNode = struct {
232 src_node: NodeOffset,
233 string: NullTerminatedString,
234};
235
236pub const DataNode = struct {
237 src_node: NodeOffset,
238 data: u32,
239};
240
241pub const Inst = struct {
242 pub const List = std.MultiArrayList(Inst);
243
244 tag: Tag,
245 data: EncodedData,
246
247 /// The position of a Hir instruction within the `Hir` instructions array.
248 pub const Index = enum(u32) {
249 /// Hir is structured so that the outermost "main" struct of any file
250 /// is always at index 0.
251 main_module_inst = 0,
252 _,
253
254 pub const static_len = @intFromEnum(Ref.empty_tuple) + 1;
255
256 pub fn toRef(i: Index) Inst.Ref {
257 return @enumFromInt(static_len + @intFromEnum(i));
258 }
259
260 pub fn toOptional(i: Index) Optional {
261 return @enumFromInt(@intFromEnum(i));
262 }
263
264 pub fn op(i: Index, operand: Report.Operand) InstOp {
265 return .{ .index = i, .op = operand };
266 }
267
268 pub const Optional = enum(u32) {
269 none = std.math.maxInt(u32),
270 _,
271
272 pub fn unwrap(oi: Optional) ?Index {
273 return if (oi == .none) null else @enumFromInt(@intFromEnum(oi));
274 }
275 };
276 };
277
278 /// A reference to an instruction, or to a static InternPool value.
279 ///
280 /// If the integer tag value is < InternPool.static_len, then it
281 /// corresponds to a Value. Otherwise, it is an instruction index.
282 pub const Ref = enum(u32) {
283 opaque_type,
284 bool_type,
285 int32_type,
286 float_type,
287 string_type,
288 never_type,
289 type_type,
290 numeric_literal_type,
291 numeric_literal_array_type,
292 never_array_type,
293 empty_tuple_type,
294
295 void,
296 bool_true,
297 bool_false,
298 int32_zero,
299 float_zero,
300 nan,
301 empty_string,
302 empty_array,
303 empty_tuple,
304 _,
305
306 pub const Decoded = union(enum) {
307 value: InternPool.Value,
308 index: Hir.Inst.Index,
309 };
310
311 pub fn decode(ref: Ref) Decoded {
312 const ref_int = @intFromEnum(ref);
313 return if (ref_int >= Index.static_len)
314 .{ .index = @enumFromInt(ref_int - Index.static_len) }
315 else
316 .{ .value = @enumFromInt(ref_int) };
317 }
318
319 pub fn index(ref: Ref) ?Index {
320 const ref_int = @intFromEnum(ref);
321 if (ref_int >= Index.static_len) {
322 return @enumFromInt(ref_int - Index.static_len);
323 } else {
324 return null;
325 }
326 }
327
328 pub fn value(inst: Ref) ?InternPool.Value {
329 const ref_int = @intFromEnum(inst);
330
331 // TODO: is this > or >=
332 if (ref_int > Index.static_len)
333 return null;
334
335 return @enumFromInt(ref_int);
336 }
337
338 pub fn toOptional(ref: Ref) Optional {
339 return @enumFromInt(@intFromEnum(ref));
340 }
341
342 pub const Optional = enum(u32) {
343 none = std.math.maxInt(u32),
344 _,
345
346 pub fn unwrap(opt: Optional) ?Ref {
347 return if (opt != .none) @enumFromInt(@intFromEnum(opt)) else null;
348 }
349 };
350 };
351};
352
353pub const Bin = struct {
354 lhs: Inst.Ref,
355 rhs: Inst.Ref,
356};
357
358pub const Store = struct {
359 memory: Inst.Index,
360 operand: Inst.Ref,
361};
362
363pub const StoreVarDecl = struct {
364 var_decl: NullTerminatedString,
365 operand: Inst.Ref,
366};
367
368pub const StoreField = struct {
369 base: Inst.Ref,
370 field: Inst.Ref,
371 operand: Inst.Ref,
372};
373
374pub const Coercion = struct {
375 type: Inst.Ref,
376 operand: Inst.Ref,
377};
378
379pub const Break = struct {
380 block: Inst.Index,
381 operand: Inst.Ref,
382};
383
384pub const Call = struct {
385 base: Inst.Ref,
386 args: RefList,
387};
388
389pub const If = struct {
390 cond: Inst.Ref,
391 yes: Inst.Ref,
392 no: Inst.Ref,
393};
394
395pub const JsNew = struct {
396 base: Inst.Ref,
397 args: RefList,
398};
399
400/// Index into extra which contains `field_len * 2` u32s. The first half
401/// are the keys (NullTerminatedString) and the second half are field types
402/// (Ref). This basically is like a specialized MultiArrayList.
403pub const StructDecl = struct {
404 payload_index: u32,
405 field_len: u32,
406
407 pub fn keys(decl: StructDecl, hir: *const Hir) []const NullTerminatedString {
408 return @ptrCast(
409 hir.extra.list.items[decl.payload_index..][0..decl.field_len],
410 );
411 }
412
413 pub fn types(decl: StructDecl, hir: *const Hir) []const Inst.Ref {
414 return @ptrCast(
415 hir.extra.list.items[decl.payload_index + decl.field_len ..][0..decl.field_len],
416 );
417 }
418};
419
420/// This memory is laid out with the same strategy described in StructDecl
421pub const StructInit = struct {
422 base_type: Inst.Ref,
423 payload_index: u32,
424 field_len: u32,
425
426 pub fn keys(decl: StructInit, hir: *const Hir) []const NullTerminatedString {
427 return @ptrCast(
428 hir.extra.list.items[decl.payload_index..][0..decl.field_len],
429 );
430 }
431
432 pub fn inits(decl: StructInit, hir: *const Hir) []const Inst.Ref {
433 return @ptrCast(
434 hir.extra.list.items[decl.payload_index + decl.field_len ..][0..decl.field_len],
435 );
436 }
437};
438
439pub const ArrayInit = struct {
440 /// Optionally provide a result type for more reliable type coercion.
441 /// If specified, this must be an array type. Otherwise, the first
442 /// element's type is used.
443 type_hint: Inst.Ref.Optional,
444 /// The items in the array
445 list: RefList,
446};
447
448pub const TupleType = struct {
449 /// The items in the tuple
450 items: RefList,
451};
452
453pub const TupleInit = struct {
454 /// Optionally provide a result type for more reliable type coercion.
455 /// If specified, this must be an array type. Otherwise, the first
456 /// element's type is used.
457 type_hint: Inst.Ref.Optional,
458 /// The items in the tuple
459 items: RefList,
460};
461
462pub const Decl = struct {
463 name: NullTerminatedString,
464 hash: Cache.BinDigest,
465 instruction_count: u32,
466 body: Inst.Ref,
467};
468
469pub const Func = struct {
470 params: OptionalRefList,
471 return_type: Inst.Ref.Optional,
472 body: Inst.Index,
473 captures: IndexList,
474 instruction_count: u32,
475};
476
477pub const ResultTypeArgument = struct {
478 /// The VALUE that is going to be called.
479 call_base: Inst.Ref,
480 /// Which argument
481 argument: u32,
482};
483
484pub const ResultTypeStructField = struct {
485 /// The type of the struct itself,
486 type: Inst.Ref,
487 /// Which field
488 field: NullTerminatedString,
489};
490
491pub const ResultTypeTupleField = struct {
492 /// The type of the tuple itself,
493 type: Inst.Ref,
494 /// Which item
495 item: u32,
496};
497
498pub const ResultTypeArrayValue = struct {
499 /// The type of the array itself,
500 type: Inst.Ref,
501 unused: u32 = 0,
502};
503
504// TODO: make this an enum
505const NodeOffset = i32;
506
507// TODO: remove InstOp. LazyNode should not have a decl
508pub const InstOp = struct {
509 index: Inst.Index,
510 op: Report.Operand,
511
512 pub fn lazyNode(inst: InstOp, decl: Comp.Decl.Index) LazyNode {
513 return .{
514 .inst = inst.index,
515 .operand = inst.op,
516 .decl = decl,
517 };
518 }
519};
520
521pub const NullTerminatedString = enum(u32) {
522 empty = 0,
523 _,
524
525 pub fn slice(str: NullTerminatedString, string_bytes: []const u8) [:0]const u8 {
526 if (str == .empty) return "";
527 const sub = string_bytes[@intFromEnum(str)..];
528 const i = mem.indexOfScalar(u8, sub, 0) orelse unreachable;
529
530 return sub[0..i :0];
531 }
532};
533
534pub const SlicedString = struct {
535 start: u32,
536 len: u32,
537
538 pub fn slice(str: SlicedString, string_bytes: []const u8) []const u8 {
539 return string_bytes[str.start..][0..str.len];
540 }
541};
542
543// TODO: Make the next three structs using a generic comptime thing
544
545pub const RefList = struct {
546 start: u32,
547 len: u32,
548
549 pub fn slice(self: RefList, hir: *const Hir) []const Inst.Ref {
550 return @ptrCast(hir.extra.list.items[self.start..][0..self.len]);
551 }
552};
553
554pub const OptionalRefList = struct {
555 start: u32,
556 len: u32,
557
558 pub fn slice(self: OptionalRefList, hir: *const Hir) []const Inst.Ref.Optional {
559 return @ptrCast(hir.extra.list.items[self.start..][0..self.len]);
560 }
561};
562
563pub const IndexList = struct {
564 start: u32,
565 len: u32,
566
567 pub fn slice(self: IndexList, hir: *const Hir) []const Inst.Index {
568 return @ptrCast(hir.extra.list.items[self.start..][0..self.len]);
569 }
570};
571
572pub const NullTermStringList = struct {
573 start: u32,
574 len: u32,
575
576 pub fn slice(self: IndexList, hir: *const Hir) []const NullTerminatedString {
577 return @ptrCast(hir.extra.list.items[self.start..][0..self.len]);
578 }
579};
580
581pub fn deinit(hir: *Hir, gpa: std.mem.Allocator) void {
582 gpa.free(hir.string_bytes);
583 hir.extra.deinit(gpa);
584 hir.insts.deinit(gpa);
585 hir.report.deinit(gpa);
586 hir.* = undefined;
587}
588
589pub fn instTag(hir: *const Hir, index: Inst.Index) Hir.Tag {
590 return hir.insts.items(.tag)[@intFromEnum(index)];
591}
592
593pub fn instEncodedData(hir: *const Hir, index: Inst.Index) EncodedData {
594 return hir.insts.items(.data)[@intFromEnum(index)];
595}
596
597pub fn instData(hir: *const Hir, comptime tag: Hir.Tag, index: Inst.Index) tag.Data() {
598 return hir.instEncodedData(index).decode(tag, hir.instTag(index));
599}
600
601pub fn instDataUnsafe(hir: *const Hir, comptime T: type, index: Inst.Index) T {
602 return hir.instEncodedData(index).decodeUnsafe(T, hir.instTag(index));
603}
604
605pub fn instPayload(hir: *const Hir, comptime tag: Hir.Tag, index: Inst.Index) tag.Data().T {
606 return hir.instData(tag, index).extra_index.get(hir);
607}
608
609pub fn instNodeOffset(hir: *const Hir, index: Inst.Index) i32 {
610 return switch (hir.instTag(index)) {
611 // these nodes never get errors, but Hir.Dump will print these regardless
612 // TODO:
613 .declaration,
614 .result_type_argument,
615 .result_type_struct_field,
616 .result_type_tuple_field,
617 .result_type_array_value,
618 => 0,
619
620 inline else => |tag| hir.instData(tag, index).src_node,
621 };
622}
623
624pub fn blockInstructions(hir: *const Hir, index: Inst.Index) []const Inst.Index {
625 return hir.instData(.block, index).extra_index.get(hir).slice(hir);
626}
627
628pub fn fullFuncDecl(hir: *const Hir, index: Inst.Index) struct { Hir.Decl, Hir.DeclFn } {
629 const decl, var trail = hir.instData(.declaration, index).extra_index.getTrail(hir);
630 assert(decl.kind == .function);
631 const decl_fn = trail.read(&hir.extra, Hir.DeclFn);
632 return .{ decl, decl_fn };
633}
634
635pub fn fullDecl(hir: *const Hir, index: Inst.Index) Decl {
636 const decl = hir.instEncodedData(index).decodeUnsafe(PlNode(Decl), hir.instTag(index)).extra_index.get(hir);
637 return .{
638 .name = decl.name,
639 .hash = decl.hash,
640 .instruction_count = decl.instruction_count,
641 .body = decl.body,
642 };
643}
644
645pub fn declData(hir: *const Hir, index: Inst.Index) Hir.Decl {
646 return hir.instDataUnsafe(PlNode(Decl), index).extra_index.get(hir);
647}
648
649pub fn moduleDeclarations(hir: *const Hir, module_index: Inst.Index) []const Inst.Index {
650 const module = hir.instData(.module, module_index);
651 const module_data: IndexList = module.extra_index.get(hir);
652 const slice = module_data.slice(hir);
653 if (core.runtime_safety) {
654 for (slice) |item| {
655 switch (hir.instTag(item)) {
656 .declaration, .variable_declaration, .test_declaration => {},
657 else => {
658 std.debug.panic("\ngot unknown tag {s}\n", .{@tagName(hir.instTag(item))});
659 },
660 }
661 }
662 }
663 return slice;
664}
665
666pub fn rootFunction(hir: *const Hir) Inst.Index {
667 const decls = hir.moduleDeclarations(.main_module_inst);
668 const root_fn = decls[decls.len - 1];
669 assert(hir.declName(root_fn).len == 0);
670 return root_fn;
671}
672
673pub fn declName(hir: *const Hir, decl_index: Inst.Index) []const u8 {
674 const decl = hir.instDataUnsafe(PlNode(Decl), decl_index);
675 const decl_data: Decl = decl.extra_index.get(hir);
676 return decl_data.name.slice(hir.string_bytes);
677}
678
679pub fn dumpToWriter(hir: Hir, writer: anytype, tty: ?std.io.tty.Config) !void {
680 try writer.print("{}", .{Dump{ .hir = hir, .tty = tty }});
681}
682
683/// Used for generating decl hashes
684fn isolatedRefValue(decl_index: Inst.Index, ref_to_hash: Inst.Ref) i32 {
685 return switch (ref_to_hash.decode()) {
686 .value => |value| -@as(i32, @intCast(@intFromEnum(value))),
687 .index => |index| @intCast(@intFromEnum(index) - @intFromEnum(decl_index)),
688 };
689}
690
691/// Used for generating decl hashes
692fn isolatedIndexValue(decl_index: Inst.Index, index_to_hash: Inst.Index) u32 {
693 return @intFromEnum(index_to_hash) - @intFromEnum(decl_index);
694}
695
696pub fn writeToHasher(
697 hir: *const Hir,
698 hasher: *HashHelper,
699 tag_list: []Tag,
700 data_list: []EncodedData,
701 decl_index: Inst.Index,
702) void {
703 // Add all of the tags
704 hasher.addBytes(std.mem.sliceAsBytes(tag_list));
705
706 // Add all of the data
707 for (tag_list, data_list) |tag, data| {
708
709 // This should roughly line up with Hir.Tag.Data
710 switch (tag) {
711 // PlNode(IndexList),
712 .module,
713 .block,
714 .@"comptime",
715 => {
716 const pl_node = data.decodeUnsafe(PlNode(IndexList), tag);
717 hasher.add(pl_node.src_node);
718 writeIndexListToHasher(hasher, pl_node.extra_index.get(hir).slice(hir), decl_index);
719 },
720
721 // PlNode
722 inline .declaration,
723 .variable_declaration,
724 .test_declaration,
725 .coercion,
726 .@"break",
727 .number,
728 .string,
729 .call,
730 .@"if",
731 .store,
732 .js_new,
733 .struct_decl,
734 .struct_init,
735 .array_init,
736 .tuple_init,
737 .tuple_type,
738 .func,
739 .store_decl,
740 .store_field,
741 => |pl_node_tag| {
742 const pl_node = data.decode(pl_node_tag, tag);
743 hasher.add(pl_node.src_node);
744 hir.writePlNodeInnerToHasher(hasher, pl_node_tag.Data().T, pl_node.extra_index.get(hir), decl_index);
745 },
746 // StrNode
747 .load_decl => {
748 const str_node = data.decodeUnsafe(StrNode, tag);
749 hasher.add(str_node.src_node);
750 hasher.addBytes(str_node.string.slice(hir.string_bytes));
751 },
752 // DataNode
753 .param => {
754 const data_node = data.decodeUnsafe(DataNode, tag);
755 hasher.add(data_node.src_node);
756 hasher.add(data_node.data);
757 },
758 // RefNode
759 .memory,
760 .discard,
761 .ensure_void,
762 .negate,
763 .bool_not,
764 .bit_not,
765 .@"extern",
766 .import,
767 .fetch,
768 .compile_error,
769 .type_of,
770 .array_type,
771 .assert,
772 => {
773 const ref_node = data.decodeUnsafe(RefNode, tag);
774 hasher.add(ref_node.src_node);
775 hasher.add(isolatedRefValue(decl_index, ref_node.operand));
776 },
777 // PlNode(Bin)
778 .field,
779 .index,
780 .add,
781 .sub,
782 .mul,
783 .div,
784 .rem,
785 .pow,
786 .@"and",
787 .@"or",
788 .bit_and,
789 .bit_or,
790 .bit_xor,
791 .shl,
792 .shr,
793 .eq,
794 .ne,
795 .lt,
796 .le,
797 .gt,
798 .ge,
799 => {
800 const pl_node = data.decodeUnsafe(PlNode(Bin), tag);
801 const bin = pl_node.extra_index.get(hir);
802 hasher.add(isolatedRefValue(decl_index, bin.lhs));
803 hasher.add(isolatedRefValue(decl_index, bin.rhs));
804 },
805 // Result Types
806 .result_type_argument => {
807 const arg = data.decodeUnsafe(ResultTypeArgument, tag);
808 hasher.add(isolatedRefValue(decl_index, arg.call_base));
809 hasher.add(arg.argument);
810 },
811 .result_type_struct_field => {
812 const struct_field = data.decodeUnsafe(ResultTypeStructField, tag);
813 hasher.add(isolatedRefValue(decl_index, struct_field.type));
814 hasher.addBytes(struct_field.field.slice(hir.string_bytes));
815 },
816 .result_type_tuple_field => {
817 const tuple_field = data.decodeUnsafe(ResultTypeTupleField, tag);
818 hasher.add(isolatedRefValue(decl_index, tuple_field.type));
819 hasher.add(tuple_field.item);
820 },
821 .result_type_array_value => {
822 const array_value = data.decodeUnsafe(ResultTypeArrayValue, tag);
823 hasher.add(isolatedRefValue(decl_index, array_value.type));
824 },
825 }
826 }
827}
828
829fn writeIndexListToHasher(
830 hasher: *HashHelper,
831 index_list: []const Inst.Index,
832 decl_index: Inst.Index,
833) void {
834 hasher.add(index_list.len);
835 for (index_list) |idx| {
836 hasher.add(isolatedIndexValue(decl_index, idx));
837 }
838}
839
840fn writeRefListToHasher(
841 hasher: *HashHelper,
842 decl_index: Inst.Index,
843 index_list: []const Inst.Ref,
844) void {
845 hasher.add(index_list.len);
846 for (index_list) |idx| {
847 hasher.add(isolatedRefValue(decl_index, idx));
848 }
849}
850
851fn writePlNodeInnerToHasher(
852 hir: *const Hir,
853 hasher: *HashHelper,
854 comptime T: type,
855 data: T,
856 decl_index: Inst.Index,
857) void {
858 switch (T) {
859 Decl => {
860 hasher.addBytes(&data.hash);
861 hasher.addBytes(data.name.slice(hir.string_bytes));
862 },
863 Float => {
864 const as_bytes: [2]u32 = .{
865 data.low,
866 data.high,
867 };
868 hasher.add(@as(u64, @bitCast(as_bytes)));
869 },
870 SlicedString => {
871 hasher.addBytes(data.slice(hir.string_bytes));
872 },
873 Coercion => {
874 hasher.add(isolatedRefValue(decl_index, data.type));
875 hasher.add(isolatedRefValue(decl_index, data.operand));
876 },
877 Break => {
878 hasher.add(isolatedIndexValue(decl_index, data.block));
879 hasher.add(isolatedRefValue(decl_index, data.operand));
880 },
881 Call => {
882 hasher.add(isolatedRefValue(decl_index, data.base));
883 writeRefListToHasher(hasher, decl_index, data.args.slice(hir));
884 },
885 If => {
886 hasher.add(isolatedRefValue(decl_index, data.cond));
887 hasher.add(isolatedRefValue(decl_index, data.yes));
888 hasher.add(isolatedRefValue(decl_index, data.no));
889 },
890 Store => {
891 hasher.add(isolatedIndexValue(decl_index, data.memory));
892 hasher.add(isolatedRefValue(decl_index, data.operand));
893 },
894 StoreField => {
895 hasher.add(isolatedRefValue(decl_index, data.base));
896 hasher.add(isolatedRefValue(decl_index, data.field));
897 hasher.add(isolatedRefValue(decl_index, data.operand));
898 },
899 StoreVarDecl => {
900 hasher.addBytes(data.var_decl.slice(hir.string_bytes));
901 hasher.add(isolatedRefValue(decl_index, data.operand));
902 },
903 JsNew => {
904 hasher.add(isolatedRefValue(decl_index, data.base));
905 writeRefListToHasher(hasher, decl_index, data.args.slice(hir));
906 },
907 StructDecl => {
908 hasher.add(data.field_len);
909 for (data.keys(hir), data.types(hir)) |key, ty| {
910 hasher.addBytes(key.slice(hir.string_bytes));
911 hasher.add(isolatedRefValue(decl_index, ty));
912 }
913 },
914 StructInit => {
915 hasher.add(isolatedRefValue(decl_index, data.base_type));
916 hasher.add(data.field_len);
917 for (data.keys(hir), data.inits(hir)) |key, init| {
918 hasher.addBytes(key.slice(hir.string_bytes));
919 hasher.add(isolatedRefValue(decl_index, init));
920 }
921 },
922 ArrayInit => {
923 if (data.type_hint.unwrap()) |type_hint|
924 hasher.add(isolatedRefValue(decl_index, type_hint))
925 else
926 hasher.add(@as(i32, std.math.maxInt(i32)));
927 writeRefListToHasher(hasher, decl_index, data.list.slice(hir));
928 },
929 Func => {
930 for (data.params.slice(hir)) |optional| {
931 if (optional.unwrap()) |item|
932 hasher.add(isolatedRefValue(decl_index, item));
933 }
934
935 if (data.return_type.unwrap()) |type_hint|
936 hasher.add(isolatedRefValue(decl_index, type_hint))
937 else
938 hasher.add(@as(i32, std.math.maxInt(i32)));
939
940 hasher.add(isolatedIndexValue(decl_index, data.body));
941 hasher.add(data.instruction_count);
942 },
943 TupleType => {
944 hasher.add(data.items.len);
945 for (data.items.slice(hir)) |item| {
946 hasher.add(isolatedRefValue(decl_index, item));
947 }
948 },
949 TupleInit => {
950 if (data.type_hint.unwrap()) |type_hint|
951 hasher.add(isolatedRefValue(decl_index, type_hint))
952 else
953 hasher.add(@as(i32, std.math.maxInt(i32)));
954 writeRefListToHasher(hasher, decl_index, data.items.slice(hir));
955 },
956 else => @compileError("hash not implemented for " ++ @typeName(T)),
957 }
958}
959
960pub const SerializedHeader = extern struct {
961 const version = @import("config").version_string ++ "\x00";
962
963 instruction_len: u32,
964 string_bytes: u32,
965 extra_len: u32,
966 report_err_len: u32,
967 report_warn_len: u32,
968};
969
970pub fn serialize(hir: Hir, target: core.SerializeTarget) !void {
971 const header: SerializedHeader = .{
972 .instruction_len = @intCast(hir.insts.len),
973 .string_bytes = @intCast(hir.string_bytes.len),
974 .extra_len = @intCast(hir.extra.list.items.len),
975 .report_err_len = @intCast(hir.report.errors.len),
976 .report_warn_len = @intCast(hir.report.warnings.len),
977 };
978
979 var iovecs = [_]std.posix.iovec_const{
980 core.iovecConst(SerializedHeader.version),
981 core.iovecConst(std.mem.asBytes(&header)),
982 core.iovecConst(std.mem.sliceAsBytes(hir.insts.items(.tag))),
983 core.iovecConst(std.mem.sliceAsBytes(hir.insts.items(.data))),
984 core.iovecConst(hir.string_bytes),
985 core.iovecConst(std.mem.sliceAsBytes(hir.extra.list.items)),
986 core.iovecConst(std.mem.sliceAsBytes(hir.report.errors)),
987 core.iovecConst(std.mem.sliceAsBytes(hir.report.warnings)),
988 };
989 try target.writev(&iovecs);
990}
991
992const Hir = @This();
993
994const std = @import("std");
995const mem = std.mem;
996const assert = std.debug.assert;
997const HashHelper = std.Build.Cache.HashHelper;
998
999const core = @import("mod.zig");
1000const Cache = core.Cache;
1001
1002const Ast = core.Ast;
1003const Unused = Ast.Unused;
1004const NodeIndex = Ast.NodeIndex;
1005const TokenIndex = Ast.TokenIndex;
1006const OptionalNodeIndex = Ast.OptionalNodeIndex;
1007const OptionalTokenIndex = Ast.OptionalTokenIndex;
1008const OptionalExtraIndex = Ast.OptionalExtraIndex;
1009
1010const dod = core.array;
1011const Float = dod.Float;
1012
1013const InternPool = core.InternPool;
1014
1015const Air = core.Air;
1016
1017const Comp = core.Comp;
1018
1019const Report = core.Report;
1020const LazyNode = Report.LazyNode;