authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-08 20:42:47-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 12:18:14-07:00
log5d6f7b44c19b064a543b0c1eecb6ef5c671b612e
tree394376c930ddafef6d5455d9b80baff5abbae4b4
parentbfe20051673e285d3b1788cd637fab9ca84d1cb1

stage2: rework AIR memory layout

This commit changes the AIR file and the documentation of the memory layout. The actual work of modifying the surrounding code (in Sema and codegen) is not yet done.

16 files changed, 1073 insertions(+), 1202 deletions(-)

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