authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-22 14:27:56-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-22 14:29:16-07:00
log79dee75b1ccd8f3f595aad0d4150851cff58f691
tree964cff23e1de4c43f1d1d3ba01313b1c1a0fe4bf
parent9d311e9960952838edb19a422e5023c670f9995d

stage2: rename ir.zig to air.zig

We've settled on the nomenclature for the artifacts the compiler pipeline produces: 1. Tokens 2. AST (Abstract Syntax Tree) 3. ZIR (Zig Intermediate Representation) 4. AIR (Analyzed Intermediate Representation) 5. Machine Code Renaming `ir` identifiers to `air` will come with the inevitable air-memory-layout branch that I plan to start after the 0.8.0 release.

15 files changed, 1163 insertions(+), 1163 deletions(-)

CMakeLists.txt+1-1
......@@ -554,7 +554,7 @@ set(ZIG_STAGE2_SOURCES
554554 "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig"
555555 "${CMAKE_SOURCE_DIR}/src/glibc.zig"
556556 "${CMAKE_SOURCE_DIR}/src/introspect.zig"
557 "${CMAKE_SOURCE_DIR}/src/ir.zig"
557 "${CMAKE_SOURCE_DIR}/src/air.zig"
558558 "${CMAKE_SOURCE_DIR}/src/libc_installation.zig"
559559 "${CMAKE_SOURCE_DIR}/src/libcxx.zig"
560560 "${CMAKE_SOURCE_DIR}/src/libtsan.zig"
src/Module.zig+1-1
......@@ -21,7 +21,7 @@ const Type = @import("type.zig").Type;
2121const TypedValue = @import("TypedValue.zig");
2222const Package = @import("Package.zig");
2323const link = @import("link.zig");
24const ir = @import("ir.zig");
24const ir = @import("air.zig");
2525const Zir = @import("Zir.zig");
2626const trace = @import("tracy.zig").trace;
2727const AstGen = @import("AstGen.zig");
src/Sema.zig+1-1
......@@ -52,7 +52,7 @@ const Sema = @This();
5252const Value = @import("value.zig").Value;
5353const Type = @import("type.zig").Type;
5454const TypedValue = @import("TypedValue.zig");
55const ir = @import("ir.zig");
55const ir = @import("air.zig");
5656const Zir = @import("Zir.zig");
5757const Module = @import("Module.zig");
5858const Inst = ir.Inst;
src/Zir.zig+1-1
......@@ -22,7 +22,7 @@ const Zir = @This();
2222const Type = @import("type.zig").Type;
2323const Value = @import("value.zig").Value;
2424const TypedValue = @import("TypedValue.zig");
25const ir = @import("ir.zig");
25const ir = @import("air.zig");
2626const Module = @import("Module.zig");
2727const LazySrcLoc = Module.LazySrcLoc;
2828
src/air.zig created+1150
......@@ -0,0 +1,1150 @@
1const std = @import("std");
2const Value = @import("value.zig").Value;
3const Type = @import("type.zig").Type;
4const Module = @import("Module.zig");
5const assert = std.debug.assert;
6const codegen = @import("codegen.zig");
7const ast = std.zig.ast;
8
9/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
10/// of instructions that correspond to the ZIR text format.
11/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
12/// so are the `Value` and `Type`. The value of a constant must be copied into
13/// a memory location for the value to survive after a const instruction.
14pub const Inst = struct {
15 tag: Tag,
16 /// Each bit represents the index of an `Inst` parameter in the `args` field.
17 /// If a bit is set, it marks the end of the lifetime of the corresponding
18 /// instruction parameter. For example, 0b101 means that the first and
19 /// third `Inst` parameters' lifetimes end after this instruction, and will
20 /// not have any more following references.
21 /// The most significant bit being set means that the instruction itself is
22 /// never referenced, in other words its lifetime ends as soon as it finishes.
23 /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced.
24 /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
25 /// lifetimes of operands are encoded elsewhere.
26 deaths: DeathsInt = undefined,
27 ty: Type,
28 src: Module.LazySrcLoc,
29
30 pub const DeathsInt = u16;
31 pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
32 pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1;
33 pub const deaths_bits = unreferenced_bit_index - 1;
34
35 pub fn isUnused(self: Inst) bool {
36 return (self.deaths & (1 << unreferenced_bit_index)) != 0;
37 }
38
39 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
40 assert(index < deaths_bits);
41 return @truncate(u1, self.deaths >> index) != 0;
42 }
43
44 pub fn clearOperandDeath(self: *Inst, index: DeathsBitIndex) void {
45 assert(index < deaths_bits);
46 self.deaths &= ~(@as(DeathsInt, 1) << index);
47 }
48
49 pub fn specialOperandDeaths(self: Inst) bool {
50 return (self.deaths & (1 << deaths_bits)) != 0;
51 }
52
53 pub const Tag = enum {
54 add,
55 addwrap,
56 alloc,
57 arg,
58 assembly,
59 bit_and,
60 bitcast,
61 bit_or,
62 block,
63 br,
64 /// Same as `br` except the operand is a list of instructions to be treated as
65 /// a flat block; that is there is only 1 break instruction from the block, and
66 /// it is implied to be after the last instruction, and the last instruction is
67 /// the break operand.
68 /// This instruction exists for late-stage semantic analysis patch ups, to
69 /// replace one br operand with multiple instructions, without moving anything else around.
70 br_block_flat,
71 breakpoint,
72 br_void,
73 call,
74 cmp_lt,
75 cmp_lte,
76 cmp_eq,
77 cmp_gte,
78 cmp_gt,
79 cmp_neq,
80 condbr,
81 constant,
82 dbg_stmt,
83 /// ?T => bool
84 is_null,
85 /// ?T => bool (inverted logic)
86 is_non_null,
87 /// *?T => bool
88 is_null_ptr,
89 /// *?T => bool (inverted logic)
90 is_non_null_ptr,
91 /// E!T => bool
92 is_err,
93 /// *E!T => bool
94 is_err_ptr,
95 /// E => u16
96 error_to_int,
97 /// u16 => E
98 int_to_error,
99 bool_and,
100 bool_or,
101 /// Read a value from a pointer.
102 load,
103 /// A labeled block of code that loops forever. At the end of the body it is implied
104 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
105 loop,
106 ptrtoint,
107 ref,
108 ret,
109 retvoid,
110 varptr,
111 /// Write a value to a pointer. LHS is pointer, RHS is value.
112 store,
113 sub,
114 subwrap,
115 unreach,
116 mul,
117 mulwrap,
118 div,
119 not,
120 floatcast,
121 intcast,
122 /// ?T => T
123 optional_payload,
124 /// *?T => *T
125 optional_payload_ptr,
126 wrap_optional,
127 /// E!T -> T
128 unwrap_errunion_payload,
129 /// E!T -> E
130 unwrap_errunion_err,
131 /// *(E!T) -> *T
132 unwrap_errunion_payload_ptr,
133 /// *(E!T) -> E
134 unwrap_errunion_err_ptr,
135 /// wrap from T to E!T
136 wrap_errunion_payload,
137 /// wrap from E to E!T
138 wrap_errunion_err,
139 xor,
140 switchbr,
141 /// Given a pointer to a struct and a field index, returns a pointer to the field.
142 struct_field_ptr,
143
144 pub fn Type(tag: Tag) type {
145 return switch (tag) {
146 .alloc,
147 .retvoid,
148 .unreach,
149 .breakpoint,
150 => NoOp,
151
152 .ref,
153 .ret,
154 .bitcast,
155 .not,
156 .is_non_null,
157 .is_non_null_ptr,
158 .is_null,
159 .is_null_ptr,
160 .is_err,
161 .is_err_ptr,
162 .int_to_error,
163 .error_to_int,
164 .ptrtoint,
165 .floatcast,
166 .intcast,
167 .load,
168 .optional_payload,
169 .optional_payload_ptr,
170 .wrap_optional,
171 .unwrap_errunion_payload,
172 .unwrap_errunion_err,
173 .unwrap_errunion_payload_ptr,
174 .unwrap_errunion_err_ptr,
175 .wrap_errunion_payload,
176 .wrap_errunion_err,
177 => UnOp,
178
179 .add,
180 .addwrap,
181 .sub,
182 .subwrap,
183 .mul,
184 .mulwrap,
185 .div,
186 .cmp_lt,
187 .cmp_lte,
188 .cmp_eq,
189 .cmp_gte,
190 .cmp_gt,
191 .cmp_neq,
192 .store,
193 .bool_and,
194 .bool_or,
195 .bit_and,
196 .bit_or,
197 .xor,
198 => BinOp,
199
200 .arg => Arg,
201 .assembly => Assembly,
202 .block => Block,
203 .br => Br,
204 .br_block_flat => BrBlockFlat,
205 .br_void => BrVoid,
206 .call => Call,
207 .condbr => CondBr,
208 .constant => Constant,
209 .loop => Loop,
210 .varptr => VarPtr,
211 .struct_field_ptr => StructFieldPtr,
212 .switchbr => SwitchBr,
213 .dbg_stmt => DbgStmt,
214 };
215 }
216
217 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
218 return switch (op) {
219 .lt => .cmp_lt,
220 .lte => .cmp_lte,
221 .eq => .cmp_eq,
222 .gte => .cmp_gte,
223 .gt => .cmp_gt,
224 .neq => .cmp_neq,
225 };
226 }
227 };
228
229 /// Prefer `castTag` to this.
230 pub fn cast(base: *Inst, comptime T: type) ?*T {
231 if (@hasField(T, "base_tag")) {
232 return base.castTag(T.base_tag);
233 }
234 inline for (@typeInfo(Tag).Enum.fields) |field| {
235 const tag = @intToEnum(Tag, field.value);
236 if (base.tag == tag) {
237 if (T == tag.Type()) {
238 return @fieldParentPtr(T, "base", base);
239 }
240 return null;
241 }
242 }
243 unreachable;
244 }
245
246 pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
247 if (base.tag == tag) {
248 return @fieldParentPtr(tag.Type(), "base", base);
249 }
250 return null;
251 }
252
253 pub fn Args(comptime T: type) type {
254 return std.meta.fieldInfo(T, .args).field_type;
255 }
256
257 /// Returns `null` if runtime-known.
258 /// Should be called by codegen, not by Sema. Sema functions should call
259 /// `resolvePossiblyUndefinedValue` or `resolveDefinedValue` instead.
260 /// TODO audit Sema code for violations to the above guidance.
261 pub fn value(base: *Inst) ?Value {
262 if (base.ty.onePossibleValue()) |opv| return opv;
263
264 const inst = base.castTag(.constant) orelse return null;
265 return inst.val;
266 }
267
268 pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
269 return switch (base.tag) {
270 .cmp_lt => .lt,
271 .cmp_lte => .lte,
272 .cmp_eq => .eq,
273 .cmp_gte => .gte,
274 .cmp_gt => .gt,
275 .cmp_neq => .neq,
276 else => null,
277 };
278 }
279
280 pub fn operandCount(base: *Inst) usize {
281 inline for (@typeInfo(Tag).Enum.fields) |field| {
282 const tag = @intToEnum(Tag, field.value);
283 if (tag == base.tag) {
284 return @fieldParentPtr(tag.Type(), "base", base).operandCount();
285 }
286 }
287 unreachable;
288 }
289
290 pub fn getOperand(base: *Inst, index: usize) ?*Inst {
291 inline for (@typeInfo(Tag).Enum.fields) |field| {
292 const tag = @intToEnum(Tag, field.value);
293 if (tag == base.tag) {
294 return @fieldParentPtr(tag.Type(), "base", base).getOperand(index);
295 }
296 }
297 unreachable;
298 }
299
300 pub fn breakBlock(base: *Inst) ?*Block {
301 return switch (base.tag) {
302 .br => base.castTag(.br).?.block,
303 .br_void => base.castTag(.br_void).?.block,
304 .br_block_flat => base.castTag(.br_block_flat).?.block,
305 else => null,
306 };
307 }
308
309 pub const NoOp = struct {
310 base: Inst,
311
312 pub fn operandCount(self: *const NoOp) usize {
313 return 0;
314 }
315 pub fn getOperand(self: *const NoOp, index: usize) ?*Inst {
316 return null;
317 }
318 };
319
320 pub const UnOp = struct {
321 base: Inst,
322 operand: *Inst,
323
324 pub fn operandCount(self: *const UnOp) usize {
325 return 1;
326 }
327 pub fn getOperand(self: *const UnOp, index: usize) ?*Inst {
328 if (index == 0)
329 return self.operand;
330 return null;
331 }
332 };
333
334 pub const BinOp = struct {
335 base: Inst,
336 lhs: *Inst,
337 rhs: *Inst,
338
339 pub fn operandCount(self: *const BinOp) usize {
340 return 2;
341 }
342 pub fn getOperand(self: *const BinOp, index: usize) ?*Inst {
343 var i = index;
344
345 if (i < 1)
346 return self.lhs;
347 i -= 1;
348
349 if (i < 1)
350 return self.rhs;
351 i -= 1;
352
353 return null;
354 }
355 };
356
357 pub const Arg = struct {
358 pub const base_tag = Tag.arg;
359
360 base: Inst,
361 /// This exists to be emitted into debug info.
362 name: [*:0]const u8,
363
364 pub fn operandCount(self: *const Arg) usize {
365 return 0;
366 }
367 pub fn getOperand(self: *const Arg, index: usize) ?*Inst {
368 return null;
369 }
370 };
371
372 pub const Assembly = struct {
373 pub const base_tag = Tag.assembly;
374
375 base: Inst,
376 asm_source: []const u8,
377 is_volatile: bool,
378 output_constraint: ?[]const u8,
379 inputs: []const []const u8,
380 clobbers: []const []const u8,
381 args: []const *Inst,
382
383 pub fn operandCount(self: *const Assembly) usize {
384 return self.args.len;
385 }
386 pub fn getOperand(self: *const Assembly, index: usize) ?*Inst {
387 if (index < self.args.len)
388 return self.args[index];
389 return null;
390 }
391 };
392
393 pub const Block = struct {
394 pub const base_tag = Tag.block;
395
396 base: Inst,
397 body: Body,
398 /// This memory is reserved for codegen code to do whatever it needs to here.
399 codegen: codegen.BlockData = .{},
400
401 pub fn operandCount(self: *const Block) usize {
402 return 0;
403 }
404 pub fn getOperand(self: *const Block, index: usize) ?*Inst {
405 return null;
406 }
407 };
408
409 pub const convertable_br_size = std.math.max(@sizeOf(BrBlockFlat), @sizeOf(Br));
410 pub const convertable_br_align = std.math.max(@alignOf(BrBlockFlat), @alignOf(Br));
411 comptime {
412 assert(@byteOffsetOf(BrBlockFlat, "base") == @byteOffsetOf(Br, "base"));
413 }
414
415 pub const BrBlockFlat = struct {
416 pub const base_tag = Tag.br_block_flat;
417
418 base: Inst,
419 block: *Block,
420 body: Body,
421
422 pub fn operandCount(self: *const BrBlockFlat) usize {
423 return 0;
424 }
425 pub fn getOperand(self: *const BrBlockFlat, index: usize) ?*Inst {
426 return null;
427 }
428 };
429
430 pub const Br = struct {
431 pub const base_tag = Tag.br;
432
433 base: Inst,
434 block: *Block,
435 operand: *Inst,
436
437 pub fn operandCount(self: *const Br) usize {
438 return 1;
439 }
440 pub fn getOperand(self: *const Br, index: usize) ?*Inst {
441 if (index == 0)
442 return self.operand;
443 return null;
444 }
445 };
446
447 pub const BrVoid = struct {
448 pub const base_tag = Tag.br_void;
449
450 base: Inst,
451 block: *Block,
452
453 pub fn operandCount(self: *const BrVoid) usize {
454 return 0;
455 }
456 pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst {
457 return null;
458 }
459 };
460
461 pub const Call = struct {
462 pub const base_tag = Tag.call;
463
464 base: Inst,
465 func: *Inst,
466 args: []const *Inst,
467
468 pub fn operandCount(self: *const Call) usize {
469 return self.args.len + 1;
470 }
471 pub fn getOperand(self: *const Call, index: usize) ?*Inst {
472 var i = index;
473
474 if (i < 1)
475 return self.func;
476 i -= 1;
477
478 if (i < self.args.len)
479 return self.args[i];
480 i -= self.args.len;
481
482 return null;
483 }
484 };
485
486 pub const CondBr = struct {
487 pub const base_tag = Tag.condbr;
488
489 base: Inst,
490 condition: *Inst,
491 then_body: Body,
492 else_body: Body,
493 /// Set of instructions whose lifetimes end at the start of one of the branches.
494 /// The `then` branch is first: `deaths[0..then_death_count]`.
495 /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`.
496 deaths: [*]*Inst = undefined,
497 then_death_count: u32 = 0,
498 else_death_count: u32 = 0,
499
500 pub fn operandCount(self: *const CondBr) usize {
501 return 1;
502 }
503 pub fn getOperand(self: *const CondBr, index: usize) ?*Inst {
504 var i = index;
505
506 if (i < 1)
507 return self.condition;
508 i -= 1;
509
510 return null;
511 }
512 pub fn thenDeaths(self: *const CondBr) []*Inst {
513 return self.deaths[0..self.then_death_count];
514 }
515 pub fn elseDeaths(self: *const CondBr) []*Inst {
516 return (self.deaths + self.then_death_count)[0..self.else_death_count];
517 }
518 };
519
520 pub const Constant = struct {
521 pub const base_tag = Tag.constant;
522
523 base: Inst,
524 val: Value,
525
526 pub fn operandCount(self: *const Constant) usize {
527 return 0;
528 }
529 pub fn getOperand(self: *const Constant, index: usize) ?*Inst {
530 return null;
531 }
532 };
533
534 pub const Loop = struct {
535 pub const base_tag = Tag.loop;
536
537 base: Inst,
538 body: Body,
539
540 pub fn operandCount(self: *const Loop) usize {
541 return 0;
542 }
543 pub fn getOperand(self: *const Loop, index: usize) ?*Inst {
544 return null;
545 }
546 };
547
548 pub const VarPtr = struct {
549 pub const base_tag = Tag.varptr;
550
551 base: Inst,
552 variable: *Module.Var,
553
554 pub fn operandCount(self: *const VarPtr) usize {
555 return 0;
556 }
557 pub fn getOperand(self: *const VarPtr, index: usize) ?*Inst {
558 return null;
559 }
560 };
561
562 pub const StructFieldPtr = struct {
563 pub const base_tag = Tag.struct_field_ptr;
564
565 base: Inst,
566 struct_ptr: *Inst,
567 field_index: usize,
568
569 pub fn operandCount(self: *const StructFieldPtr) usize {
570 return 1;
571 }
572 pub fn getOperand(self: *const StructFieldPtr, index: usize) ?*Inst {
573 var i = index;
574
575 if (i < 1)
576 return self.struct_ptr;
577 i -= 1;
578
579 return null;
580 }
581 };
582
583 pub const SwitchBr = struct {
584 pub const base_tag = Tag.switchbr;
585
586 base: Inst,
587 target: *Inst,
588 cases: []Case,
589 /// Set of instructions whose lifetimes end at the start of one of the cases.
590 /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ].
591 deaths: [*]*Inst = undefined,
592 else_index: u32 = 0,
593 else_deaths: u32 = 0,
594 else_body: Body,
595
596 pub const Case = struct {
597 item: Value,
598 body: Body,
599 index: u32 = 0,
600 deaths: u32 = 0,
601 };
602
603 pub fn operandCount(self: *const SwitchBr) usize {
604 return 1;
605 }
606 pub fn getOperand(self: *const SwitchBr, index: usize) ?*Inst {
607 var i = index;
608
609 if (i < 1)
610 return self.target;
611 i -= 1;
612
613 return null;
614 }
615 pub fn caseDeaths(self: *const SwitchBr, case_index: usize) []*Inst {
616 const case = self.cases[case_index];
617 return (self.deaths + case.index)[0..case.deaths];
618 }
619 pub fn elseDeaths(self: *const SwitchBr) []*Inst {
620 return (self.deaths + self.else_index)[0..self.else_deaths];
621 }
622 };
623
624 pub const DbgStmt = struct {
625 pub const base_tag = Tag.dbg_stmt;
626
627 base: Inst,
628 line: u32,
629 column: u32,
630
631 pub fn operandCount(self: *const DbgStmt) usize {
632 return 0;
633 }
634 pub fn getOperand(self: *const DbgStmt, index: usize) ?*Inst {
635 return null;
636 }
637 };
638};
639
640pub const Body = struct {
641 instructions: []*Inst,
642};
643
644/// For debugging purposes, prints a function representation to stderr.
645pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
646 const allocator = old_module.gpa;
647 var ctx: DumpTzir = .{
648 .allocator = allocator,
649 .arena = std.heap.ArenaAllocator.init(allocator),
650 .old_module = &old_module,
651 .module_fn = module_fn,
652 .indent = 2,
653 .inst_table = DumpTzir.InstTable.init(allocator),
654 .partial_inst_table = DumpTzir.InstTable.init(allocator),
655 .const_table = DumpTzir.InstTable.init(allocator),
656 };
657 defer ctx.inst_table.deinit();
658 defer ctx.partial_inst_table.deinit();
659 defer ctx.const_table.deinit();
660 defer ctx.arena.deinit();
661
662 switch (module_fn.state) {
663 .queued => std.debug.print("(queued)", .{}),
664 .inline_only => std.debug.print("(inline_only)", .{}),
665 .in_progress => std.debug.print("(in_progress)", .{}),
666 .sema_failure => std.debug.print("(sema_failure)", .{}),
667 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
668 .success => {
669 const writer = std.io.getStdErr().writer();
670 ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR");
671 },
672 }
673}
674
675const DumpTzir = struct {
676 allocator: *std.mem.Allocator,
677 arena: std.heap.ArenaAllocator,
678 old_module: *const Module,
679 module_fn: *Module.Fn,
680 indent: usize,
681 inst_table: InstTable,
682 partial_inst_table: InstTable,
683 const_table: InstTable,
684 next_index: usize = 0,
685 next_partial_index: usize = 0,
686 next_const_index: usize = 0,
687
688 const InstTable = std.AutoArrayHashMap(*Inst, usize);
689
690 /// TODO: Improve this code to include a stack of Body and store the instructions
691 /// in there. Now we are putting all the instructions in a function local table,
692 /// however instructions that are in a Body can be thown away when the Body ends.
693 fn dump(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) !void {
694 // First pass to pre-populate the table so that we can show even invalid references.
695 // Must iterate the same order we iterate the second time.
696 // We also look for constants and put them in the const_table.
697 try dtz.fetchInstsAndResolveConsts(body);
698
699 std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name});
700
701 for (dtz.const_table.items()) |entry| {
702 const constant = entry.key.castTag(.constant).?;
703 try writer.print(" @{d}: {} = {};\n", .{
704 entry.value, constant.base.ty, constant.val,
705 });
706 }
707
708 return dtz.dumpBody(body, writer);
709 }
710
711 fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void {
712 for (body.instructions) |inst| {
713 try dtz.inst_table.put(inst, dtz.next_index);
714 dtz.next_index += 1;
715 switch (inst.tag) {
716 .alloc,
717 .retvoid,
718 .unreach,
719 .breakpoint,
720 .dbg_stmt,
721 .arg,
722 => {},
723
724 .ref,
725 .ret,
726 .bitcast,
727 .not,
728 .is_non_null,
729 .is_non_null_ptr,
730 .is_null,
731 .is_null_ptr,
732 .is_err,
733 .is_err_ptr,
734 .error_to_int,
735 .int_to_error,
736 .ptrtoint,
737 .floatcast,
738 .intcast,
739 .load,
740 .optional_payload,
741 .optional_payload_ptr,
742 .wrap_optional,
743 .wrap_errunion_payload,
744 .wrap_errunion_err,
745 .unwrap_errunion_payload,
746 .unwrap_errunion_err,
747 .unwrap_errunion_payload_ptr,
748 .unwrap_errunion_err_ptr,
749 => {
750 const un_op = inst.cast(Inst.UnOp).?;
751 try dtz.findConst(un_op.operand);
752 },
753
754 .add,
755 .addwrap,
756 .sub,
757 .subwrap,
758 .mul,
759 .mulwrap,
760 .div,
761 .cmp_lt,
762 .cmp_lte,
763 .cmp_eq,
764 .cmp_gte,
765 .cmp_gt,
766 .cmp_neq,
767 .store,
768 .bool_and,
769 .bool_or,
770 .bit_and,
771 .bit_or,
772 .xor,
773 => {
774 const bin_op = inst.cast(Inst.BinOp).?;
775 try dtz.findConst(bin_op.lhs);
776 try dtz.findConst(bin_op.rhs);
777 },
778
779 .br => {
780 const br = inst.castTag(.br).?;
781 try dtz.findConst(&br.block.base);
782 try dtz.findConst(br.operand);
783 },
784
785 .br_block_flat => {
786 const br_block_flat = inst.castTag(.br_block_flat).?;
787 try dtz.findConst(&br_block_flat.block.base);
788 try dtz.fetchInstsAndResolveConsts(br_block_flat.body);
789 },
790
791 .br_void => {
792 const br_void = inst.castTag(.br_void).?;
793 try dtz.findConst(&br_void.block.base);
794 },
795
796 .block => {
797 const block = inst.castTag(.block).?;
798 try dtz.fetchInstsAndResolveConsts(block.body);
799 },
800
801 .condbr => {
802 const condbr = inst.castTag(.condbr).?;
803 try dtz.findConst(condbr.condition);
804 try dtz.fetchInstsAndResolveConsts(condbr.then_body);
805 try dtz.fetchInstsAndResolveConsts(condbr.else_body);
806 },
807 .switchbr => {
808 const switchbr = inst.castTag(.switchbr).?;
809 try dtz.findConst(switchbr.target);
810 try dtz.fetchInstsAndResolveConsts(switchbr.else_body);
811 for (switchbr.cases) |case| {
812 try dtz.fetchInstsAndResolveConsts(case.body);
813 }
814 },
815
816 .loop => {
817 const loop = inst.castTag(.loop).?;
818 try dtz.fetchInstsAndResolveConsts(loop.body);
819 },
820 .call => {
821 const call = inst.castTag(.call).?;
822 try dtz.findConst(call.func);
823 for (call.args) |arg| {
824 try dtz.findConst(arg);
825 }
826 },
827 .struct_field_ptr => {
828 const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
829 try dtz.findConst(struct_field_ptr.struct_ptr);
830 },
831
832 // TODO fill out this debug printing
833 .assembly,
834 .constant,
835 .varptr,
836 => {},
837 }
838 }
839 }
840
841 fn dumpBody(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void {
842 for (body.instructions) |inst| {
843 const my_index = dtz.next_partial_index;
844 try dtz.partial_inst_table.put(inst, my_index);
845 dtz.next_partial_index += 1;
846
847 try writer.writeByteNTimes(' ', dtz.indent);
848 try writer.print("%{d}: {} = {s}(", .{
849 my_index, inst.ty, @tagName(inst.tag),
850 });
851 switch (inst.tag) {
852 .alloc,
853 .retvoid,
854 .unreach,
855 .breakpoint,
856 .dbg_stmt,
857 => try writer.writeAll(")\n"),
858
859 .ref,
860 .ret,
861 .bitcast,
862 .not,
863 .is_non_null,
864 .is_null,
865 .is_non_null_ptr,
866 .is_null_ptr,
867 .is_err,
868 .is_err_ptr,
869 .error_to_int,
870 .int_to_error,
871 .ptrtoint,
872 .floatcast,
873 .intcast,
874 .load,
875 .optional_payload,
876 .optional_payload_ptr,
877 .wrap_optional,
878 .wrap_errunion_err,
879 .wrap_errunion_payload,
880 .unwrap_errunion_err,
881 .unwrap_errunion_payload,
882 .unwrap_errunion_payload_ptr,
883 .unwrap_errunion_err_ptr,
884 => {
885 const un_op = inst.cast(Inst.UnOp).?;
886 const kinky = try dtz.writeInst(writer, un_op.operand);
887 if (kinky != null) {
888 try writer.writeAll(") // Instruction does not dominate all uses!\n");
889 } else {
890 try writer.writeAll(")\n");
891 }
892 },
893
894 .add,
895 .addwrap,
896 .sub,
897 .subwrap,
898 .mul,
899 .mulwrap,
900 .div,
901 .cmp_lt,
902 .cmp_lte,
903 .cmp_eq,
904 .cmp_gte,
905 .cmp_gt,
906 .cmp_neq,
907 .store,
908 .bool_and,
909 .bool_or,
910 .bit_and,
911 .bit_or,
912 .xor,
913 => {
914 const bin_op = inst.cast(Inst.BinOp).?;
915
916 const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs);
917 try writer.writeAll(", ");
918 const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs);
919
920 if (lhs_kinky != null or rhs_kinky != null) {
921 try writer.writeAll(") // Instruction does not dominate all uses!");
922 if (lhs_kinky) |lhs| {
923 try writer.print(" %{d}", .{lhs});
924 }
925 if (rhs_kinky) |rhs| {
926 try writer.print(" %{d}", .{rhs});
927 }
928 try writer.writeAll("\n");
929 } else {
930 try writer.writeAll(")\n");
931 }
932 },
933
934 .arg => {
935 const arg = inst.castTag(.arg).?;
936 try writer.print("{s})\n", .{arg.name});
937 },
938
939 .br => {
940 const br = inst.castTag(.br).?;
941
942 const lhs_kinky = try dtz.writeInst(writer, &br.block.base);
943 try writer.writeAll(", ");
944 const rhs_kinky = try dtz.writeInst(writer, br.operand);
945
946 if (lhs_kinky != null or rhs_kinky != null) {
947 try writer.writeAll(") // Instruction does not dominate all uses!");
948 if (lhs_kinky) |lhs| {
949 try writer.print(" %{d}", .{lhs});
950 }
951 if (rhs_kinky) |rhs| {
952 try writer.print(" %{d}", .{rhs});
953 }
954 try writer.writeAll("\n");
955 } else {
956 try writer.writeAll(")\n");
957 }
958 },
959
960 .br_block_flat => {
961 const br_block_flat = inst.castTag(.br_block_flat).?;
962 const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base);
963 if (block_kinky != null) {
964 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
965 } else {
966 try writer.writeAll(", {\n");
967 }
968
969 const old_indent = dtz.indent;
970 dtz.indent += 2;
971 try dtz.dumpBody(br_block_flat.body, writer);
972 dtz.indent = old_indent;
973
974 try writer.writeByteNTimes(' ', dtz.indent);
975 try writer.writeAll("})\n");
976 },
977
978 .br_void => {
979 const br_void = inst.castTag(.br_void).?;
980 const kinky = try dtz.writeInst(writer, &br_void.block.base);
981 if (kinky) |_| {
982 try writer.writeAll(") // Instruction does not dominate all uses!\n");
983 } else {
984 try writer.writeAll(")\n");
985 }
986 },
987
988 .block => {
989 const block = inst.castTag(.block).?;
990
991 try writer.writeAll("{\n");
992
993 const old_indent = dtz.indent;
994 dtz.indent += 2;
995 try dtz.dumpBody(block.body, writer);
996 dtz.indent = old_indent;
997
998 try writer.writeByteNTimes(' ', dtz.indent);
999 try writer.writeAll("})\n");
1000 },
1001
1002 .condbr => {
1003 const condbr = inst.castTag(.condbr).?;
1004
1005 const condition_kinky = try dtz.writeInst(writer, condbr.condition);
1006 if (condition_kinky != null) {
1007 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
1008 } else {
1009 try writer.writeAll(", {\n");
1010 }
1011
1012 const old_indent = dtz.indent;
1013 dtz.indent += 2;
1014 try dtz.dumpBody(condbr.then_body, writer);
1015
1016 try writer.writeByteNTimes(' ', old_indent);
1017 try writer.writeAll("}, {\n");
1018
1019 try dtz.dumpBody(condbr.else_body, writer);
1020 dtz.indent = old_indent;
1021
1022 try writer.writeByteNTimes(' ', old_indent);
1023 try writer.writeAll("})\n");
1024 },
1025
1026 .switchbr => {
1027 const switchbr = inst.castTag(.switchbr).?;
1028
1029 const condition_kinky = try dtz.writeInst(writer, switchbr.target);
1030 if (condition_kinky != null) {
1031 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
1032 } else {
1033 try writer.writeAll(", {\n");
1034 }
1035 const old_indent = dtz.indent;
1036
1037 if (switchbr.else_body.instructions.len != 0) {
1038 dtz.indent += 2;
1039 try dtz.dumpBody(switchbr.else_body, writer);
1040
1041 try writer.writeByteNTimes(' ', old_indent);
1042 try writer.writeAll("}, {\n");
1043 dtz.indent = old_indent;
1044 }
1045 for (switchbr.cases) |case| {
1046 dtz.indent += 2;
1047 try dtz.dumpBody(case.body, writer);
1048
1049 try writer.writeByteNTimes(' ', old_indent);
1050 try writer.writeAll("}, {\n");
1051 dtz.indent = old_indent;
1052 }
1053
1054 try writer.writeByteNTimes(' ', old_indent);
1055 try writer.writeAll("})\n");
1056 },
1057
1058 .loop => {
1059 const loop = inst.castTag(.loop).?;
1060
1061 try writer.writeAll("{\n");
1062
1063 const old_indent = dtz.indent;
1064 dtz.indent += 2;
1065 try dtz.dumpBody(loop.body, writer);
1066 dtz.indent = old_indent;
1067
1068 try writer.writeByteNTimes(' ', dtz.indent);
1069 try writer.writeAll("})\n");
1070 },
1071
1072 .call => {
1073 const call = inst.castTag(.call).?;
1074
1075 const args_kinky = try dtz.allocator.alloc(?usize, call.args.len);
1076 defer dtz.allocator.free(args_kinky);
1077 std.mem.set(?usize, args_kinky, null);
1078 var any_kinky_args = false;
1079
1080 const func_kinky = try dtz.writeInst(writer, call.func);
1081
1082 for (call.args) |arg, i| {
1083 try writer.writeAll(", ");
1084
1085 args_kinky[i] = try dtz.writeInst(writer, arg);
1086 any_kinky_args = any_kinky_args or args_kinky[i] != null;
1087 }
1088
1089 if (func_kinky != null or any_kinky_args) {
1090 try writer.writeAll(") // Instruction does not dominate all uses!");
1091 if (func_kinky) |func_index| {
1092 try writer.print(" %{d}", .{func_index});
1093 }
1094 for (args_kinky) |arg_kinky| {
1095 if (arg_kinky) |arg_index| {
1096 try writer.print(" %{d}", .{arg_index});
1097 }
1098 }
1099 try writer.writeAll("\n");
1100 } else {
1101 try writer.writeAll(")\n");
1102 }
1103 },
1104
1105 .struct_field_ptr => {
1106 const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
1107 const kinky = try dtz.writeInst(writer, struct_field_ptr.struct_ptr);
1108 if (kinky != null) {
1109 try writer.print("{d}) // Instruction does not dominate all uses!\n", .{
1110 struct_field_ptr.field_index,
1111 });
1112 } else {
1113 try writer.print("{d})\n", .{struct_field_ptr.field_index});
1114 }
1115 },
1116
1117 // TODO fill out this debug printing
1118 .assembly,
1119 .constant,
1120 .varptr,
1121 => {
1122 try writer.writeAll("!TODO!)\n");
1123 },
1124 }
1125 }
1126 }
1127
1128 fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *Inst) !?usize {
1129 if (dtz.partial_inst_table.get(inst)) |operand_index| {
1130 try writer.print("%{d}", .{operand_index});
1131 return null;
1132 } else if (dtz.const_table.get(inst)) |operand_index| {
1133 try writer.print("@{d}", .{operand_index});
1134 return null;
1135 } else if (dtz.inst_table.get(inst)) |operand_index| {
1136 try writer.print("%{d}", .{operand_index});
1137 return operand_index;
1138 } else {
1139 try writer.writeAll("!BADREF!");
1140 return null;
1141 }
1142 }
1143
1144 fn findConst(dtz: *DumpTzir, operand: *Inst) !void {
1145 if (operand.tag == .constant) {
1146 try dtz.const_table.put(operand, dtz.next_const_index);
1147 dtz.next_const_index += 1;
1148 }
1149 }
1150};
src/codegen.zig+1-1
......@@ -2,7 +2,7 @@ const std = @import("std");
22const mem = std.mem;
33const math = std.math;
44const assert = std.debug.assert;
5const ir = @import("ir.zig");
5const ir = @import("air.zig");
66const Type = @import("type.zig").Type;
77const Value = @import("value.zig").Value;
88const TypedValue = @import("TypedValue.zig");
src/codegen/c.zig+1-1
......@@ -6,7 +6,7 @@ const log = std.log.scoped(.c);
66const link = @import("../link.zig");
77const Module = @import("../Module.zig");
88const Compilation = @import("../Compilation.zig");
9const ir = @import("../ir.zig");
9const ir = @import("../air.zig");
1010const Inst = ir.Inst;
1111const Value = @import("../value.zig").Value;
1212const Type = @import("../type.zig").Type;
src/codegen/llvm.zig+1-1
......@@ -9,7 +9,7 @@ const math = std.math;
99
1010const Module = @import("../Module.zig");
1111const TypedValue = @import("../TypedValue.zig");
12const ir = @import("../ir.zig");
12const ir = @import("../air.zig");
1313const Inst = ir.Inst;
1414
1515const Value = @import("../value.zig").Value;
src/codegen/spirv.zig+1-1
......@@ -11,7 +11,7 @@ const Decl = Module.Decl;
1111const Type = @import("../type.zig").Type;
1212const Value = @import("../value.zig").Value;
1313const LazySrcLoc = Module.LazySrcLoc;
14const ir = @import("../ir.zig");
14const ir = @import("../air.zig");
1515const Inst = ir.Inst;
1616
1717pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage);
src/codegen/wasm.zig+1-1
......@@ -9,7 +9,7 @@ const wasm = std.wasm;
99
1010const Module = @import("../Module.zig");
1111const Decl = Module.Decl;
12const ir = @import("../ir.zig");
12const ir = @import("../air.zig");
1313const Inst = ir.Inst;
1414const Type = @import("../type.zig").Type;
1515const Value = @import("../value.zig").Value;
src/ir.zig deleted-1150
......@@ -1,1150 +0,0 @@
1const std = @import("std");
2const Value = @import("value.zig").Value;
3const Type = @import("type.zig").Type;
4const Module = @import("Module.zig");
5const assert = std.debug.assert;
6const codegen = @import("codegen.zig");
7const ast = std.zig.ast;
8
9/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
10/// of instructions that correspond to the ZIR text format.
11/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
12/// so are the `Value` and `Type`. The value of a constant must be copied into
13/// a memory location for the value to survive after a const instruction.
14pub const Inst = struct {
15 tag: Tag,
16 /// Each bit represents the index of an `Inst` parameter in the `args` field.
17 /// If a bit is set, it marks the end of the lifetime of the corresponding
18 /// instruction parameter. For example, 0b101 means that the first and
19 /// third `Inst` parameters' lifetimes end after this instruction, and will
20 /// not have any more following references.
21 /// The most significant bit being set means that the instruction itself is
22 /// never referenced, in other words its lifetime ends as soon as it finishes.
23 /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced.
24 /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
25 /// lifetimes of operands are encoded elsewhere.
26 deaths: DeathsInt = undefined,
27 ty: Type,
28 src: Module.LazySrcLoc,
29
30 pub const DeathsInt = u16;
31 pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
32 pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1;
33 pub const deaths_bits = unreferenced_bit_index - 1;
34
35 pub fn isUnused(self: Inst) bool {
36 return (self.deaths & (1 << unreferenced_bit_index)) != 0;
37 }
38
39 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
40 assert(index < deaths_bits);
41 return @truncate(u1, self.deaths >> index) != 0;
42 }
43
44 pub fn clearOperandDeath(self: *Inst, index: DeathsBitIndex) void {
45 assert(index < deaths_bits);
46 self.deaths &= ~(@as(DeathsInt, 1) << index);
47 }
48
49 pub fn specialOperandDeaths(self: Inst) bool {
50 return (self.deaths & (1 << deaths_bits)) != 0;
51 }
52
53 pub const Tag = enum {
54 add,
55 addwrap,
56 alloc,
57 arg,
58 assembly,
59 bit_and,
60 bitcast,
61 bit_or,
62 block,
63 br,
64 /// Same as `br` except the operand is a list of instructions to be treated as
65 /// a flat block; that is there is only 1 break instruction from the block, and
66 /// it is implied to be after the last instruction, and the last instruction is
67 /// the break operand.
68 /// This instruction exists for late-stage semantic analysis patch ups, to
69 /// replace one br operand with multiple instructions, without moving anything else around.
70 br_block_flat,
71 breakpoint,
72 br_void,
73 call,
74 cmp_lt,
75 cmp_lte,
76 cmp_eq,
77 cmp_gte,
78 cmp_gt,
79 cmp_neq,
80 condbr,
81 constant,
82 dbg_stmt,
83 /// ?T => bool
84 is_null,
85 /// ?T => bool (inverted logic)
86 is_non_null,
87 /// *?T => bool
88 is_null_ptr,
89 /// *?T => bool (inverted logic)
90 is_non_null_ptr,
91 /// E!T => bool
92 is_err,
93 /// *E!T => bool
94 is_err_ptr,
95 /// E => u16
96 error_to_int,
97 /// u16 => E
98 int_to_error,
99 bool_and,
100 bool_or,
101 /// Read a value from a pointer.
102 load,
103 /// A labeled block of code that loops forever. At the end of the body it is implied
104 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
105 loop,
106 ptrtoint,
107 ref,
108 ret,
109 retvoid,
110 varptr,
111 /// Write a value to a pointer. LHS is pointer, RHS is value.
112 store,
113 sub,
114 subwrap,
115 unreach,
116 mul,
117 mulwrap,
118 div,
119 not,
120 floatcast,
121 intcast,
122 /// ?T => T
123 optional_payload,
124 /// *?T => *T
125 optional_payload_ptr,
126 wrap_optional,
127 /// E!T -> T
128 unwrap_errunion_payload,
129 /// E!T -> E
130 unwrap_errunion_err,
131 /// *(E!T) -> *T
132 unwrap_errunion_payload_ptr,
133 /// *(E!T) -> E
134 unwrap_errunion_err_ptr,
135 /// wrap from T to E!T
136 wrap_errunion_payload,
137 /// wrap from E to E!T
138 wrap_errunion_err,
139 xor,
140 switchbr,
141 /// Given a pointer to a struct and a field index, returns a pointer to the field.
142 struct_field_ptr,
143
144 pub fn Type(tag: Tag) type {
145 return switch (tag) {
146 .alloc,
147 .retvoid,
148 .unreach,
149 .breakpoint,
150 => NoOp,
151
152 .ref,
153 .ret,
154 .bitcast,
155 .not,
156 .is_non_null,
157 .is_non_null_ptr,
158 .is_null,
159 .is_null_ptr,
160 .is_err,
161 .is_err_ptr,
162 .int_to_error,
163 .error_to_int,
164 .ptrtoint,
165 .floatcast,
166 .intcast,
167 .load,
168 .optional_payload,
169 .optional_payload_ptr,
170 .wrap_optional,
171 .unwrap_errunion_payload,
172 .unwrap_errunion_err,
173 .unwrap_errunion_payload_ptr,
174 .unwrap_errunion_err_ptr,
175 .wrap_errunion_payload,
176 .wrap_errunion_err,
177 => UnOp,
178
179 .add,
180 .addwrap,
181 .sub,
182 .subwrap,
183 .mul,
184 .mulwrap,
185 .div,
186 .cmp_lt,
187 .cmp_lte,
188 .cmp_eq,
189 .cmp_gte,
190 .cmp_gt,
191 .cmp_neq,
192 .store,
193 .bool_and,
194 .bool_or,
195 .bit_and,
196 .bit_or,
197 .xor,
198 => BinOp,
199
200 .arg => Arg,
201 .assembly => Assembly,
202 .block => Block,
203 .br => Br,
204 .br_block_flat => BrBlockFlat,
205 .br_void => BrVoid,
206 .call => Call,
207 .condbr => CondBr,
208 .constant => Constant,
209 .loop => Loop,
210 .varptr => VarPtr,
211 .struct_field_ptr => StructFieldPtr,
212 .switchbr => SwitchBr,
213 .dbg_stmt => DbgStmt,
214 };
215 }
216
217 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
218 return switch (op) {
219 .lt => .cmp_lt,
220 .lte => .cmp_lte,
221 .eq => .cmp_eq,
222 .gte => .cmp_gte,
223 .gt => .cmp_gt,
224 .neq => .cmp_neq,
225 };
226 }
227 };
228
229 /// Prefer `castTag` to this.
230 pub fn cast(base: *Inst, comptime T: type) ?*T {
231 if (@hasField(T, "base_tag")) {
232 return base.castTag(T.base_tag);
233 }
234 inline for (@typeInfo(Tag).Enum.fields) |field| {
235 const tag = @intToEnum(Tag, field.value);
236 if (base.tag == tag) {
237 if (T == tag.Type()) {
238 return @fieldParentPtr(T, "base", base);
239 }
240 return null;
241 }
242 }
243 unreachable;
244 }
245
246 pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
247 if (base.tag == tag) {
248 return @fieldParentPtr(tag.Type(), "base", base);
249 }
250 return null;
251 }
252
253 pub fn Args(comptime T: type) type {
254 return std.meta.fieldInfo(T, .args).field_type;
255 }
256
257 /// Returns `null` if runtime-known.
258 /// Should be called by codegen, not by Sema. Sema functions should call
259 /// `resolvePossiblyUndefinedValue` or `resolveDefinedValue` instead.
260 /// TODO audit Sema code for violations to the above guidance.
261 pub fn value(base: *Inst) ?Value {
262 if (base.ty.onePossibleValue()) |opv| return opv;
263
264 const inst = base.castTag(.constant) orelse return null;
265 return inst.val;
266 }
267
268 pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
269 return switch (base.tag) {
270 .cmp_lt => .lt,
271 .cmp_lte => .lte,
272 .cmp_eq => .eq,
273 .cmp_gte => .gte,
274 .cmp_gt => .gt,
275 .cmp_neq => .neq,
276 else => null,
277 };
278 }
279
280 pub fn operandCount(base: *Inst) usize {
281 inline for (@typeInfo(Tag).Enum.fields) |field| {
282 const tag = @intToEnum(Tag, field.value);
283 if (tag == base.tag) {
284 return @fieldParentPtr(tag.Type(), "base", base).operandCount();
285 }
286 }
287 unreachable;
288 }
289
290 pub fn getOperand(base: *Inst, index: usize) ?*Inst {
291 inline for (@typeInfo(Tag).Enum.fields) |field| {
292 const tag = @intToEnum(Tag, field.value);
293 if (tag == base.tag) {
294 return @fieldParentPtr(tag.Type(), "base", base).getOperand(index);
295 }
296 }
297 unreachable;
298 }
299
300 pub fn breakBlock(base: *Inst) ?*Block {
301 return switch (base.tag) {
302 .br => base.castTag(.br).?.block,
303 .br_void => base.castTag(.br_void).?.block,
304 .br_block_flat => base.castTag(.br_block_flat).?.block,
305 else => null,
306 };
307 }
308
309 pub const NoOp = struct {
310 base: Inst,
311
312 pub fn operandCount(self: *const NoOp) usize {
313 return 0;
314 }
315 pub fn getOperand(self: *const NoOp, index: usize) ?*Inst {
316 return null;
317 }
318 };
319
320 pub const UnOp = struct {
321 base: Inst,
322 operand: *Inst,
323
324 pub fn operandCount(self: *const UnOp) usize {
325 return 1;
326 }
327 pub fn getOperand(self: *const UnOp, index: usize) ?*Inst {
328 if (index == 0)
329 return self.operand;
330 return null;
331 }
332 };
333
334 pub const BinOp = struct {
335 base: Inst,
336 lhs: *Inst,
337 rhs: *Inst,
338
339 pub fn operandCount(self: *const BinOp) usize {
340 return 2;
341 }
342 pub fn getOperand(self: *const BinOp, index: usize) ?*Inst {
343 var i = index;
344
345 if (i < 1)
346 return self.lhs;
347 i -= 1;
348
349 if (i < 1)
350 return self.rhs;
351 i -= 1;
352
353 return null;
354 }
355 };
356
357 pub const Arg = struct {
358 pub const base_tag = Tag.arg;
359
360 base: Inst,
361 /// This exists to be emitted into debug info.
362 name: [*:0]const u8,
363
364 pub fn operandCount(self: *const Arg) usize {
365 return 0;
366 }
367 pub fn getOperand(self: *const Arg, index: usize) ?*Inst {
368 return null;
369 }
370 };
371
372 pub const Assembly = struct {
373 pub const base_tag = Tag.assembly;
374
375 base: Inst,
376 asm_source: []const u8,
377 is_volatile: bool,
378 output_constraint: ?[]const u8,
379 inputs: []const []const u8,
380 clobbers: []const []const u8,
381 args: []const *Inst,
382
383 pub fn operandCount(self: *const Assembly) usize {
384 return self.args.len;
385 }
386 pub fn getOperand(self: *const Assembly, index: usize) ?*Inst {
387 if (index < self.args.len)
388 return self.args[index];
389 return null;
390 }
391 };
392
393 pub const Block = struct {
394 pub const base_tag = Tag.block;
395
396 base: Inst,
397 body: Body,
398 /// This memory is reserved for codegen code to do whatever it needs to here.
399 codegen: codegen.BlockData = .{},
400
401 pub fn operandCount(self: *const Block) usize {
402 return 0;
403 }
404 pub fn getOperand(self: *const Block, index: usize) ?*Inst {
405 return null;
406 }
407 };
408
409 pub const convertable_br_size = std.math.max(@sizeOf(BrBlockFlat), @sizeOf(Br));
410 pub const convertable_br_align = std.math.max(@alignOf(BrBlockFlat), @alignOf(Br));
411 comptime {
412 assert(@byteOffsetOf(BrBlockFlat, "base") == @byteOffsetOf(Br, "base"));
413 }
414
415 pub const BrBlockFlat = struct {
416 pub const base_tag = Tag.br_block_flat;
417
418 base: Inst,
419 block: *Block,
420 body: Body,
421
422 pub fn operandCount(self: *const BrBlockFlat) usize {
423 return 0;
424 }
425 pub fn getOperand(self: *const BrBlockFlat, index: usize) ?*Inst {
426 return null;
427 }
428 };
429
430 pub const Br = struct {
431 pub const base_tag = Tag.br;
432
433 base: Inst,
434 block: *Block,
435 operand: *Inst,
436
437 pub fn operandCount(self: *const Br) usize {
438 return 1;
439 }
440 pub fn getOperand(self: *const Br, index: usize) ?*Inst {
441 if (index == 0)
442 return self.operand;
443 return null;
444 }
445 };
446
447 pub const BrVoid = struct {
448 pub const base_tag = Tag.br_void;
449
450 base: Inst,
451 block: *Block,
452
453 pub fn operandCount(self: *const BrVoid) usize {
454 return 0;
455 }
456 pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst {
457 return null;
458 }
459 };
460
461 pub const Call = struct {
462 pub const base_tag = Tag.call;
463
464 base: Inst,
465 func: *Inst,
466 args: []const *Inst,
467
468 pub fn operandCount(self: *const Call) usize {
469 return self.args.len + 1;
470 }
471 pub fn getOperand(self: *const Call, index: usize) ?*Inst {
472 var i = index;
473
474 if (i < 1)
475 return self.func;
476 i -= 1;
477
478 if (i < self.args.len)
479 return self.args[i];
480 i -= self.args.len;
481
482 return null;
483 }
484 };
485
486 pub const CondBr = struct {
487 pub const base_tag = Tag.condbr;
488
489 base: Inst,
490 condition: *Inst,
491 then_body: Body,
492 else_body: Body,
493 /// Set of instructions whose lifetimes end at the start of one of the branches.
494 /// The `then` branch is first: `deaths[0..then_death_count]`.
495 /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`.
496 deaths: [*]*Inst = undefined,
497 then_death_count: u32 = 0,
498 else_death_count: u32 = 0,
499
500 pub fn operandCount(self: *const CondBr) usize {
501 return 1;
502 }
503 pub fn getOperand(self: *const CondBr, index: usize) ?*Inst {
504 var i = index;
505
506 if (i < 1)
507 return self.condition;
508 i -= 1;
509
510 return null;
511 }
512 pub fn thenDeaths(self: *const CondBr) []*Inst {
513 return self.deaths[0..self.then_death_count];
514 }
515 pub fn elseDeaths(self: *const CondBr) []*Inst {
516 return (self.deaths + self.then_death_count)[0..self.else_death_count];
517 }
518 };
519
520 pub const Constant = struct {
521 pub const base_tag = Tag.constant;
522
523 base: Inst,
524 val: Value,
525
526 pub fn operandCount(self: *const Constant) usize {
527 return 0;
528 }
529 pub fn getOperand(self: *const Constant, index: usize) ?*Inst {
530 return null;
531 }
532 };
533
534 pub const Loop = struct {
535 pub const base_tag = Tag.loop;
536
537 base: Inst,
538 body: Body,
539
540 pub fn operandCount(self: *const Loop) usize {
541 return 0;
542 }
543 pub fn getOperand(self: *const Loop, index: usize) ?*Inst {
544 return null;
545 }
546 };
547
548 pub const VarPtr = struct {
549 pub const base_tag = Tag.varptr;
550
551 base: Inst,
552 variable: *Module.Var,
553
554 pub fn operandCount(self: *const VarPtr) usize {
555 return 0;
556 }
557 pub fn getOperand(self: *const VarPtr, index: usize) ?*Inst {
558 return null;
559 }
560 };
561
562 pub const StructFieldPtr = struct {
563 pub const base_tag = Tag.struct_field_ptr;
564
565 base: Inst,
566 struct_ptr: *Inst,
567 field_index: usize,
568
569 pub fn operandCount(self: *const StructFieldPtr) usize {
570 return 1;
571 }
572 pub fn getOperand(self: *const StructFieldPtr, index: usize) ?*Inst {
573 var i = index;
574
575 if (i < 1)
576 return self.struct_ptr;
577 i -= 1;
578
579 return null;
580 }
581 };
582
583 pub const SwitchBr = struct {
584 pub const base_tag = Tag.switchbr;
585
586 base: Inst,
587 target: *Inst,
588 cases: []Case,
589 /// Set of instructions whose lifetimes end at the start of one of the cases.
590 /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ].
591 deaths: [*]*Inst = undefined,
592 else_index: u32 = 0,
593 else_deaths: u32 = 0,
594 else_body: Body,
595
596 pub const Case = struct {
597 item: Value,
598 body: Body,
599 index: u32 = 0,
600 deaths: u32 = 0,
601 };
602
603 pub fn operandCount(self: *const SwitchBr) usize {
604 return 1;
605 }
606 pub fn getOperand(self: *const SwitchBr, index: usize) ?*Inst {
607 var i = index;
608
609 if (i < 1)
610 return self.target;
611 i -= 1;
612
613 return null;
614 }
615 pub fn caseDeaths(self: *const SwitchBr, case_index: usize) []*Inst {
616 const case = self.cases[case_index];
617 return (self.deaths + case.index)[0..case.deaths];
618 }
619 pub fn elseDeaths(self: *const SwitchBr) []*Inst {
620 return (self.deaths + self.else_index)[0..self.else_deaths];
621 }
622 };
623
624 pub const DbgStmt = struct {
625 pub const base_tag = Tag.dbg_stmt;
626
627 base: Inst,
628 line: u32,
629 column: u32,
630
631 pub fn operandCount(self: *const DbgStmt) usize {
632 return 0;
633 }
634 pub fn getOperand(self: *const DbgStmt, index: usize) ?*Inst {
635 return null;
636 }
637 };
638};
639
640pub const Body = struct {
641 instructions: []*Inst,
642};
643
644/// For debugging purposes, prints a function representation to stderr.
645pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
646 const allocator = old_module.gpa;
647 var ctx: DumpTzir = .{
648 .allocator = allocator,
649 .arena = std.heap.ArenaAllocator.init(allocator),
650 .old_module = &old_module,
651 .module_fn = module_fn,
652 .indent = 2,
653 .inst_table = DumpTzir.InstTable.init(allocator),
654 .partial_inst_table = DumpTzir.InstTable.init(allocator),
655 .const_table = DumpTzir.InstTable.init(allocator),
656 };
657 defer ctx.inst_table.deinit();
658 defer ctx.partial_inst_table.deinit();
659 defer ctx.const_table.deinit();
660 defer ctx.arena.deinit();
661
662 switch (module_fn.state) {
663 .queued => std.debug.print("(queued)", .{}),
664 .inline_only => std.debug.print("(inline_only)", .{}),
665 .in_progress => std.debug.print("(in_progress)", .{}),
666 .sema_failure => std.debug.print("(sema_failure)", .{}),
667 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
668 .success => {
669 const writer = std.io.getStdErr().writer();
670 ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR");
671 },
672 }
673}
674
675const DumpTzir = struct {
676 allocator: *std.mem.Allocator,
677 arena: std.heap.ArenaAllocator,
678 old_module: *const Module,
679 module_fn: *Module.Fn,
680 indent: usize,
681 inst_table: InstTable,
682 partial_inst_table: InstTable,
683 const_table: InstTable,
684 next_index: usize = 0,
685 next_partial_index: usize = 0,
686 next_const_index: usize = 0,
687
688 const InstTable = std.AutoArrayHashMap(*Inst, usize);
689
690 /// TODO: Improve this code to include a stack of Body and store the instructions
691 /// in there. Now we are putting all the instructions in a function local table,
692 /// however instructions that are in a Body can be thown away when the Body ends.
693 fn dump(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) !void {
694 // First pass to pre-populate the table so that we can show even invalid references.
695 // Must iterate the same order we iterate the second time.
696 // We also look for constants and put them in the const_table.
697 try dtz.fetchInstsAndResolveConsts(body);
698
699 std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name});
700
701 for (dtz.const_table.items()) |entry| {
702 const constant = entry.key.castTag(.constant).?;
703 try writer.print(" @{d}: {} = {};\n", .{
704 entry.value, constant.base.ty, constant.val,
705 });
706 }
707
708 return dtz.dumpBody(body, writer);
709 }
710
711 fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void {
712 for (body.instructions) |inst| {
713 try dtz.inst_table.put(inst, dtz.next_index);
714 dtz.next_index += 1;
715 switch (inst.tag) {
716 .alloc,
717 .retvoid,
718 .unreach,
719 .breakpoint,
720 .dbg_stmt,
721 .arg,
722 => {},
723
724 .ref,
725 .ret,
726 .bitcast,
727 .not,
728 .is_non_null,
729 .is_non_null_ptr,
730 .is_null,
731 .is_null_ptr,
732 .is_err,
733 .is_err_ptr,
734 .error_to_int,
735 .int_to_error,
736 .ptrtoint,
737 .floatcast,
738 .intcast,
739 .load,
740 .optional_payload,
741 .optional_payload_ptr,
742 .wrap_optional,
743 .wrap_errunion_payload,
744 .wrap_errunion_err,
745 .unwrap_errunion_payload,
746 .unwrap_errunion_err,
747 .unwrap_errunion_payload_ptr,
748 .unwrap_errunion_err_ptr,
749 => {
750 const un_op = inst.cast(Inst.UnOp).?;
751 try dtz.findConst(un_op.operand);
752 },
753
754 .add,
755 .addwrap,
756 .sub,
757 .subwrap,
758 .mul,
759 .mulwrap,
760 .div,
761 .cmp_lt,
762 .cmp_lte,
763 .cmp_eq,
764 .cmp_gte,
765 .cmp_gt,
766 .cmp_neq,
767 .store,
768 .bool_and,
769 .bool_or,
770 .bit_and,
771 .bit_or,
772 .xor,
773 => {
774 const bin_op = inst.cast(Inst.BinOp).?;
775 try dtz.findConst(bin_op.lhs);
776 try dtz.findConst(bin_op.rhs);
777 },
778
779 .br => {
780 const br = inst.castTag(.br).?;
781 try dtz.findConst(&br.block.base);
782 try dtz.findConst(br.operand);
783 },
784
785 .br_block_flat => {
786 const br_block_flat = inst.castTag(.br_block_flat).?;
787 try dtz.findConst(&br_block_flat.block.base);
788 try dtz.fetchInstsAndResolveConsts(br_block_flat.body);
789 },
790
791 .br_void => {
792 const br_void = inst.castTag(.br_void).?;
793 try dtz.findConst(&br_void.block.base);
794 },
795
796 .block => {
797 const block = inst.castTag(.block).?;
798 try dtz.fetchInstsAndResolveConsts(block.body);
799 },
800
801 .condbr => {
802 const condbr = inst.castTag(.condbr).?;
803 try dtz.findConst(condbr.condition);
804 try dtz.fetchInstsAndResolveConsts(condbr.then_body);
805 try dtz.fetchInstsAndResolveConsts(condbr.else_body);
806 },
807 .switchbr => {
808 const switchbr = inst.castTag(.switchbr).?;
809 try dtz.findConst(switchbr.target);
810 try dtz.fetchInstsAndResolveConsts(switchbr.else_body);
811 for (switchbr.cases) |case| {
812 try dtz.fetchInstsAndResolveConsts(case.body);
813 }
814 },
815
816 .loop => {
817 const loop = inst.castTag(.loop).?;
818 try dtz.fetchInstsAndResolveConsts(loop.body);
819 },
820 .call => {
821 const call = inst.castTag(.call).?;
822 try dtz.findConst(call.func);
823 for (call.args) |arg| {
824 try dtz.findConst(arg);
825 }
826 },
827 .struct_field_ptr => {
828 const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
829 try dtz.findConst(struct_field_ptr.struct_ptr);
830 },
831
832 // TODO fill out this debug printing
833 .assembly,
834 .constant,
835 .varptr,
836 => {},
837 }
838 }
839 }
840
841 fn dumpBody(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void {
842 for (body.instructions) |inst| {
843 const my_index = dtz.next_partial_index;
844 try dtz.partial_inst_table.put(inst, my_index);
845 dtz.next_partial_index += 1;
846
847 try writer.writeByteNTimes(' ', dtz.indent);
848 try writer.print("%{d}: {} = {s}(", .{
849 my_index, inst.ty, @tagName(inst.tag),
850 });
851 switch (inst.tag) {
852 .alloc,
853 .retvoid,
854 .unreach,
855 .breakpoint,
856 .dbg_stmt,
857 => try writer.writeAll(")\n"),
858
859 .ref,
860 .ret,
861 .bitcast,
862 .not,
863 .is_non_null,
864 .is_null,
865 .is_non_null_ptr,
866 .is_null_ptr,
867 .is_err,
868 .is_err_ptr,
869 .error_to_int,
870 .int_to_error,
871 .ptrtoint,
872 .floatcast,
873 .intcast,
874 .load,
875 .optional_payload,
876 .optional_payload_ptr,
877 .wrap_optional,
878 .wrap_errunion_err,
879 .wrap_errunion_payload,
880 .unwrap_errunion_err,
881 .unwrap_errunion_payload,
882 .unwrap_errunion_payload_ptr,
883 .unwrap_errunion_err_ptr,
884 => {
885 const un_op = inst.cast(Inst.UnOp).?;
886 const kinky = try dtz.writeInst(writer, un_op.operand);
887 if (kinky != null) {
888 try writer.writeAll(") // Instruction does not dominate all uses!\n");
889 } else {
890 try writer.writeAll(")\n");
891 }
892 },
893
894 .add,
895 .addwrap,
896 .sub,
897 .subwrap,
898 .mul,
899 .mulwrap,
900 .div,
901 .cmp_lt,
902 .cmp_lte,
903 .cmp_eq,
904 .cmp_gte,
905 .cmp_gt,
906 .cmp_neq,
907 .store,
908 .bool_and,
909 .bool_or,
910 .bit_and,
911 .bit_or,
912 .xor,
913 => {
914 const bin_op = inst.cast(Inst.BinOp).?;
915
916 const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs);
917 try writer.writeAll(", ");
918 const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs);
919
920 if (lhs_kinky != null or rhs_kinky != null) {
921 try writer.writeAll(") // Instruction does not dominate all uses!");
922 if (lhs_kinky) |lhs| {
923 try writer.print(" %{d}", .{lhs});
924 }
925 if (rhs_kinky) |rhs| {
926 try writer.print(" %{d}", .{rhs});
927 }
928 try writer.writeAll("\n");
929 } else {
930 try writer.writeAll(")\n");
931 }
932 },
933
934 .arg => {
935 const arg = inst.castTag(.arg).?;
936 try writer.print("{s})\n", .{arg.name});
937 },
938
939 .br => {
940 const br = inst.castTag(.br).?;
941
942 const lhs_kinky = try dtz.writeInst(writer, &br.block.base);
943 try writer.writeAll(", ");
944 const rhs_kinky = try dtz.writeInst(writer, br.operand);
945
946 if (lhs_kinky != null or rhs_kinky != null) {
947 try writer.writeAll(") // Instruction does not dominate all uses!");
948 if (lhs_kinky) |lhs| {
949 try writer.print(" %{d}", .{lhs});
950 }
951 if (rhs_kinky) |rhs| {
952 try writer.print(" %{d}", .{rhs});
953 }
954 try writer.writeAll("\n");
955 } else {
956 try writer.writeAll(")\n");
957 }
958 },
959
960 .br_block_flat => {
961 const br_block_flat = inst.castTag(.br_block_flat).?;
962 const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base);
963 if (block_kinky != null) {
964 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
965 } else {
966 try writer.writeAll(", {\n");
967 }
968
969 const old_indent = dtz.indent;
970 dtz.indent += 2;
971 try dtz.dumpBody(br_block_flat.body, writer);
972 dtz.indent = old_indent;
973
974 try writer.writeByteNTimes(' ', dtz.indent);
975 try writer.writeAll("})\n");
976 },
977
978 .br_void => {
979 const br_void = inst.castTag(.br_void).?;
980 const kinky = try dtz.writeInst(writer, &br_void.block.base);
981 if (kinky) |_| {
982 try writer.writeAll(") // Instruction does not dominate all uses!\n");
983 } else {
984 try writer.writeAll(")\n");
985 }
986 },
987
988 .block => {
989 const block = inst.castTag(.block).?;
990
991 try writer.writeAll("{\n");
992
993 const old_indent = dtz.indent;
994 dtz.indent += 2;
995 try dtz.dumpBody(block.body, writer);
996 dtz.indent = old_indent;
997
998 try writer.writeByteNTimes(' ', dtz.indent);
999 try writer.writeAll("})\n");
1000 },
1001
1002 .condbr => {
1003 const condbr = inst.castTag(.condbr).?;
1004
1005 const condition_kinky = try dtz.writeInst(writer, condbr.condition);
1006 if (condition_kinky != null) {
1007 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
1008 } else {
1009 try writer.writeAll(", {\n");
1010 }
1011
1012 const old_indent = dtz.indent;
1013 dtz.indent += 2;
1014 try dtz.dumpBody(condbr.then_body, writer);
1015
1016 try writer.writeByteNTimes(' ', old_indent);
1017 try writer.writeAll("}, {\n");
1018
1019 try dtz.dumpBody(condbr.else_body, writer);
1020 dtz.indent = old_indent;
1021
1022 try writer.writeByteNTimes(' ', old_indent);
1023 try writer.writeAll("})\n");
1024 },
1025
1026 .switchbr => {
1027 const switchbr = inst.castTag(.switchbr).?;
1028
1029 const condition_kinky = try dtz.writeInst(writer, switchbr.target);
1030 if (condition_kinky != null) {
1031 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
1032 } else {
1033 try writer.writeAll(", {\n");
1034 }
1035 const old_indent = dtz.indent;
1036
1037 if (switchbr.else_body.instructions.len != 0) {
1038 dtz.indent += 2;
1039 try dtz.dumpBody(switchbr.else_body, writer);
1040
1041 try writer.writeByteNTimes(' ', old_indent);
1042 try writer.writeAll("}, {\n");
1043 dtz.indent = old_indent;
1044 }
1045 for (switchbr.cases) |case| {
1046 dtz.indent += 2;
1047 try dtz.dumpBody(case.body, writer);
1048
1049 try writer.writeByteNTimes(' ', old_indent);
1050 try writer.writeAll("}, {\n");
1051 dtz.indent = old_indent;
1052 }
1053
1054 try writer.writeByteNTimes(' ', old_indent);
1055 try writer.writeAll("})\n");
1056 },
1057
1058 .loop => {
1059 const loop = inst.castTag(.loop).?;
1060
1061 try writer.writeAll("{\n");
1062
1063 const old_indent = dtz.indent;
1064 dtz.indent += 2;
1065 try dtz.dumpBody(loop.body, writer);
1066 dtz.indent = old_indent;
1067
1068 try writer.writeByteNTimes(' ', dtz.indent);
1069 try writer.writeAll("})\n");
1070 },
1071
1072 .call => {
1073 const call = inst.castTag(.call).?;
1074
1075 const args_kinky = try dtz.allocator.alloc(?usize, call.args.len);
1076 defer dtz.allocator.free(args_kinky);
1077 std.mem.set(?usize, args_kinky, null);
1078 var any_kinky_args = false;
1079
1080 const func_kinky = try dtz.writeInst(writer, call.func);
1081
1082 for (call.args) |arg, i| {
1083 try writer.writeAll(", ");
1084
1085 args_kinky[i] = try dtz.writeInst(writer, arg);
1086 any_kinky_args = any_kinky_args or args_kinky[i] != null;
1087 }
1088
1089 if (func_kinky != null or any_kinky_args) {
1090 try writer.writeAll(") // Instruction does not dominate all uses!");
1091 if (func_kinky) |func_index| {
1092 try writer.print(" %{d}", .{func_index});
1093 }
1094 for (args_kinky) |arg_kinky| {
1095 if (arg_kinky) |arg_index| {
1096 try writer.print(" %{d}", .{arg_index});
1097 }
1098 }
1099 try writer.writeAll("\n");
1100 } else {
1101 try writer.writeAll(")\n");
1102 }
1103 },
1104
1105 .struct_field_ptr => {
1106 const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
1107 const kinky = try dtz.writeInst(writer, struct_field_ptr.struct_ptr);
1108 if (kinky != null) {
1109 try writer.print("{d}) // Instruction does not dominate all uses!\n", .{
1110 struct_field_ptr.field_index,
1111 });
1112 } else {
1113 try writer.print("{d})\n", .{struct_field_ptr.field_index});
1114 }
1115 },
1116
1117 // TODO fill out this debug printing
1118 .assembly,
1119 .constant,
1120 .varptr,
1121 => {
1122 try writer.writeAll("!TODO!)\n");
1123 },
1124 }
1125 }
1126 }
1127
1128 fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *Inst) !?usize {
1129 if (dtz.partial_inst_table.get(inst)) |operand_index| {
1130 try writer.print("%{d}", .{operand_index});
1131 return null;
1132 } else if (dtz.const_table.get(inst)) |operand_index| {
1133 try writer.print("@{d}", .{operand_index});
1134 return null;
1135 } else if (dtz.inst_table.get(inst)) |operand_index| {
1136 try writer.print("%{d}", .{operand_index});
1137 return operand_index;
1138 } else {
1139 try writer.writeAll("!BADREF!");
1140 return null;
1141 }
1142 }
1143
1144 fn findConst(dtz: *DumpTzir, operand: *Inst) !void {
1145 if (operand.tag == .constant) {
1146 try dtz.const_table.put(operand, dtz.next_const_index);
1147 dtz.next_const_index += 1;
1148 }
1149 }
1150};
src/link/Elf.zig+1-1
......@@ -10,7 +10,7 @@ const log = std.log.scoped(.link);
1010const DW = std.dwarf;
1111const leb128 = std.leb;
1212
13const ir = @import("../ir.zig");
13const ir = @import("../air.zig");
1414const Module = @import("../Module.zig");
1515const Compilation = @import("../Compilation.zig");
1616const codegen = @import("../codegen.zig");
src/liveness.zig+1-1
......@@ -1,5 +1,5 @@
11const std = @import("std");
2const ir = @import("ir.zig");
2const ir = @import("air.zig");
33const trace = @import("tracy.zig").trace;
44const log = std.log.scoped(.liveness);
55
src/register_manager.zig+1-1
......@@ -3,7 +3,7 @@ const math = std.math;
33const mem = std.mem;
44const assert = std.debug.assert;
55const Allocator = std.mem.Allocator;
6const ir = @import("ir.zig");
6const ir = @import("air.zig");
77const Type = @import("type.zig").Type;
88const Module = @import("Module.zig");
99const LazySrcLoc = Module.LazySrcLoc;
src/value.zig+1-1
......@@ -7,7 +7,7 @@ const BigIntMutable = std.math.big.int.Mutable;
77const Target = std.Target;
88const Allocator = std.mem.Allocator;
99const Module = @import("Module.zig");
10const ir = @import("ir.zig");
10const ir = @import("air.zig");
1111
1212/// This is the raw data, with no bookkeeping, no memory awareness,
1313/// no de-duplication, and no type system awareness.