authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-15 15:52:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 12:19:16-07:00
logeadbee2041bba1cd03b24d8f30161025af8e3590
treeaebad285c7cd852fcc1c9d62f3beeb4395c6d04e
parent12c10139e3e0166e91d2dbb1801c2054ca12d413

stage2: first pass at printing AIR/Liveness to text

* some instructions are not implemented yet * fix off-by-1 in Air.getMainBody * Compilation: use `@import("builtin")` rather than `std.builtin` for the values that are different for different build configurations. * Sema: avoid calling `addType` in between air_instructions.ensureUnusedCapacity and corresponding appendAssumeCapacity because it can possibly add an instruction. * Value: functions print their names

7 files changed, 307 insertions(+), 574 deletions(-)

BRANCH_TODO deleted-566
...@@ -1,566 +0,0 @@
1 * be sure to test debug info of parameters
2
3
4 pub fn specialOperandDeaths(self: Inst) bool {
5 return (self.deaths & (1 << deaths_bits)) != 0;
6 }
7
8 /// Returns `null` if runtime-known.
9 /// Should be called by codegen, not by Sema. Sema functions should call
10 /// `resolvePossiblyUndefinedValue` or `resolveDefinedValue` instead.
11 /// TODO audit Sema code for violations to the above guidance.
12 pub fn value(base: *Inst) ?Value {
13 if (base.ty.onePossibleValue()) |opv| return opv;
14
15 const inst = base.castTag(.constant) orelse return null;
16 return inst.val;
17 }
18
19
20
21/// For debugging purposes, prints a function representation to stderr.
22pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
23 const allocator = old_module.gpa;
24 var ctx: DumpAir = .{
25 .allocator = allocator,
26 .arena = std.heap.ArenaAllocator.init(allocator),
27 .old_module = &old_module,
28 .module_fn = module_fn,
29 .indent = 2,
30 .inst_table = DumpAir.InstTable.init(allocator),
31 .partial_inst_table = DumpAir.InstTable.init(allocator),
32 .const_table = DumpAir.InstTable.init(allocator),
33 };
34 defer ctx.inst_table.deinit();
35 defer ctx.partial_inst_table.deinit();
36 defer ctx.const_table.deinit();
37 defer ctx.arena.deinit();
38
39 switch (module_fn.state) {
40 .queued => std.debug.print("(queued)", .{}),
41 .inline_only => std.debug.print("(inline_only)", .{}),
42 .in_progress => std.debug.print("(in_progress)", .{}),
43 .sema_failure => std.debug.print("(sema_failure)", .{}),
44 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
45 .success => {
46 const writer = std.io.getStdErr().writer();
47 ctx.dump(module_fn.body, writer) catch @panic("failed to dump AIR");
48 },
49 }
50}
51
52const DumpAir = struct {
53 allocator: *std.mem.Allocator,
54 arena: std.heap.ArenaAllocator,
55 old_module: *const Module,
56 module_fn: *Module.Fn,
57 indent: usize,
58 inst_table: InstTable,
59 partial_inst_table: InstTable,
60 const_table: InstTable,
61 next_index: usize = 0,
62 next_partial_index: usize = 0,
63 next_const_index: usize = 0,
64
65 const InstTable = std.AutoArrayHashMap(*Inst, usize);
66
67 /// TODO: Improve this code to include a stack of Body and store the instructions
68 /// in there. Now we are putting all the instructions in a function local table,
69 /// however instructions that are in a Body can be thown away when the Body ends.
70 fn dump(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) !void {
71 // First pass to pre-populate the table so that we can show even invalid references.
72 // Must iterate the same order we iterate the second time.
73 // We also look for constants and put them in the const_table.
74 try dtz.fetchInstsAndResolveConsts(body);
75
76 std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name});
77
78 var it = dtz.const_table.iterator();
79 while (it.next()) |entry| {
80 const constant = entry.key_ptr.*.castTag(.constant).?;
81 try writer.print(" @{d}: {} = {};\n", .{
82 entry.value_ptr.*, constant.base.ty, constant.val,
83 });
84 }
85
86 return dtz.dumpBody(body, writer);
87 }
88
89 fn fetchInstsAndResolveConsts(dtz: *DumpAir, body: Body) error{OutOfMemory}!void {
90 for (body.instructions) |inst| {
91 try dtz.inst_table.put(inst, dtz.next_index);
92 dtz.next_index += 1;
93 switch (inst.tag) {
94 .alloc,
95 .retvoid,
96 .unreach,
97 .breakpoint,
98 .dbg_stmt,
99 .arg,
100 => {},
101
102 .ref,
103 .ret,
104 .bitcast,
105 .not,
106 .is_non_null,
107 .is_non_null_ptr,
108 .is_null,
109 .is_null_ptr,
110 .is_err,
111 .is_non_err,
112 .is_err_ptr,
113 .is_non_err_ptr,
114 .ptrtoint,
115 .floatcast,
116 .intcast,
117 .load,
118 .optional_payload,
119 .optional_payload_ptr,
120 .wrap_optional,
121 .wrap_errunion_payload,
122 .wrap_errunion_err,
123 .unwrap_errunion_payload,
124 .unwrap_errunion_err,
125 .unwrap_errunion_payload_ptr,
126 .unwrap_errunion_err_ptr,
127 => {
128 const un_op = inst.cast(Inst.UnOp).?;
129 try dtz.findConst(un_op.operand);
130 },
131
132 .add,
133 .addwrap,
134 .sub,
135 .subwrap,
136 .mul,
137 .mulwrap,
138 .div,
139 .cmp_lt,
140 .cmp_lte,
141 .cmp_eq,
142 .cmp_gte,
143 .cmp_gt,
144 .cmp_neq,
145 .store,
146 .bool_and,
147 .bool_or,
148 .bit_and,
149 .bit_or,
150 .xor,
151 => {
152 const bin_op = inst.cast(Inst.BinOp).?;
153 try dtz.findConst(bin_op.lhs);
154 try dtz.findConst(bin_op.rhs);
155 },
156
157 .br => {
158 const br = inst.castTag(.br).?;
159 try dtz.findConst(&br.block.base);
160 try dtz.findConst(br.operand);
161 },
162
163 .br_block_flat => {
164 const br_block_flat = inst.castTag(.br_block_flat).?;
165 try dtz.findConst(&br_block_flat.block.base);
166 try dtz.fetchInstsAndResolveConsts(br_block_flat.body);
167 },
168
169 .br_void => {
170 const br_void = inst.castTag(.br_void).?;
171 try dtz.findConst(&br_void.block.base);
172 },
173
174 .block => {
175 const block = inst.castTag(.block).?;
176 try dtz.fetchInstsAndResolveConsts(block.body);
177 },
178
179 .condbr => {
180 const condbr = inst.castTag(.condbr).?;
181 try dtz.findConst(condbr.condition);
182 try dtz.fetchInstsAndResolveConsts(condbr.then_body);
183 try dtz.fetchInstsAndResolveConsts(condbr.else_body);
184 },
185 .switchbr => {
186 const switchbr = inst.castTag(.switchbr).?;
187 try dtz.findConst(switchbr.target);
188 try dtz.fetchInstsAndResolveConsts(switchbr.else_body);
189 for (switchbr.cases) |case| {
190 try dtz.fetchInstsAndResolveConsts(case.body);
191 }
192 },
193
194 .loop => {
195 const loop = inst.castTag(.loop).?;
196 try dtz.fetchInstsAndResolveConsts(loop.body);
197 },
198 .call => {
199 const call = inst.castTag(.call).?;
200 try dtz.findConst(call.func);
201 for (call.args) |arg| {
202 try dtz.findConst(arg);
203 }
204 },
205 .struct_field_ptr => {
206 const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
207 try dtz.findConst(struct_field_ptr.struct_ptr);
208 },
209
210 // TODO fill out this debug printing
211 .assembly,
212 .constant,
213 .varptr,
214 => {},
215 }
216 }
217 }
218
219 fn dumpBody(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void {
220 for (body.instructions) |inst| {
221 const my_index = dtz.next_partial_index;
222 try dtz.partial_inst_table.put(inst, my_index);
223 dtz.next_partial_index += 1;
224
225 try writer.writeByteNTimes(' ', dtz.indent);
226 try writer.print("%{d}: {} = {s}(", .{
227 my_index, inst.ty, @tagName(inst.tag),
228 });
229 switch (inst.tag) {
230 .alloc,
231 .retvoid,
232 .unreach,
233 .breakpoint,
234 .dbg_stmt,
235 => try writer.writeAll(")\n"),
236
237 .ref,
238 .ret,
239 .bitcast,
240 .not,
241 .is_non_null,
242 .is_non_null_ptr,
243 .is_null,
244 .is_null_ptr,
245 .is_err,
246 .is_err_ptr,
247 .is_non_err,
248 .is_non_err_ptr,
249 .ptrtoint,
250 .floatcast,
251 .intcast,
252 .load,
253 .optional_payload,
254 .optional_payload_ptr,
255 .wrap_optional,
256 .wrap_errunion_err,
257 .wrap_errunion_payload,
258 .unwrap_errunion_err,
259 .unwrap_errunion_payload,
260 .unwrap_errunion_payload_ptr,
261 .unwrap_errunion_err_ptr,
262 => {
263 const un_op = inst.cast(Inst.UnOp).?;
264 const kinky = try dtz.writeInst(writer, un_op.operand);
265 if (kinky != null) {
266 try writer.writeAll(") // Instruction does not dominate all uses!\n");
267 } else {
268 try writer.writeAll(")\n");
269 }
270 },
271
272 .add,
273 .addwrap,
274 .sub,
275 .subwrap,
276 .mul,
277 .mulwrap,
278 .div,
279 .cmp_lt,
280 .cmp_lte,
281 .cmp_eq,
282 .cmp_gte,
283 .cmp_gt,
284 .cmp_neq,
285 .store,
286 .bool_and,
287 .bool_or,
288 .bit_and,
289 .bit_or,
290 .xor,
291 => {
292 const bin_op = inst.cast(Inst.BinOp).?;
293
294 const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs);
295 try writer.writeAll(", ");
296 const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs);
297
298 if (lhs_kinky != null or rhs_kinky != null) {
299 try writer.writeAll(") // Instruction does not dominate all uses!");
300 if (lhs_kinky) |lhs| {
301 try writer.print(" %{d}", .{lhs});
302 }
303 if (rhs_kinky) |rhs| {
304 try writer.print(" %{d}", .{rhs});
305 }
306 try writer.writeAll("\n");
307 } else {
308 try writer.writeAll(")\n");
309 }
310 },
311
312 .arg => {
313 const arg = inst.castTag(.arg).?;
314 try writer.print("{s})\n", .{arg.name});
315 },
316
317 .br => {
318 const br = inst.castTag(.br).?;
319
320 const lhs_kinky = try dtz.writeInst(writer, &br.block.base);
321 try writer.writeAll(", ");
322 const rhs_kinky = try dtz.writeInst(writer, br.operand);
323
324 if (lhs_kinky != null or rhs_kinky != null) {
325 try writer.writeAll(") // Instruction does not dominate all uses!");
326 if (lhs_kinky) |lhs| {
327 try writer.print(" %{d}", .{lhs});
328 }
329 if (rhs_kinky) |rhs| {
330 try writer.print(" %{d}", .{rhs});
331 }
332 try writer.writeAll("\n");
333 } else {
334 try writer.writeAll(")\n");
335 }
336 },
337
338 .br_block_flat => {
339 const br_block_flat = inst.castTag(.br_block_flat).?;
340 const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base);
341 if (block_kinky != null) {
342 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
343 } else {
344 try writer.writeAll(", {\n");
345 }
346
347 const old_indent = dtz.indent;
348 dtz.indent += 2;
349 try dtz.dumpBody(br_block_flat.body, writer);
350 dtz.indent = old_indent;
351
352 try writer.writeByteNTimes(' ', dtz.indent);
353 try writer.writeAll("})\n");
354 },
355
356 .br_void => {
357 const br_void = inst.castTag(.br_void).?;
358 const kinky = try dtz.writeInst(writer, &br_void.block.base);
359 if (kinky) |_| {
360 try writer.writeAll(") // Instruction does not dominate all uses!\n");
361 } else {
362 try writer.writeAll(")\n");
363 }
364 },
365
366 .block => {
367 const block = inst.castTag(.block).?;
368
369 try writer.writeAll("{\n");
370
371 const old_indent = dtz.indent;
372 dtz.indent += 2;
373 try dtz.dumpBody(block.body, writer);
374 dtz.indent = old_indent;
375
376 try writer.writeByteNTimes(' ', dtz.indent);
377 try writer.writeAll("})\n");
378 },
379
380 .condbr => {
381 const condbr = inst.castTag(.condbr).?;
382
383 const condition_kinky = try dtz.writeInst(writer, condbr.condition);
384 if (condition_kinky != null) {
385 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
386 } else {
387 try writer.writeAll(", {\n");
388 }
389
390 const old_indent = dtz.indent;
391 dtz.indent += 2;
392 try dtz.dumpBody(condbr.then_body, writer);
393
394 try writer.writeByteNTimes(' ', old_indent);
395 try writer.writeAll("}, {\n");
396
397 try dtz.dumpBody(condbr.else_body, writer);
398 dtz.indent = old_indent;
399
400 try writer.writeByteNTimes(' ', old_indent);
401 try writer.writeAll("})\n");
402 },
403
404 .switchbr => {
405 const switchbr = inst.castTag(.switchbr).?;
406
407 const condition_kinky = try dtz.writeInst(writer, switchbr.target);
408 if (condition_kinky != null) {
409 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
410 } else {
411 try writer.writeAll(", {\n");
412 }
413 const old_indent = dtz.indent;
414
415 if (switchbr.else_body.instructions.len != 0) {
416 dtz.indent += 2;
417 try dtz.dumpBody(switchbr.else_body, writer);
418
419 try writer.writeByteNTimes(' ', old_indent);
420 try writer.writeAll("}, {\n");
421 dtz.indent = old_indent;
422 }
423 for (switchbr.cases) |case| {
424 dtz.indent += 2;
425 try dtz.dumpBody(case.body, writer);
426
427 try writer.writeByteNTimes(' ', old_indent);
428 try writer.writeAll("}, {\n");
429 dtz.indent = old_indent;
430 }
431
432 try writer.writeByteNTimes(' ', old_indent);
433 try writer.writeAll("})\n");
434 },
435
436 .loop => {
437 const loop = inst.castTag(.loop).?;
438
439 try writer.writeAll("{\n");
440
441 const old_indent = dtz.indent;
442 dtz.indent += 2;
443 try dtz.dumpBody(loop.body, writer);
444 dtz.indent = old_indent;
445
446 try writer.writeByteNTimes(' ', dtz.indent);
447 try writer.writeAll("})\n");
448 },
449
450 .call => {
451 const call = inst.castTag(.call).?;
452
453 const args_kinky = try dtz.allocator.alloc(?usize, call.args.len);
454 defer dtz.allocator.free(args_kinky);
455 std.mem.set(?usize, args_kinky, null);
456 var any_kinky_args = false;
457
458 const func_kinky = try dtz.writeInst(writer, call.func);
459
460 for (call.args) |arg, i| {
461 try writer.writeAll(", ");
462
463 args_kinky[i] = try dtz.writeInst(writer, arg);
464 any_kinky_args = any_kinky_args or args_kinky[i] != null;
465 }
466
467 if (func_kinky != null or any_kinky_args) {
468 try writer.writeAll(") // Instruction does not dominate all uses!");
469 if (func_kinky) |func_index| {
470 try writer.print(" %{d}", .{func_index});
471 }
472 for (args_kinky) |arg_kinky| {
473 if (arg_kinky) |arg_index| {
474 try writer.print(" %{d}", .{arg_index});
475 }
476 }
477 try writer.writeAll("\n");
478 } else {
479 try writer.writeAll(")\n");
480 }
481 },
482
483 .struct_field_ptr => {
484 const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
485 const kinky = try dtz.writeInst(writer, struct_field_ptr.struct_ptr);
486 if (kinky != null) {
487 try writer.print("{d}) // Instruction does not dominate all uses!\n", .{
488 struct_field_ptr.field_index,
489 });
490 } else {
491 try writer.print("{d})\n", .{struct_field_ptr.field_index});
492 }
493 },
494
495 // TODO fill out this debug printing
496 .assembly,
497 .constant,
498 .varptr,
499 => {
500 try writer.writeAll("!TODO!)\n");
501 },
502 }
503 }
504 }
505
506 fn writeInst(dtz: *DumpAir, writer: std.fs.File.Writer, inst: *Inst) !?usize {
507 if (dtz.partial_inst_table.get(inst)) |operand_index| {
508 try writer.print("%{d}", .{operand_index});
509 return null;
510 } else if (dtz.const_table.get(inst)) |operand_index| {
511 try writer.print("@{d}", .{operand_index});
512 return null;
513 } else if (dtz.inst_table.get(inst)) |operand_index| {
514 try writer.print("%{d}", .{operand_index});
515 return operand_index;
516 } else {
517 try writer.writeAll("!BADREF!");
518 return null;
519 }
520 }
521
522 fn findConst(dtz: *DumpAir, operand: *Inst) !void {
523 if (operand.tag == .constant) {
524 try dtz.const_table.put(operand, dtz.next_const_index);
525 dtz.next_const_index += 1;
526 }
527 }
528};
529
530pub fn dumpInst(mod: *Module, scope: *Scope, inst: *ir.Inst) void {
531 const zir_module = scope.namespace();
532 const source = zir_module.getSource(mod) catch @panic("dumpInst failed to get source");
533 const loc = std.zig.findLineColumn(source, inst.src);
534 if (inst.tag == .constant) {
535 std.debug.print("constant ty={} val={} src={s}:{d}:{d}\n", .{
536 inst.ty,
537 inst.castTag(.constant).?.val,
538 zir_module.subFilePath(),
539 loc.line + 1,
540 loc.column + 1,
541 });
542 } else if (inst.deaths == 0) {
543 std.debug.print("{s} ty={} src={s}:{d}:{d}\n", .{
544 @tagName(inst.tag),
545 inst.ty,
546 zir_module.subFilePath(),
547 loc.line + 1,
548 loc.column + 1,
549 });
550 } else {
551 std.debug.print("{s} ty={} deaths={b} src={s}:{d}:{d}\n", .{
552 @tagName(inst.tag),
553 inst.ty,
554 inst.deaths,
555 zir_module.subFilePath(),
556 loc.line + 1,
557 loc.column + 1,
558 });
559 }
560}
561
562 /// For debugging purposes.
563 pub fn dump(func: *Fn, mod: Module) void {
564 ir.dumpFn(mod, func);
565 }
566
src/Air.zig+2-2
...@@ -374,8 +374,8 @@ pub const Asm = struct {...@@ -374,8 +374,8 @@ pub const Asm = struct {
374374
375pub fn getMainBody(air: Air) []const Air.Inst.Index {375pub fn getMainBody(air: Air) []const Air.Inst.Index {
376 const body_index = air.extra[@enumToInt(ExtraIndex.main_block)];376 const body_index = air.extra[@enumToInt(ExtraIndex.main_block)];
377 const body_len = air.extra[body_index];377 const extra = air.extraData(Block, body_index);
378 return air.extra[body_index..][0..body_len];378 return air.extra[extra.end..][0..extra.data.body_len];
379}379}
380380
381pub fn getType(air: Air, inst: Air.Inst.Index) Type {381pub fn getType(air: Air, inst: Air.Inst.Index) Type {
src/Compilation.zig+6-3
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const Compilation = @This();1const Compilation = @This();
22
3const std = @import("std");3const std = @import("std");
4const builtin = @import("builtin");
4const mem = std.mem;5const mem = std.mem;
5const Allocator = std.mem.Allocator;6const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;7const assert = std.debug.assert;
...@@ -907,7 +908,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -907,7 +908,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
907 // comptime conditions908 // comptime conditions
908 ((build_options.have_llvm and comptime std.Target.current.isDarwin()) and909 ((build_options.have_llvm and comptime std.Target.current.isDarwin()) and
909 // runtime conditions910 // runtime conditions
910 (use_lld and std.builtin.os.tag == .macos and options.target.isDarwin()));911 (use_lld and builtin.os.tag == .macos and options.target.isDarwin()));
911912
912 const sysroot = blk: {913 const sysroot = blk: {
913 if (options.sysroot) |sysroot| {914 if (options.sysroot) |sysroot| {
...@@ -2026,8 +2027,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2026,8 +2027,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2026 var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);2027 var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);
2027 defer liveness.deinit(gpa);2028 defer liveness.deinit(gpa);
20282029
2029 if (std.builtin.mode == .Debug and self.verbose_air) {2030 if (builtin.mode == .Debug and self.verbose_air) {
2030 @panic("TODO implement dumping AIR and liveness");2031 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
2032 @import("print_air.zig").dump(gpa, air, liveness);
2033 std.debug.print("# End Function AIR: {s}:\n", .{decl.name});
2031 }2034 }
20322035
2033 assert(decl.ty.hasCodeGenBits());2036 assert(decl.ty.hasCodeGenBits());
src/Module.zig+2-1
...@@ -3551,7 +3551,8 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {...@@ -3551,7 +3551,8 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
3551 try sema.analyzeFnBody(&inner_block, func.zir_body_inst);3551 try sema.analyzeFnBody(&inner_block, func.zir_body_inst);
35523552
3553 // Copy the block into place and mark that as the main block.3553 // Copy the block into place and mark that as the main block.
3554 try sema.air_extra.ensureUnusedCapacity(gpa, inner_block.instructions.items.len + 1);3554 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
3555 inner_block.instructions.items.len);
3555 const main_block_index = sema.addExtraAssumeCapacity(Air.Block{3556 const main_block_index = sema.addExtraAssumeCapacity(Air.Block{
3556 .body_len = @intCast(u32, inner_block.instructions.items.len),3557 .body_len = @intCast(u32, inner_block.instructions.items.len),
3557 });3558 });
src/Sema.zig+2-1
...@@ -2028,6 +2028,7 @@ fn analyzeBlockBody(...@@ -2028,6 +2028,7 @@ fn analyzeBlockBody(
2028 refToIndex(coerced_operand).?);2028 refToIndex(coerced_operand).?);
20292029
2030 // Convert the br operand to a block.2030 // Convert the br operand to a block.
2031 const br_operand_ty_ref = try sema.addType(br_operand_ty);
2031 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +2032 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
2032 coerce_block.instructions.items.len);2033 coerce_block.instructions.items.len);
2033 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);2034 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
...@@ -2037,7 +2038,7 @@ fn analyzeBlockBody(...@@ -2037,7 +2038,7 @@ fn analyzeBlockBody(
2037 sema.air_instructions.appendAssumeCapacity(.{2038 sema.air_instructions.appendAssumeCapacity(.{
2038 .tag = .block,2039 .tag = .block,
2039 .data = .{ .ty_pl = .{2040 .data = .{ .ty_pl = .{
2040 .ty = try sema.addType(br_operand_ty),2041 .ty = br_operand_ty_ref,
2041 .payload = sema.addExtraAssumeCapacity(Air.Block{2042 .payload = sema.addExtraAssumeCapacity(Air.Block{
2042 .body_len = @intCast(u32, coerce_block.instructions.items.len),2043 .body_len = @intCast(u32, coerce_block.instructions.items.len),
2043 }),2044 }),
src/print_air.zig created+294
...@@ -0,0 +1,294 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
4
5const Module = @import("Module.zig");
6const Value = @import("value.zig").Value;
7const Air = @import("Air.zig");
8const Liveness = @import("Liveness.zig");
9
10pub fn dump(gpa: *Allocator, air: Air, liveness: Liveness) void {
11 const instruction_bytes = air.instructions.len *
12 // Here we don't use @sizeOf(Air.Inst.Data) because it would include
13 // the debug safety tag but we want to measure release size.
14 (@sizeOf(Air.Inst.Tag) + 8);
15 const extra_bytes = air.extra.len * @sizeOf(u32);
16 const values_bytes = air.values.len * @sizeOf(Value);
17 const variables_bytes = air.variables.len * @sizeOf(*Module.Var);
18 const tomb_bytes = liveness.tomb_bits.len * @sizeOf(usize);
19 const liveness_extra_bytes = liveness.extra.len * @sizeOf(u32);
20 const liveness_special_bytes = liveness.special.count() * 8;
21 const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes +
22 values_bytes * variables_bytes + @sizeOf(Liveness) + liveness_extra_bytes +
23 liveness_special_bytes + tomb_bytes;
24
25 // zig fmt: off
26 std.debug.print(
27 \\# Total AIR+Liveness bytes: {}
28 \\# AIR Instructions: {d} ({})
29 \\# AIR Extra Data: {d} ({})
30 \\# AIR Values Bytes: {d} ({})
31 \\# AIR Variables Bytes: {d} ({})
32 \\# Liveness tomb_bits: {}
33 \\# Liveness Extra Data: {d} ({})
34 \\# Liveness special table: {d} ({})
35 \\
36 , .{
37 fmtIntSizeBin(total_bytes),
38 air.instructions.len, fmtIntSizeBin(instruction_bytes),
39 air.extra.len, fmtIntSizeBin(extra_bytes),
40 air.values.len, fmtIntSizeBin(values_bytes),
41 air.variables.len, fmtIntSizeBin(variables_bytes),
42 fmtIntSizeBin(tomb_bytes),
43 liveness.extra.len, fmtIntSizeBin(liveness_extra_bytes),
44 liveness.special.count(), fmtIntSizeBin(liveness_special_bytes),
45 });
46 // zig fmt: on
47 var arena = std.heap.ArenaAllocator.init(gpa);
48 defer arena.deinit();
49
50 var writer: Writer = .{
51 .gpa = gpa,
52 .arena = &arena.allocator,
53 .air = air,
54 .liveness = liveness,
55 .indent = 0,
56 };
57 const stream = std.io.getStdErr().writer();
58 writer.writeAllConstants(stream) catch return;
59 writer.writeBody(stream, air.getMainBody()) catch return;
60}
61
62const Writer = struct {
63 gpa: *Allocator,
64 arena: *Allocator,
65 air: Air,
66 liveness: Liveness,
67 indent: usize,
68
69 fn writeAllConstants(w: *Writer, s: anytype) @TypeOf(s).Error!void {
70 for (w.air.instructions.items(.tag)) |tag, i| {
71 const inst = @intCast(u32, i);
72 switch (tag) {
73 .constant, .const_ty => {
74 try s.writeByteNTimes(' ', w.indent);
75 try s.print("%{d} ", .{inst});
76 try w.writeInst(s, inst);
77 try s.writeAll(")\n");
78 },
79 else => continue,
80 }
81 }
82 }
83
84 fn writeBody(w: *Writer, s: anytype, body: []const Air.Inst.Index) @TypeOf(s).Error!void {
85 for (body) |inst| {
86 try s.writeByteNTimes(' ', w.indent);
87 try s.print("%{d} ", .{inst});
88 try w.writeInst(s, inst);
89 if (w.liveness.isUnused(inst)) {
90 try s.writeAll(") unused\n");
91 } else {
92 try s.writeAll("\n");
93 }
94 }
95 }
96
97 fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
98 const tags = w.air.instructions.items(.tag);
99 const tag = tags[inst];
100 try s.print("= {s}(", .{@tagName(tags[inst])});
101 switch (tag) {
102 .arg => try w.writeTyStr(s, inst),
103
104 .add,
105 .addwrap,
106 .sub,
107 .subwrap,
108 .mul,
109 .mulwrap,
110 .div,
111 .bit_and,
112 .bit_or,
113 .xor,
114 .cmp_lt,
115 .cmp_lte,
116 .cmp_eq,
117 .cmp_gte,
118 .cmp_gt,
119 .cmp_neq,
120 .bool_and,
121 .bool_or,
122 .store,
123 => try w.writeBinOp(s, inst),
124
125 .is_null,
126 .is_non_null,
127 .is_null_ptr,
128 .is_non_null_ptr,
129 .is_err,
130 .is_non_err,
131 .is_err_ptr,
132 .is_non_err_ptr,
133 .ptrtoint,
134 .ret,
135 => try w.writeUnOp(s, inst),
136
137 .breakpoint,
138 .unreach,
139 => try w.writeNoOp(s, inst),
140
141 .const_ty,
142 .alloc,
143 => try w.writeTy(s, inst),
144
145 .not,
146 .bitcast,
147 .load,
148 .ref,
149 .floatcast,
150 .intcast,
151 .optional_payload,
152 .optional_payload_ptr,
153 .wrap_optional,
154 .unwrap_errunion_payload,
155 .unwrap_errunion_err,
156 .unwrap_errunion_payload_ptr,
157 .unwrap_errunion_err_ptr,
158 .wrap_errunion_payload,
159 .wrap_errunion_err,
160 => try w.writeTyOp(s, inst),
161
162 .block,
163 .loop,
164 => try w.writeBlock(s, inst),
165
166 .struct_field_ptr => try w.writeStructFieldPtr(s, inst),
167 .varptr => try w.writeVarPtr(s, inst),
168 .constant => try w.writeConstant(s, inst),
169 .assembly => try w.writeAssembly(s, inst),
170 .dbg_stmt => try w.writeDbgStmt(s, inst),
171 .call => try w.writeCall(s, inst),
172 .br => try w.writeBr(s, inst),
173 .cond_br => try w.writeCondBr(s, inst),
174 .switch_br => try w.writeSwitchBr(s, inst),
175 }
176 }
177
178 fn writeTyStr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
179 _ = w;
180 _ = inst;
181 try s.writeAll("TODO");
182 }
183
184 fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
185 _ = w;
186 _ = inst;
187 try s.writeAll("TODO");
188 }
189
190 fn writeUnOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
191 _ = w;
192 _ = inst;
193 try s.writeAll("TODO");
194 }
195
196 fn writeNoOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
197 _ = w;
198 _ = inst;
199 try s.writeAll("TODO");
200 }
201
202 fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
203 const ty = w.air.instructions.items(.data)[inst].ty;
204 try s.print("{}", .{ty});
205 }
206
207 fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
208 _ = w;
209 _ = inst;
210 try s.writeAll("TODO");
211 }
212
213 fn writeBlock(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
214 _ = w;
215 _ = inst;
216 try s.writeAll("TODO");
217 }
218
219 fn writeStructFieldPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
220 _ = w;
221 _ = inst;
222 try s.writeAll("TODO");
223 }
224
225 fn writeVarPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
226 _ = w;
227 _ = inst;
228 try s.writeAll("TODO");
229 }
230
231 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
232 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
233 const val = w.air.values[ty_pl.payload];
234 try s.print("{}, {}", .{ ty_pl.ty, val });
235 }
236
237 fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
238 _ = w;
239 _ = inst;
240 try s.writeAll("TODO");
241 }
242
243 fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
244 const dbg_stmt = w.air.instructions.items(.data)[inst].dbg_stmt;
245 try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
246 }
247
248 fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
249 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
250 const extra = w.air.extraData(Air.Call, pl_op.payload);
251 const args = w.air.extra[extra.end..][0..extra.data.args_len];
252 try w.writeInstRef(s, pl_op.operand);
253 try s.writeAll(", [");
254 for (args) |arg, i| {
255 if (i != 0) try s.writeAll(", ");
256 try w.writeInstRef(s, @intToEnum(Air.Inst.Ref, arg));
257 }
258 try s.writeAll("]");
259 }
260
261 fn writeBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
262 _ = w;
263 _ = inst;
264 try s.writeAll("TODO");
265 }
266
267 fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
268 _ = w;
269 _ = inst;
270 try s.writeAll("TODO");
271 }
272
273 fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
274 _ = w;
275 _ = inst;
276 try s.writeAll("TODO");
277 }
278
279 fn writeInstRef(w: *Writer, s: anytype, inst: Air.Inst.Ref) @TypeOf(s).Error!void {
280 var i: usize = @enumToInt(inst);
281
282 if (i < Air.Inst.Ref.typed_value_map.len) {
283 return s.print("@{}", .{inst});
284 }
285 i -= Air.Inst.Ref.typed_value_map.len;
286
287 return w.writeInstIndex(s, @intCast(Air.Inst.Index, i));
288 }
289
290 fn writeInstIndex(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
291 _ = w;
292 return s.print("%{d}", .{inst});
293 }
294};
src/value.zig+1-1
...@@ -573,7 +573,7 @@ pub const Value = extern union {...@@ -573,7 +573,7 @@ pub const Value = extern union {
573 .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, out_stream),573 .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, out_stream),
574 .int_big_positive => return out_stream.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}),574 .int_big_positive => return out_stream.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}),
575 .int_big_negative => return out_stream.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}),575 .int_big_negative => return out_stream.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}),
576 .function => return out_stream.writeAll("(function)"),576 .function => return out_stream.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}),
577 .extern_fn => return out_stream.writeAll("(extern function)"),577 .extern_fn => return out_stream.writeAll("(extern function)"),
578 .variable => return out_stream.writeAll("(variable)"),578 .variable => return out_stream.writeAll("(variable)"),
579 .ref_val => {579 .ref_val => {