authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-21 13:32:25-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-04-21 13:32:25-07:00
log528b66f6ec9cfb140abff3dc0c4735c179520f42
tree6fc4f164a93a6bd3c2c3467457aedce6fa7959b3
parent391663e497f1871f6bddcf9cbc500710aa9aac4d
parentb3f9fe6d0439bcbb5c6baa77c0646c4da2e06dd7
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15355 from mlugg/feat/liveness-control-flow

Liveness: control flow analysis and other goodies

249 files changed, 5004 insertions(+), 5878 deletions(-)

lib/test_runner.zig+1-1
......@@ -13,7 +13,7 @@ var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
1313
1414pub fn main() void {
1515 if (builtin.zig_backend == .stage2_wasm or
16 builtin.zig_backend == .stage2_x86_64 or
16 (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag != .linux) or
1717 builtin.zig_backend == .stage2_aarch64)
1818 {
1919 return mainSimple() catch @panic("test failure");
src/Air.zig+214
......@@ -1375,3 +1375,217 @@ pub fn nullTerminatedString(air: Air, index: usize) [:0]const u8 {
13751375 }
13761376 return bytes[0..end :0];
13771377}
1378
1379/// Returns whether the given instruction must always be lowered, for instance because it can cause
1380/// side effects. If an instruction does not need to be lowered, and Liveness determines its result
1381/// is unused, backends should avoid lowering it.
1382pub fn mustLower(air: Air, inst: Air.Inst.Index) bool {
1383 const data = air.instructions.items(.data)[inst];
1384 return switch (air.instructions.items(.tag)[inst]) {
1385 .arg,
1386 .block,
1387 .loop,
1388 .br,
1389 .trap,
1390 .breakpoint,
1391 .call,
1392 .call_always_tail,
1393 .call_never_tail,
1394 .call_never_inline,
1395 .cond_br,
1396 .switch_br,
1397 .@"try",
1398 .try_ptr,
1399 .dbg_stmt,
1400 .dbg_block_begin,
1401 .dbg_block_end,
1402 .dbg_inline_begin,
1403 .dbg_inline_end,
1404 .dbg_var_ptr,
1405 .dbg_var_val,
1406 .ret,
1407 .ret_load,
1408 .store,
1409 .unreach,
1410 .optional_payload_ptr_set,
1411 .errunion_payload_ptr_set,
1412 .set_union_tag,
1413 .memset,
1414 .memcpy,
1415 .cmpxchg_weak,
1416 .cmpxchg_strong,
1417 .fence,
1418 .atomic_store_unordered,
1419 .atomic_store_monotonic,
1420 .atomic_store_release,
1421 .atomic_store_seq_cst,
1422 .atomic_rmw,
1423 .prefetch,
1424 .wasm_memory_grow,
1425 .set_err_return_trace,
1426 .vector_store_elem,
1427 .c_va_arg,
1428 .c_va_copy,
1429 .c_va_end,
1430 .c_va_start,
1431 => true,
1432
1433 .add,
1434 .add_optimized,
1435 .addwrap,
1436 .addwrap_optimized,
1437 .add_sat,
1438 .sub,
1439 .sub_optimized,
1440 .subwrap,
1441 .subwrap_optimized,
1442 .sub_sat,
1443 .mul,
1444 .mul_optimized,
1445 .mulwrap,
1446 .mulwrap_optimized,
1447 .mul_sat,
1448 .div_float,
1449 .div_float_optimized,
1450 .div_trunc,
1451 .div_trunc_optimized,
1452 .div_floor,
1453 .div_floor_optimized,
1454 .div_exact,
1455 .div_exact_optimized,
1456 .rem,
1457 .rem_optimized,
1458 .mod,
1459 .mod_optimized,
1460 .ptr_add,
1461 .ptr_sub,
1462 .max,
1463 .min,
1464 .add_with_overflow,
1465 .sub_with_overflow,
1466 .mul_with_overflow,
1467 .shl_with_overflow,
1468 .alloc,
1469 .ret_ptr,
1470 .bit_and,
1471 .bit_or,
1472 .shr,
1473 .shr_exact,
1474 .shl,
1475 .shl_exact,
1476 .shl_sat,
1477 .xor,
1478 .not,
1479 .bitcast,
1480 .ret_addr,
1481 .frame_addr,
1482 .clz,
1483 .ctz,
1484 .popcount,
1485 .byte_swap,
1486 .bit_reverse,
1487 .sqrt,
1488 .sin,
1489 .cos,
1490 .tan,
1491 .exp,
1492 .exp2,
1493 .log,
1494 .log2,
1495 .log10,
1496 .fabs,
1497 .floor,
1498 .ceil,
1499 .round,
1500 .trunc_float,
1501 .neg,
1502 .neg_optimized,
1503 .cmp_lt,
1504 .cmp_lt_optimized,
1505 .cmp_lte,
1506 .cmp_lte_optimized,
1507 .cmp_eq,
1508 .cmp_eq_optimized,
1509 .cmp_gte,
1510 .cmp_gte_optimized,
1511 .cmp_gt,
1512 .cmp_gt_optimized,
1513 .cmp_neq,
1514 .cmp_neq_optimized,
1515 .cmp_vector,
1516 .cmp_vector_optimized,
1517 .constant,
1518 .const_ty,
1519 .is_null,
1520 .is_non_null,
1521 .is_null_ptr,
1522 .is_non_null_ptr,
1523 .is_err,
1524 .is_non_err,
1525 .is_err_ptr,
1526 .is_non_err_ptr,
1527 .bool_and,
1528 .bool_or,
1529 .ptrtoint,
1530 .bool_to_int,
1531 .fptrunc,
1532 .fpext,
1533 .intcast,
1534 .trunc,
1535 .optional_payload,
1536 .optional_payload_ptr,
1537 .wrap_optional,
1538 .unwrap_errunion_payload,
1539 .unwrap_errunion_err,
1540 .unwrap_errunion_payload_ptr,
1541 .unwrap_errunion_err_ptr,
1542 .wrap_errunion_payload,
1543 .wrap_errunion_err,
1544 .struct_field_ptr,
1545 .struct_field_ptr_index_0,
1546 .struct_field_ptr_index_1,
1547 .struct_field_ptr_index_2,
1548 .struct_field_ptr_index_3,
1549 .struct_field_val,
1550 .get_union_tag,
1551 .slice,
1552 .slice_len,
1553 .slice_ptr,
1554 .ptr_slice_len_ptr,
1555 .ptr_slice_ptr_ptr,
1556 .array_elem_val,
1557 .slice_elem_ptr,
1558 .ptr_elem_ptr,
1559 .array_to_slice,
1560 .float_to_int,
1561 .float_to_int_optimized,
1562 .int_to_float,
1563 .reduce,
1564 .reduce_optimized,
1565 .splat,
1566 .shuffle,
1567 .select,
1568 .is_named_enum_value,
1569 .tag_name,
1570 .error_name,
1571 .error_set_has_value,
1572 .aggregate_init,
1573 .union_init,
1574 .mul_add,
1575 .field_parent_ptr,
1576 .wasm_memory_size,
1577 .cmp_lt_errors_len,
1578 .err_return_trace,
1579 .addrspace_cast,
1580 .save_err_return_trace_index,
1581 .work_item_id,
1582 .work_group_size,
1583 .work_group_id,
1584 => false,
1585
1586 .assembly => @truncate(u1, air.extraData(Air.Asm, data.ty_pl.payload).data.flags >> 31) != 0,
1587 .load => air.typeOf(data.ty_op.operand).isVolatilePtr(),
1588 .slice_elem_val, .ptr_elem_val => air.typeOf(data.bin_op.lhs).isVolatilePtr(),
1589 .atomic_load => air.typeOf(data.atomic_load.ptr).isVolatilePtr(),
1590 };
1591}
src/Compilation.zig+2-1
......@@ -3095,6 +3095,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
30953095
30963096 .file_failure,
30973097 .sema_failure,
3098 .liveness_failure,
30983099 .codegen_failure,
30993100 .dependency_failure,
31003101 .sema_failure_retryable,
......@@ -3145,7 +3146,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
31453146
31463147 // emit-h only requires semantic analysis of the Decl to be complete,
31473148 // it does not depend on machine code generation to succeed.
3148 .codegen_failure, .codegen_failure_retryable, .complete => {
3149 .liveness_failure, .codegen_failure, .codegen_failure_retryable, .complete => {
31493150 const named_frame = tracy.namedFrame("emit_h_decl");
31503151 defer named_frame.end();
31513152
src/Liveness.zig+978-863
......@@ -14,6 +14,8 @@ const Allocator = std.mem.Allocator;
1414const Air = @import("Air.zig");
1515const Log2Int = std.math.Log2Int;
1616
17pub const Verify = @import("Liveness/Verify.zig");
18
1719/// This array is split into sets of 4 bits per AIR instruction.
1820/// The MSB (0bX000) is whether the instruction is unreferenced.
1921/// The LSB (0b000X) is the first operand, and so on, up to 3 operands. A set bit means the
......@@ -24,8 +26,10 @@ tomb_bits: []usize,
2426/// Sparse table of specially handled instructions. The value is an index into the `extra`
2527/// array. The meaning of the data depends on the AIR tag.
2628/// * `cond_br` - points to a `CondBr` in `extra` at this index.
29/// * `try`, `try_ptr` - points to a `CondBr` in `extra` at this index. The error path (the block
30/// in the instruction) is considered the "else" path, and the rest of the block the "then".
2731/// * `switch_br` - points to a `SwitchBr` in `extra` at this index.
28/// * `loop` - points to a `Loop` in `extra` at this index.
32/// * `block` - points to a `Block` in `extra` at this index.
2933/// * `asm`, `call`, `aggregate_init` - the value is a set of bits which are the extra tomb
3034/// bits of operands.
3135/// The main tomb bits are still used and the extra ones are starting with the lsb of the
......@@ -52,11 +56,88 @@ pub const SwitchBr = struct {
5256 else_death_count: u32,
5357};
5458
55/// Trailing is the set of instructions whose lifetimes end at the end of the loop body.
56pub const Loop = struct {
59/// Trailing is the set of instructions which die in the block. Note that these are not additional
60/// deaths (they are all recorded as normal within the block), but backends may use this information
61/// as a more efficient way to track which instructions are still alive after a block.
62pub const Block = struct {
5763 death_count: u32,
5864};
5965
66/// Liveness analysis runs in several passes. Each pass iterates backwards over instructions in
67/// bodies, and recurses into bodies.
68const LivenessPass = enum {
69 /// In this pass, we perform some basic analysis of loops to gain information the main pass
70 /// needs. In particular, for every `loop`, we track the following information:
71 /// * Every block which the loop body contains a `br` to.
72 /// * Every operand referenced within the loop body but created outside the loop.
73 /// This gives the main analysis pass enough information to determine the full set of
74 /// instructions which need to be alive when a loop repeats. This data is TEMPORARILY stored in
75 /// `a.extra`. It is not re-added to `extra` by the main pass, since it is not useful to
76 /// backends.
77 loop_analysis,
78
79 /// This pass performs the main liveness analysis, setting up tombs and extra data while
80 /// considering control flow etc.
81 main_analysis,
82};
83
84/// Each analysis pass may wish to pass data through calls. A pointer to a `LivenessPassData(pass)`
85/// stored on the stack is passed through calls to `analyzeInst` etc.
86fn LivenessPassData(comptime pass: LivenessPass) type {
87 return switch (pass) {
88 .loop_analysis => struct {
89 /// The set of blocks which are exited with a `br` instruction at some point within this
90 /// body and which we are currently within.
91 breaks: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
92
93 /// The set of operands for which we have seen at least one usage but not their birth.
94 live_set: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
95
96 fn deinit(self: *@This(), gpa: Allocator) void {
97 self.breaks.deinit(gpa);
98 self.live_set.deinit(gpa);
99 }
100 },
101
102 .main_analysis => struct {
103 /// Every `block` currently under analysis.
104 block_scopes: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockScope) = .{},
105
106 /// The set of deaths which should be made to occur at the earliest possible point in
107 /// this control flow branch. These instructions die when they are last referenced in
108 /// the current branch; if unreferenced, they die at the start of the branch. Populated
109 /// when a `br` instruction is reached. If deaths are common to all branches of control
110 /// flow, they may be bubbled up to the parent branch.
111 branch_deaths: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
112
113 /// The set of instructions currently alive. Instructions which must die in this branch
114 /// (i.e. those in `branch_deaths`) are not in this set, because they must die before
115 /// this point.
116 live_set: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
117
118 /// The extra data initialized by the `loop_analysis` pass for this pass to consume.
119 /// Owned by this struct during this pass.
120 old_extra: std.ArrayListUnmanaged(u32) = .{},
121
122 const BlockScope = struct {
123 /// The set of instructions which are alive upon a `br` to this block.
124 live_set: std.AutoHashMapUnmanaged(Air.Inst.Index, void),
125 };
126
127 fn deinit(self: *@This(), gpa: Allocator) void {
128 var it = self.block_scopes.valueIterator();
129 while (it.next()) |block| {
130 block.live_set.deinit(gpa);
131 }
132 self.block_scopes.deinit(gpa);
133 self.branch_deaths.deinit(gpa);
134 self.live_set.deinit(gpa);
135 self.old_extra.deinit(gpa);
136 }
137 },
138 };
139}
140
60141pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {
61142 const tracy = trace(@src());
62143 defer tracy.end();
......@@ -64,7 +145,6 @@ pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {
64145 var a: Analysis = .{
65146 .gpa = gpa,
66147 .air = air,
67 .table = .{},
68148 .tomb_bits = try gpa.alloc(
69149 usize,
70150 (air.instructions.len * bpi + @bitSizeOf(usize) - 1) / @bitSizeOf(usize),
......@@ -75,19 +155,27 @@ pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {
75155 errdefer gpa.free(a.tomb_bits);
76156 errdefer a.special.deinit(gpa);
77157 defer a.extra.deinit(gpa);
78 defer a.table.deinit(gpa);
79158
80159 std.mem.set(usize, a.tomb_bits, 0);
81160
82161 const main_body = air.getMainBody();
83 try a.table.ensureTotalCapacity(gpa, @intCast(u32, main_body.len));
84 try analyzeWithContext(&a, null, main_body);
162
163 {
164 var data: LivenessPassData(.loop_analysis) = .{};
165 defer data.deinit(gpa);
166 try analyzeBody(&a, .loop_analysis, &data, main_body);
167 }
168
85169 {
86 var to_remove: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{};
87 defer to_remove.deinit(gpa);
88 try removeDeaths(&a, &to_remove, main_body);
170 var data: LivenessPassData(.main_analysis) = .{};
171 defer data.deinit(gpa);
172 data.old_extra = a.extra;
173 a.extra = .{};
174 try analyzeBody(&a, .main_analysis, &data, main_body);
175 assert(data.branch_deaths.count() == 0);
89176 }
90 return Liveness{
177
178 return .{
91179 .tomb_bits = a.tomb_bits,
92180 .special = a.special,
93181 .extra = try a.extra.toOwnedSlice(gpa),
......@@ -661,18 +749,27 @@ pub fn getSwitchBr(l: Liveness, gpa: Allocator, inst: Air.Inst.Index, cases_len:
661749 };
662750}
663751
664pub const LoopSlice = struct {
752/// Note that this information is technically redundant, but is useful for
753/// backends nonetheless: see `Block`.
754pub const BlockSlices = struct {
665755 deaths: []const Air.Inst.Index,
666756};
667757
668pub fn getLoop(l: Liveness, inst: Air.Inst.Index) LoopSlice {
758pub fn getBlock(l: Liveness, inst: Air.Inst.Index) BlockSlices {
669759 const index: usize = l.special.get(inst) orelse return .{
670760 .deaths = &.{},
671761 };
672762 const death_count = l.extra[index];
673 return .{ .deaths = l.extra[index + 1 ..][0..death_count] };
763 const deaths = l.extra[index + 1 ..][0..death_count];
764 return .{
765 .deaths = deaths,
766 };
674767}
675768
769pub const LoopSlice = struct {
770 deaths: []const Air.Inst.Index,
771};
772
676773pub fn deinit(l: *Liveness, gpa: Allocator) void {
677774 gpa.free(l.tomb_bits);
678775 gpa.free(l.extra);
......@@ -687,6 +784,7 @@ pub fn iterateBigTomb(l: Liveness, inst: Air.Inst.Index) BigTomb {
687784 .extra_offset = 0,
688785 .extra = l.extra,
689786 .bit_index = 0,
787 .reached_end = false,
690788 };
691789}
692790
......@@ -702,13 +800,16 @@ pub const BigTomb = struct {
702800 extra_start: u32,
703801 extra_offset: u32,
704802 extra: []const u32,
803 reached_end: bool,
705804
706805 /// Returns whether the next operand dies.
707806 pub fn feed(bt: *BigTomb) bool {
807 if (bt.reached_end) return false;
808
708809 const this_bit_index = bt.bit_index;
709810 bt.bit_index += 1;
710811
711 const small_tombs = Liveness.bpi - 1;
812 const small_tombs = bpi - 1;
712813 if (this_bit_index < small_tombs) {
713814 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
714815 return dies;
......@@ -716,6 +817,10 @@ pub const BigTomb = struct {
716817
717818 const big_bit_index = this_bit_index - small_tombs;
718819 while (big_bit_index - bt.extra_offset * 31 >= 31) {
820 if (@truncate(u1, bt.extra[bt.extra_start + bt.extra_offset] >> 31) != 0) {
821 bt.reached_end = true;
822 return false;
823 }
719824 bt.extra_offset += 1;
720825 }
721826 const dies = @truncate(u1, bt.extra[bt.extra_start + bt.extra_offset] >>
......@@ -728,7 +833,6 @@ pub const BigTomb = struct {
728833const Analysis = struct {
729834 gpa: Allocator,
730835 air: Air,
731 table: std.AutoHashMapUnmanaged(Air.Inst.Index, void),
732836 tomb_bits: []usize,
733837 special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
734838 extra: std.ArrayListUnmanaged(u32),
......@@ -758,46 +862,70 @@ const Analysis = struct {
758862 }
759863};
760864
761fn analyzeWithContext(
865fn analyzeBody(
762866 a: *Analysis,
763 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),
867 comptime pass: LivenessPass,
868 data: *LivenessPassData(pass),
764869 body: []const Air.Inst.Index,
765870) Allocator.Error!void {
766871 var i: usize = body.len;
872 while (i != 0) {
873 i -= 1;
874 const inst = body[i];
875 try analyzeInst(a, pass, data, inst);
876 }
877}
767878
768 if (new_set) |ns| {
769 // We are only interested in doing this for instructions which are born
770 // before a conditional branch, so after obtaining the new set for
771 // each branch we prune the instructions which were born within.
772 while (i != 0) {
773 i -= 1;
774 const inst = body[i];
775 _ = ns.remove(inst);
776 try analyzeInst(a, new_set, inst);
777 }
778 } else {
779 while (i != 0) {
780 i -= 1;
781 const inst = body[i];
782 try analyzeInst(a, new_set, inst);
783 }
879const ControlBranchInfo = struct {
880 branch_deaths: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
881 live_set: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
882};
883
884/// Helper function for running `analyzeBody`, but resetting `branch_deaths` and `live_set` to their
885/// original states before returning, returning the modified versions of them. Only makes sense in
886/// the `main_analysis` pass.
887fn analyzeBodyResetBranch(
888 a: *Analysis,
889 comptime pass: LivenessPass,
890 data: *LivenessPassData(pass),
891 body: []const Air.Inst.Index,
892) !ControlBranchInfo {
893 switch (pass) {
894 .main_analysis => {},
895 else => @compileError("Liveness.analyzeBodyResetBranch only makes sense in LivenessPass.main_analysis"),
896 }
897
898 const gpa = a.gpa;
899
900 const old_branch_deaths = try data.branch_deaths.clone(a.gpa);
901 defer {
902 data.branch_deaths.deinit(gpa);
903 data.branch_deaths = old_branch_deaths;
904 }
905
906 const old_live_set = try data.live_set.clone(a.gpa);
907 defer {
908 data.live_set.deinit(gpa);
909 data.live_set = old_live_set;
784910 }
911
912 try analyzeBody(a, pass, data, body);
913
914 return .{
915 .branch_deaths = data.branch_deaths.move(),
916 .live_set = data.live_set.move(),
917 };
785918}
786919
787920fn analyzeInst(
788921 a: *Analysis,
789 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),
922 comptime pass: LivenessPass,
923 data: *LivenessPassData(pass),
790924 inst: Air.Inst.Index,
791925) Allocator.Error!void {
792 const gpa = a.gpa;
793 const table = &a.table;
794926 const inst_tags = a.air.instructions.items(.tag);
795927 const inst_datas = a.air.instructions.items(.data);
796928
797 // No tombstone for this instruction means it is never referenced,
798 // and its birth marks its own death. Very metal 🤘
799 const main_tomb = !table.contains(inst);
800
801929 switch (inst_tags[inst]) {
802930 .add,
803931 .add_optimized,
......@@ -861,28 +989,24 @@ fn analyzeInst(
861989 .max,
862990 => {
863991 const o = inst_datas[inst].bin_op;
864 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });
992 return analyzeOperands(a, pass, data, inst, .{ o.lhs, o.rhs, .none });
865993 },
866994
867995 .vector_store_elem => {
868996 const o = inst_datas[inst].vector_store_elem;
869997 const extra = a.air.extraData(Air.Bin, o.payload).data;
870 return trackOperands(a, new_set, inst, main_tomb, .{ o.vector_ptr, extra.lhs, extra.rhs });
998 return analyzeOperands(a, pass, data, inst, .{ o.vector_ptr, extra.lhs, extra.rhs });
871999 },
8721000
8731001 .arg,
8741002 .alloc,
8751003 .ret_ptr,
876 .constant,
877 .const_ty,
878 .trap,
8791004 .breakpoint,
8801005 .dbg_stmt,
8811006 .dbg_inline_begin,
8821007 .dbg_inline_end,
8831008 .dbg_block_begin,
8841009 .dbg_block_end,
885 .unreach,
8861010 .fence,
8871011 .ret_addr,
8881012 .frame_addr,
......@@ -893,7 +1017,15 @@ fn analyzeInst(
8931017 .work_item_id,
8941018 .work_group_size,
8951019 .work_group_id,
896 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
1020 => return analyzeOperands(a, pass, data, inst, .{ .none, .none, .none }),
1021
1022 .constant,
1023 .const_ty,
1024 => unreachable,
1025
1026 .trap,
1027 .unreach,
1028 => return analyzeFuncEnd(a, pass, data, inst, .{ .none, .none, .none }),
8971029
8981030 .not,
8991031 .bitcast,
......@@ -938,7 +1070,7 @@ fn analyzeInst(
9381070 .c_va_copy,
9391071 => {
9401072 const o = inst_datas[inst].ty_op;
941 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
1073 return analyzeOperands(a, pass, data, inst, .{ o.operand, .none, .none });
9421074 },
9431075
9441076 .is_null,
......@@ -951,8 +1083,6 @@ fn analyzeInst(
9511083 .is_non_err_ptr,
9521084 .ptrtoint,
9531085 .bool_to_int,
954 .ret,
955 .ret_load,
9561086 .is_named_enum_value,
9571087 .tag_name,
9581088 .error_name,
......@@ -977,7 +1107,14 @@ fn analyzeInst(
9771107 .c_va_end,
9781108 => {
9791109 const operand = inst_datas[inst].un_op;
980 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
1110 return analyzeOperands(a, pass, data, inst, .{ operand, .none, .none });
1111 },
1112
1113 .ret,
1114 .ret_load,
1115 => {
1116 const operand = inst_datas[inst].un_op;
1117 return analyzeFuncEnd(a, pass, data, inst, .{ operand, .none, .none });
9811118 },
9821119
9831120 .add_with_overflow,
......@@ -992,19 +1129,19 @@ fn analyzeInst(
9921129 => {
9931130 const ty_pl = inst_datas[inst].ty_pl;
9941131 const extra = a.air.extraData(Air.Bin, ty_pl.payload).data;
995 return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none });
1132 return analyzeOperands(a, pass, data, inst, .{ extra.lhs, extra.rhs, .none });
9961133 },
9971134
9981135 .dbg_var_ptr,
9991136 .dbg_var_val,
10001137 => {
10011138 const operand = inst_datas[inst].pl_op.operand;
1002 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
1139 return analyzeOperands(a, pass, data, inst, .{ operand, .none, .none });
10031140 },
10041141
10051142 .prefetch => {
10061143 const prefetch = inst_datas[inst].prefetch;
1007 return trackOperands(a, new_set, inst, main_tomb, .{ prefetch.ptr, .none, .none });
1144 return analyzeOperands(a, pass, data, inst, .{ prefetch.ptr, .none, .none });
10081145 },
10091146
10101147 .call, .call_always_tail, .call_never_tail, .call_never_inline => {
......@@ -1016,37 +1153,35 @@ fn analyzeInst(
10161153 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
10171154 buf[0] = callee;
10181155 std.mem.copy(Air.Inst.Ref, buf[1..], args);
1019 return trackOperands(a, new_set, inst, main_tomb, buf);
1156 return analyzeOperands(a, pass, data, inst, buf);
10201157 }
1021 var extra_tombs: ExtraTombs = .{
1022 .analysis = a,
1023 .new_set = new_set,
1024 .inst = inst,
1025 .main_tomb = main_tomb,
1026 };
1027 defer extra_tombs.deinit();
1028 try extra_tombs.feed(callee);
1029 for (args) |arg| {
1030 try extra_tombs.feed(arg);
1158
1159 var big = try AnalyzeBigOperands(pass).init(a, data, inst, args.len + 1);
1160 defer big.deinit();
1161 var i: usize = args.len;
1162 while (i > 0) {
1163 i -= 1;
1164 try big.feed(args[i]);
10311165 }
1032 return extra_tombs.finish();
1166 try big.feed(callee);
1167 return big.finish();
10331168 },
10341169 .select => {
10351170 const pl_op = inst_datas[inst].pl_op;
10361171 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
1037 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs });
1172 return analyzeOperands(a, pass, data, inst, .{ pl_op.operand, extra.lhs, extra.rhs });
10381173 },
10391174 .shuffle => {
10401175 const extra = a.air.extraData(Air.Shuffle, inst_datas[inst].ty_pl.payload).data;
1041 return trackOperands(a, new_set, inst, main_tomb, .{ extra.a, extra.b, .none });
1176 return analyzeOperands(a, pass, data, inst, .{ extra.a, extra.b, .none });
10421177 },
10431178 .reduce, .reduce_optimized => {
10441179 const reduce = inst_datas[inst].reduce;
1045 return trackOperands(a, new_set, inst, main_tomb, .{ reduce.operand, .none, .none });
1180 return analyzeOperands(a, pass, data, inst, .{ reduce.operand, .none, .none });
10461181 },
10471182 .cmp_vector, .cmp_vector_optimized => {
10481183 const extra = a.air.extraData(Air.VectorCmp, inst_datas[inst].ty_pl.payload).data;
1049 return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none });
1184 return analyzeOperands(a, pass, data, inst, .{ extra.lhs, extra.rhs, .none });
10501185 },
10511186 .aggregate_init => {
10521187 const ty_pl = inst_datas[inst].ty_pl;
......@@ -1057,62 +1192,58 @@ fn analyzeInst(
10571192 if (elements.len <= bpi - 1) {
10581193 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
10591194 std.mem.copy(Air.Inst.Ref, &buf, elements);
1060 return trackOperands(a, new_set, inst, main_tomb, buf);
1195 return analyzeOperands(a, pass, data, inst, buf);
10611196 }
1062 var extra_tombs: ExtraTombs = .{
1063 .analysis = a,
1064 .new_set = new_set,
1065 .inst = inst,
1066 .main_tomb = main_tomb,
1067 };
1068 defer extra_tombs.deinit();
1069 for (elements) |elem| {
1070 try extra_tombs.feed(elem);
1197
1198 var big = try AnalyzeBigOperands(pass).init(a, data, inst, elements.len);
1199 defer big.deinit();
1200 var i: usize = elements.len;
1201 while (i > 0) {
1202 i -= 1;
1203 try big.feed(elements[i]);
10711204 }
1072 return extra_tombs.finish();
1205 return big.finish();
10731206 },
10741207 .union_init => {
10751208 const extra = a.air.extraData(Air.UnionInit, inst_datas[inst].ty_pl.payload).data;
1076 return trackOperands(a, new_set, inst, main_tomb, .{ extra.init, .none, .none });
1209 return analyzeOperands(a, pass, data, inst, .{ extra.init, .none, .none });
10771210 },
10781211 .struct_field_ptr, .struct_field_val => {
10791212 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
1080 return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none });
1213 return analyzeOperands(a, pass, data, inst, .{ extra.struct_operand, .none, .none });
10811214 },
10821215 .field_parent_ptr => {
10831216 const extra = a.air.extraData(Air.FieldParentPtr, inst_datas[inst].ty_pl.payload).data;
1084 return trackOperands(a, new_set, inst, main_tomb, .{ extra.field_ptr, .none, .none });
1217 return analyzeOperands(a, pass, data, inst, .{ extra.field_ptr, .none, .none });
10851218 },
10861219 .cmpxchg_strong, .cmpxchg_weak => {
10871220 const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data;
1088 return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value });
1221 return analyzeOperands(a, pass, data, inst, .{ extra.ptr, extra.expected_value, extra.new_value });
10891222 },
10901223 .mul_add => {
10911224 const pl_op = inst_datas[inst].pl_op;
10921225 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
1093 return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, pl_op.operand });
1226 return analyzeOperands(a, pass, data, inst, .{ extra.lhs, extra.rhs, pl_op.operand });
10941227 },
10951228 .atomic_load => {
10961229 const ptr = inst_datas[inst].atomic_load.ptr;
1097 return trackOperands(a, new_set, inst, main_tomb, .{ ptr, .none, .none });
1230 return analyzeOperands(a, pass, data, inst, .{ ptr, .none, .none });
10981231 },
10991232 .atomic_rmw => {
11001233 const pl_op = inst_datas[inst].pl_op;
11011234 const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;
1102 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none });
1235 return analyzeOperands(a, pass, data, inst, .{ pl_op.operand, extra.operand, .none });
11031236 },
11041237 .memset,
11051238 .memcpy,
11061239 => {
11071240 const pl_op = inst_datas[inst].pl_op;
11081241 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
1109 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs });
1242 return analyzeOperands(a, pass, data, inst, .{ pl_op.operand, extra.lhs, extra.rhs });
11101243 },
11111244
1112 .br => {
1113 const br = inst_datas[inst].br;
1114 return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none });
1115 },
1245 .br => return analyzeInstBr(a, pass, data, inst),
1246
11161247 .assembly => {
11171248 const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload);
11181249 var extra_i: usize = extra.end;
......@@ -1121,912 +1252,896 @@ fn analyzeInst(
11211252 const inputs = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]);
11221253 extra_i += inputs.len;
11231254
1124 simple: {
1255 const num_operands = simple: {
11251256 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
11261257 var buf_index: usize = 0;
11271258 for (outputs) |output| {
11281259 if (output != .none) {
1129 if (buf_index >= buf.len) break :simple;
1130 buf[buf_index] = output;
1260 if (buf_index < buf.len) buf[buf_index] = output;
11311261 buf_index += 1;
11321262 }
11331263 }
1134 if (buf_index + inputs.len > buf.len) break :simple;
1264 if (buf_index + inputs.len > buf.len) {
1265 break :simple buf_index + inputs.len;
1266 }
11351267 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
1136 return trackOperands(a, new_set, inst, main_tomb, buf);
1137 }
1138 var extra_tombs: ExtraTombs = .{
1139 .analysis = a,
1140 .new_set = new_set,
1141 .inst = inst,
1142 .main_tomb = main_tomb,
1268 return analyzeOperands(a, pass, data, inst, buf);
11431269 };
1144 defer extra_tombs.deinit();
1145 for (outputs) |output| {
1146 if (output != .none) {
1147 try extra_tombs.feed(output);
1148 }
1270
1271 var big = try AnalyzeBigOperands(pass).init(a, data, inst, num_operands);
1272 defer big.deinit();
1273 var i: usize = inputs.len;
1274 while (i > 0) {
1275 i -= 1;
1276 try big.feed(inputs[i]);
11491277 }
1150 for (inputs) |input| {
1151 try extra_tombs.feed(input);
1278 i = outputs.len;
1279 while (i > 0) {
1280 i -= 1;
1281 if (outputs[i] != .none) {
1282 try big.feed(outputs[i]);
1283 }
11521284 }
1153 return extra_tombs.finish();
1285 return big.finish();
11541286 },
1155 .block => {
1156 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
1157 const body = a.air.extra[extra.end..][0..extra.data.body_len];
1158 try analyzeWithContext(a, new_set, body);
1159 return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none });
1287
1288 .block => return analyzeInstBlock(a, pass, data, inst),
1289 .loop => return analyzeInstLoop(a, pass, data, inst),
1290
1291 .@"try" => return analyzeInstCondBr(a, pass, data, inst, .@"try"),
1292 .try_ptr => return analyzeInstCondBr(a, pass, data, inst, .try_ptr),
1293 .cond_br => return analyzeInstCondBr(a, pass, data, inst, .cond_br),
1294 .switch_br => return analyzeInstSwitchBr(a, pass, data, inst),
1295
1296 .wasm_memory_grow => {
1297 const pl_op = inst_datas[inst].pl_op;
1298 return analyzeOperands(a, pass, data, inst, .{ pl_op.operand, .none, .none });
11601299 },
1161 .loop => {
1162 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
1163 const body = a.air.extra[extra.end..][0..extra.data.body_len];
1300 }
1301}
11641302
1165 var body_table: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{};
1166 defer body_table.deinit(gpa);
1303/// Every instruction should hit this (after handling any nested bodies), in every pass. In the
1304/// initial pass, it is responsible for marking deaths of the (first three) operands and noticing
1305/// immediate deaths.
1306fn analyzeOperands(
1307 a: *Analysis,
1308 comptime pass: LivenessPass,
1309 data: *LivenessPassData(pass),
1310 inst: Air.Inst.Index,
1311 operands: [bpi - 1]Air.Inst.Ref,
1312) Allocator.Error!void {
1313 const gpa = a.gpa;
1314 const inst_tags = a.air.instructions.items(.tag);
11671315
1168 // Instructions outside the loop body cannot die within the loop, since further loop
1169 // iterations may occur. Track deaths from the loop body - we'll remove all of these
1170 // retroactively, and add them to our extra data.
1316 switch (pass) {
1317 .loop_analysis => {
1318 _ = data.live_set.remove(inst);
11711319
1172 try analyzeWithContext(a, &body_table, body);
1320 for (operands) |op_ref| {
1321 const operand = Air.refToIndex(op_ref) orelse continue;
11731322
1174 if (new_set) |ns| {
1175 try ns.ensureUnusedCapacity(gpa, body_table.count());
1176 var it = body_table.keyIterator();
1177 while (it.next()) |key| {
1178 _ = ns.putAssumeCapacity(key.*, {});
1323 // Don't compute any liveness for constants
1324 switch (inst_tags[operand]) {
1325 .constant, .const_ty => continue,
1326 else => {},
11791327 }
1328
1329 _ = try data.live_set.put(gpa, operand, {});
11801330 }
1331 },
11811332
1182 try a.extra.ensureUnusedCapacity(gpa, std.meta.fields(Loop).len + body_table.count());
1183 const extra_index = a.addExtraAssumeCapacity(Loop{
1184 .death_count = body_table.count(),
1185 });
1186 {
1187 var it = body_table.keyIterator();
1188 while (it.next()) |key| {
1189 a.extra.appendAssumeCapacity(key.*);
1190 }
1333 .main_analysis => {
1334 const usize_index = (inst * bpi) / @bitSizeOf(usize);
1335
1336 // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`.
1337 var immediate_death = false;
1338 if (data.branch_deaths.remove(inst)) {
1339 log.debug("[{}] %{}: resolved branch death to birth (immediate death)", .{ pass, inst });
1340 immediate_death = true;
1341 assert(!data.live_set.contains(inst));
1342 } else if (data.live_set.remove(inst)) {
1343 log.debug("[{}] %{}: removed from live set", .{ pass, inst });
1344 } else {
1345 log.debug("[{}] %{}: immediate death", .{ pass, inst });
1346 immediate_death = true;
11911347 }
1192 try a.special.put(gpa, inst, extra_index);
11931348
1194 // We'll remove invalid deaths in a separate pass after main liveness analysis. See
1195 // removeDeaths for more details.
1349 var tomb_bits: Bpi = @as(Bpi, @boolToInt(immediate_death)) << (bpi - 1);
1350
1351 // If our result is unused and the instruction doesn't need to be lowered, backends will
1352 // skip the lowering of this instruction, so we don't want to record uses of operands.
1353 // That way, we can mark as many instructions as possible unused.
1354 if (!immediate_death or a.air.mustLower(inst)) {
1355 // Note that it's important we iterate over the operands backwards, so that if a dying
1356 // operand is used multiple times we mark its last use as its death.
1357 var i = operands.len;
1358 while (i > 0) {
1359 i -= 1;
1360 const op_ref = operands[i];
1361 const operand = Air.refToIndex(op_ref) orelse continue;
1362
1363 // Don't compute any liveness for constants
1364 switch (inst_tags[operand]) {
1365 .constant, .const_ty => continue,
1366 else => {},
1367 }
1368
1369 const mask = @as(Bpi, 1) << @intCast(OperandInt, i);
11961370
1197 return; // Loop has no operands and it is always unreferenced.
1198 },
1199 .@"try" => {
1200 const pl_op = inst_datas[inst].pl_op;
1201 const extra = a.air.extraData(Air.Try, pl_op.payload);
1202 const body = a.air.extra[extra.end..][0..extra.data.body_len];
1203 try analyzeWithContext(a, new_set, body);
1204 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, .none, .none });
1371 if ((try data.live_set.fetchPut(gpa, operand, {})) == null) {
1372 log.debug("[{}] %{}: added %{} to live set (operand dies here)", .{ pass, inst, operand });
1373 tomb_bits |= mask;
1374 if (data.branch_deaths.remove(operand)) {
1375 log.debug("[{}] %{}: resolved branch death of %{} to this usage", .{ pass, inst, operand });
1376 }
1377 }
1378 }
1379 }
1380
1381 a.tomb_bits[usize_index] |= @as(usize, tomb_bits) <<
1382 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi);
12051383 },
1206 .try_ptr => {
1207 const extra = a.air.extraData(Air.TryPtr, inst_datas[inst].ty_pl.payload);
1208 const body = a.air.extra[extra.end..][0..extra.data.body_len];
1209 try analyzeWithContext(a, new_set, body);
1210 return trackOperands(a, new_set, inst, main_tomb, .{ extra.data.ptr, .none, .none });
1384 }
1385}
1386
1387/// Like `analyzeOperands`, but for an instruction which returns from a function, so should
1388/// effectively kill every remaining live value other than its operands.
1389fn analyzeFuncEnd(
1390 a: *Analysis,
1391 comptime pass: LivenessPass,
1392 data: *LivenessPassData(pass),
1393 inst: Air.Inst.Index,
1394 operands: [bpi - 1]Air.Inst.Ref,
1395) Allocator.Error!void {
1396 switch (pass) {
1397 .loop_analysis => {
1398 // No operands need to be alive if we're returning from the function, so we don't need
1399 // to touch `breaks` here even though this is sort of like a break to the top level.
12111400 },
1212 .cond_br => {
1213 // Each death that occurs inside one branch, but not the other, needs
1214 // to be added as a death immediately upon entering the other branch.
1215 const inst_data = inst_datas[inst].pl_op;
1216 const condition = inst_data.operand;
1217 const extra = a.air.extraData(Air.CondBr, inst_data.payload);
1218 const then_body = a.air.extra[extra.end..][0..extra.data.then_body_len];
1219 const else_body = a.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
12201401
1221 var then_table: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{};
1222 defer then_table.deinit(gpa);
1223 try analyzeWithContext(a, &then_table, then_body);
1402 .main_analysis => {
1403 const gpa = a.gpa;
12241404
1225 // Reset the table back to its state from before the branch.
1226 {
1227 var it = then_table.keyIterator();
1228 while (it.next()) |key| {
1229 assert(table.remove(key.*));
1230 }
1405 // Note that we preserve previous branch deaths - anything that needs to die in our
1406 // "parent" branch also needs to die for us.
1407
1408 try data.branch_deaths.ensureUnusedCapacity(gpa, data.live_set.count());
1409 var it = data.live_set.keyIterator();
1410 while (it.next()) |key| {
1411 const alive = key.*;
1412 data.branch_deaths.putAssumeCapacity(alive, {});
12311413 }
1414 data.live_set.clearRetainingCapacity();
1415 },
1416 }
1417
1418 return analyzeOperands(a, pass, data, inst, operands);
1419}
1420
1421fn analyzeInstBr(
1422 a: *Analysis,
1423 comptime pass: LivenessPass,
1424 data: *LivenessPassData(pass),
1425 inst: Air.Inst.Index,
1426) !void {
1427 const inst_datas = a.air.instructions.items(.data);
1428 const br = inst_datas[inst].br;
1429 const gpa = a.gpa;
12321430
1233 var else_table: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{};
1234 defer else_table.deinit(gpa);
1235 try analyzeWithContext(a, &else_table, else_body);
1431 switch (pass) {
1432 .loop_analysis => {
1433 try data.breaks.put(gpa, br.block_inst, {});
1434 },
12361435
1237 var then_entry_deaths = std.ArrayList(Air.Inst.Index).init(gpa);
1238 defer then_entry_deaths.deinit();
1239 var else_entry_deaths = std.ArrayList(Air.Inst.Index).init(gpa);
1240 defer else_entry_deaths.deinit();
1436 .main_analysis => {
1437 const block_scope = data.block_scopes.get(br.block_inst).?; // we should always be breaking from an enclosing block
12411438
1242 {
1243 var it = else_table.keyIterator();
1244 while (it.next()) |key| {
1245 const else_death = key.*;
1246 if (!then_table.contains(else_death)) {
1247 try then_entry_deaths.append(else_death);
1248 }
1249 }
1250 }
1251 // This loop is the same, except it's for the then branch, and it additionally
1252 // has to put its items back into the table to undo the reset.
1253 {
1254 var it = then_table.keyIterator();
1255 while (it.next()) |key| {
1256 const then_death = key.*;
1257 if (!else_table.contains(then_death)) {
1258 try else_entry_deaths.append(then_death);
1259 }
1260 try table.put(gpa, then_death, {});
1261 }
1439 // We mostly preserve previous branch deaths - anything that should die for our
1440 // enclosing branch should die for us too. However, if our break target requires such an
1441 // operand to be alive, it's actually not something we want to kill, since its "last
1442 // use" (i.e. the point at which it should die) is outside of our scope.
1443 var it = block_scope.live_set.keyIterator();
1444 while (it.next()) |key| {
1445 const alive = key.*;
1446 _ = data.branch_deaths.remove(alive);
12621447 }
1263 // Now we have to correctly populate new_set.
1264 if (new_set) |ns| {
1265 try ns.ensureUnusedCapacity(gpa, @intCast(u32, then_table.count() + else_table.count()));
1266 var it = then_table.keyIterator();
1267 while (it.next()) |key| {
1268 _ = ns.putAssumeCapacity(key.*, {});
1269 }
1270 it = else_table.keyIterator();
1271 while (it.next()) |key| {
1272 _ = ns.putAssumeCapacity(key.*, {});
1448 log.debug("[{}] %{}: preserved branch deaths are {}", .{ pass, inst, fmtInstSet(&data.branch_deaths) });
1449
1450 // Anything that's currently alive but our target doesn't need becomes a branch death.
1451 it = data.live_set.keyIterator();
1452 while (it.next()) |key| {
1453 const alive = key.*;
1454 if (!block_scope.live_set.contains(alive)) {
1455 _ = try data.branch_deaths.put(gpa, alive, {});
1456 log.debug("[{}] %{}: added branch death of {}", .{ pass, inst, alive });
12731457 }
12741458 }
1275 const then_death_count = @intCast(u32, then_entry_deaths.items.len);
1276 const else_death_count = @intCast(u32, else_entry_deaths.items.len);
1459 const new_live_set = try block_scope.live_set.clone(gpa);
1460 data.live_set.deinit(gpa);
1461 data.live_set = new_live_set;
1462 },
1463 }
12771464
1278 try a.extra.ensureUnusedCapacity(gpa, std.meta.fields(Air.CondBr).len +
1279 then_death_count + else_death_count);
1280 const extra_index = a.addExtraAssumeCapacity(CondBr{
1281 .then_death_count = then_death_count,
1282 .else_death_count = else_death_count,
1283 });
1284 a.extra.appendSliceAssumeCapacity(then_entry_deaths.items);
1285 a.extra.appendSliceAssumeCapacity(else_entry_deaths.items);
1286 try a.special.put(gpa, inst, extra_index);
1465 return analyzeOperands(a, pass, data, inst, .{ br.operand, .none, .none });
1466}
12871467
1288 // Continue on with the instruction analysis. The following code will find the condition
1289 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
1290 // condition's lifetime ends immediately before entering any branch.
1291 return trackOperands(a, new_set, inst, main_tomb, .{ condition, .none, .none });
1292 },
1293 .switch_br => {
1294 const pl_op = inst_datas[inst].pl_op;
1295 const condition = pl_op.operand;
1296 const switch_br = a.air.extraData(Air.SwitchBr, pl_op.payload);
1468fn analyzeInstBlock(
1469 a: *Analysis,
1470 comptime pass: LivenessPass,
1471 data: *LivenessPassData(pass),
1472 inst: Air.Inst.Index,
1473) !void {
1474 const inst_datas = a.air.instructions.items(.data);
1475 const ty_pl = inst_datas[inst].ty_pl;
1476 const extra = a.air.extraData(Air.Block, ty_pl.payload);
1477 const body = a.air.extra[extra.end..][0..extra.data.body_len];
12971478
1298 const Table = std.AutoHashMapUnmanaged(Air.Inst.Index, void);
1299 const case_tables = try gpa.alloc(Table, switch_br.data.cases_len + 1); // +1 for else
1300 defer gpa.free(case_tables);
1479 const gpa = a.gpa;
13011480
1302 std.mem.set(Table, case_tables, .{});
1303 defer for (case_tables) |*ct| ct.deinit(gpa);
1481 // We actually want to do `analyzeOperands` *first*, since our result logically doesn't
1482 // exist until the block body ends (and we're iterating backwards)
1483 try analyzeOperands(a, pass, data, inst, .{ .none, .none, .none });
13041484
1305 var air_extra_index: usize = switch_br.end;
1306 for (case_tables[0..switch_br.data.cases_len]) |*case_table| {
1307 const case = a.air.extraData(Air.SwitchBr.Case, air_extra_index);
1308 const case_body = a.air.extra[case.end + case.data.items_len ..][0..case.data.body_len];
1309 air_extra_index = case.end + case.data.items_len + case_body.len;
1310 try analyzeWithContext(a, case_table, case_body);
1485 switch (pass) {
1486 .loop_analysis => {
1487 try analyzeBody(a, pass, data, body);
1488 _ = data.breaks.remove(inst);
1489 },
13111490
1312 // Reset the table back to its state from before the case.
1313 var it = case_table.keyIterator();
1314 while (it.next()) |key| {
1315 assert(table.remove(key.*));
1316 }
1491 .main_analysis => {
1492 log.debug("[{}] %{}: block live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });
1493 try data.block_scopes.put(gpa, inst, .{
1494 .live_set = try data.live_set.clone(gpa),
1495 });
1496 defer {
1497 log.debug("[{}] %{}: popped block scope", .{ pass, inst });
1498 var scope = data.block_scopes.fetchRemove(inst).?.value;
1499 scope.live_set.deinit(gpa);
13171500 }
1318 { // else
1319 const else_table = &case_tables[case_tables.len - 1];
1320 const else_body = a.air.extra[air_extra_index..][0..switch_br.data.else_body_len];
1321 try analyzeWithContext(a, else_table, else_body);
13221501
1323 // Reset the table back to its state from before the case.
1324 var it = else_table.keyIterator();
1325 while (it.next()) |key| {
1326 assert(table.remove(key.*));
1327 }
1328 }
1502 log.debug("[{}] %{}: pushed new block scope", .{ pass, inst });
1503 try analyzeBody(a, pass, data, body);
13291504
1330 const List = std.ArrayListUnmanaged(Air.Inst.Index);
1331 const case_deaths = try gpa.alloc(List, case_tables.len); // includes else
1332 defer gpa.free(case_deaths);
1505 // If the block is noreturn, block deaths not only aren't useful, they're impossible to
1506 // find: there could be more stuff alive after the block than before it!
1507 if (!a.air.getRefType(ty_pl.ty).isNoReturn()) {
1508 // The block kills the difference in the live sets
1509 const block_scope = data.block_scopes.get(inst).?;
1510 const num_deaths = data.live_set.count() - block_scope.live_set.count();
13331511
1334 std.mem.set(List, case_deaths, .{});
1335 defer for (case_deaths) |*cd| cd.deinit(gpa);
1512 try a.extra.ensureUnusedCapacity(gpa, num_deaths + std.meta.fields(Block).len);
1513 const extra_index = a.addExtraAssumeCapacity(Block{
1514 .death_count = num_deaths,
1515 });
13361516
1337 var total_deaths: u32 = 0;
1338 for (case_tables, 0..) |*ct, i| {
1339 total_deaths += ct.count();
1340 var it = ct.keyIterator();
1517 var measured_num: u32 = 0;
1518 var it = data.live_set.keyIterator();
13411519 while (it.next()) |key| {
1342 const case_death = key.*;
1343 for (case_tables, 0..) |*ct_inner, j| {
1344 if (i == j) continue;
1345 if (!ct_inner.contains(case_death)) {
1346 // instruction is not referenced in this case
1347 try case_deaths[j].append(gpa, case_death);
1348 }
1520 const alive = key.*;
1521 if (!block_scope.live_set.contains(alive)) {
1522 // Dies in block
1523 a.extra.appendAssumeCapacity(alive);
1524 measured_num += 1;
13491525 }
1350 // undo resetting the table
1351 try table.put(gpa, case_death, {});
13521526 }
1527 assert(measured_num == num_deaths); // post-live-set should be a subset of pre-live-set
1528 try a.special.put(gpa, inst, extra_index);
1529 log.debug("[{}] %{}: block deaths are {}", .{
1530 pass,
1531 inst,
1532 fmtInstList(a.extra.items[extra_index + 1 ..][0..num_deaths]),
1533 });
13531534 }
1535 },
1536 }
1537}
13541538
1355 // Now we have to correctly populate new_set.
1356 if (new_set) |ns| {
1357 try ns.ensureUnusedCapacity(gpa, total_deaths);
1358 for (case_tables) |*ct| {
1359 var it = ct.keyIterator();
1360 while (it.next()) |key| {
1361 _ = ns.putAssumeCapacity(key.*, {});
1362 }
1363 }
1539fn analyzeInstLoop(
1540 a: *Analysis,
1541 comptime pass: LivenessPass,
1542 data: *LivenessPassData(pass),
1543 inst: Air.Inst.Index,
1544) !void {
1545 const inst_datas = a.air.instructions.items(.data);
1546 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
1547 const body = a.air.extra[extra.end..][0..extra.data.body_len];
1548 const gpa = a.gpa;
1549
1550 try analyzeOperands(a, pass, data, inst, .{ .none, .none, .none });
1551
1552 switch (pass) {
1553 .loop_analysis => {
1554 var old_breaks = data.breaks.move();
1555 defer old_breaks.deinit(gpa);
1556
1557 var old_live = data.live_set.move();
1558 defer old_live.deinit(gpa);
1559
1560 try analyzeBody(a, pass, data, body);
1561
1562 const num_breaks = data.breaks.count();
1563 try a.extra.ensureUnusedCapacity(gpa, 1 + num_breaks);
1564
1565 const extra_index = @intCast(u32, a.extra.items.len);
1566 a.extra.appendAssumeCapacity(num_breaks);
1567
1568 var it = data.breaks.keyIterator();
1569 while (it.next()) |key| {
1570 const block_inst = key.*;
1571 a.extra.appendAssumeCapacity(block_inst);
13641572 }
1573 log.debug("[{}] %{}: includes breaks to {}", .{ pass, inst, fmtInstSet(&data.breaks) });
13651574
1366 const else_death_count = @intCast(u32, case_deaths[case_deaths.len - 1].items.len);
1367 const extra_index = try a.addExtra(SwitchBr{
1368 .else_death_count = else_death_count,
1369 });
1370 for (case_deaths[0 .. case_deaths.len - 1]) |*cd| {
1371 const case_death_count = @intCast(u32, cd.items.len);
1372 try a.extra.ensureUnusedCapacity(gpa, 1 + case_death_count + else_death_count);
1373 a.extra.appendAssumeCapacity(case_death_count);
1374 a.extra.appendSliceAssumeCapacity(cd.items);
1575 // Now we put the live operands from the loop body in too
1576 const num_live = data.live_set.count();
1577 try a.extra.ensureUnusedCapacity(gpa, 1 + num_live);
1578
1579 a.extra.appendAssumeCapacity(num_live);
1580 it = data.live_set.keyIterator();
1581 while (it.next()) |key| {
1582 const alive = key.*;
1583 a.extra.appendAssumeCapacity(alive);
13751584 }
1376 a.extra.appendSliceAssumeCapacity(case_deaths[case_deaths.len - 1].items);
1585 log.debug("[{}] %{}: maintain liveness of {}", .{ pass, inst, fmtInstSet(&data.live_set) });
1586
13771587 try a.special.put(gpa, inst, extra_index);
13781588
1379 return trackOperands(a, new_set, inst, main_tomb, .{ condition, .none, .none });
1380 },
1381 .wasm_memory_grow => {
1382 const pl_op = inst_datas[inst].pl_op;
1383 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, .none, .none });
1589 // Add back operands which were previously alive
1590 it = old_live.keyIterator();
1591 while (it.next()) |key| {
1592 const alive = key.*;
1593 try data.live_set.put(gpa, alive, {});
1594 }
1595
1596 // And the same for breaks
1597 it = old_breaks.keyIterator();
1598 while (it.next()) |key| {
1599 const block_inst = key.*;
1600 try data.breaks.put(gpa, block_inst, {});
1601 }
13841602 },
1385 }
1386}
13871603
1388fn trackOperands(
1389 a: *Analysis,
1390 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),
1391 inst: Air.Inst.Index,
1392 main_tomb: bool,
1393 operands: [bpi - 1]Air.Inst.Ref,
1394) Allocator.Error!void {
1395 const table = &a.table;
1396 const gpa = a.gpa;
1604 .main_analysis => {
1605 const extra_idx = a.special.fetchRemove(inst).?.value; // remove because this data does not exist after analysis
13971606
1398 var tomb_bits: Bpi = @boolToInt(main_tomb);
1399 var i = operands.len;
1607 const num_breaks = data.old_extra.items[extra_idx];
1608 const breaks = data.old_extra.items[extra_idx + 1 ..][0..num_breaks];
14001609
1401 while (i > 0) {
1402 i -= 1;
1403 tomb_bits <<= 1;
1404 const op_int = @enumToInt(operands[i]);
1405 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
1406 const operand: Air.Inst.Index = op_int - @intCast(u32, Air.Inst.Ref.typed_value_map.len);
1407 const prev = try table.fetchPut(gpa, operand, {});
1408 if (prev == null) {
1409 // Death.
1410 tomb_bits |= 1;
1411 if (new_set) |ns| try ns.putNoClobber(gpa, operand, {});
1412 }
1413 }
1414 a.storeTombBits(inst, tomb_bits);
1415}
1610 const num_loop_live = data.old_extra.items[extra_idx + num_breaks + 1];
1611 const loop_live = data.old_extra.items[extra_idx + num_breaks + 2 ..][0..num_loop_live];
14161612
1417const ExtraTombs = struct {
1418 analysis: *Analysis,
1419 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),
1420 inst: Air.Inst.Index,
1421 main_tomb: bool,
1422 bit_index: usize = 0,
1423 tomb_bits: Bpi = 0,
1424 big_tomb_bits: u32 = 0,
1425 big_tomb_bits_extra: std.ArrayListUnmanaged(u32) = .{},
1426
1427 fn feed(et: *ExtraTombs, op_ref: Air.Inst.Ref) !void {
1428 const this_bit_index = et.bit_index;
1429 et.bit_index += 1;
1430 const gpa = et.analysis.gpa;
1431 const op_index = Air.refToIndex(op_ref) orelse return;
1432 const prev = try et.analysis.table.fetchPut(gpa, op_index, {});
1433 if (prev == null) {
1434 // Death.
1435 if (et.new_set) |ns| try ns.putNoClobber(gpa, op_index, {});
1436 const available_tomb_bits = bpi - 1;
1437 if (this_bit_index < available_tomb_bits) {
1438 et.tomb_bits |= @as(Bpi, 1) << @intCast(OperandInt, this_bit_index);
1439 } else {
1440 const big_bit_index = this_bit_index - available_tomb_bits;
1441 while (big_bit_index >= (et.big_tomb_bits_extra.items.len + 1) * 31) {
1442 // We need another element in the extra array.
1443 try et.big_tomb_bits_extra.append(gpa, et.big_tomb_bits);
1444 et.big_tomb_bits = 0;
1445 } else {
1446 const final_bit_index = big_bit_index - et.big_tomb_bits_extra.items.len * 31;
1447 et.big_tomb_bits |= @as(u32, 1) << @intCast(u5, final_bit_index);
1448 }
1613 // This is necessarily not in the same control flow branch, because loops are noreturn
1614 data.live_set.clearRetainingCapacity();
1615
1616 try data.live_set.ensureUnusedCapacity(gpa, @intCast(u32, loop_live.len));
1617 for (loop_live) |alive| {
1618 data.live_set.putAssumeCapacity(alive, {});
1619 // If the loop requires a branch death operand to be alive, it's not something we
1620 // want to kill: its "last use" (i.e. the point at which it should die) is the loop
1621 // body itself.
1622 _ = data.branch_deaths.remove(alive);
14491623 }
1450 }
1451 }
14521624
1453 fn finish(et: *ExtraTombs) !void {
1454 et.tomb_bits |= @as(Bpi, @boolToInt(et.main_tomb)) << (bpi - 1);
1455 // Signal the terminal big_tomb_bits element.
1456 et.big_tomb_bits |= @as(u32, 1) << 31;
1457
1458 et.analysis.storeTombBits(et.inst, et.tomb_bits);
1459 const extra_index = @intCast(u32, et.analysis.extra.items.len);
1460 try et.analysis.extra.ensureUnusedCapacity(et.analysis.gpa, et.big_tomb_bits_extra.items.len + 1);
1461 try et.analysis.special.put(et.analysis.gpa, et.inst, extra_index);
1462 et.analysis.extra.appendSliceAssumeCapacity(et.big_tomb_bits_extra.items);
1463 et.analysis.extra.appendAssumeCapacity(et.big_tomb_bits);
1464 }
1625 log.debug("[{}] %{}: block live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });
14651626
1466 fn deinit(et: *ExtraTombs) void {
1467 et.big_tomb_bits_extra.deinit(et.analysis.gpa);
1468 }
1469};
1627 for (breaks) |block_inst| {
1628 // We might break to this block, so include every operand that the block needs alive
1629 const block_scope = data.block_scopes.get(block_inst).?;
14701630
1471/// Remove any deaths invalidated by the deaths from an enclosing `loop`. Reshuffling deaths stored
1472/// in `extra` causes it to become non-dense, but that's fine - we won't remove too much data.
1473/// Making it dense would be a lot more work - it'd require recomputing every index in `special`.
1474fn removeDeaths(
1475 a: *Analysis,
1476 to_remove: *std.AutoHashMapUnmanaged(Air.Inst.Index, void),
1477 body: []const Air.Inst.Index,
1478) error{OutOfMemory}!void {
1479 for (body) |inst| {
1480 try removeInstDeaths(a, to_remove, inst);
1631 var it = block_scope.live_set.keyIterator();
1632 while (it.next()) |key| {
1633 const alive = key.*;
1634 try data.live_set.put(gpa, alive, {});
1635 }
1636 }
1637
1638 try analyzeBody(a, pass, data, body);
1639 },
14811640 }
14821641}
14831642
1484fn removeInstDeaths(
1643/// Despite its name, this function is used for analysis of not only `cond_br` instructions, but
1644/// also `try` and `try_ptr`, which are highly related. The `inst_type` parameter indicates which
1645/// type of instruction `inst` points to.
1646fn analyzeInstCondBr(
14851647 a: *Analysis,
1486 to_remove: *std.AutoHashMapUnmanaged(Air.Inst.Index, void),
1648 comptime pass: LivenessPass,
1649 data: *LivenessPassData(pass),
14871650 inst: Air.Inst.Index,
1651 comptime inst_type: enum { cond_br, @"try", try_ptr },
14881652) !void {
1489 const inst_tags = a.air.instructions.items(.tag);
14901653 const inst_datas = a.air.instructions.items(.data);
1654 const gpa = a.gpa;
14911655
1492 switch (inst_tags[inst]) {
1493 .add,
1494 .add_optimized,
1495 .addwrap,
1496 .addwrap_optimized,
1497 .add_sat,
1498 .sub,
1499 .sub_optimized,
1500 .subwrap,
1501 .subwrap_optimized,
1502 .sub_sat,
1503 .mul,
1504 .mul_optimized,
1505 .mulwrap,
1506 .mulwrap_optimized,
1507 .mul_sat,
1508 .div_float,
1509 .div_float_optimized,
1510 .div_trunc,
1511 .div_trunc_optimized,
1512 .div_floor,
1513 .div_floor_optimized,
1514 .div_exact,
1515 .div_exact_optimized,
1516 .rem,
1517 .rem_optimized,
1518 .mod,
1519 .mod_optimized,
1520 .bit_and,
1521 .bit_or,
1522 .xor,
1523 .cmp_lt,
1524 .cmp_lt_optimized,
1525 .cmp_lte,
1526 .cmp_lte_optimized,
1527 .cmp_eq,
1528 .cmp_eq_optimized,
1529 .cmp_gte,
1530 .cmp_gte_optimized,
1531 .cmp_gt,
1532 .cmp_gt_optimized,
1533 .cmp_neq,
1534 .cmp_neq_optimized,
1535 .bool_and,
1536 .bool_or,
1537 .store,
1538 .array_elem_val,
1539 .slice_elem_val,
1540 .ptr_elem_val,
1541 .shl,
1542 .shl_exact,
1543 .shl_sat,
1544 .shr,
1545 .shr_exact,
1546 .atomic_store_unordered,
1547 .atomic_store_monotonic,
1548 .atomic_store_release,
1549 .atomic_store_seq_cst,
1550 .set_union_tag,
1551 .min,
1552 .max,
1553 => {
1554 const o = inst_datas[inst].bin_op;
1555 removeOperandDeaths(a, to_remove, inst, .{ o.lhs, o.rhs, .none });
1556 },
1656 const extra = switch (inst_type) {
1657 .cond_br => a.air.extraData(Air.CondBr, inst_datas[inst].pl_op.payload),
1658 .@"try" => a.air.extraData(Air.Try, inst_datas[inst].pl_op.payload),
1659 .try_ptr => a.air.extraData(Air.TryPtr, inst_datas[inst].ty_pl.payload),
1660 };
15571661
1558 .vector_store_elem => {
1559 const o = inst_datas[inst].vector_store_elem;
1560 const extra = a.air.extraData(Air.Bin, o.payload).data;
1561 removeOperandDeaths(a, to_remove, inst, .{ o.vector_ptr, extra.lhs, extra.rhs });
1562 },
1662 const condition = switch (inst_type) {
1663 .cond_br, .@"try" => inst_datas[inst].pl_op.operand,
1664 .try_ptr => extra.data.ptr,
1665 };
15631666
1564 .arg,
1565 .alloc,
1566 .ret_ptr,
1567 .constant,
1568 .const_ty,
1569 .trap,
1570 .breakpoint,
1571 .dbg_stmt,
1572 .dbg_inline_begin,
1573 .dbg_inline_end,
1574 .dbg_block_begin,
1575 .dbg_block_end,
1576 .unreach,
1577 .fence,
1578 .ret_addr,
1579 .frame_addr,
1580 .wasm_memory_size,
1581 .err_return_trace,
1582 .save_err_return_trace_index,
1583 .c_va_start,
1584 .work_item_id,
1585 .work_group_size,
1586 .work_group_id,
1587 => {},
1667 const then_body = switch (inst_type) {
1668 .cond_br => a.air.extra[extra.end..][0..extra.data.then_body_len],
1669 else => {}, // we won't use this
1670 };
15881671
1589 .not,
1590 .bitcast,
1591 .load,
1592 .fpext,
1593 .fptrunc,
1594 .intcast,
1595 .trunc,
1596 .optional_payload,
1597 .optional_payload_ptr,
1598 .optional_payload_ptr_set,
1599 .errunion_payload_ptr_set,
1600 .wrap_optional,
1601 .unwrap_errunion_payload,
1602 .unwrap_errunion_err,
1603 .unwrap_errunion_payload_ptr,
1604 .unwrap_errunion_err_ptr,
1605 .wrap_errunion_payload,
1606 .wrap_errunion_err,
1607 .slice_ptr,
1608 .slice_len,
1609 .ptr_slice_len_ptr,
1610 .ptr_slice_ptr_ptr,
1611 .struct_field_ptr_index_0,
1612 .struct_field_ptr_index_1,
1613 .struct_field_ptr_index_2,
1614 .struct_field_ptr_index_3,
1615 .array_to_slice,
1616 .float_to_int,
1617 .float_to_int_optimized,
1618 .int_to_float,
1619 .get_union_tag,
1620 .clz,
1621 .ctz,
1622 .popcount,
1623 .byte_swap,
1624 .bit_reverse,
1625 .splat,
1626 .error_set_has_value,
1627 .addrspace_cast,
1628 .c_va_arg,
1629 .c_va_copy,
1630 => {
1631 const o = inst_datas[inst].ty_op;
1632 removeOperandDeaths(a, to_remove, inst, .{ o.operand, .none, .none });
1633 },
1672 const else_body = switch (inst_type) {
1673 .cond_br => a.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len],
1674 .@"try", .try_ptr => a.air.extra[extra.end..][0..extra.data.body_len],
1675 };
16341676
1635 .is_null,
1636 .is_non_null,
1637 .is_null_ptr,
1638 .is_non_null_ptr,
1639 .is_err,
1640 .is_non_err,
1641 .is_err_ptr,
1642 .is_non_err_ptr,
1643 .ptrtoint,
1644 .bool_to_int,
1645 .ret,
1646 .ret_load,
1647 .is_named_enum_value,
1648 .tag_name,
1649 .error_name,
1650 .sqrt,
1651 .sin,
1652 .cos,
1653 .tan,
1654 .exp,
1655 .exp2,
1656 .log,
1657 .log2,
1658 .log10,
1659 .fabs,
1660 .floor,
1661 .ceil,
1662 .round,
1663 .trunc_float,
1664 .neg,
1665 .neg_optimized,
1666 .cmp_lt_errors_len,
1667 .set_err_return_trace,
1668 .c_va_end,
1669 => {
1670 const operand = inst_datas[inst].un_op;
1671 removeOperandDeaths(a, to_remove, inst, .{ operand, .none, .none });
1672 },
1677 switch (pass) {
1678 .loop_analysis => {
1679 switch (inst_type) {
1680 .cond_br => try analyzeBody(a, pass, data, then_body),
1681 .@"try", .try_ptr => {},
1682 }
1683 try analyzeBody(a, pass, data, else_body);
1684 },
1685
1686 .main_analysis => {
1687 var then_info: ControlBranchInfo = switch (inst_type) {
1688 .cond_br => try analyzeBodyResetBranch(a, pass, data, then_body),
1689 .@"try", .try_ptr => blk: {
1690 var branch_deaths = try data.branch_deaths.clone(gpa);
1691 errdefer branch_deaths.deinit(gpa);
1692 var live_set = try data.live_set.clone(gpa);
1693 errdefer live_set.deinit(gpa);
1694 break :blk .{
1695 .branch_deaths = branch_deaths,
1696 .live_set = live_set,
1697 };
1698 },
1699 };
1700 defer then_info.branch_deaths.deinit(gpa);
1701 defer then_info.live_set.deinit(gpa);
1702
1703 // If this is a `try`, the "then body" (rest of the branch) might have referenced our
1704 // result. If so, we want to avoid this value being considered live while analyzing the
1705 // else branch.
1706 switch (inst_type) {
1707 .cond_br => {},
1708 .@"try", .try_ptr => _ = data.live_set.remove(inst),
1709 }
16731710
1674 .add_with_overflow,
1675 .sub_with_overflow,
1676 .mul_with_overflow,
1677 .shl_with_overflow,
1678 .ptr_add,
1679 .ptr_sub,
1680 .ptr_elem_ptr,
1681 .slice_elem_ptr,
1682 .slice,
1683 => {
1684 const ty_pl = inst_datas[inst].ty_pl;
1685 const extra = a.air.extraData(Air.Bin, ty_pl.payload).data;
1686 removeOperandDeaths(a, to_remove, inst, .{ extra.lhs, extra.rhs, .none });
1687 },
1711 try analyzeBody(a, pass, data, else_body);
1712 var else_info: ControlBranchInfo = .{
1713 .branch_deaths = data.branch_deaths.move(),
1714 .live_set = data.live_set.move(),
1715 };
1716 defer else_info.branch_deaths.deinit(gpa);
1717 defer else_info.live_set.deinit(gpa);
16881718
1689 .dbg_var_ptr,
1690 .dbg_var_val,
1691 => {
1692 const operand = inst_datas[inst].pl_op.operand;
1693 removeOperandDeaths(a, to_remove, inst, .{ operand, .none, .none });
1694 },
1719 // Any queued deaths shared between both branches can be queued for us instead
1720 {
1721 var it = then_info.branch_deaths.keyIterator();
1722 while (it.next()) |key| {
1723 const death = key.*;
1724 if (else_info.branch_deaths.remove(death)) {
1725 // We'll remove it from then_deaths below
1726 try data.branch_deaths.put(gpa, death, {});
1727 }
1728 }
1729 log.debug("[{}] %{}: bubbled deaths {}", .{ pass, inst, fmtInstSet(&data.branch_deaths) });
1730 it = data.branch_deaths.keyIterator();
1731 while (it.next()) |key| {
1732 const death = key.*;
1733 assert(then_info.branch_deaths.remove(death));
1734 }
1735 }
16951736
1696 .prefetch => {
1697 const prefetch = inst_datas[inst].prefetch;
1698 removeOperandDeaths(a, to_remove, inst, .{ prefetch.ptr, .none, .none });
1699 },
1737 log.debug("[{}] %{}: remaining 'then' branch deaths are {}", .{ pass, inst, fmtInstSet(&then_info.branch_deaths) });
1738 log.debug("[{}] %{}: remaining 'else' branch deaths are {}", .{ pass, inst, fmtInstSet(&else_info.branch_deaths) });
17001739
1701 .call, .call_always_tail, .call_never_tail, .call_never_inline => {
1702 const inst_data = inst_datas[inst].pl_op;
1703 const callee = inst_data.operand;
1704 const extra = a.air.extraData(Air.Call, inst_data.payload);
1705 const args = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..extra.data.args_len]);
1740 // Deaths that occur in one branch but not another need to be made to occur at the start
1741 // of the other branch.
17061742
1707 var death_remover = BigTombDeathRemover.init(a, to_remove, inst);
1708 death_remover.feed(callee);
1709 for (args) |operand| {
1710 death_remover.feed(operand);
1711 }
1712 death_remover.finish();
1713 },
1714 .select => {
1715 const pl_op = inst_datas[inst].pl_op;
1716 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
1717 removeOperandDeaths(a, to_remove, inst, .{ pl_op.operand, extra.lhs, extra.rhs });
1718 },
1719 .shuffle => {
1720 const extra = a.air.extraData(Air.Shuffle, inst_datas[inst].ty_pl.payload).data;
1721 removeOperandDeaths(a, to_remove, inst, .{ extra.a, extra.b, .none });
1722 },
1723 .reduce, .reduce_optimized => {
1724 const reduce = inst_datas[inst].reduce;
1725 removeOperandDeaths(a, to_remove, inst, .{ reduce.operand, .none, .none });
1726 },
1727 .cmp_vector, .cmp_vector_optimized => {
1728 const extra = a.air.extraData(Air.VectorCmp, inst_datas[inst].ty_pl.payload).data;
1729 removeOperandDeaths(a, to_remove, inst, .{ extra.lhs, extra.rhs, .none });
1730 },
1731 .aggregate_init => {
1732 const ty_pl = inst_datas[inst].ty_pl;
1733 const aggregate_ty = a.air.getRefType(ty_pl.ty);
1734 const len = @intCast(usize, aggregate_ty.arrayLen());
1735 const elements = @ptrCast([]const Air.Inst.Ref, a.air.extra[ty_pl.payload..][0..len]);
1743 var then_mirrored_deaths: std.ArrayListUnmanaged(Air.Inst.Index) = .{};
1744 defer then_mirrored_deaths.deinit(gpa);
17361745
1737 var death_remover = BigTombDeathRemover.init(a, to_remove, inst);
1738 for (elements) |elem| {
1739 death_remover.feed(elem);
1740 }
1741 death_remover.finish();
1742 },
1743 .union_init => {
1744 const extra = a.air.extraData(Air.UnionInit, inst_datas[inst].ty_pl.payload).data;
1745 removeOperandDeaths(a, to_remove, inst, .{ extra.init, .none, .none });
1746 },
1747 .struct_field_ptr, .struct_field_val => {
1748 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
1749 removeOperandDeaths(a, to_remove, inst, .{ extra.struct_operand, .none, .none });
1750 },
1751 .field_parent_ptr => {
1752 const extra = a.air.extraData(Air.FieldParentPtr, inst_datas[inst].ty_pl.payload).data;
1753 removeOperandDeaths(a, to_remove, inst, .{ extra.field_ptr, .none, .none });
1754 },
1755 .cmpxchg_strong, .cmpxchg_weak => {
1756 const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data;
1757 removeOperandDeaths(a, to_remove, inst, .{ extra.ptr, extra.expected_value, extra.new_value });
1758 },
1759 .mul_add => {
1760 const pl_op = inst_datas[inst].pl_op;
1761 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
1762 removeOperandDeaths(a, to_remove, inst, .{ extra.lhs, extra.rhs, pl_op.operand });
1763 },
1764 .atomic_load => {
1765 const ptr = inst_datas[inst].atomic_load.ptr;
1766 removeOperandDeaths(a, to_remove, inst, .{ ptr, .none, .none });
1767 },
1768 .atomic_rmw => {
1769 const pl_op = inst_datas[inst].pl_op;
1770 const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;
1771 removeOperandDeaths(a, to_remove, inst, .{ pl_op.operand, extra.operand, .none });
1772 },
1773 .memset,
1774 .memcpy,
1775 => {
1776 const pl_op = inst_datas[inst].pl_op;
1777 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
1778 removeOperandDeaths(a, to_remove, inst, .{ pl_op.operand, extra.lhs, extra.rhs });
1779 },
1746 var else_mirrored_deaths: std.ArrayListUnmanaged(Air.Inst.Index) = .{};
1747 defer else_mirrored_deaths.deinit(gpa);
17801748
1781 .br => {
1782 const br = inst_datas[inst].br;
1783 removeOperandDeaths(a, to_remove, inst, .{ br.operand, .none, .none });
1784 },
1785 .assembly => {
1786 const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload);
1787 var extra_i: usize = extra.end;
1788 const outputs = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]);
1789 extra_i += outputs.len;
1790 const inputs = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]);
1791 extra_i += inputs.len;
1749 // Note: this invalidates `else_info.live_set`, but expands `then_info.live_set` to
1750 // be their union
1751 {
1752 var it = then_info.live_set.keyIterator();
1753 while (it.next()) |key| {
1754 const death = key.*;
1755 if (else_info.live_set.remove(death)) continue; // removing makes the loop below faster
1756 if (else_info.branch_deaths.contains(death)) continue;
1757
1758 // If this is a `try`, the "then body" (rest of the branch) might have
1759 // referenced our result. We want to avoid killing this value in the else branch
1760 // if that's the case, since it only exists in the (fake) then branch.
1761 switch (inst_type) {
1762 .cond_br => {},
1763 .@"try", .try_ptr => if (death == inst) continue,
1764 }
17921765
1793 var death_remover = BigTombDeathRemover.init(a, to_remove, inst);
1794 for (outputs) |output| {
1795 if (output != .none) {
1796 death_remover.feed(output);
1766 try else_mirrored_deaths.append(gpa, death);
1767 }
1768 // Since we removed common stuff above, `else_info.live_set` is now only operands
1769 // which are *only* alive in the else branch
1770 it = else_info.live_set.keyIterator();
1771 while (it.next()) |key| {
1772 const death = key.*;
1773 if (!then_info.branch_deaths.contains(death)) {
1774 try then_mirrored_deaths.append(gpa, death);
1775 }
1776 // Make `then_info.live_set` contain the full live set (i.e. union of both)
1777 try then_info.live_set.put(gpa, death, {});
17971778 }
17981779 }
1799 for (inputs) |input| {
1800 death_remover.feed(input);
1801 }
1802 death_remover.finish();
1803 },
1804 .block => {
1805 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
1806 const body = a.air.extra[extra.end..][0..extra.data.body_len];
1807 try removeDeaths(a, to_remove, body);
1808 },
1809 .loop => {
1810 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
1811 const body = a.air.extra[extra.end..][0..extra.data.body_len];
18121780
1813 const liveness_extra_idx = a.special.get(inst) orelse {
1814 try removeDeaths(a, to_remove, body);
1815 return;
1816 };
1781 log.debug("[{}] %{}: 'then' branch mirrored deaths are {}", .{ pass, inst, fmtInstList(then_mirrored_deaths.items) });
1782 log.debug("[{}] %{}: 'else' branch mirrored deaths are {}", .{ pass, inst, fmtInstList(else_mirrored_deaths.items) });
18171783
1818 const death_count = a.extra.items[liveness_extra_idx];
1819 var deaths = a.extra.items[liveness_extra_idx + 1 ..][0..death_count];
1784 data.live_set.deinit(gpa);
1785 data.live_set = then_info.live_set.move();
18201786
1821 // Remove any deaths in `to_remove` from this loop's deaths
1822 deaths.len = removeExtraDeaths(to_remove, deaths);
1823 a.extra.items[liveness_extra_idx] = @intCast(u32, deaths.len);
1787 log.debug("[{}] %{}: new live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });
18241788
1825 // Temporarily add any deaths of ours to `to_remove`
1826 try to_remove.ensureUnusedCapacity(a.gpa, @intCast(u32, deaths.len));
1827 for (deaths) |d| {
1828 to_remove.putAssumeCapacity(d, {});
1789 // Write the branch deaths to `extra`
1790 const then_death_count = then_info.branch_deaths.count() + @intCast(u32, then_mirrored_deaths.items.len);
1791 const else_death_count = else_info.branch_deaths.count() + @intCast(u32, else_mirrored_deaths.items.len);
1792
1793 try a.extra.ensureUnusedCapacity(gpa, std.meta.fields(CondBr).len + then_death_count + else_death_count);
1794 const extra_index = a.addExtraAssumeCapacity(CondBr{
1795 .then_death_count = then_death_count,
1796 .else_death_count = else_death_count,
1797 });
1798 a.extra.appendSliceAssumeCapacity(then_mirrored_deaths.items);
1799 {
1800 var it = then_info.branch_deaths.keyIterator();
1801 while (it.next()) |key| a.extra.appendAssumeCapacity(key.*);
18291802 }
1830 try removeDeaths(a, to_remove, body);
1831 for (deaths) |d| {
1832 _ = to_remove.remove(d);
1803 a.extra.appendSliceAssumeCapacity(else_mirrored_deaths.items);
1804 {
1805 var it = else_info.branch_deaths.keyIterator();
1806 while (it.next()) |key| a.extra.appendAssumeCapacity(key.*);
18331807 }
1808 try a.special.put(gpa, inst, extra_index);
18341809 },
1835 .@"try" => {
1836 const pl_op = inst_datas[inst].pl_op;
1837 const extra = a.air.extraData(Air.Try, pl_op.payload);
1838 const body = a.air.extra[extra.end..][0..extra.data.body_len];
1839 try removeDeaths(a, to_remove, body);
1840 removeOperandDeaths(a, to_remove, inst, .{ pl_op.operand, .none, .none });
1841 },
1842 .try_ptr => {
1843 const extra = a.air.extraData(Air.TryPtr, inst_datas[inst].ty_pl.payload);
1844 const body = a.air.extra[extra.end..][0..extra.data.body_len];
1845 try removeDeaths(a, to_remove, body);
1846 removeOperandDeaths(a, to_remove, inst, .{ extra.data.ptr, .none, .none });
1847 },
1848 .cond_br => {
1849 const inst_data = inst_datas[inst].pl_op;
1850 const condition = inst_data.operand;
1851 const extra = a.air.extraData(Air.CondBr, inst_data.payload);
1852 const then_body = a.air.extra[extra.end..][0..extra.data.then_body_len];
1853 const else_body = a.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1854
1855 if (a.special.get(inst)) |liveness_extra_idx| {
1856 const then_death_count = a.extra.items[liveness_extra_idx + 0];
1857 const else_death_count = a.extra.items[liveness_extra_idx + 1];
1858 var then_deaths = a.extra.items[liveness_extra_idx + 2 ..][0..then_death_count];
1859 var else_deaths = a.extra.items[liveness_extra_idx + 2 + then_death_count ..][0..else_death_count];
1860
1861 const new_then_death_count = removeExtraDeaths(to_remove, then_deaths);
1862 const new_else_death_count = removeExtraDeaths(to_remove, else_deaths);
1863
1864 a.extra.items[liveness_extra_idx + 0] = new_then_death_count;
1865 a.extra.items[liveness_extra_idx + 1] = new_else_death_count;
1866
1867 if (new_then_death_count < then_death_count) {
1868 // `else` deaths need to be moved earlier in `extra`
1869 const src = a.extra.items[liveness_extra_idx + 2 + then_death_count ..];
1870 const dest = a.extra.items[liveness_extra_idx + 2 + new_then_death_count ..];
1871 std.mem.copy(u32, dest, src[0..new_else_death_count]);
1872 }
1873 }
1810 }
18741811
1875 try removeDeaths(a, to_remove, then_body);
1876 try removeDeaths(a, to_remove, else_body);
1812 try analyzeOperands(a, pass, data, inst, .{ condition, .none, .none });
1813}
1814
1815fn analyzeInstSwitchBr(
1816 a: *Analysis,
1817 comptime pass: LivenessPass,
1818 data: *LivenessPassData(pass),
1819 inst: Air.Inst.Index,
1820) !void {
1821 const inst_datas = a.air.instructions.items(.data);
1822 const pl_op = inst_datas[inst].pl_op;
1823 const condition = pl_op.operand;
1824 const switch_br = a.air.extraData(Air.SwitchBr, pl_op.payload);
1825 const gpa = a.gpa;
1826 const ncases = switch_br.data.cases_len;
18771827
1878 removeOperandDeaths(a, to_remove, inst, .{ condition, .none, .none });
1828 switch (pass) {
1829 .loop_analysis => {
1830 var air_extra_index: usize = switch_br.end;
1831 for (0..ncases) |_| {
1832 const case = a.air.extraData(Air.SwitchBr.Case, air_extra_index);
1833 const case_body = a.air.extra[case.end + case.data.items_len ..][0..case.data.body_len];
1834 air_extra_index = case.end + case.data.items_len + case_body.len;
1835 try analyzeBody(a, pass, data, case_body);
1836 }
1837 { // else
1838 const else_body = a.air.extra[air_extra_index..][0..switch_br.data.else_body_len];
1839 try analyzeBody(a, pass, data, else_body);
1840 }
18791841 },
1880 .switch_br => {
1881 const pl_op = inst_datas[inst].pl_op;
1882 const condition = pl_op.operand;
1883 const switch_br = a.air.extraData(Air.SwitchBr, pl_op.payload);
1842
1843 .main_analysis => {
1844 // This is, all in all, just a messier version of the `cond_br` logic. If you're trying
1845 // to understand it, I encourage looking at `analyzeInstCondBr` first.
1846
1847 const DeathSet = std.AutoHashMapUnmanaged(Air.Inst.Index, void);
1848 const DeathList = std.ArrayListUnmanaged(Air.Inst.Index);
1849
1850 var case_infos = try gpa.alloc(ControlBranchInfo, ncases + 1); // +1 for else
1851 defer gpa.free(case_infos);
1852
1853 std.mem.set(ControlBranchInfo, case_infos, .{});
1854 defer for (case_infos) |*info| {
1855 info.branch_deaths.deinit(gpa);
1856 info.live_set.deinit(gpa);
1857 };
18841858
18851859 var air_extra_index: usize = switch_br.end;
1886 for (0..switch_br.data.cases_len) |_| {
1860 for (case_infos[0..ncases]) |*info| {
18871861 const case = a.air.extraData(Air.SwitchBr.Case, air_extra_index);
18881862 const case_body = a.air.extra[case.end + case.data.items_len ..][0..case.data.body_len];
18891863 air_extra_index = case.end + case.data.items_len + case_body.len;
1890 try removeDeaths(a, to_remove, case_body);
1864 info.* = try analyzeBodyResetBranch(a, pass, data, case_body);
18911865 }
18921866 { // else
18931867 const else_body = a.air.extra[air_extra_index..][0..switch_br.data.else_body_len];
1894 try removeDeaths(a, to_remove, else_body);
1868 try analyzeBody(a, pass, data, else_body);
1869 case_infos[ncases] = .{
1870 .branch_deaths = data.branch_deaths.move(),
1871 .live_set = data.live_set.move(),
1872 };
18951873 }
18961874
1897 if (a.special.get(inst)) |liveness_extra_idx| {
1898 const else_death_count = a.extra.items[liveness_extra_idx];
1899 var read_idx = liveness_extra_idx + 1;
1900 var write_idx = read_idx; // write_idx <= read_idx always
1901 for (0..switch_br.data.cases_len) |_| {
1902 const case_death_count = a.extra.items[read_idx];
1903 const case_deaths = a.extra.items[read_idx + 1 ..][0..case_death_count];
1904 const new_death_count = removeExtraDeaths(to_remove, case_deaths);
1905 a.extra.items[write_idx] = new_death_count;
1906 if (write_idx < read_idx) {
1907 std.mem.copy(u32, a.extra.items[write_idx + 1 ..], a.extra.items[read_idx + 1 ..][0..new_death_count]);
1875 // Queued deaths common to all cases can be bubbled up
1876 {
1877 // We can't remove from the set we're iterating over, so we'll store the shared deaths here
1878 // temporarily to remove them
1879 var shared_deaths: DeathSet = .{};
1880 defer shared_deaths.deinit(gpa);
1881
1882 var it = case_infos[0].branch_deaths.keyIterator();
1883 while (it.next()) |key| {
1884 const death = key.*;
1885 for (case_infos[1..]) |*info| {
1886 if (!info.branch_deaths.contains(death)) break;
1887 } else try shared_deaths.put(gpa, death, {});
1888 }
1889
1890 log.debug("[{}] %{}: bubbled deaths {}", .{ pass, inst, fmtInstSet(&shared_deaths) });
1891
1892 try data.branch_deaths.ensureUnusedCapacity(gpa, shared_deaths.count());
1893 it = shared_deaths.keyIterator();
1894 while (it.next()) |key| {
1895 const death = key.*;
1896 data.branch_deaths.putAssumeCapacity(death, {});
1897 for (case_infos) |*info| {
1898 _ = info.branch_deaths.remove(death);
19081899 }
1909 read_idx += 1 + case_death_count;
1910 write_idx += 1 + new_death_count;
19111900 }
1912 const else_deaths = a.extra.items[read_idx..][0..else_death_count];
1913 const new_else_death_count = removeExtraDeaths(to_remove, else_deaths);
1914 a.extra.items[liveness_extra_idx] = new_else_death_count;
1915 if (write_idx < read_idx) {
1916 std.mem.copy(u32, a.extra.items[write_idx..], a.extra.items[read_idx..][0..new_else_death_count]);
1901
1902 for (case_infos, 0..) |*info, i| {
1903 log.debug("[{}] %{}: case {} remaining branch deaths are {}", .{ pass, inst, i, fmtInstSet(&info.branch_deaths) });
19171904 }
19181905 }
19191906
1920 removeOperandDeaths(a, to_remove, inst, .{ condition, .none, .none });
1921 },
1922 .wasm_memory_grow => {
1923 const pl_op = inst_datas[inst].pl_op;
1924 removeOperandDeaths(a, to_remove, inst, .{ pl_op.operand, .none, .none });
1907 const mirrored_deaths = try gpa.alloc(DeathList, ncases + 1);
1908 defer gpa.free(mirrored_deaths);
1909
1910 std.mem.set(DeathList, mirrored_deaths, .{});
1911 defer for (mirrored_deaths) |*md| md.deinit(gpa);
1912
1913 {
1914 var all_alive: DeathSet = .{};
1915 defer all_alive.deinit(gpa);
1916
1917 for (case_infos) |*info| {
1918 try all_alive.ensureUnusedCapacity(gpa, info.live_set.count());
1919 var it = info.live_set.keyIterator();
1920 while (it.next()) |key| {
1921 const alive = key.*;
1922 all_alive.putAssumeCapacity(alive, {});
1923 }
1924 }
1925
1926 for (mirrored_deaths, case_infos) |*mirrored, *info| {
1927 var it = all_alive.keyIterator();
1928 while (it.next()) |key| {
1929 const alive = key.*;
1930 if (!info.live_set.contains(alive) and !info.branch_deaths.contains(alive)) {
1931 // Should die at the start of this branch
1932 try mirrored.append(gpa, alive);
1933 }
1934 }
1935 }
1936
1937 for (mirrored_deaths, 0..) |mirrored, i| {
1938 log.debug("[{}] %{}: case {} mirrored deaths are {}", .{ pass, inst, i, fmtInstList(mirrored.items) });
1939 }
1940
1941 data.live_set.deinit(gpa);
1942 data.live_set = all_alive.move();
1943
1944 log.debug("[{}] %{}: new live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });
1945 }
1946
1947 const else_death_count = case_infos[ncases].branch_deaths.count() + @intCast(u32, mirrored_deaths[ncases].items.len);
1948
1949 const extra_index = try a.addExtra(SwitchBr{
1950 .else_death_count = else_death_count,
1951 });
1952 for (mirrored_deaths[0..ncases], case_infos[0..ncases]) |mirrored, info| {
1953 const num = info.branch_deaths.count() + @intCast(u32, mirrored.items.len);
1954 try a.extra.ensureUnusedCapacity(gpa, num + 1);
1955 a.extra.appendAssumeCapacity(num);
1956 a.extra.appendSliceAssumeCapacity(mirrored.items);
1957 {
1958 var it = info.branch_deaths.keyIterator();
1959 while (it.next()) |key| a.extra.appendAssumeCapacity(key.*);
1960 }
1961 }
1962 try a.extra.ensureUnusedCapacity(gpa, else_death_count);
1963 a.extra.appendSliceAssumeCapacity(mirrored_deaths[ncases].items);
1964 {
1965 var it = case_infos[ncases].branch_deaths.keyIterator();
1966 while (it.next()) |key| a.extra.appendAssumeCapacity(key.*);
1967 }
1968 try a.special.put(gpa, inst, extra_index);
19251969 },
19261970 }
1971
1972 try analyzeOperands(a, pass, data, inst, .{ condition, .none, .none });
19271973}
19281974
1929fn removeOperandDeaths(
1930 a: *Analysis,
1931 to_remove: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void),
1932 inst: Air.Inst.Index,
1933 operands: [bpi - 1]Air.Inst.Ref,
1934) void {
1935 const usize_index = (inst * bpi) / @bitSizeOf(usize);
1975fn AnalyzeBigOperands(comptime pass: LivenessPass) type {
1976 return struct {
1977 a: *Analysis,
1978 data: *LivenessPassData(pass),
1979 inst: Air.Inst.Index,
1980
1981 operands_remaining: u32,
1982 small: [bpi - 1]Air.Inst.Ref = .{.none} ** (bpi - 1),
1983 extra_tombs: []u32,
1984
1985 // Only used in `LivenessPass.main_analysis`
1986 will_die_immediately: bool,
1987
1988 const Self = @This();
1989
1990 fn init(
1991 a: *Analysis,
1992 data: *LivenessPassData(pass),
1993 inst: Air.Inst.Index,
1994 total_operands: usize,
1995 ) !Self {
1996 const extra_operands = @intCast(u32, total_operands) -| (bpi - 1);
1997 const max_extra_tombs = (extra_operands + 30) / 31;
1998
1999 const extra_tombs: []u32 = switch (pass) {
2000 .loop_analysis => &.{},
2001 .main_analysis => try a.gpa.alloc(u32, max_extra_tombs),
2002 };
2003 errdefer a.gpa.free(extra_tombs);
19362004
1937 const cur_tomb = @truncate(Bpi, a.tomb_bits[usize_index] >>
1938 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi));
2005 std.mem.set(u32, extra_tombs, 0);
19392006
1940 var toggle_bits: Bpi = 0;
2007 const will_die_immediately: bool = switch (pass) {
2008 .loop_analysis => false, // track everything, since we don't have full liveness information yet
2009 .main_analysis => data.branch_deaths.contains(inst) and !data.live_set.contains(inst),
2010 };
19412011
1942 for (operands, 0..) |op_ref, i| {
1943 const mask = @as(Bpi, 1) << @intCast(OperandInt, i);
1944 const op_int = @enumToInt(op_ref);
1945 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
1946 const operand: Air.Inst.Index = op_int - @intCast(u32, Air.Inst.Ref.typed_value_map.len);
1947 if ((cur_tomb & mask) != 0 and to_remove.contains(operand)) {
1948 log.debug("remove death of %{} in %{}", .{ operand, inst });
1949 toggle_bits ^= mask;
2012 return .{
2013 .a = a,
2014 .data = data,
2015 .inst = inst,
2016 .operands_remaining = @intCast(u32, total_operands),
2017 .extra_tombs = extra_tombs,
2018 .will_die_immediately = will_die_immediately,
2019 };
19502020 }
1951 }
19522021
1953 a.tomb_bits[usize_index] ^= @as(usize, toggle_bits) <<
1954 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi);
1955}
2022 /// Must be called with operands in reverse order.
2023 fn feed(big: *Self, op_ref: Air.Inst.Ref) !void {
2024 // Note that after this, `operands_remaining` becomes the index of the current operand
2025 big.operands_remaining -= 1;
19562026
1957fn removeExtraDeaths(
1958 to_remove: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void),
1959 deaths: []Air.Inst.Index,
1960) u32 {
1961 var new_len = @intCast(u32, deaths.len);
1962 var i: usize = 0;
1963 while (i < new_len) {
1964 if (to_remove.contains(deaths[i])) {
1965 log.debug("remove extra death of %{}", .{deaths[i]});
1966 deaths[i] = deaths[new_len - 1];
1967 new_len -= 1;
1968 } else {
1969 i += 1;
1970 }
1971 }
1972 return new_len;
1973}
2027 if (big.operands_remaining < bpi - 1) {
2028 big.small[big.operands_remaining] = op_ref;
2029 return;
2030 }
19742031
1975const BigTombDeathRemover = struct {
1976 a: *Analysis,
1977 to_remove: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void),
1978 inst: Air.Inst.Index,
2032 const operand = Air.refToIndex(op_ref) orelse return;
19792033
1980 operands: [bpi - 1]Air.Inst.Ref = .{.none} ** (bpi - 1),
1981 next_oper: OperandInt = 0,
2034 // Don't compute any liveness for constants
2035 const inst_tags = big.a.air.instructions.items(.tag);
2036 switch (inst_tags[operand]) {
2037 .constant, .const_ty => return,
2038 else => {},
2039 }
19822040
1983 bit_index: u32 = 0,
1984 // Initialized once we finish the small tomb operands: see `feed`
1985 extra_start: u32 = undefined,
1986 extra_offset: u32 = 0,
2041 // If our result is unused and the instruction doesn't need to be lowered, backends will
2042 // skip the lowering of this instruction, so we don't want to record uses of operands.
2043 // That way, we can mark as many instructions as possible unused.
2044 if (big.will_die_immediately and !big.a.air.mustLower(big.inst)) return;
19872045
1988 fn init(a: *Analysis, to_remove: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void), inst: Air.Inst.Index) BigTombDeathRemover {
1989 return .{
1990 .a = a,
1991 .to_remove = to_remove,
1992 .inst = inst,
1993 };
1994 }
2046 const extra_byte = (big.operands_remaining - (bpi - 1)) / 31;
2047 const extra_bit = @intCast(u5, big.operands_remaining - (bpi - 1) - extra_byte * 31);
2048
2049 const gpa = big.a.gpa;
2050
2051 switch (pass) {
2052 .loop_analysis => {
2053 _ = try big.data.live_set.put(gpa, operand, {});
2054 },
19952055
1996 fn feed(dr: *BigTombDeathRemover, operand: Air.Inst.Ref) void {
1997 if (dr.next_oper < bpi - 1) {
1998 dr.operands[dr.next_oper] = operand;
1999 dr.next_oper += 1;
2000 if (dr.next_oper == bpi - 1) {
2001 removeOperandDeaths(dr.a, dr.to_remove, dr.inst, dr.operands);
2002 if (dr.a.special.get(dr.inst)) |idx| dr.extra_start = idx;
2056 .main_analysis => {
2057 if ((try big.data.live_set.fetchPut(gpa, operand, {})) == null) {
2058 log.debug("[{}] %{}: added %{} to live set (operand dies here)", .{ pass, big.inst, operand });
2059 big.extra_tombs[extra_byte] |= @as(u32, 1) << extra_bit;
2060 if (big.data.branch_deaths.remove(operand)) {
2061 log.debug("[{}] %{}: resolved branch death of %{} to this usage", .{ pass, big.inst, operand });
2062 }
2063 }
2064 },
20032065 }
2004 return;
20052066 }
20062067
2007 defer dr.bit_index += 1;
2068 fn finish(big: *Self) !void {
2069 const gpa = big.a.gpa;
2070
2071 std.debug.assert(big.operands_remaining == 0);
2072
2073 switch (pass) {
2074 .loop_analysis => {},
2075
2076 .main_analysis => {
2077 // Note that the MSB is set on the final tomb to indicate the terminal element. This
2078 // allows for an optimisation where we only add as many extra tombs as are needed to
2079 // represent the dying operands. Each pass modifies operand bits and so needs to write
2080 // back, so let's figure out how many extra tombs we really need. Note that we always
2081 // keep at least one.
2082 var num: usize = big.extra_tombs.len;
2083 while (num > 1) {
2084 if (@truncate(u31, big.extra_tombs[num - 1]) != 0) {
2085 // Some operand dies here
2086 break;
2087 }
2088 num -= 1;
2089 }
2090 // Mark final tomb
2091 big.extra_tombs[num - 1] |= @as(u32, 1) << 31;
20082092
2009 const op_int = @enumToInt(operand);
2010 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
2093 const extra_tombs = big.extra_tombs[0..num];
20112094
2012 const op_inst: Air.Inst.Index = op_int - @intCast(u32, Air.Inst.Ref.typed_value_map.len);
2095 const extra_index = @intCast(u32, big.a.extra.items.len);
2096 try big.a.extra.appendSlice(gpa, extra_tombs);
2097 try big.a.special.put(gpa, big.inst, extra_index);
2098 },
2099 }
20132100
2014 while (dr.bit_index - dr.extra_offset * 31 >= 31) {
2015 dr.extra_offset += 1;
2101 try analyzeOperands(big.a, pass, big.data, big.inst, big.small);
20162102 }
2017 const dies = @truncate(u1, dr.a.extra.items[dr.extra_start + dr.extra_offset] >>
2018 @intCast(u5, dr.bit_index - dr.extra_offset * 31)) != 0;
20192103
2020 if (dies and dr.to_remove.contains(op_inst)) {
2021 log.debug("remove big death of %{}", .{op_inst});
2022 dr.a.extra.items[dr.extra_start + dr.extra_offset] ^=
2023 (@as(u32, 1) << @intCast(u5, dr.bit_index - dr.extra_offset * 31));
2104 fn deinit(big: *Self) void {
2105 big.a.gpa.free(big.extra_tombs);
2106 }
2107 };
2108}
2109
2110fn fmtInstSet(set: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void)) FmtInstSet {
2111 return .{ .set = set };
2112}
2113
2114const FmtInstSet = struct {
2115 set: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void),
2116
2117 pub fn format(val: FmtInstSet, comptime _: []const u8, _: std.fmt.FormatOptions, w: anytype) !void {
2118 if (val.set.count() == 0) {
2119 try w.writeAll("[no instructions]");
2120 return;
2121 }
2122 var it = val.set.keyIterator();
2123 try w.print("%{}", .{it.next().?.*});
2124 while (it.next()) |key| {
2125 try w.print(" %{}", .{key.*});
20242126 }
20252127 }
2128};
2129
2130fn fmtInstList(list: []const Air.Inst.Index) FmtInstList {
2131 return .{ .list = list };
2132}
20262133
2027 fn finish(dr: *BigTombDeathRemover) void {
2028 if (dr.next_oper < bpi) {
2029 removeOperandDeaths(dr.a, dr.to_remove, dr.inst, dr.operands);
2134const FmtInstList = struct {
2135 list: []const Air.Inst.Index,
2136
2137 pub fn format(val: FmtInstList, comptime _: []const u8, _: std.fmt.FormatOptions, w: anytype) !void {
2138 if (val.list.len == 0) {
2139 try w.writeAll("[no instructions]");
2140 return;
2141 }
2142 try w.print("%{}", .{val.list[0]});
2143 for (val.list[1..]) |inst| {
2144 try w.print(" %{}", .{inst});
20302145 }
20312146 }
20322147};
src/Liveness/Verify.zig created+610
......@@ -0,0 +1,610 @@
1//! Verifies that liveness information is valid.
2
3gpa: std.mem.Allocator,
4air: Air,
5liveness: Liveness,
6live: LiveMap = .{},
7blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, LiveMap) = .{},
8
9pub const Error = error{ LivenessInvalid, OutOfMemory };
10
11pub fn deinit(self: *Verify) void {
12 self.live.deinit(self.gpa);
13 var block_it = self.blocks.valueIterator();
14 while (block_it.next()) |block| block.deinit(self.gpa);
15 self.blocks.deinit(self.gpa);
16 self.* = undefined;
17}
18
19pub fn verify(self: *Verify) Error!void {
20 self.live.clearRetainingCapacity();
21 self.blocks.clearRetainingCapacity();
22 try self.verifyBody(self.air.getMainBody());
23 // We don't care about `self.live` now, because the loop body was noreturn - everything being dead was checked on `ret` etc
24 assert(self.blocks.count() == 0);
25}
26
27const LiveMap = std.AutoHashMapUnmanaged(Air.Inst.Index, void);
28
29fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
30 const tag = self.air.instructions.items(.tag);
31 const data = self.air.instructions.items(.data);
32 for (body) |inst| {
33 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
34 // This instruction will not be lowered and should be ignored.
35 continue;
36 }
37
38 switch (tag[inst]) {
39 // no operands
40 .arg,
41 .alloc,
42 .ret_ptr,
43 .constant,
44 .const_ty,
45 .breakpoint,
46 .dbg_stmt,
47 .dbg_inline_begin,
48 .dbg_inline_end,
49 .dbg_block_begin,
50 .dbg_block_end,
51 .fence,
52 .ret_addr,
53 .frame_addr,
54 .wasm_memory_size,
55 .err_return_trace,
56 .save_err_return_trace_index,
57 .c_va_start,
58 .work_item_id,
59 .work_group_size,
60 .work_group_id,
61 => try self.verifyInst(inst, .{ .none, .none, .none }),
62
63 .trap, .unreach => {
64 try self.verifyInst(inst, .{ .none, .none, .none });
65 // This instruction terminates the function, so everything should be dead
66 if (self.live.count() > 0) return invalid("%{}: instructions still alive", .{inst});
67 },
68
69 // unary
70 .not,
71 .bitcast,
72 .load,
73 .fpext,
74 .fptrunc,
75 .intcast,
76 .trunc,
77 .optional_payload,
78 .optional_payload_ptr,
79 .optional_payload_ptr_set,
80 .errunion_payload_ptr_set,
81 .wrap_optional,
82 .unwrap_errunion_payload,
83 .unwrap_errunion_err,
84 .unwrap_errunion_payload_ptr,
85 .unwrap_errunion_err_ptr,
86 .wrap_errunion_payload,
87 .wrap_errunion_err,
88 .slice_ptr,
89 .slice_len,
90 .ptr_slice_len_ptr,
91 .ptr_slice_ptr_ptr,
92 .struct_field_ptr_index_0,
93 .struct_field_ptr_index_1,
94 .struct_field_ptr_index_2,
95 .struct_field_ptr_index_3,
96 .array_to_slice,
97 .float_to_int,
98 .float_to_int_optimized,
99 .int_to_float,
100 .get_union_tag,
101 .clz,
102 .ctz,
103 .popcount,
104 .byte_swap,
105 .bit_reverse,
106 .splat,
107 .error_set_has_value,
108 .addrspace_cast,
109 .c_va_arg,
110 .c_va_copy,
111 => {
112 const ty_op = data[inst].ty_op;
113 try self.verifyInst(inst, .{ ty_op.operand, .none, .none });
114 },
115 .is_null,
116 .is_non_null,
117 .is_null_ptr,
118 .is_non_null_ptr,
119 .is_err,
120 .is_non_err,
121 .is_err_ptr,
122 .is_non_err_ptr,
123 .ptrtoint,
124 .bool_to_int,
125 .is_named_enum_value,
126 .tag_name,
127 .error_name,
128 .sqrt,
129 .sin,
130 .cos,
131 .tan,
132 .exp,
133 .exp2,
134 .log,
135 .log2,
136 .log10,
137 .fabs,
138 .floor,
139 .ceil,
140 .round,
141 .trunc_float,
142 .neg,
143 .neg_optimized,
144 .cmp_lt_errors_len,
145 .set_err_return_trace,
146 .c_va_end,
147 => {
148 const un_op = data[inst].un_op;
149 try self.verifyInst(inst, .{ un_op, .none, .none });
150 },
151 .ret,
152 .ret_load,
153 => {
154 const un_op = data[inst].un_op;
155 try self.verifyInst(inst, .{ un_op, .none, .none });
156 // This instruction terminates the function, so everything should be dead
157 if (self.live.count() > 0) return invalid("%{}: instructions still alive", .{inst});
158 },
159 .dbg_var_ptr,
160 .dbg_var_val,
161 .wasm_memory_grow,
162 => {
163 const pl_op = data[inst].pl_op;
164 try self.verifyInst(inst, .{ pl_op.operand, .none, .none });
165 },
166 .prefetch => {
167 const prefetch = data[inst].prefetch;
168 try self.verifyInst(inst, .{ prefetch.ptr, .none, .none });
169 },
170 .reduce,
171 .reduce_optimized,
172 => {
173 const reduce = data[inst].reduce;
174 try self.verifyInst(inst, .{ reduce.operand, .none, .none });
175 },
176 .union_init => {
177 const ty_pl = data[inst].ty_pl;
178 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
179 try self.verifyInst(inst, .{ extra.init, .none, .none });
180 },
181 .struct_field_ptr, .struct_field_val => {
182 const ty_pl = data[inst].ty_pl;
183 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
184 try self.verifyInst(inst, .{ extra.struct_operand, .none, .none });
185 },
186 .field_parent_ptr => {
187 const ty_pl = data[inst].ty_pl;
188 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
189 try self.verifyInst(inst, .{ extra.field_ptr, .none, .none });
190 },
191 .atomic_load => {
192 const atomic_load = data[inst].atomic_load;
193 try self.verifyInst(inst, .{ atomic_load.ptr, .none, .none });
194 },
195
196 // binary
197 .add,
198 .add_optimized,
199 .addwrap,
200 .addwrap_optimized,
201 .add_sat,
202 .sub,
203 .sub_optimized,
204 .subwrap,
205 .subwrap_optimized,
206 .sub_sat,
207 .mul,
208 .mul_optimized,
209 .mulwrap,
210 .mulwrap_optimized,
211 .mul_sat,
212 .div_float,
213 .div_float_optimized,
214 .div_trunc,
215 .div_trunc_optimized,
216 .div_floor,
217 .div_floor_optimized,
218 .div_exact,
219 .div_exact_optimized,
220 .rem,
221 .rem_optimized,
222 .mod,
223 .mod_optimized,
224 .bit_and,
225 .bit_or,
226 .xor,
227 .cmp_lt,
228 .cmp_lt_optimized,
229 .cmp_lte,
230 .cmp_lte_optimized,
231 .cmp_eq,
232 .cmp_eq_optimized,
233 .cmp_gte,
234 .cmp_gte_optimized,
235 .cmp_gt,
236 .cmp_gt_optimized,
237 .cmp_neq,
238 .cmp_neq_optimized,
239 .bool_and,
240 .bool_or,
241 .store,
242 .array_elem_val,
243 .slice_elem_val,
244 .ptr_elem_val,
245 .shl,
246 .shl_exact,
247 .shl_sat,
248 .shr,
249 .shr_exact,
250 .atomic_store_unordered,
251 .atomic_store_monotonic,
252 .atomic_store_release,
253 .atomic_store_seq_cst,
254 .set_union_tag,
255 .min,
256 .max,
257 => {
258 const bin_op = data[inst].bin_op;
259 try self.verifyInst(inst, .{ bin_op.lhs, bin_op.rhs, .none });
260 },
261 .add_with_overflow,
262 .sub_with_overflow,
263 .mul_with_overflow,
264 .shl_with_overflow,
265 .ptr_add,
266 .ptr_sub,
267 .ptr_elem_ptr,
268 .slice_elem_ptr,
269 .slice,
270 => {
271 const ty_pl = data[inst].ty_pl;
272 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
273 try self.verifyInst(inst, .{ extra.lhs, extra.rhs, .none });
274 },
275 .shuffle => {
276 const ty_pl = data[inst].ty_pl;
277 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
278 try self.verifyInst(inst, .{ extra.a, extra.b, .none });
279 },
280 .cmp_vector,
281 .cmp_vector_optimized,
282 => {
283 const ty_pl = data[inst].ty_pl;
284 const extra = self.air.extraData(Air.VectorCmp, ty_pl.payload).data;
285 try self.verifyInst(inst, .{ extra.lhs, extra.rhs, .none });
286 },
287 .atomic_rmw => {
288 const pl_op = data[inst].pl_op;
289 const extra = self.air.extraData(Air.AtomicRmw, pl_op.payload).data;
290 try self.verifyInst(inst, .{ pl_op.operand, extra.operand, .none });
291 },
292
293 // ternary
294 .select => {
295 const pl_op = data[inst].pl_op;
296 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
297 try self.verifyInst(inst, .{ pl_op.operand, extra.lhs, extra.rhs });
298 },
299 .mul_add => {
300 const pl_op = data[inst].pl_op;
301 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
302 try self.verifyInst(inst, .{ extra.lhs, extra.rhs, pl_op.operand });
303 },
304 .vector_store_elem => {
305 const vector_store_elem = data[inst].vector_store_elem;
306 const extra = self.air.extraData(Air.Bin, vector_store_elem.payload).data;
307 try self.verifyInst(inst, .{ vector_store_elem.vector_ptr, extra.lhs, extra.rhs });
308 },
309 .memset,
310 .memcpy,
311 => {
312 const pl_op = data[inst].pl_op;
313 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
314 try self.verifyInst(inst, .{ pl_op.operand, extra.lhs, extra.rhs });
315 },
316 .cmpxchg_strong,
317 .cmpxchg_weak,
318 => {
319 const ty_pl = data[inst].ty_pl;
320 const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
321 try self.verifyInst(inst, .{ extra.ptr, extra.expected_value, extra.new_value });
322 },
323
324 // big tombs
325 .aggregate_init => {
326 const ty_pl = data[inst].ty_pl;
327 const aggregate_ty = self.air.getRefType(ty_pl.ty);
328 const len = @intCast(usize, aggregate_ty.arrayLen());
329 const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
330
331 var bt = self.liveness.iterateBigTomb(inst);
332 for (elements) |element| {
333 try self.verifyOperand(inst, element, bt.feed());
334 }
335 try self.verifyInst(inst, .{ .none, .none, .none });
336 },
337 .call, .call_always_tail, .call_never_tail, .call_never_inline => {
338 const pl_op = data[inst].pl_op;
339 const extra = self.air.extraData(Air.Call, pl_op.payload);
340 const args = @ptrCast(
341 []const Air.Inst.Ref,
342 self.air.extra[extra.end..][0..extra.data.args_len],
343 );
344
345 var bt = self.liveness.iterateBigTomb(inst);
346 try self.verifyOperand(inst, pl_op.operand, bt.feed());
347 for (args) |arg| {
348 try self.verifyOperand(inst, arg, bt.feed());
349 }
350 try self.verifyInst(inst, .{ .none, .none, .none });
351 },
352 .assembly => {
353 const ty_pl = data[inst].ty_pl;
354 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
355 var extra_i = extra.end;
356 const outputs = @ptrCast(
357 []const Air.Inst.Ref,
358 self.air.extra[extra_i..][0..extra.data.outputs_len],
359 );
360 extra_i += outputs.len;
361 const inputs = @ptrCast(
362 []const Air.Inst.Ref,
363 self.air.extra[extra_i..][0..extra.data.inputs_len],
364 );
365 extra_i += inputs.len;
366
367 var bt = self.liveness.iterateBigTomb(inst);
368 for (outputs) |output| {
369 if (output != .none) {
370 try self.verifyOperand(inst, output, bt.feed());
371 }
372 }
373 for (inputs) |input| {
374 try self.verifyOperand(inst, input, bt.feed());
375 }
376 try self.verifyInst(inst, .{ .none, .none, .none });
377 },
378
379 // control flow
380 .@"try" => {
381 const pl_op = data[inst].pl_op;
382 const extra = self.air.extraData(Air.Try, pl_op.payload);
383 const try_body = self.air.extra[extra.end..][0..extra.data.body_len];
384
385 const cond_br_liveness = self.liveness.getCondBr(inst);
386
387 try self.verifyOperand(inst, pl_op.operand, self.liveness.operandDies(inst, 0));
388
389 var live = try self.live.clone(self.gpa);
390 defer live.deinit(self.gpa);
391
392 for (cond_br_liveness.else_deaths) |death| try self.verifyDeath(inst, death);
393 try self.verifyBody(try_body);
394
395 self.live.deinit(self.gpa);
396 self.live = live.move();
397
398 for (cond_br_liveness.then_deaths) |death| try self.verifyDeath(inst, death);
399
400 try self.verifyInst(inst, .{ .none, .none, .none });
401 },
402 .try_ptr => {
403 const ty_pl = data[inst].ty_pl;
404 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
405 const try_body = self.air.extra[extra.end..][0..extra.data.body_len];
406
407 const cond_br_liveness = self.liveness.getCondBr(inst);
408
409 try self.verifyOperand(inst, extra.data.ptr, self.liveness.operandDies(inst, 0));
410
411 var live = try self.live.clone(self.gpa);
412 defer live.deinit(self.gpa);
413
414 for (cond_br_liveness.else_deaths) |death| try self.verifyDeath(inst, death);
415 try self.verifyBody(try_body);
416
417 self.live.deinit(self.gpa);
418 self.live = live.move();
419
420 for (cond_br_liveness.then_deaths) |death| try self.verifyDeath(inst, death);
421
422 try self.verifyInst(inst, .{ .none, .none, .none });
423 },
424 .br => {
425 const br = data[inst].br;
426 const gop = try self.blocks.getOrPut(self.gpa, br.block_inst);
427
428 try self.verifyOperand(inst, br.operand, self.liveness.operandDies(inst, 0));
429 if (gop.found_existing) {
430 try self.verifyMatchingLiveness(br.block_inst, gop.value_ptr.*);
431 } else {
432 gop.value_ptr.* = try self.live.clone(self.gpa);
433 }
434 try self.verifyInst(inst, .{ .none, .none, .none });
435 },
436 .block => {
437 const ty_pl = data[inst].ty_pl;
438 const block_ty = self.air.getRefType(ty_pl.ty);
439 const extra = self.air.extraData(Air.Block, ty_pl.payload);
440 const block_body = self.air.extra[extra.end..][0..extra.data.body_len];
441 const block_liveness = self.liveness.getBlock(inst);
442
443 var orig_live = try self.live.clone(self.gpa);
444 defer orig_live.deinit(self.gpa);
445
446 assert(!self.blocks.contains(inst));
447 try self.verifyBody(block_body);
448
449 // Liveness data after the block body is garbage, but we want to
450 // restore it to verify deaths
451 self.live.deinit(self.gpa);
452 self.live = orig_live.move();
453
454 for (block_liveness.deaths) |death| try self.verifyDeath(inst, death);
455
456 if (block_ty.isNoReturn()) {
457 assert(!self.blocks.contains(inst));
458 } else {
459 var live = self.blocks.fetchRemove(inst).?.value;
460 defer live.deinit(self.gpa);
461
462 try self.verifyMatchingLiveness(inst, live);
463 }
464
465 try self.verifyInst(inst, .{ .none, .none, .none });
466 },
467 .loop => {
468 const ty_pl = data[inst].ty_pl;
469 const extra = self.air.extraData(Air.Block, ty_pl.payload);
470 const loop_body = self.air.extra[extra.end..][0..extra.data.body_len];
471
472 var live = try self.live.clone(self.gpa);
473 defer live.deinit(self.gpa);
474
475 try self.verifyBody(loop_body);
476
477 // The same stuff should be alive after the loop as before it
478 try self.verifyMatchingLiveness(inst, live);
479
480 try self.verifyInst(inst, .{ .none, .none, .none });
481 },
482 .cond_br => {
483 const pl_op = data[inst].pl_op;
484 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
485 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
486 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
487 const cond_br_liveness = self.liveness.getCondBr(inst);
488
489 try self.verifyOperand(inst, pl_op.operand, self.liveness.operandDies(inst, 0));
490
491 var live = try self.live.clone(self.gpa);
492 defer live.deinit(self.gpa);
493
494 for (cond_br_liveness.then_deaths) |death| try self.verifyDeath(inst, death);
495 try self.verifyBody(then_body);
496
497 self.live.deinit(self.gpa);
498 self.live = live.move();
499
500 for (cond_br_liveness.else_deaths) |death| try self.verifyDeath(inst, death);
501 try self.verifyBody(else_body);
502
503 try self.verifyInst(inst, .{ .none, .none, .none });
504 },
505 .switch_br => {
506 const pl_op = data[inst].pl_op;
507 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
508 var extra_index = switch_br.end;
509 var case_i: u32 = 0;
510 const switch_br_liveness = try self.liveness.getSwitchBr(
511 self.gpa,
512 inst,
513 switch_br.data.cases_len + 1,
514 );
515 defer self.gpa.free(switch_br_liveness.deaths);
516
517 try self.verifyOperand(inst, pl_op.operand, self.liveness.operandDies(inst, 0));
518
519 var live = self.live.move();
520 defer live.deinit(self.gpa);
521
522 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
523 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
524 const items = @ptrCast(
525 []const Air.Inst.Ref,
526 self.air.extra[case.end..][0..case.data.items_len],
527 );
528 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
529 extra_index = case.end + items.len + case_body.len;
530
531 self.live.deinit(self.gpa);
532 self.live = try live.clone(self.gpa);
533
534 for (switch_br_liveness.deaths[case_i]) |death| try self.verifyDeath(inst, death);
535 try self.verifyBody(case_body);
536 }
537
538 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
539 if (else_body.len > 0) {
540 self.live.deinit(self.gpa);
541 self.live = try live.clone(self.gpa);
542
543 for (switch_br_liveness.deaths[case_i]) |death| try self.verifyDeath(inst, death);
544 try self.verifyBody(else_body);
545 }
546
547 try self.verifyInst(inst, .{ .none, .none, .none });
548 },
549 }
550 }
551}
552
553fn verifyDeath(self: *Verify, inst: Air.Inst.Index, operand: Air.Inst.Index) Error!void {
554 try self.verifyOperand(inst, Air.indexToRef(operand), true);
555}
556
557fn verifyOperand(self: *Verify, inst: Air.Inst.Index, op_ref: Air.Inst.Ref, dies: bool) Error!void {
558 const operand = Air.refToIndex(op_ref) orelse return;
559 switch (self.air.instructions.items(.tag)[operand]) {
560 .constant, .const_ty => {},
561 else => {
562 if (dies) {
563 if (!self.live.remove(operand)) return invalid("%{}: dead operand %{} reused and killed again", .{ inst, operand });
564 } else {
565 if (!self.live.contains(operand)) return invalid("%{}: dead operand %{} reused", .{ inst, operand });
566 }
567 },
568 }
569}
570
571fn verifyInst(
572 self: *Verify,
573 inst: Air.Inst.Index,
574 operands: [Liveness.bpi - 1]Air.Inst.Ref,
575) Error!void {
576 for (operands, 0..) |operand, operand_index| {
577 const dies = self.liveness.operandDies(inst, @intCast(Liveness.OperandInt, operand_index));
578 try self.verifyOperand(inst, operand, dies);
579 }
580 const tag = self.air.instructions.items(.tag);
581 switch (tag[inst]) {
582 .constant, .const_ty => unreachable,
583 else => {
584 if (self.liveness.isUnused(inst)) {
585 assert(!self.live.contains(inst));
586 } else {
587 try self.live.putNoClobber(self.gpa, inst, {});
588 }
589 },
590 }
591}
592
593fn verifyMatchingLiveness(self: *Verify, block: Air.Inst.Index, live: LiveMap) Error!void {
594 if (self.live.count() != live.count()) return invalid("%{}: different deaths across branches", .{block});
595 var live_it = self.live.keyIterator();
596 while (live_it.next()) |live_inst| if (!live.contains(live_inst.*)) return invalid("%{}: different deaths across branches", .{block});
597}
598
599fn invalid(comptime fmt: []const u8, args: anytype) error{LivenessInvalid} {
600 log.err(fmt, args);
601 return error.LivenessInvalid;
602}
603
604const std = @import("std");
605const assert = std.debug.assert;
606const log = std.log.scoped(.liveness_verify);
607
608const Air = @import("../Air.zig");
609const Liveness = @import("../Liveness.zig");
610const Verify = @This();
src/Module.zig+33
......@@ -483,6 +483,8 @@ pub const Decl = struct {
483483 /// and attempting semantic analysis again may succeed.
484484 sema_failure_retryable,
485485 /// There will be a corresponding ErrorMsg in Module.failed_decls.
486 liveness_failure,
487 /// There will be a corresponding ErrorMsg in Module.failed_decls.
486488 codegen_failure,
487489 /// There will be a corresponding ErrorMsg in Module.failed_decls.
488490 /// This indicates the failure was something like running out of disk space,
......@@ -4129,6 +4131,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
41294131 .file_failure,
41304132 .sema_failure,
41314133 .sema_failure_retryable,
4134 .liveness_failure,
41324135 .codegen_failure,
41334136 .dependency_failure,
41344137 .codegen_failure_retryable,
......@@ -4222,6 +4225,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
42224225 .dependency_failure,
42234226 .sema_failure,
42244227 .sema_failure_retryable,
4228 .liveness_failure,
42254229 .codegen_failure,
42264230 .codegen_failure_retryable,
42274231 .complete,
......@@ -4247,6 +4251,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
42474251
42484252 .file_failure,
42494253 .sema_failure,
4254 .liveness_failure,
42504255 .codegen_failure,
42514256 .dependency_failure,
42524257 .sema_failure_retryable,
......@@ -4306,6 +4311,33 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
43064311 std.debug.print("# End Function AIR: {s}\n\n", .{fqn});
43074312 }
43084313
4314 if (std.debug.runtime_safety) {
4315 var verify = Liveness.Verify{
4316 .gpa = gpa,
4317 .air = air,
4318 .liveness = liveness,
4319 };
4320 defer verify.deinit();
4321
4322 verify.verify() catch |err| switch (err) {
4323 error.OutOfMemory => return error.OutOfMemory,
4324 else => {
4325 try mod.failed_decls.ensureUnusedCapacity(gpa, 1);
4326 mod.failed_decls.putAssumeCapacityNoClobber(
4327 decl_index,
4328 try Module.ErrorMsg.create(
4329 gpa,
4330 decl.srcLoc(),
4331 "invalid liveness: {s}",
4332 .{@errorName(err)},
4333 ),
4334 );
4335 decl.analysis = .liveness_failure;
4336 return error.AnalysisFail;
4337 },
4338 };
4339 }
4340
43094341 if (no_bin_file and !dump_llvm_ir) return;
43104342
43114343 comp.bin_file.updateFunc(mod, func, air, liveness) catch |err| switch (err) {
......@@ -4349,6 +4381,7 @@ pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void {
43494381 .dependency_failure,
43504382 .sema_failure,
43514383 .sema_failure_retryable,
4384 .liveness_failure,
43524385 .codegen_failure,
43534386 .codegen_failure_retryable,
43544387 .complete,
src/arch/aarch64/CodeGen.zig+5-6
......@@ -655,6 +655,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
655655 const air_tags = self.air.instructions.items(.tag);
656656
657657 for (body) |inst| {
658 // TODO: remove now-redundant isUnused calls from AIR handler functions
659 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
660 continue;
661 }
662
658663 const old_air_bookkeeping = self.air_bookkeeping;
659664 try self.ensureProcessDeathCapacity(Liveness.bpi);
660665
......@@ -5000,17 +5005,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
50005005 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
50015006 const loop = self.air.extraData(Air.Block, ty_pl.payload);
50025007 const body = self.air.extra[loop.end..][0..loop.data.body_len];
5003 const liveness_loop = self.liveness.getLoop(inst);
50045008 const start_index = @intCast(u32, self.mir_instructions.len);
50055009
50065010 try self.genBody(body);
50075011 try self.jump(start_index);
50085012
5009 try self.ensureProcessDeathCapacity(liveness_loop.deaths.len);
5010 for (liveness_loop.deaths) |operand| {
5011 self.processDeath(operand);
5012 }
5013
50145013 return self.finishAirBookkeeping();
50155014}
50165015
src/arch/arm/CodeGen.zig+5-6
......@@ -639,6 +639,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
639639 const air_tags = self.air.instructions.items(.tag);
640640
641641 for (body) |inst| {
642 // TODO: remove now-redundant isUnused calls from AIR handler functions
643 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
644 continue;
645 }
646
642647 const old_air_bookkeeping = self.air_bookkeeping;
643648 try self.ensureProcessDeathCapacity(Liveness.bpi);
644649
......@@ -4923,17 +4928,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
49234928 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
49244929 const loop = self.air.extraData(Air.Block, ty_pl.payload);
49254930 const body = self.air.extra[loop.end..][0..loop.data.body_len];
4926 const liveness_loop = self.liveness.getLoop(inst);
49274931 const start_index = @intCast(Mir.Inst.Index, self.mir_instructions.len);
49284932
49294933 try self.genBody(body);
49304934 try self.jump(start_index);
49314935
4932 try self.ensureProcessDeathCapacity(liveness_loop.deaths.len);
4933 for (liveness_loop.deaths) |operand| {
4934 self.processDeath(operand);
4935 }
4936
49374936 return self.finishAirBookkeeping();
49384937}
49394938
src/arch/riscv64/CodeGen.zig+5
......@@ -473,6 +473,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
473473 const air_tags = self.air.instructions.items(.tag);
474474
475475 for (body) |inst| {
476 // TODO: remove now-redundant isUnused calls from AIR handler functions
477 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
478 continue;
479 }
480
476481 const old_air_bookkeeping = self.air_bookkeeping;
477482 try self.ensureProcessDeathCapacity(Liveness.bpi);
478483
src/arch/sparc64/CodeGen.zig+5-6
......@@ -489,6 +489,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
489489 const air_tags = self.air.instructions.items(.tag);
490490
491491 for (body) |inst| {
492 // TODO: remove now-redundant isUnused calls from AIR handler functions
493 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
494 continue;
495 }
496
492497 const old_air_bookkeeping = self.air_bookkeeping;
493498 try self.ensureProcessDeathCapacity(Liveness.bpi);
494499
......@@ -1750,17 +1755,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
17501755 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
17511756 const loop = self.air.extraData(Air.Block, ty_pl.payload);
17521757 const body = self.air.extra[loop.end .. loop.end + loop.data.body_len];
1753 const liveness_loop = self.liveness.getLoop(inst);
17541758 const start = @intCast(u32, self.mir_instructions.len);
17551759
17561760 try self.genBody(body);
17571761 try self.jump(start);
17581762
1759 try self.ensureProcessDeathCapacity(liveness_loop.deaths.len);
1760 for (liveness_loop.deaths) |operand| {
1761 self.processDeath(operand);
1762 }
1763
17641763 return self.finishAirBookkeeping();
17651764}
17661765
src/arch/wasm/CodeGen.zig+11-88
......@@ -2009,9 +2009,11 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
20092009
20102010fn genBody(func: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
20112011 for (body) |inst| {
2012 if (func.liveness.isUnused(inst) and !func.air.mustLower(inst)) {
2013 continue;
2014 }
20122015 const old_bookkeeping_value = func.air_bookkeeping;
2013 // TODO: Determine why we need to pre-allocate an extra 4 possible values here.
2014 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi + 4);
2016 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi);
20152017 try func.genInst(inst);
20162018
20172019 if (builtin.mode == .Debug and func.air_bookkeeping < old_bookkeeping_value + 1) {
......@@ -2185,7 +2187,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
21852187 }
21862188
21872189 const result_value = result_value: {
2188 if (func.liveness.isUnused(inst) or (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError())) {
2190 if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
21892191 break :result_value WValue{ .none = {} };
21902192 } else if (ret_ty.isNoReturn()) {
21912193 try func.addTag(.@"unreachable");
......@@ -2494,7 +2496,6 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
24942496
24952497fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
24962498 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
2497 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
24982499 const lhs = try func.resolveInst(bin_op.lhs);
24992500 const rhs = try func.resolveInst(bin_op.rhs);
25002501 const ty = func.air.typeOf(bin_op.lhs);
......@@ -2649,7 +2650,6 @@ const FloatOp = enum {
26492650
26502651fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void {
26512652 const un_op = func.air.instructions.items(.data)[inst].un_op;
2652 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
26532653 const operand = try func.resolveInst(un_op);
26542654 const ty = func.air.typeOf(un_op);
26552655
......@@ -2723,7 +2723,6 @@ fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) In
27232723
27242724fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
27252725 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
2726 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
27272726
27282727 const lhs = try func.resolveInst(bin_op.lhs);
27292728 const rhs = try func.resolveInst(bin_op.rhs);
......@@ -3183,7 +3182,6 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
31833182 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
31843183 const loop = func.air.extraData(Air.Block, ty_pl.payload);
31853184 const body = func.air.extra[loop.end..][0..loop.data.body_len];
3186 const liveness_loop = func.liveness.getLoop(inst);
31873185
31883186 // result type of loop is always 'noreturn', meaning we can always
31893187 // emit the wasm type 'block_empty'.
......@@ -3194,11 +3192,6 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
31943192 try func.addLabel(.br, 0);
31953193 try func.endBlock();
31963194
3197 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_loop.deaths.len));
3198 for (liveness_loop.deaths) |death| {
3199 func.processDeath(Air.indexToRef(death));
3200 }
3201
32023195 func.finishAir(inst, .none, &.{});
32033196}
32043197
......@@ -3224,9 +3217,6 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
32243217
32253218 func.branches.appendAssumeCapacity(.{});
32263219 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.else_deaths.len));
3227 for (liveness_condbr.else_deaths) |death| {
3228 func.processDeath(Air.indexToRef(death));
3229 }
32303220 try func.genBody(else_body);
32313221 try func.endBlock();
32323222 var else_stack = func.branches.pop();
......@@ -3235,9 +3225,6 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
32353225 // Outer block that matches the condition
32363226 func.branches.appendAssumeCapacity(.{});
32373227 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.then_deaths.len));
3238 for (liveness_condbr.then_deaths) |death| {
3239 func.processDeath(Air.indexToRef(death));
3240 }
32413228 try func.genBody(then_body);
32423229 var then_stack = func.branches.pop();
32433230 defer then_stack.deinit(func.gpa);
......@@ -3255,7 +3242,7 @@ fn mergeBranch(func: *CodeGen, branch: *const Branch) !void {
32553242 const target_keys = target_slice.items(.key);
32563243 const target_values = target_slice.items(.value);
32573244
3258 try parent.values.ensureUnusedCapacity(func.gpa, branch.values.count());
3245 try parent.values.ensureTotalCapacity(func.gpa, parent.values.capacity() + branch.values.count());
32593246 for (target_keys, 0..) |key, index| {
32603247 // TODO: process deaths from branches
32613248 parent.values.putAssumeCapacity(key, target_values[index]);
......@@ -3264,7 +3251,6 @@ fn mergeBranch(func: *CodeGen, branch: *const Branch) !void {
32643251
32653252fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {
32663253 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
3267 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
32683254
32693255 const lhs = try func.resolveInst(bin_op.lhs);
32703256 const rhs = try func.resolveInst(bin_op.rhs);
......@@ -3381,7 +3367,6 @@ fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
33813367
33823368fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
33833369 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3384 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
33853370
33863371 const operand = try func.resolveInst(ty_op.operand);
33873372 const operand_ty = func.air.typeOf(ty_op.operand);
......@@ -3447,7 +3432,7 @@ fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
34473432
34483433fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
34493434 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3450 const result = if (!func.liveness.isUnused(inst)) result: {
3435 const result = result: {
34513436 const operand = try func.resolveInst(ty_op.operand);
34523437 const wanted_ty = func.air.typeOfIndex(inst);
34533438 const given_ty = func.air.typeOf(ty_op.operand);
......@@ -3456,7 +3441,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
34563441 break :result try bitcast_result.toLocal(func, wanted_ty);
34573442 }
34583443 break :result func.reuseOperand(ty_op.operand, operand);
3459 } else WValue{ .none = {} };
3444 };
34603445 func.finishAir(inst, result, &.{ty_op.operand});
34613446}
34623447
......@@ -3480,7 +3465,6 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn
34803465fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
34813466 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
34823467 const extra = func.air.extraData(Air.StructField, ty_pl.payload);
3483 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.data.struct_operand});
34843468
34853469 const struct_ptr = try func.resolveInst(extra.data.struct_operand);
34863470 const struct_ty = func.air.typeOf(extra.data.struct_operand).childType();
......@@ -3490,7 +3474,6 @@ fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
34903474
34913475fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void {
34923476 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3493 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
34943477 const struct_ptr = try func.resolveInst(ty_op.operand);
34953478 const struct_ty = func.air.typeOf(ty_op.operand).childType();
34963479
......@@ -3535,7 +3518,6 @@ fn structFieldPtr(
35353518fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
35363519 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
35373520 const struct_field = func.air.extraData(Air.StructField, ty_pl.payload).data;
3538 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{struct_field.struct_operand});
35393521
35403522 const struct_ty = func.air.typeOf(struct_field.struct_operand);
35413523 const operand = try func.resolveInst(struct_field.struct_operand);
......@@ -3801,7 +3783,6 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
38013783
38023784fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void {
38033785 const un_op = func.air.instructions.items(.data)[inst].un_op;
3804 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
38053786 const operand = try func.resolveInst(un_op);
38063787 const err_union_ty = func.air.typeOf(un_op);
38073788 const pl_ty = err_union_ty.errorUnionPayload();
......@@ -3836,7 +3817,6 @@ fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerErro
38363817
38373818fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
38383819 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3839 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
38403820
38413821 const operand = try func.resolveInst(ty_op.operand);
38423822 const op_ty = func.air.typeOf(ty_op.operand);
......@@ -3859,7 +3839,6 @@ fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: boo
38593839
38603840fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
38613841 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3862 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
38633842
38643843 const operand = try func.resolveInst(ty_op.operand);
38653844 const op_ty = func.air.typeOf(ty_op.operand);
......@@ -3883,7 +3862,6 @@ fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool)
38833862
38843863fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
38853864 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3886 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
38873865
38883866 const operand = try func.resolveInst(ty_op.operand);
38893867 const err_ty = func.air.typeOfIndex(inst);
......@@ -3910,7 +3888,6 @@ fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void
39103888
39113889fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
39123890 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3913 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
39143891
39153892 const operand = try func.resolveInst(ty_op.operand);
39163893 const err_ty = func.air.getRefType(ty_op.ty);
......@@ -3937,7 +3914,6 @@ fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
39373914
39383915fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
39393916 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3940 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
39413917
39423918 const ty = func.air.getRefType(ty_op.ty);
39433919 const operand = try func.resolveInst(ty_op.operand);
......@@ -4004,7 +3980,6 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
40043980
40053981fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
40063982 const un_op = func.air.instructions.items(.data)[inst].un_op;
4007 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
40083983 const operand = try func.resolveInst(un_op);
40093984
40103985 const op_ty = func.air.typeOf(un_op);
......@@ -4049,7 +4024,7 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
40494024 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
40504025 const opt_ty = func.air.typeOf(ty_op.operand);
40514026 const payload_ty = func.air.typeOfIndex(inst);
4052 if (func.liveness.isUnused(inst) or !payload_ty.hasRuntimeBitsIgnoreComptime()) {
4027 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
40534028 return func.finishAir(inst, .none, &.{ty_op.operand});
40544029 }
40554030
......@@ -4069,7 +4044,6 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
40694044
40704045fn airOptionalPayloadPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
40714046 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4072 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
40734047 const operand = try func.resolveInst(ty_op.operand);
40744048 const opt_ty = func.air.typeOf(ty_op.operand).childType();
40754049
......@@ -4114,7 +4088,6 @@ fn airOptionalPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi
41144088
41154089fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41164090 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4117 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
41184091 const payload_ty = func.air.typeOf(ty_op.operand);
41194092
41204093 const result = result: {
......@@ -4153,7 +4126,6 @@ fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41534126fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41544127 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
41554128 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
4156 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
41574129
41584130 const lhs = try func.resolveInst(bin_op.lhs);
41594131 const rhs = try func.resolveInst(bin_op.rhs);
......@@ -4168,7 +4140,6 @@ fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41684140
41694141fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41704142 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4171 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
41724143
41734144 const operand = try func.resolveInst(ty_op.operand);
41744145 const len = try func.load(operand, Type.usize, func.ptrSize());
......@@ -4178,7 +4149,6 @@ fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41784149
41794150fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41804151 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
4181 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
41824152
41834153 const slice_ty = func.air.typeOf(bin_op.lhs);
41844154 const slice = try func.resolveInst(bin_op.lhs);
......@@ -4209,7 +4179,6 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42094179fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42104180 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
42114181 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
4212 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
42134182
42144183 const elem_ty = func.air.getRefType(ty_pl.ty).childType();
42154184 const elem_size = elem_ty.abiSize(func.target);
......@@ -4232,7 +4201,6 @@ fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42324201
42334202fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42344203 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4235 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
42364204 const operand = try func.resolveInst(ty_op.operand);
42374205 const ptr = try func.load(operand, Type.usize, 0);
42384206 const result = try ptr.toLocal(func, Type.usize);
......@@ -4241,7 +4209,6 @@ fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42414209
42424210fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42434211 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4244 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
42454212
42464213 const operand = try func.resolveInst(ty_op.operand);
42474214 const wanted_ty = func.air.getRefType(ty_op.ty);
......@@ -4270,19 +4237,14 @@ fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) Inner
42704237
42714238fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42724239 const un_op = func.air.instructions.items(.data)[inst].un_op;
4273 const result = if (func.liveness.isUnused(inst))
4274 WValue{ .none = {} }
4275 else result: {
4276 const operand = try func.resolveInst(un_op);
4277 break :result func.reuseOperand(un_op, operand);
4278 };
4240 const operand = try func.resolveInst(un_op);
4241 const result = func.reuseOperand(un_op, operand);
42794242
42804243 func.finishAir(inst, result, &.{un_op});
42814244}
42824245
42834246fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42844247 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4285 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
42864248
42874249 const operand = try func.resolveInst(ty_op.operand);
42884250 const array_ty = func.air.typeOf(ty_op.operand).childType();
......@@ -4305,7 +4267,6 @@ fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43054267
43064268fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43074269 const un_op = func.air.instructions.items(.data)[inst].un_op;
4308 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
43094270 const operand = try func.resolveInst(un_op);
43104271
43114272 const result = switch (operand) {
......@@ -4318,7 +4279,6 @@ fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43184279
43194280fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43204281 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
4321 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
43224282
43234283 const ptr_ty = func.air.typeOf(bin_op.lhs);
43244284 const ptr = try func.resolveInst(bin_op.lhs);
......@@ -4356,7 +4316,6 @@ fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43564316fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43574317 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
43584318 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
4359 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
43604319
43614320 const ptr_ty = func.air.typeOf(bin_op.lhs);
43624321 const elem_ty = func.air.getRefType(ty_pl.ty).childType();
......@@ -4386,7 +4345,6 @@ fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43864345fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
43874346 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
43884347 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
4389 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
43904348
43914349 const ptr = try func.resolveInst(bin_op.lhs);
43924350 const offset = try func.resolveInst(bin_op.rhs);
......@@ -4510,7 +4468,6 @@ fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!vo
45104468
45114469fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
45124470 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
4513 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
45144471
45154472 const array_ty = func.air.typeOf(bin_op.lhs);
45164473 const array = try func.resolveInst(bin_op.lhs);
......@@ -4579,7 +4536,6 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
45794536
45804537fn airFloatToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
45814538 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4582 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
45834539
45844540 const operand = try func.resolveInst(ty_op.operand);
45854541 const dest_ty = func.air.typeOfIndex(inst);
......@@ -4604,7 +4560,6 @@ fn airFloatToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
46044560
46054561fn airIntToFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
46064562 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4607 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
46084563
46094564 const operand = try func.resolveInst(ty_op.operand);
46104565 const dest_ty = func.air.typeOfIndex(inst);
......@@ -4719,10 +4674,6 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
47194674 const child_ty = inst_ty.childType();
47204675 const elem_size = child_ty.abiSize(func.target);
47214676
4722 if (func.liveness.isUnused(inst)) {
4723 return func.finishAir(inst, .none, &.{ extra.a, extra.b });
4724 }
4725
47264677 const module = func.bin_file.base.options.module.?;
47274678 // TODO: One of them could be by ref; handle in loop
47284679 if (isByRef(func.air.typeOf(extra.a), func.target) or isByRef(inst_ty, func.target)) {
......@@ -4788,7 +4739,6 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
47884739 const elements = @ptrCast([]const Air.Inst.Ref, func.air.extra[ty_pl.payload..][0..len]);
47894740
47904741 const result: WValue = result_value: {
4791 if (func.liveness.isUnused(inst)) break :result_value WValue.none;
47924742 switch (result_ty.zigTypeTag()) {
47934743 .Array => {
47944744 const result = try func.allocStack(result_ty);
......@@ -4894,7 +4844,6 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
48944844fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
48954845 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
48964846 const extra = func.air.extraData(Air.UnionInit, ty_pl.payload).data;
4897 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.init});
48984847
48994848 const result = result: {
49004849 const union_ty = func.air.typeOfIndex(inst);
......@@ -4933,7 +4882,6 @@ fn airPrefetch(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
49334882
49344883fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
49354884 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
4936 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{pl_op.operand});
49374885
49384886 const result = try func.allocLocal(func.air.typeOfIndex(inst));
49394887 try func.addLabel(.memory_size, pl_op.payload);
......@@ -4943,7 +4891,6 @@ fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
49434891
49444892fn airWasmMemoryGrow(func: *CodeGen, inst: Air.Inst.Index) !void {
49454893 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
4946 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{pl_op.operand});
49474894
49484895 const operand = try func.resolveInst(pl_op.operand);
49494896 const result = try func.allocLocal(func.air.typeOfIndex(inst));
......@@ -5055,7 +5002,6 @@ fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
50555002
50565003fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
50575004 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
5058 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
50595005
50605006 const un_ty = func.air.typeOf(ty_op.operand);
50615007 const tag_ty = func.air.typeOfIndex(inst);
......@@ -5075,7 +5021,6 @@ fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
50755021
50765022fn airFpext(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
50775023 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
5078 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
50795024
50805025 const dest_ty = func.air.typeOfIndex(inst);
50815026 const operand = try func.resolveInst(ty_op.operand);
......@@ -5121,7 +5066,6 @@ fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!
51215066
51225067fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
51235068 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
5124 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
51255069
51265070 const dest_ty = func.air.typeOfIndex(inst);
51275071 const operand = try func.resolveInst(ty_op.operand);
......@@ -5162,7 +5106,6 @@ fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
51625106
51635107fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
51645108 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
5165 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
51665109
51675110 const err_set_ty = func.air.typeOf(ty_op.operand).childType();
51685111 const payload_ty = err_set_ty.errorUnionPayload();
......@@ -5177,8 +5120,6 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi
51775120 );
51785121
51795122 const result = result: {
5180 if (func.liveness.isUnused(inst)) break :result WValue{ .none = {} };
5181
51825123 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
51835124 break :result func.reuseOperand(ty_op.operand, operand);
51845125 }
......@@ -5191,7 +5132,6 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi
51915132fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
51925133 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
51935134 const extra = func.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
5194 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.field_ptr});
51955135
51965136 const field_ptr = try func.resolveInst(extra.field_ptr);
51975137 const parent_ty = func.air.getRefType(ty_pl.ty).childType();
......@@ -5231,7 +5171,6 @@ fn airRetAddr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52315171
52325172fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52335173 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
5234 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
52355174
52365175 const operand = try func.resolveInst(ty_op.operand);
52375176 const op_ty = func.air.typeOf(ty_op.operand);
......@@ -5276,7 +5215,6 @@ fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52765215
52775216fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52785217 const un_op = func.air.instructions.items(.data)[inst].un_op;
5279 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
52805218
52815219 const operand = try func.resolveInst(un_op);
52825220 // First retrieve the symbol index to the error name table
......@@ -5318,7 +5256,6 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
53185256
53195257fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void {
53205258 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
5321 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
53225259 const slice_ptr = try func.resolveInst(ty_op.operand);
53235260 const result = try func.buildPointerOffset(slice_ptr, offset, .new);
53245261 func.finishAir(inst, result, &.{ty_op.operand});
......@@ -5328,7 +5265,6 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
53285265 assert(op == .add or op == .sub);
53295266 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
53305267 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
5331 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
53325268
53335269 const lhs_op = try func.resolveInst(extra.lhs);
53345270 const rhs_op = try func.resolveInst(extra.rhs);
......@@ -5471,7 +5407,6 @@ fn addSubWithOverflowBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type,
54715407fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
54725408 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
54735409 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
5474 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
54755410
54765411 const lhs = try func.resolveInst(extra.lhs);
54775412 const rhs = try func.resolveInst(extra.rhs);
......@@ -5519,7 +5454,6 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55195454fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55205455 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
55215456 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
5522 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
55235457
55245458 const lhs = try func.resolveInst(extra.lhs);
55255459 const rhs = try func.resolveInst(extra.rhs);
......@@ -5605,7 +5539,6 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56055539
56065540fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerError!void {
56075541 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
5608 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
56095542
56105543 const ty = func.air.typeOfIndex(inst);
56115544 if (ty.zigTypeTag() == .Vector) {
......@@ -5637,8 +5570,6 @@ fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerE
56375570fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56385571 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
56395572 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;
5640 if (func.liveness.isUnused(inst))
5641 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });
56425573
56435574 const ty = func.air.typeOfIndex(inst);
56445575 if (ty.zigTypeTag() == .Vector) {
......@@ -5671,7 +5602,6 @@ fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56715602
56725603fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56735604 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
5674 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
56755605
56765606 const ty = func.air.typeOf(ty_op.operand);
56775607 const result_ty = func.air.typeOfIndex(inst);
......@@ -5724,7 +5654,6 @@ fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
57245654
57255655fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
57265656 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
5727 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
57285657
57295658 const ty = func.air.typeOf(ty_op.operand);
57305659 const result_ty = func.air.typeOfIndex(inst);
......@@ -5892,7 +5821,6 @@ fn lowerTry(
58925821
58935822fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58945823 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
5895 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
58965824
58975825 const ty = func.air.typeOfIndex(inst);
58985826 const operand = try func.resolveInst(ty_op.operand);
......@@ -5963,7 +5891,6 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59635891
59645892fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59655893 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
5966 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
59675894
59685895 const ty = func.air.typeOfIndex(inst);
59695896 const lhs = try func.resolveInst(bin_op.lhs);
......@@ -5978,7 +5905,6 @@ fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59785905
59795906fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59805907 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
5981 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
59825908
59835909 const ty = func.air.typeOfIndex(inst);
59845910 const lhs = try func.resolveInst(bin_op.lhs);
......@@ -6127,7 +6053,6 @@ fn signAbsValue(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
61276053fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
61286054 assert(op == .add or op == .sub);
61296055 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
6130 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
61316056
61326057 const ty = func.air.typeOfIndex(inst);
61336058 const lhs = try func.resolveInst(bin_op.lhs);
......@@ -6240,7 +6165,6 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type,
62406165
62416166fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
62426167 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
6243 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
62446168
62456169 const ty = func.air.typeOfIndex(inst);
62466170 const int_info = ty.intInfo(func.target);
......@@ -6399,7 +6323,6 @@ fn callIntrinsic(
63996323
64006324fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64016325 const un_op = func.air.instructions.items(.data)[inst].un_op;
6402 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
64036326 const operand = try func.resolveInst(un_op);
64046327 const enum_ty = func.air.typeOf(un_op);
64056328
src/arch/x86_64/CodeGen.zig+1431-1731
......@@ -79,14 +79,8 @@ end_di_column: u32,
7979/// which is a relative jump, based on the address following the reloc.
8080exitlude_jump_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
8181
82/// Whenever there is a runtime branch, we push a Branch onto this stack,
83/// and pop it off when the runtime branch joins. This provides an "overlay"
84/// of the table of mappings from instructions to `MCValue` from within the branch.
85/// This way we can modify the `MCValue` for an instruction in different ways
86/// within different branches. Special consideration is needed when a branch
87/// joins with its parent, to make sure all instructions have the same MCValue
88/// across each runtime branch upon joining.
89branch_stack: *std.ArrayList(Branch),
82const_tracking: InstTrackingMap = .{},
83inst_tracking: InstTrackingMap = .{},
9084
9185// Key is the block instruction
9286blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
......@@ -95,6 +89,9 @@ register_manager: RegisterManager = .{},
9589/// Maps offset to what is stored there.
9690stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
9791
92/// Generation of the current scope, increments by 1 for every entered scope.
93scope_generation: u32 = 0,
94
9895/// Offset from the stack base, representing the end of the stack frame.
9996max_end_stack: u32 = 0,
10097/// Represents the current end stack offset. If there is no existing slot
......@@ -105,10 +102,12 @@ next_stack_offset: u32 = 0,
105102air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
106103
107104/// For mir debug info, maps a mir index to a air index
108mir_to_air_map: if (builtin.mode == .Debug) std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index) else void,
105mir_to_air_map: @TypeOf(mir_to_air_map_init) = mir_to_air_map_init,
109106
110107const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
111108
109const mir_to_air_map_init = if (builtin.mode == .Debug) std.AutoHashMapUnmanaged(Mir.Inst.Index, Air.Inst.Index){} else {};
110
112111pub const MCValue = union(enum) {
113112 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
114113 /// TODO Look into deleting this tag and using `dead` instead, since every use
......@@ -117,7 +116,8 @@ pub const MCValue = union(enum) {
117116 /// Control flow will not allow this value to be observed.
118117 unreach,
119118 /// No more references to this value remain.
120 dead,
119 /// The payload is the value of scope_generation at the point where the death occurred
120 dead: u32,
121121 /// The value is undefined.
122122 undef,
123123 /// A pointer-sized integer that fits in a register.
......@@ -183,47 +183,95 @@ pub const MCValue = union(enum) {
183183 }
184184};
185185
186const Branch = struct {
187 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
186const InstTrackingMap = std.AutoArrayHashMapUnmanaged(Air.Inst.Index, InstTracking);
187const InstTracking = struct {
188 long: MCValue,
189 short: MCValue,
188190
189 fn deinit(self: *Branch, gpa: Allocator) void {
190 self.inst_table.deinit(gpa);
191 self.* = undefined;
191 fn init(result: MCValue) InstTracking {
192 return .{ .long = result, .short = result };
192193 }
193194
194 const FormatContext = struct {
195 insts: []const Air.Inst.Index,
196 mcvs: []const MCValue,
197 };
195 fn getReg(self: InstTracking) ?Register {
196 return switch (self.short) {
197 .register => |reg| reg,
198 .register_overflow => |ro| ro.reg,
199 else => null,
200 };
201 }
202
203 fn getCondition(self: InstTracking) ?Condition {
204 return switch (self.short) {
205 .eflags => |eflags| eflags,
206 .register_overflow => |ro| ro.eflags,
207 else => null,
208 };
209 }
210
211 fn spill(self: *InstTracking, function: *Self, inst: Air.Inst.Index) !void {
212 switch (self.long) {
213 .none,
214 .dead,
215 .unreach,
216 => unreachable,
217 .register,
218 .register_overflow,
219 .eflags,
220 => self.long = try function.allocRegOrMem(inst, self.short == .eflags),
221 .stack_offset => {},
222 .undef,
223 .immediate,
224 .memory,
225 .load_direct,
226 .lea_direct,
227 .load_got,
228 .load_tlv,
229 .lea_tlv,
230 .ptr_stack_offset,
231 => return, // these can be rematerialized without using a stack slot
232 }
233 log.debug("spilling %{d} from {} to {}", .{ inst, self.short, self.long });
234 const ty = function.air.typeOfIndex(inst);
235 try function.setRegOrMem(ty, self.long, self.short);
236 }
198237
199 fn fmt(
200 ctx: FormatContext,
201 comptime unused_format_string: []const u8,
202 options: std.fmt.FormatOptions,
203 writer: anytype,
204 ) @TypeOf(writer).Error!void {
205 _ = options;
206 comptime assert(unused_format_string.len == 0);
207 try writer.writeAll("Branch {\n");
208 for (ctx.insts, ctx.mcvs) |inst, mcv| {
209 try writer.print(" %{d} => {}\n", .{ inst, mcv });
238 fn trackSpill(self: *InstTracking, function: *Self) void {
239 if (self.getReg()) |reg| function.register_manager.freeReg(reg);
240 switch (self.short) {
241 .none, .dead, .unreach => unreachable,
242 else => {},
210243 }
211 try writer.writeAll("}");
244 self.short = self.long;
212245 }
213246
214 fn format(branch: Branch, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
215 _ = branch;
216 _ = unused_format_string;
217 _ = options;
218 _ = writer;
219 @compileError("do not format Branch directly; use ty.fmtDebug()");
247 fn materialize(self: *InstTracking, function: *Self, inst: Air.Inst.Index, reg: Register) !void {
248 const ty = function.air.typeOfIndex(inst);
249 try function.genSetReg(ty, reg, self.long);
220250 }
221251
222 fn fmtDebug(self: @This()) std.fmt.Formatter(fmt) {
223 return .{ .data = .{
224 .insts = self.inst_table.keys(),
225 .mcvs = self.inst_table.values(),
226 } };
252 fn trackMaterialize(self: *InstTracking, function: *Self, inst: Air.Inst.Index, reg: Register) void {
253 assert(inst == function.register_manager.registers[
254 RegisterManager.indexOfRegIntoTracked(reg).?
255 ]);
256 self.short = .{ .register = reg };
257 }
258
259 fn resurrect(self: *InstTracking, scope_generation: u32) void {
260 switch (self.short) {
261 .dead => |die_generation| if (die_generation >= scope_generation) {
262 self.short = self.long;
263 },
264 else => {},
265 }
266 }
267
268 fn die(self: *InstTracking, function: *Self) void {
269 function.freeValue(self.short);
270 self.reuse(function);
271 }
272
273 fn reuse(self: *InstTracking, function: *Self) void {
274 self.short = .{ .dead = function.scope_generation };
227275 }
228276};
229277
......@@ -235,39 +283,14 @@ const StackAllocation = struct {
235283
236284const BlockData = struct {
237285 relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
238 branch: Branch = .{},
239 branch_depth: u32,
286 state: State,
240287
241288 fn deinit(self: *BlockData, gpa: Allocator) void {
242 self.branch.deinit(gpa);
243289 self.relocs.deinit(gpa);
244290 self.* = undefined;
245291 }
246292};
247293
248const BigTomb = struct {
249 function: *Self,
250 inst: Air.Inst.Index,
251 lbt: Liveness.BigTomb,
252
253 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
254 const dies = bt.lbt.feed();
255 const op_index = Air.refToIndex(op_ref) orelse return;
256 if (!dies) return;
257 bt.function.processDeath(op_index);
258 }
259
260 fn finishAir(bt: *BigTomb, result: MCValue) void {
261 const is_used = !bt.function.liveness.isUnused(bt.inst);
262 if (is_used) {
263 log.debug(" (saving %{d} => {})", .{ bt.inst, result });
264 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
265 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
266 }
267 bt.function.finishAirBookkeeping();
268 }
269};
270
271294const Self = @This();
272295
273296pub fn generate(
......@@ -294,19 +317,9 @@ pub fn generate(
294317 stderr.writeAll(":\n") catch {};
295318 }
296319
297 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
298 try branch_stack.ensureUnusedCapacity(2);
299 // The outermost branch is used for constants only.
300 branch_stack.appendAssumeCapacity(.{});
301 branch_stack.appendAssumeCapacity(.{});
302 defer {
303 assert(branch_stack.items.len == 2);
304 for (branch_stack.items) |*branch| branch.deinit(bin_file.allocator);
305 branch_stack.deinit();
306 }
307
320 const gpa = bin_file.allocator;
308321 var function = Self{
309 .gpa = bin_file.allocator,
322 .gpa = gpa,
310323 .air = air,
311324 .liveness = liveness,
312325 .target = &bin_file.options.target,
......@@ -318,21 +331,23 @@ pub fn generate(
318331 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
319332 .fn_type = fn_type,
320333 .arg_index = 0,
321 .branch_stack = &branch_stack,
322334 .src_loc = src_loc,
323335 .stack_align = undefined,
324336 .end_di_line = module_fn.rbrace_line,
325337 .end_di_column = module_fn.rbrace_column,
326 .mir_to_air_map = if (builtin.mode == .Debug)
327 std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator)
328 else {},
329338 };
330 defer function.stack.deinit(bin_file.allocator);
331 defer function.blocks.deinit(bin_file.allocator);
332 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
333 defer function.mir_instructions.deinit(bin_file.allocator);
334 defer function.mir_extra.deinit(bin_file.allocator);
335 defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit();
339 defer {
340 function.stack.deinit(gpa);
341 var block_it = function.blocks.valueIterator();
342 while (block_it.next()) |block| block.deinit(gpa);
343 function.blocks.deinit(gpa);
344 function.inst_tracking.deinit(gpa);
345 function.const_tracking.deinit(gpa);
346 function.exitlude_jump_relocs.deinit(gpa);
347 function.mir_instructions.deinit(gpa);
348 function.mir_extra.deinit(gpa);
349 if (builtin.mode == .Debug) function.mir_to_air_map.deinit(gpa);
350 }
336351
337352 var call_info = function.resolveCallingConventionValues(fn_type, &.{}) catch |err| switch (err) {
338353 error.CodegenFail => return Result{ .fail = function.err_msg.? },
......@@ -905,11 +920,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
905920 const air_tags = self.air.instructions.items(.tag);
906921
907922 for (body) |inst| {
908 const old_air_bookkeeping = self.air_bookkeeping;
909 try self.ensureProcessDeathCapacity(Liveness.bpi);
910923 if (builtin.mode == .Debug) {
911 try self.mir_to_air_map.put(@intCast(Mir.Inst.Index, self.mir_instructions.len), inst);
924 const mir_inst = @intCast(Mir.Inst.Index, self.mir_instructions.len);
925 try self.mir_to_air_map.put(self.gpa, mir_inst, inst);
912926 }
927
928 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) continue;
913929 if (debug_wip_mir) @import("../../print_air.zig").dumpInst(
914930 inst,
915931 self.bin_file.options.module.?,
......@@ -917,6 +933,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
917933 self.liveness,
918934 );
919935
936 const old_air_bookkeeping = self.air_bookkeeping;
937 try self.inst_tracking.ensureUnusedCapacity(self.gpa, 1);
920938 switch (air_tags[inst]) {
921939 // zig fmt: off
922940 .not,
......@@ -1080,7 +1098,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
10801098
10811099 .field_parent_ptr => try self.airFieldParentPtr(inst),
10821100
1083 .switch_br => try self.airSwitch(inst),
1101 .switch_br => try self.airSwitchBr(inst),
10841102 .slice_ptr => try self.airSlicePtr(inst),
10851103 .slice_len => try self.airSliceLen(inst),
10861104
......@@ -1166,8 +1184,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
11661184 var it = self.register_manager.free_registers.iterator(.{ .kind = .unset });
11671185 while (it.next()) |index| {
11681186 const tracked_inst = self.register_manager.registers[index];
1169 const tracked_mcv = self.getResolvedInstValue(tracked_inst).?.*;
1170 assert(RegisterManager.indexOfRegIntoTracked(switch (tracked_mcv) {
1187 const tracking = self.getResolvedInstValue(tracked_inst);
1188 assert(RegisterManager.indexOfRegIntoTracked(switch (tracking.short) {
11711189 .register => |reg| reg,
11721190 .register_overflow => |ro| ro.reg,
11731191 else => unreachable,
......@@ -1205,16 +1223,16 @@ fn freeValue(self: *Self, value: MCValue) void {
12051223 }
12061224}
12071225
1226fn feed(self: *Self, bt: *Liveness.BigTomb, operand: Air.Inst.Ref) void {
1227 if (bt.feed()) if (Air.refToIndex(operand)) |inst| self.processDeath(inst);
1228}
1229
12081230/// Asserts there is already capacity to insert into top branch inst_table.
12091231fn processDeath(self: *Self, inst: Air.Inst.Index) void {
12101232 const air_tags = self.air.instructions.items(.tag);
1211 if (air_tags[inst] == .constant) return; // Constants are immortal.
1212 const prev_value = (self.getResolvedInstValue(inst) orelse return).*;
1233 if (air_tags[inst] == .constant) return;
12131234 log.debug("%{d} => {}", .{ inst, MCValue.dead });
1214 // When editing this function, note that the logic must synchronize with `reuseOperand`.
1215 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1216 branch.inst_table.putAssumeCapacity(inst, .dead);
1217 self.freeValue(prev_value);
1235 self.inst_tracking.getPtr(inst).?.die(self);
12181236}
12191237
12201238/// Called when there are no operands, and the instruction is always unreferenced.
......@@ -1224,6 +1242,21 @@ fn finishAirBookkeeping(self: *Self) void {
12241242 }
12251243}
12261244
1245fn finishAirResult(self: *Self, inst: Air.Inst.Index, result: MCValue) void {
1246 if (self.liveness.isUnused(inst)) switch (result) {
1247 .none, .dead, .unreach => {},
1248 else => unreachable, // Why didn't the result die?
1249 } else {
1250 log.debug("%{d} => {}", .{ inst, result });
1251 self.inst_tracking.putAssumeCapacityNoClobber(inst, InstTracking.init(result));
1252 // In some cases, an operand may be reused as the result.
1253 // If that operand died and was a register, it was freed by
1254 // processDeath, so we have to "re-allocate" the register.
1255 self.getValue(result, inst);
1256 }
1257 self.finishAirBookkeeping();
1258}
1259
12271260fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
12281261 var tomb_bits = self.liveness.getTombBits(inst);
12291262 for (operands) |op| {
......@@ -1235,26 +1268,7 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
12351268 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
12361269 self.processDeath(op_index);
12371270 }
1238 const is_used = @truncate(u1, tomb_bits) == 0;
1239 if (is_used) {
1240 log.debug("%{d} => {}", .{ inst, result });
1241 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1242 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
1243 // In some cases, an operand may be reused as the result.
1244 // If that operand died and was a register, it was freed by
1245 // processDeath, so we have to "re-allocate" the register.
1246 self.getValue(result, inst);
1247 } else switch (result) {
1248 .none, .dead, .unreach => {},
1249 else => unreachable, // Why didn't the result die?
1250 }
1251 self.finishAirBookkeeping();
1252}
1253
1254fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
1255 // In addition to the caller's needs, we need enough space to spill every register and eflags.
1256 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
1257 try table.ensureUnusedCapacity(self.gpa, additional_count + self.register_manager.registers.len + 1);
1271 self.finishAirResult(inst, result);
12581272}
12591273
12601274fn allocMem(self: *Self, inst: ?Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
......@@ -1339,66 +1353,115 @@ fn allocRegOrMemAdvanced(self: *Self, elem_ty: Type, inst: ?Air.Inst.Index, reg_
13391353}
13401354
13411355const State = struct {
1342 registers: abi.RegisterManager.TrackedRegisters,
1343 free_registers: abi.RegisterManager.RegisterBitSet,
1344 eflags_inst: ?Air.Inst.Index,
1356 registers: RegisterManager.TrackedRegisters,
1357 free_registers: RegisterManager.RegisterBitSet,
1358 inst_tracking_len: u32,
1359 scope_generation: u32,
13451360};
13461361
1347fn captureState(self: *Self) State {
1348 return State{
1349 .registers = self.register_manager.registers,
1350 .free_registers = self.register_manager.free_registers,
1351 .eflags_inst = self.eflags_inst,
1352 };
1362fn initRetroactiveState(self: *Self) State {
1363 var state: State = undefined;
1364 state.inst_tracking_len = @intCast(u32, self.inst_tracking.count());
1365 state.scope_generation = self.scope_generation;
1366 return state;
13531367}
13541368
1355fn revertState(self: *Self, state: State) void {
1356 self.eflags_inst = state.eflags_inst;
1357 self.register_manager.free_registers = state.free_registers;
1358 self.register_manager.registers = state.registers;
1369fn saveRetroactiveState(self: *Self, state: *State) !void {
1370 try self.spillEflagsIfOccupied();
1371 state.registers = self.register_manager.registers;
1372 state.free_registers = self.register_manager.free_registers;
13591373}
13601374
1361pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
1362 const stack_mcv = try self.allocRegOrMem(inst, false);
1363 log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv });
1364 const reg_mcv = self.getResolvedInstValue(inst).?.*;
1365 switch (reg_mcv) {
1366 .register => |other| {
1367 assert(reg.to64() == other.to64());
1368 },
1369 .register_overflow => |ro| {
1370 assert(reg.to64() == ro.reg.to64());
1371 },
1372 else => {},
1373 }
1374 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1375 branch.inst_table.putAssumeCapacity(inst, stack_mcv);
1376 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{});
1375fn saveState(self: *Self) !State {
1376 var state = self.initRetroactiveState();
1377 try self.saveRetroactiveState(&state);
1378 return state;
13771379}
13781380
1379pub fn spillEflagsIfOccupied(self: *Self) !void {
1380 if (self.eflags_inst) |inst_to_save| {
1381 const mcv = self.getResolvedInstValue(inst_to_save).?.*;
1382 const new_mcv = switch (mcv) {
1383 .register_overflow => try self.allocRegOrMem(inst_to_save, false),
1384 .eflags => try self.allocRegOrMem(inst_to_save, true),
1385 else => unreachable,
1386 };
1387
1388 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);
1389 log.debug("spilling %{d} to mcv {any}", .{ inst_to_save, new_mcv });
1381fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, comptime opts: struct {
1382 emit_instructions: bool,
1383 update_tracking: bool,
1384 resurrect: bool,
1385 close_scope: bool,
1386}) !void {
1387 if (opts.close_scope) {
1388 for (self.inst_tracking.values()[state.inst_tracking_len..]) |*tracking| tracking.die(self);
1389 self.inst_tracking.shrinkRetainingCapacity(state.inst_tracking_len);
1390 }
13901391
1391 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1392 branch.inst_table.putAssumeCapacity(inst_to_save, new_mcv);
1392 if (opts.resurrect) for (self.inst_tracking.values()[0..state.inst_tracking_len]) |*tracking|
1393 tracking.resurrect(state.scope_generation);
1394 const air_tags = self.air.instructions.items(.tag);
1395 for (deaths) |death| switch (air_tags[death]) {
1396 .constant => {},
1397 .const_ty => unreachable,
1398 else => self.inst_tracking.getPtr(death).?.die(self),
1399 };
13931400
1401 for (0..state.registers.len) |index| {
1402 const current_maybe_inst = if (self.register_manager.free_registers.isSet(index))
1403 null
1404 else
1405 self.register_manager.registers[index];
1406 const target_maybe_inst = if (state.free_registers.isSet(index))
1407 null
1408 else
1409 state.registers[index];
1410 if (std.debug.runtime_safety) if (target_maybe_inst) |target_inst|
1411 assert(self.inst_tracking.getIndex(target_inst).? < state.inst_tracking_len);
1412 if (current_maybe_inst == target_maybe_inst) continue;
1413 const reg = RegisterManager.regAtTrackedIndex(
1414 @intCast(RegisterManager.RegisterBitSet.ShiftInt, index),
1415 );
1416 if (opts.emit_instructions) {
1417 if (current_maybe_inst) |current_inst| {
1418 try self.inst_tracking.getPtr(current_inst).?.spill(self, current_inst);
1419 }
1420 if (target_maybe_inst) |target_inst| {
1421 try self.inst_tracking.getPtr(target_inst).?.materialize(self, target_inst, reg);
1422 }
1423 }
1424 if (opts.update_tracking) {
1425 if (current_maybe_inst) |current_inst| {
1426 self.inst_tracking.getPtr(current_inst).?.trackSpill(self);
1427 }
1428 self.register_manager.freeReg(reg);
1429 self.register_manager.getRegAssumeFree(reg, target_maybe_inst);
1430 if (target_maybe_inst) |target_inst| {
1431 self.inst_tracking.getPtr(target_inst).?.trackMaterialize(self, target_inst, reg);
1432 }
1433 }
1434 }
1435 if (opts.emit_instructions) if (self.eflags_inst) |inst|
1436 try self.inst_tracking.getPtr(inst).?.spill(self, inst);
1437 if (opts.update_tracking) if (self.eflags_inst) |inst| {
13941438 self.eflags_inst = null;
1439 self.inst_tracking.getPtr(inst).?.trackSpill(self);
1440 };
13951441
1396 // TODO consolidate with register manager and spillInstruction
1397 // this call should really belong in the register manager!
1398 switch (mcv) {
1399 .register_overflow => |ro| self.register_manager.freeReg(ro.reg),
1400 else => {},
1401 }
1442 if (opts.update_tracking and std.debug.runtime_safety) {
1443 assert(self.eflags_inst == null);
1444 assert(self.register_manager.free_registers.eql(state.free_registers));
1445 var used_reg_it = state.free_registers.iterator(.{ .kind = .unset });
1446 while (used_reg_it.next()) |index|
1447 assert(self.register_manager.registers[index] == state.registers[index]);
1448 }
1449}
1450
1451pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
1452 const tracking = self.inst_tracking.getPtr(inst).?;
1453 assert(tracking.getReg().?.to64() == reg.to64());
1454 try tracking.spill(self, inst);
1455 tracking.trackSpill(self);
1456}
1457
1458pub fn spillEflagsIfOccupied(self: *Self) !void {
1459 if (self.eflags_inst) |inst| {
1460 self.eflags_inst = null;
1461 const tracking = self.inst_tracking.getPtr(inst).?;
1462 assert(tracking.getCondition() != null);
1463 try tracking.spill(self, inst);
1464 tracking.trackSpill(self);
14021465 }
14031466}
14041467
......@@ -1442,22 +1505,14 @@ fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Ty
14421505}
14431506
14441507fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
1445 const result: MCValue = result: {
1446 if (self.liveness.isUnused(inst)) break :result .dead;
1447
1448 const stack_offset = try self.allocMemPtr(inst);
1449 break :result .{ .ptr_stack_offset = @intCast(i32, stack_offset) };
1450 };
1508 const stack_offset = try self.allocMemPtr(inst);
1509 const result = MCValue{ .ptr_stack_offset = @intCast(i32, stack_offset) };
14511510 return self.finishAir(inst, result, .{ .none, .none, .none });
14521511}
14531512
14541513fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
1455 const result: MCValue = result: {
1456 if (self.liveness.isUnused(inst)) break :result .dead;
1457
1458 const stack_offset = try self.allocMemPtr(inst);
1459 break :result .{ .ptr_stack_offset = @intCast(i32, stack_offset) };
1460 };
1514 const stack_offset = try self.allocMemPtr(inst);
1515 const result = MCValue{ .ptr_stack_offset = @intCast(i32, stack_offset) };
14611516 return self.finishAir(inst, result, .{ .none, .none, .none });
14621517}
14631518
......@@ -1477,126 +1532,125 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
14771532
14781533fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
14791534 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1480 const result = if (self.liveness.isUnused(inst)) .dead else result: {
1481 const src_ty = self.air.typeOf(ty_op.operand);
1482 const src_int_info = src_ty.intInfo(self.target.*);
1483 const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*));
1484 const src_mcv = try self.resolveInst(ty_op.operand);
1485 const src_lock = switch (src_mcv) {
1486 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1487 else => null,
1488 };
1489 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
1490
1491 const dst_ty = self.air.typeOfIndex(inst);
1492 const dst_int_info = dst_ty.intInfo(self.target.*);
1493 const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
1494 const dst_mcv = if (dst_abi_size <= src_abi_size and
1495 self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
1496 src_mcv
1497 else
1498 try self.allocRegOrMem(inst, true);
14991535
1500 const min_ty = if (dst_int_info.bits < src_int_info.bits) dst_ty else src_ty;
1501 const signedness: std.builtin.Signedness = if (dst_int_info.signedness == .signed and
1502 src_int_info.signedness == .signed) .signed else .unsigned;
1503 switch (dst_mcv) {
1504 .register => |dst_reg| {
1505 const min_abi_size = @min(dst_abi_size, src_abi_size);
1506 const tag: Mir.Inst.Tag = switch (signedness) {
1507 .signed => .movsx,
1508 .unsigned => if (min_abi_size > 2) .mov else .movzx,
1509 };
1510 const dst_alias = switch (tag) {
1511 .movsx => dst_reg.to64(),
1512 .mov, .movzx => if (min_abi_size > 4) dst_reg.to64() else dst_reg.to32(),
1513 else => unreachable,
1514 };
1515 switch (src_mcv) {
1516 .register => |src_reg| {
1517 try self.asmRegisterRegister(
1518 tag,
1519 dst_alias,
1520 registerAlias(src_reg, min_abi_size),
1521 );
1522 },
1523 .stack_offset => |src_off| {
1524 try self.asmRegisterMemory(tag, dst_alias, Memory.sib(
1525 Memory.PtrSize.fromSize(min_abi_size),
1526 .{ .base = .rbp, .disp = -src_off },
1527 ));
1528 },
1529 else => return self.fail("TODO airIntCast from {s} to {s}", .{
1530 @tagName(src_mcv),
1531 @tagName(dst_mcv),
1532 }),
1533 }
1534 if (self.regExtraBits(min_ty) > 0) try self.truncateRegister(min_ty, dst_reg);
1535 },
1536 else => {
1537 try self.setRegOrMem(min_ty, dst_mcv, src_mcv);
1538 const extra = dst_abi_size * 8 - dst_int_info.bits;
1539 if (extra > 0) {
1540 try self.genShiftBinOpMir(switch (signedness) {
1541 .signed => .sal,
1542 .unsigned => .shl,
1543 }, dst_ty, dst_mcv, .{ .immediate = extra });
1544 try self.genShiftBinOpMir(switch (signedness) {
1545 .signed => .sar,
1546 .unsigned => .shr,
1547 }, dst_ty, dst_mcv, .{ .immediate = extra });
1548 }
1549 },
1550 }
1551 break :result dst_mcv;
1536 const src_ty = self.air.typeOf(ty_op.operand);
1537 const src_int_info = src_ty.intInfo(self.target.*);
1538 const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*));
1539 const src_mcv = try self.resolveInst(ty_op.operand);
1540 const src_lock = switch (src_mcv) {
1541 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1542 else => null,
15521543 };
1553 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1544 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
1545
1546 const dst_ty = self.air.typeOfIndex(inst);
1547 const dst_int_info = dst_ty.intInfo(self.target.*);
1548 const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
1549 const dst_mcv = if (dst_abi_size <= src_abi_size and
1550 self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
1551 src_mcv
1552 else
1553 try self.allocRegOrMem(inst, true);
1554
1555 const min_ty = if (dst_int_info.bits < src_int_info.bits) dst_ty else src_ty;
1556 const signedness: std.builtin.Signedness = if (dst_int_info.signedness == .signed and
1557 src_int_info.signedness == .signed) .signed else .unsigned;
1558 switch (dst_mcv) {
1559 .register => |dst_reg| {
1560 const min_abi_size = @min(dst_abi_size, src_abi_size);
1561 const tag: Mir.Inst.Tag = switch (signedness) {
1562 .signed => .movsx,
1563 .unsigned => if (min_abi_size > 2) .mov else .movzx,
1564 };
1565 const dst_alias = switch (tag) {
1566 .movsx => dst_reg.to64(),
1567 .mov, .movzx => if (min_abi_size > 4) dst_reg.to64() else dst_reg.to32(),
1568 else => unreachable,
1569 };
1570 switch (src_mcv) {
1571 .register => |src_reg| {
1572 try self.asmRegisterRegister(
1573 tag,
1574 dst_alias,
1575 registerAlias(src_reg, min_abi_size),
1576 );
1577 },
1578 .stack_offset => |src_off| {
1579 try self.asmRegisterMemory(tag, dst_alias, Memory.sib(
1580 Memory.PtrSize.fromSize(min_abi_size),
1581 .{ .base = .rbp, .disp = -src_off },
1582 ));
1583 },
1584 else => return self.fail("TODO airIntCast from {s} to {s}", .{
1585 @tagName(src_mcv),
1586 @tagName(dst_mcv),
1587 }),
1588 }
1589 if (self.regExtraBits(min_ty) > 0) try self.truncateRegister(min_ty, dst_reg);
1590 },
1591 else => {
1592 try self.setRegOrMem(min_ty, dst_mcv, src_mcv);
1593 const extra = dst_abi_size * 8 - dst_int_info.bits;
1594 if (extra > 0) {
1595 try self.genShiftBinOpMir(switch (signedness) {
1596 .signed => .sal,
1597 .unsigned => .shl,
1598 }, dst_ty, dst_mcv, .{ .immediate = extra });
1599 try self.genShiftBinOpMir(switch (signedness) {
1600 .signed => .sar,
1601 .unsigned => .shr,
1602 }, dst_ty, dst_mcv, .{ .immediate = extra });
1603 }
1604 },
1605 }
1606 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
15541607}
15551608
15561609fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
15571610 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1558 const result = if (self.liveness.isUnused(inst)) .dead else result: {
1559 const dst_ty = self.air.typeOfIndex(inst);
1560 const dst_abi_size = dst_ty.abiSize(self.target.*);
1561 if (dst_abi_size > 8) {
1562 return self.fail("TODO implement trunc for abi sizes larger than 8", .{});
1563 }
1564
1565 const src_mcv = try self.resolveInst(ty_op.operand);
1566 const src_lock = switch (src_mcv) {
1567 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1568 else => null,
1569 };
1570 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
15711611
1572 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
1573 src_mcv
1574 else
1575 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv);
1612 const dst_ty = self.air.typeOfIndex(inst);
1613 const dst_abi_size = dst_ty.abiSize(self.target.*);
1614 if (dst_abi_size > 8) {
1615 return self.fail("TODO implement trunc for abi sizes larger than 8", .{});
1616 }
15761617
1577 // when truncating a `u16` to `u5`, for example, those top 3 bits in the result
1578 // have to be removed. this only happens if the dst if not a power-of-two size.
1579 if (self.regExtraBits(dst_ty) > 0) try self.truncateRegister(dst_ty, dst_mcv.register.to64());
1580 break :result dst_mcv;
1618 const src_mcv = try self.resolveInst(ty_op.operand);
1619 const src_lock = switch (src_mcv) {
1620 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1621 else => null,
15811622 };
1582 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1623 defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
1624
1625 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
1626 src_mcv
1627 else
1628 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv);
1629
1630 // when truncating a `u16` to `u5`, for example, those top 3 bits in the result
1631 // have to be removed. this only happens if the dst if not a power-of-two size.
1632 if (self.regExtraBits(dst_ty) > 0) try self.truncateRegister(dst_ty, dst_mcv.register.to64());
1633
1634 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
15831635}
15841636
15851637fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
15861638 const un_op = self.air.instructions.items(.data)[inst].un_op;
1639 const ty = self.air.typeOfIndex(inst);
1640
15871641 const operand = try self.resolveInst(un_op);
1588 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
1589 return self.finishAir(inst, result, .{ un_op, .none, .none });
1642 const dst_mcv = if (self.reuseOperand(inst, un_op, 0, operand))
1643 operand
1644 else
1645 try self.copyToRegisterWithInstTracking(inst, ty, operand);
1646
1647 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
15901648}
15911649
15921650fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
15931651 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
15941652 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
15951653
1596 if (self.liveness.isUnused(inst)) {
1597 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1598 }
1599
16001654 const ptr = try self.resolveInst(bin_op.lhs);
16011655 const ptr_ty = self.air.typeOf(bin_op.lhs);
16021656 const len = try self.resolveInst(bin_op.rhs);
......@@ -1612,33 +1666,21 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
16121666
16131667fn airUnOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
16141668 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1615
1616 const result = if (self.liveness.isUnused(inst))
1617 .dead
1618 else
1619 try self.genUnOp(inst, tag, ty_op.operand);
1620 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1669 const dst_mcv = try self.genUnOp(inst, tag, ty_op.operand);
1670 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
16211671}
16221672
16231673fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
16241674 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1625
1626 const result = if (self.liveness.isUnused(inst))
1627 .dead
1628 else
1629 try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
1630 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1675 const dst_mcv = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
1676 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
16311677}
16321678
16331679fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
16341680 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
16351681 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1636
1637 const result = if (self.liveness.isUnused(inst))
1638 .dead
1639 else
1640 try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
1641 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1682 const dst_mcv = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
1683 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
16421684}
16431685
16441686fn activeIntBits(self: *Self, dst_air: Air.Inst.Ref) u16 {
......@@ -1678,7 +1720,7 @@ fn activeIntBits(self: *Self, dst_air: Air.Inst.Ref) u16 {
16781720
16791721fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
16801722 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1681 const result = if (self.liveness.isUnused(inst)) .dead else result: {
1723 const result = result: {
16821724 const tag = self.air.instructions.items(.tag)[inst];
16831725 const dst_ty = self.air.typeOfIndex(inst);
16841726 if (dst_ty.zigTypeTag() == .Float)
......@@ -1709,168 +1751,162 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
17091751
17101752fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
17111753 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1712 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1713 const ty = self.air.typeOf(bin_op.lhs);
1714
1715 const lhs_mcv = try self.resolveInst(bin_op.lhs);
1716 const dst_mcv = if (lhs_mcv.isRegister() and self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv))
1717 lhs_mcv
1718 else
1719 try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv);
1720 const dst_reg = dst_mcv.register;
1721 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
1722 defer self.register_manager.unlockReg(dst_lock);
1754 const ty = self.air.typeOf(bin_op.lhs);
17231755
1724 const rhs_mcv = try self.resolveInst(bin_op.rhs);
1725 const rhs_lock = switch (rhs_mcv) {
1726 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1727 else => null,
1728 };
1729 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
1730
1731 const limit_reg = try self.register_manager.allocReg(null, gp);
1732 const limit_mcv = MCValue{ .register = limit_reg };
1733 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg);
1734 defer self.register_manager.unlockReg(limit_lock);
1735
1736 const reg_bits = self.regBitSize(ty);
1737 const cc: Condition = if (ty.isSignedInt()) cc: {
1738 try self.genSetReg(ty, limit_reg, dst_mcv);
1739 try self.genShiftBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 });
1740 try self.genBinOpMir(.xor, ty, limit_mcv, .{
1741 .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1,
1742 });
1743 break :cc .o;
1744 } else cc: {
1745 try self.genSetReg(ty, limit_reg, .{
1746 .immediate = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits),
1747 });
1748 break :cc .c;
1749 };
1750 try self.genBinOpMir(.add, ty, dst_mcv, rhs_mcv);
1756 const lhs_mcv = try self.resolveInst(bin_op.lhs);
1757 const dst_mcv = if (lhs_mcv.isRegister() and self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv))
1758 lhs_mcv
1759 else
1760 try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv);
1761 const dst_reg = dst_mcv.register;
1762 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
1763 defer self.register_manager.unlockReg(dst_lock);
17511764
1752 const cmov_abi_size = @max(@intCast(u32, ty.abiSize(self.target.*)), 2);
1753 try self.asmCmovccRegisterRegister(
1754 registerAlias(dst_reg, cmov_abi_size),
1755 registerAlias(limit_reg, cmov_abi_size),
1756 cc,
1757 );
1758 break :result dst_mcv;
1765 const rhs_mcv = try self.resolveInst(bin_op.rhs);
1766 const rhs_lock = switch (rhs_mcv) {
1767 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1768 else => null,
17591769 };
1760 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1770 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
1771
1772 const limit_reg = try self.register_manager.allocReg(null, gp);
1773 const limit_mcv = MCValue{ .register = limit_reg };
1774 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg);
1775 defer self.register_manager.unlockReg(limit_lock);
1776
1777 const reg_bits = self.regBitSize(ty);
1778 const cc: Condition = if (ty.isSignedInt()) cc: {
1779 try self.genSetReg(ty, limit_reg, dst_mcv);
1780 try self.genShiftBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 });
1781 try self.genBinOpMir(.xor, ty, limit_mcv, .{
1782 .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1,
1783 });
1784 break :cc .o;
1785 } else cc: {
1786 try self.genSetReg(ty, limit_reg, .{
1787 .immediate = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits),
1788 });
1789 break :cc .c;
1790 };
1791 try self.genBinOpMir(.add, ty, dst_mcv, rhs_mcv);
1792
1793 const cmov_abi_size = @max(@intCast(u32, ty.abiSize(self.target.*)), 2);
1794 try self.asmCmovccRegisterRegister(
1795 registerAlias(dst_reg, cmov_abi_size),
1796 registerAlias(limit_reg, cmov_abi_size),
1797 cc,
1798 );
1799
1800 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
17611801}
17621802
17631803fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
17641804 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1765 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1766 const ty = self.air.typeOf(bin_op.lhs);
1805 const ty = self.air.typeOf(bin_op.lhs);
17671806
1768 const lhs_mcv = try self.resolveInst(bin_op.lhs);
1769 const dst_mcv = if (lhs_mcv.isRegister() and self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv))
1770 lhs_mcv
1771 else
1772 try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv);
1773 const dst_reg = dst_mcv.register;
1774 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
1775 defer self.register_manager.unlockReg(dst_lock);
1776
1777 const rhs_mcv = try self.resolveInst(bin_op.rhs);
1778 const rhs_lock = switch (rhs_mcv) {
1779 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1780 else => null,
1781 };
1782 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
1783
1784 const limit_reg = try self.register_manager.allocReg(null, gp);
1785 const limit_mcv = MCValue{ .register = limit_reg };
1786 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg);
1787 defer self.register_manager.unlockReg(limit_lock);
1788
1789 const reg_bits = self.regBitSize(ty);
1790 const cc: Condition = if (ty.isSignedInt()) cc: {
1791 try self.genSetReg(ty, limit_reg, dst_mcv);
1792 try self.genShiftBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 });
1793 try self.genBinOpMir(.xor, ty, limit_mcv, .{
1794 .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1,
1795 });
1796 break :cc .o;
1797 } else cc: {
1798 try self.genSetReg(ty, limit_reg, .{ .immediate = 0 });
1799 break :cc .c;
1800 };
1801 try self.genBinOpMir(.sub, ty, dst_mcv, rhs_mcv);
1807 const lhs_mcv = try self.resolveInst(bin_op.lhs);
1808 const dst_mcv = if (lhs_mcv.isRegister() and self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv))
1809 lhs_mcv
1810 else
1811 try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv);
1812 const dst_reg = dst_mcv.register;
1813 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
1814 defer self.register_manager.unlockReg(dst_lock);
18021815
1803 const cmov_abi_size = @max(@intCast(u32, ty.abiSize(self.target.*)), 2);
1804 try self.asmCmovccRegisterRegister(
1805 registerAlias(dst_reg, cmov_abi_size),
1806 registerAlias(limit_reg, cmov_abi_size),
1807 cc,
1808 );
1809 break :result dst_mcv;
1816 const rhs_mcv = try self.resolveInst(bin_op.rhs);
1817 const rhs_lock = switch (rhs_mcv) {
1818 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1819 else => null,
18101820 };
1811 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1821 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
1822
1823 const limit_reg = try self.register_manager.allocReg(null, gp);
1824 const limit_mcv = MCValue{ .register = limit_reg };
1825 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg);
1826 defer self.register_manager.unlockReg(limit_lock);
1827
1828 const reg_bits = self.regBitSize(ty);
1829 const cc: Condition = if (ty.isSignedInt()) cc: {
1830 try self.genSetReg(ty, limit_reg, dst_mcv);
1831 try self.genShiftBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 });
1832 try self.genBinOpMir(.xor, ty, limit_mcv, .{
1833 .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1,
1834 });
1835 break :cc .o;
1836 } else cc: {
1837 try self.genSetReg(ty, limit_reg, .{ .immediate = 0 });
1838 break :cc .c;
1839 };
1840 try self.genBinOpMir(.sub, ty, dst_mcv, rhs_mcv);
1841
1842 const cmov_abi_size = @max(@intCast(u32, ty.abiSize(self.target.*)), 2);
1843 try self.asmCmovccRegisterRegister(
1844 registerAlias(dst_reg, cmov_abi_size),
1845 registerAlias(limit_reg, cmov_abi_size),
1846 cc,
1847 );
1848
1849 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
18121850}
18131851
18141852fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
18151853 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1816 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1817 const ty = self.air.typeOf(bin_op.lhs);
1854 const ty = self.air.typeOf(bin_op.lhs);
18181855
1819 try self.spillRegisters(&.{ .rax, .rdx });
1820 const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx });
1821 defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock);
1856 try self.spillRegisters(&.{ .rax, .rdx });
1857 const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx });
1858 defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock);
18221859
1823 const lhs_mcv = try self.resolveInst(bin_op.lhs);
1824 const lhs_lock = switch (lhs_mcv) {
1825 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1826 else => null,
1827 };
1828 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
1860 const lhs_mcv = try self.resolveInst(bin_op.lhs);
1861 const lhs_lock = switch (lhs_mcv) {
1862 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1863 else => null,
1864 };
1865 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
18291866
1830 const rhs_mcv = try self.resolveInst(bin_op.rhs);
1831 const rhs_lock = switch (rhs_mcv) {
1832 .register => |reg| self.register_manager.lockReg(reg),
1833 else => null,
1834 };
1835 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
1836
1837 const limit_reg = try self.register_manager.allocReg(null, gp);
1838 const limit_mcv = MCValue{ .register = limit_reg };
1839 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg);
1840 defer self.register_manager.unlockReg(limit_lock);
1841
1842 const reg_bits = self.regBitSize(ty);
1843 const cc: Condition = if (ty.isSignedInt()) cc: {
1844 try self.genSetReg(ty, limit_reg, lhs_mcv);
1845 try self.genBinOpMir(.xor, ty, limit_mcv, rhs_mcv);
1846 try self.genShiftBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 });
1847 try self.genBinOpMir(.xor, ty, limit_mcv, .{
1848 .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1,
1849 });
1850 break :cc .o;
1851 } else cc: {
1852 try self.genSetReg(ty, limit_reg, .{
1853 .immediate = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits),
1854 });
1855 break :cc .c;
1856 };
1867 const rhs_mcv = try self.resolveInst(bin_op.rhs);
1868 const rhs_lock = switch (rhs_mcv) {
1869 .register => |reg| self.register_manager.lockReg(reg),
1870 else => null,
1871 };
1872 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
18571873
1858 const dst_mcv = try self.genMulDivBinOp(.mul, inst, ty, ty, lhs_mcv, rhs_mcv);
1859 const cmov_abi_size = @max(@intCast(u32, ty.abiSize(self.target.*)), 2);
1860 try self.asmCmovccRegisterRegister(
1861 registerAlias(dst_mcv.register, cmov_abi_size),
1862 registerAlias(limit_reg, cmov_abi_size),
1863 cc,
1864 );
1865 break :result dst_mcv;
1874 const limit_reg = try self.register_manager.allocReg(null, gp);
1875 const limit_mcv = MCValue{ .register = limit_reg };
1876 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg);
1877 defer self.register_manager.unlockReg(limit_lock);
1878
1879 const reg_bits = self.regBitSize(ty);
1880 const cc: Condition = if (ty.isSignedInt()) cc: {
1881 try self.genSetReg(ty, limit_reg, lhs_mcv);
1882 try self.genBinOpMir(.xor, ty, limit_mcv, rhs_mcv);
1883 try self.genShiftBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 });
1884 try self.genBinOpMir(.xor, ty, limit_mcv, .{
1885 .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1,
1886 });
1887 break :cc .o;
1888 } else cc: {
1889 try self.genSetReg(ty, limit_reg, .{
1890 .immediate = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits),
1891 });
1892 break :cc .c;
18661893 };
1867 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1894
1895 const dst_mcv = try self.genMulDivBinOp(.mul, inst, ty, ty, lhs_mcv, rhs_mcv);
1896 const cmov_abi_size = @max(@intCast(u32, ty.abiSize(self.target.*)), 2);
1897 try self.asmCmovccRegisterRegister(
1898 registerAlias(dst_mcv.register, cmov_abi_size),
1899 registerAlias(limit_reg, cmov_abi_size),
1900 cc,
1901 );
1902
1903 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
18681904}
18691905
18701906fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
18711907 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
18721908 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1873 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1909 const result: MCValue = result: {
18741910 const tag = self.air.instructions.items(.tag)[inst];
18751911 const ty = self.air.typeOf(bin_op.lhs);
18761912 switch (ty.zigTypeTag()) {
......@@ -1929,7 +1965,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
19291965fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
19301966 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
19311967 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1932 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1968 const result: MCValue = result: {
19331969 const lhs_ty = self.air.typeOf(bin_op.lhs);
19341970 const rhs_ty = self.air.typeOf(bin_op.rhs);
19351971 switch (lhs_ty.zigTypeTag()) {
......@@ -2051,7 +2087,7 @@ fn genSetStackTruncatedOverflowCompare(
20512087fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
20522088 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
20532089 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
2054 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2090 const result: MCValue = result: {
20552091 const dst_ty = self.air.typeOf(bin_op.lhs);
20562092 switch (dst_ty.zigTypeTag()) {
20572093 .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}),
......@@ -2240,10 +2276,6 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa
22402276fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void {
22412277 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
22422278
2243 if (self.liveness.isUnused(inst)) {
2244 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
2245 }
2246
22472279 try self.spillRegisters(&.{.rcx});
22482280
22492281 const tag = self.air.instructions.items(.tag)[inst];
......@@ -2260,18 +2292,14 @@ fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void {
22602292
22612293fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
22622294 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2263 const result: MCValue = if (self.liveness.isUnused(inst))
2264 .dead
2265 else
2266 return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
2267 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2295 _ = bin_op;
2296 return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
2297 //return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
22682298}
22692299
22702300fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
22712301 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
22722302 const result: MCValue = result: {
2273 if (self.liveness.isUnused(inst)) break :result .none;
2274
22752303 const pl_ty = self.air.typeOfIndex(inst);
22762304 const opt_mcv = try self.resolveInst(ty_op.operand);
22772305
......@@ -2296,18 +2324,15 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
22962324
22972325fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
22982326 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2299 const result: MCValue = result: {
2300 if (self.liveness.isUnused(inst)) break :result .dead;
23012327
2302 const dst_ty = self.air.typeOfIndex(inst);
2303 const opt_mcv = try self.resolveInst(ty_op.operand);
2328 const dst_ty = self.air.typeOfIndex(inst);
2329 const opt_mcv = try self.resolveInst(ty_op.operand);
23042330
2305 break :result if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv))
2306 opt_mcv
2307 else
2308 try self.copyToRegisterWithInstTracking(inst, dst_ty, opt_mcv);
2309 };
2310 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2331 const dst_mcv = if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv))
2332 opt_mcv
2333 else
2334 try self.copyToRegisterWithInstTracking(inst, dst_ty, opt_mcv);
2335 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
23112336}
23122337
23132338fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
......@@ -2320,7 +2345,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
23202345
23212346 if (opt_ty.optionalReprIsPayload()) {
23222347 break :result if (self.liveness.isUnused(inst))
2323 .dead
2348 .unreach
23242349 else if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
23252350 src_mcv
23262351 else
......@@ -2339,16 +2364,13 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
23392364 Memory.sib(.byte, .{ .base = dst_mcv.register, .disp = pl_abi_size }),
23402365 Immediate.u(1),
23412366 );
2342 break :result if (self.liveness.isUnused(inst)) .dead else dst_mcv;
2367 break :result if (self.liveness.isUnused(inst)) .unreach else dst_mcv;
23432368 };
23442369 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
23452370}
23462371
23472372fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
23482373 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2349 if (self.liveness.isUnused(inst)) {
2350 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
2351 }
23522374 const err_union_ty = self.air.typeOf(ty_op.operand);
23532375 const err_ty = err_union_ty.errorUnionSet();
23542376 const payload_ty = err_union_ty.errorUnionPayload();
......@@ -2391,9 +2413,6 @@ fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
23912413
23922414fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
23932415 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2394 if (self.liveness.isUnused(inst)) {
2395 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
2396 }
23972416 const err_union_ty = self.air.typeOf(ty_op.operand);
23982417 const operand = try self.resolveInst(ty_op.operand);
23992418 const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, operand);
......@@ -2444,72 +2463,68 @@ fn genUnwrapErrorUnionPayloadMir(
24442463// *(E!T) -> E
24452464fn airUnwrapErrUnionErrPtr(self: *Self, inst: Air.Inst.Index) !void {
24462465 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2447 const result: MCValue = result: {
2448 if (self.liveness.isUnused(inst)) break :result .dead;
24492466
2450 const src_ty = self.air.typeOf(ty_op.operand);
2451 const src_mcv = try self.resolveInst(ty_op.operand);
2452 const src_reg = switch (src_mcv) {
2453 .register => |reg| reg,
2454 else => try self.copyToTmpRegister(src_ty, src_mcv),
2455 };
2456 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
2457 defer self.register_manager.unlockReg(src_lock);
2467 const src_ty = self.air.typeOf(ty_op.operand);
2468 const src_mcv = try self.resolveInst(ty_op.operand);
2469 const src_reg = switch (src_mcv) {
2470 .register => |reg| reg,
2471 else => try self.copyToTmpRegister(src_ty, src_mcv),
2472 };
2473 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
2474 defer self.register_manager.unlockReg(src_lock);
24582475
2459 const dst_reg = try self.register_manager.allocReg(inst, gp);
2460 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
2461 defer self.register_manager.unlockReg(dst_lock);
2476 const dst_reg = try self.register_manager.allocReg(inst, gp);
2477 const dst_mcv = MCValue{ .register = dst_reg };
2478 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
2479 defer self.register_manager.unlockReg(dst_lock);
24622480
2463 const eu_ty = src_ty.childType();
2464 const pl_ty = eu_ty.errorUnionPayload();
2465 const err_ty = eu_ty.errorUnionSet();
2466 const err_off = @intCast(i32, errUnionErrorOffset(pl_ty, self.target.*));
2467 const err_abi_size = @intCast(u32, err_ty.abiSize(self.target.*));
2468 try self.asmRegisterMemory(
2469 .mov,
2470 registerAlias(dst_reg, err_abi_size),
2471 Memory.sib(Memory.PtrSize.fromSize(err_abi_size), .{ .base = src_reg, .disp = err_off }),
2472 );
2473 break :result .{ .register = dst_reg };
2474 };
2475 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2481 const eu_ty = src_ty.childType();
2482 const pl_ty = eu_ty.errorUnionPayload();
2483 const err_ty = eu_ty.errorUnionSet();
2484 const err_off = @intCast(i32, errUnionErrorOffset(pl_ty, self.target.*));
2485 const err_abi_size = @intCast(u32, err_ty.abiSize(self.target.*));
2486 try self.asmRegisterMemory(
2487 .mov,
2488 registerAlias(dst_reg, err_abi_size),
2489 Memory.sib(Memory.PtrSize.fromSize(err_abi_size), .{ .base = src_reg, .disp = err_off }),
2490 );
2491
2492 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
24762493}
24772494
24782495// *(E!T) -> *T
24792496fn airUnwrapErrUnionPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
24802497 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2481 const result: MCValue = result: {
2482 if (self.liveness.isUnused(inst)) break :result .dead;
24832498
2484 const src_ty = self.air.typeOf(ty_op.operand);
2485 const src_mcv = try self.resolveInst(ty_op.operand);
2486 const src_reg = switch (src_mcv) {
2487 .register => |reg| reg,
2488 else => try self.copyToTmpRegister(src_ty, src_mcv),
2489 };
2490 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
2491 defer self.register_manager.unlockReg(src_lock);
2499 const src_ty = self.air.typeOf(ty_op.operand);
2500 const src_mcv = try self.resolveInst(ty_op.operand);
2501 const src_reg = switch (src_mcv) {
2502 .register => |reg| reg,
2503 else => try self.copyToTmpRegister(src_ty, src_mcv),
2504 };
2505 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
2506 defer self.register_manager.unlockReg(src_lock);
24922507
2493 const dst_ty = self.air.typeOfIndex(inst);
2494 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2495 src_reg
2496 else
2497 try self.register_manager.allocReg(inst, gp);
2498 const dst_lock = self.register_manager.lockReg(dst_reg);
2499 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
2508 const dst_ty = self.air.typeOfIndex(inst);
2509 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2510 src_reg
2511 else
2512 try self.register_manager.allocReg(inst, gp);
2513 const dst_mcv = MCValue{ .register = dst_reg };
2514 const dst_lock = self.register_manager.lockReg(dst_reg);
2515 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
25002516
2501 const eu_ty = src_ty.childType();
2502 const pl_ty = eu_ty.errorUnionPayload();
2503 const pl_off = @intCast(i32, errUnionPayloadOffset(pl_ty, self.target.*));
2504 const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
2505 try self.asmRegisterMemory(
2506 .lea,
2507 registerAlias(dst_reg, dst_abi_size),
2508 Memory.sib(.qword, .{ .base = src_reg, .disp = pl_off }),
2509 );
2510 break :result .{ .register = dst_reg };
2511 };
2512 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2517 const eu_ty = src_ty.childType();
2518 const pl_ty = eu_ty.errorUnionPayload();
2519 const pl_off = @intCast(i32, errUnionPayloadOffset(pl_ty, self.target.*));
2520 const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
2521 try self.asmRegisterMemory(
2522 .lea,
2523 registerAlias(dst_reg, dst_abi_size),
2524 Memory.sib(.qword, .{ .base = src_reg, .disp = pl_off }),
2525 );
2526
2527 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
25132528}
25142529
25152530fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
......@@ -2535,7 +2550,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
25352550 Immediate.u(0),
25362551 );
25372552
2538 if (self.liveness.isUnused(inst)) break :result .dead;
2553 if (self.liveness.isUnused(inst)) break :result .unreach;
25392554
25402555 const dst_ty = self.air.typeOfIndex(inst);
25412556 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
......@@ -2558,11 +2573,9 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
25582573}
25592574
25602575fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
2561 const result: MCValue = if (self.liveness.isUnused(inst))
2562 .dead
2563 else
2564 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
2565 return self.finishAir(inst, result, .{ .none, .none, .none });
2576 _ = inst;
2577 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
2578 //return self.finishAir(inst, result, .{ .none, .none, .none });
25662579}
25672580
25682581fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
......@@ -2578,8 +2591,6 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
25782591fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
25792592 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
25802593 const result: MCValue = result: {
2581 if (self.liveness.isUnused(inst)) break :result .dead;
2582
25832594 const pl_ty = self.air.typeOf(ty_op.operand);
25842595 if (!pl_ty.hasRuntimeBits()) break :result .{ .immediate = 1 };
25852596
......@@ -2624,10 +2635,6 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
26242635fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
26252636 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
26262637
2627 if (self.liveness.isUnused(inst)) {
2628 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
2629 }
2630
26312638 const error_union_ty = self.air.getRefType(ty_op.ty);
26322639 const payload_ty = error_union_ty.errorUnionPayload();
26332640 const operand = try self.resolveInst(ty_op.operand);
......@@ -2654,9 +2661,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
26542661/// E to E!T
26552662fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
26562663 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2657 if (self.liveness.isUnused(inst)) {
2658 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
2659 }
2664
26602665 const error_union_ty = self.air.getRefType(ty_op.ty);
26612666 const payload_ty = error_union_ty.errorUnionPayload();
26622667 const operand = try self.resolveInst(ty_op.operand);
......@@ -2682,7 +2687,7 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
26822687
26832688fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
26842689 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2685 const result = if (self.liveness.isUnused(inst)) .dead else result: {
2690 const result = result: {
26862691 const src_mcv = try self.resolveInst(ty_op.operand);
26872692 if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result src_mcv;
26882693
......@@ -2696,72 +2701,65 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
26962701
26972702fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
26982703 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2699 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2700 const operand = try self.resolveInst(ty_op.operand);
2701 const dst_mcv: MCValue = blk: {
2702 switch (operand) {
2703 .stack_offset => |off| {
2704 break :blk MCValue{ .stack_offset = off - 8 };
2705 },
2706 else => return self.fail("TODO implement slice_len for {}", .{operand}),
2707 }
2708 };
2709 break :result dst_mcv;
2704
2705 const operand = try self.resolveInst(ty_op.operand);
2706 const dst_mcv: MCValue = blk: {
2707 switch (operand) {
2708 .stack_offset => |off| {
2709 break :blk MCValue{ .stack_offset = off - 8 };
2710 },
2711 else => return self.fail("TODO implement slice_len for {}", .{operand}),
2712 }
27102713 };
2711 log.debug("airSliceLen(%{d}): {}", .{ inst, result });
2712 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2714
2715 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
27132716}
27142717
27152718fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
27162719 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2717 const result: MCValue = result: {
2718 if (self.liveness.isUnused(inst)) break :result .dead;
27192720
2720 const src_ty = self.air.typeOf(ty_op.operand);
2721 const src_mcv = try self.resolveInst(ty_op.operand);
2722 const src_reg = switch (src_mcv) {
2723 .register => |reg| reg,
2724 else => try self.copyToTmpRegister(src_ty, src_mcv),
2725 };
2726 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
2727 defer self.register_manager.unlockReg(src_lock);
2721 const src_ty = self.air.typeOf(ty_op.operand);
2722 const src_mcv = try self.resolveInst(ty_op.operand);
2723 const src_reg = switch (src_mcv) {
2724 .register => |reg| reg,
2725 else => try self.copyToTmpRegister(src_ty, src_mcv),
2726 };
2727 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
2728 defer self.register_manager.unlockReg(src_lock);
27282729
2729 const dst_ty = self.air.typeOfIndex(inst);
2730 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2731 src_reg
2732 else
2733 try self.register_manager.allocReg(inst, gp);
2734 const dst_lock = self.register_manager.lockReg(dst_reg);
2735 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
2730 const dst_ty = self.air.typeOfIndex(inst);
2731 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2732 src_reg
2733 else
2734 try self.register_manager.allocReg(inst, gp);
2735 const dst_mcv = MCValue{ .register = dst_reg };
2736 const dst_lock = self.register_manager.lockReg(dst_reg);
2737 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
27362738
2737 const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
2738 try self.asmRegisterMemory(
2739 .lea,
2740 registerAlias(dst_reg, dst_abi_size),
2741 Memory.sib(.qword, .{
2742 .base = src_reg,
2743 .disp = @divExact(self.target.cpu.arch.ptrBitWidth(), 8),
2744 }),
2745 );
2746 break :result .{ .register = dst_reg };
2747 };
2748 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2739 const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
2740 try self.asmRegisterMemory(
2741 .lea,
2742 registerAlias(dst_reg, dst_abi_size),
2743 Memory.sib(.qword, .{
2744 .base = src_reg,
2745 .disp = @divExact(self.target.cpu.arch.ptrBitWidth(), 8),
2746 }),
2747 );
2748
2749 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
27492750}
27502751
27512752fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
27522753 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2753 const result: MCValue = result: {
2754 if (self.liveness.isUnused(inst)) break :result .dead;
27552754
2756 const dst_ty = self.air.typeOfIndex(inst);
2757 const opt_mcv = try self.resolveInst(ty_op.operand);
2755 const dst_ty = self.air.typeOfIndex(inst);
2756 const opt_mcv = try self.resolveInst(ty_op.operand);
27582757
2759 break :result if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv))
2760 opt_mcv
2761 else
2762 try self.copyToRegisterWithInstTracking(inst, dst_ty, opt_mcv);
2763 };
2764 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2758 const dst_mcv = if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv))
2759 opt_mcv
2760 else
2761 try self.copyToRegisterWithInstTracking(inst, dst_ty, opt_mcv);
2762 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
27652763}
27662764
27672765fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Register {
......@@ -2829,34 +2827,26 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
28292827fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
28302828 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
28312829 const slice_ty = self.air.typeOf(bin_op.lhs);
2832 const result = if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: {
2833 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
2834 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
2835 const elem_ptr = try self.genSliceElemPtr(bin_op.lhs, bin_op.rhs);
2836 const dst_mcv = try self.allocRegOrMem(inst, false);
2837 try self.load(dst_mcv, elem_ptr, slice_ptr_field_type);
2838 break :result dst_mcv;
2839 };
2840 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2830
2831 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
2832 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
2833 const elem_ptr = try self.genSliceElemPtr(bin_op.lhs, bin_op.rhs);
2834 const dst_mcv = try self.allocRegOrMem(inst, false);
2835 try self.load(dst_mcv, elem_ptr, slice_ptr_field_type);
2836
2837 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
28412838}
28422839
28432840fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
28442841 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
28452842 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2846 const result: MCValue = if (self.liveness.isUnused(inst))
2847 .dead
2848 else
2849 try self.genSliceElemPtr(extra.lhs, extra.rhs);
2850 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2843 const dst_mcv = try self.genSliceElemPtr(extra.lhs, extra.rhs);
2844 return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none });
28512845}
28522846
28532847fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
28542848 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
28552849
2856 if (self.liveness.isUnused(inst)) {
2857 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
2858 }
2859
28602850 const array_ty = self.air.typeOf(bin_op.lhs);
28612851 const array = try self.resolveInst(bin_op.lhs);
28622852 const array_lock: ?RegisterLock = switch (array) {
......@@ -2923,77 +2913,74 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
29232913fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
29242914 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
29252915 const ptr_ty = self.air.typeOf(bin_op.lhs);
2926 const result = if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: {
2927 // this is identical to the `airPtrElemPtr` codegen expect here an
2928 // additional `mov` is needed at the end to get the actual value
2929
2930 const elem_ty = ptr_ty.elemType2();
2931 const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*));
2932 const index_ty = self.air.typeOf(bin_op.rhs);
2933 const index_mcv = try self.resolveInst(bin_op.rhs);
2934 const index_lock = switch (index_mcv) {
2935 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2936 else => null,
2937 };
2938 defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
29392916
2940 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_abi_size);
2941 const offset_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
2942 defer self.register_manager.unlockReg(offset_lock);
2917 // this is identical to the `airPtrElemPtr` codegen expect here an
2918 // additional `mov` is needed at the end to get the actual value
29432919
2944 const ptr_mcv = try self.resolveInst(bin_op.lhs);
2945 const elem_ptr_reg = if (ptr_mcv.isRegister() and self.liveness.operandDies(inst, 0))
2946 ptr_mcv.register
2947 else
2948 try self.copyToTmpRegister(ptr_ty, ptr_mcv);
2949 const elem_ptr_lock = self.register_manager.lockRegAssumeUnused(elem_ptr_reg);
2950 defer self.register_manager.unlockReg(elem_ptr_lock);
2951 try self.asmRegisterRegister(.add, elem_ptr_reg, offset_reg);
2920 const elem_ty = ptr_ty.elemType2();
2921 const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*));
2922 const index_ty = self.air.typeOf(bin_op.rhs);
2923 const index_mcv = try self.resolveInst(bin_op.rhs);
2924 const index_lock = switch (index_mcv) {
2925 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2926 else => null,
2927 };
2928 defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
29522929
2953 const dst_mcv = try self.allocRegOrMem(inst, true);
2954 const dst_lock = switch (dst_mcv) {
2955 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2956 else => null,
2957 };
2958 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
2959 try self.load(dst_mcv, .{ .register = elem_ptr_reg }, ptr_ty);
2960 break :result dst_mcv;
2930 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_abi_size);
2931 const offset_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
2932 defer self.register_manager.unlockReg(offset_lock);
2933
2934 const ptr_mcv = try self.resolveInst(bin_op.lhs);
2935 const elem_ptr_reg = if (ptr_mcv.isRegister() and self.liveness.operandDies(inst, 0))
2936 ptr_mcv.register
2937 else
2938 try self.copyToTmpRegister(ptr_ty, ptr_mcv);
2939 const elem_ptr_lock = self.register_manager.lockRegAssumeUnused(elem_ptr_reg);
2940 defer self.register_manager.unlockReg(elem_ptr_lock);
2941 try self.asmRegisterRegister(.add, elem_ptr_reg, offset_reg);
2942
2943 const dst_mcv = try self.allocRegOrMem(inst, true);
2944 const dst_lock = switch (dst_mcv) {
2945 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2946 else => null,
29612947 };
2962 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2948 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
2949 try self.load(dst_mcv, .{ .register = elem_ptr_reg }, ptr_ty);
2950
2951 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
29632952}
29642953
29652954fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
29662955 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
29672956 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
29682957
2969 const result = if (self.liveness.isUnused(inst)) .dead else result: {
2970 const ptr_ty = self.air.typeOf(extra.lhs);
2971 const ptr = try self.resolveInst(extra.lhs);
2972 const ptr_lock: ?RegisterLock = switch (ptr) {
2973 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2974 else => null,
2975 };
2976 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
2958 const ptr_ty = self.air.typeOf(extra.lhs);
2959 const ptr = try self.resolveInst(extra.lhs);
2960 const ptr_lock: ?RegisterLock = switch (ptr) {
2961 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2962 else => null,
2963 };
2964 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
29772965
2978 const elem_ty = ptr_ty.elemType2();
2979 const elem_abi_size = elem_ty.abiSize(self.target.*);
2980 const index_ty = self.air.typeOf(extra.rhs);
2981 const index = try self.resolveInst(extra.rhs);
2982 const index_lock: ?RegisterLock = switch (index) {
2983 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2984 else => null,
2985 };
2986 defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
2966 const elem_ty = ptr_ty.elemType2();
2967 const elem_abi_size = elem_ty.abiSize(self.target.*);
2968 const index_ty = self.air.typeOf(extra.rhs);
2969 const index = try self.resolveInst(extra.rhs);
2970 const index_lock: ?RegisterLock = switch (index) {
2971 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2972 else => null,
2973 };
2974 defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
2975
2976 const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size);
2977 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
2978 defer self.register_manager.unlockReg(offset_reg_lock);
29872979
2988 const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size);
2989 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
2990 defer self.register_manager.unlockReg(offset_reg_lock);
2980 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr);
2981 try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
29912982
2992 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr);
2993 try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
2994 break :result dst_mcv;
2995 };
2996 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2983 return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none });
29972984}
29982985
29992986fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
......@@ -3035,9 +3022,6 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
30353022
30363023fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
30373024 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3038 if (self.liveness.isUnused(inst)) {
3039 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
3040 }
30413025
30423026 const tag_ty = self.air.typeOfIndex(inst);
30433027 const union_ty = self.air.typeOf(ty_op.operand);
......@@ -3089,8 +3073,6 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
30893073fn airClz(self: *Self, inst: Air.Inst.Index) !void {
30903074 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
30913075 const result = result: {
3092 if (self.liveness.isUnused(inst)) break :result .dead;
3093
30943076 const dst_ty = self.air.typeOfIndex(inst);
30953077 const src_ty = self.air.typeOf(ty_op.operand);
30963078
......@@ -3158,8 +3140,6 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {
31583140fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
31593141 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
31603142 const result = result: {
3161 if (self.liveness.isUnused(inst)) break :result .dead;
3162
31633143 const dst_ty = self.air.typeOfIndex(inst);
31643144 const src_ty = self.air.typeOf(ty_op.operand);
31653145 const src_bits = src_ty.bitSize(self.target.*);
......@@ -3216,8 +3196,6 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
32163196fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
32173197 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
32183198 const result: MCValue = result: {
3219 if (self.liveness.isUnused(inst)) break :result .dead;
3220
32213199 const src_ty = self.air.typeOf(ty_op.operand);
32223200 const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*));
32233201 const src_mcv = try self.resolveInst(ty_op.operand);
......@@ -3386,148 +3364,138 @@ fn byteSwap(self: *Self, inst: Air.Inst.Index, src_ty: Type, src_mcv: MCValue, m
33863364
33873365fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
33883366 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3389 const result = result: {
3390 if (self.liveness.isUnused(inst)) break :result .dead;
3391
3392 const src_ty = self.air.typeOf(ty_op.operand);
3393 const src_mcv = try self.resolveInst(ty_op.operand);
33943367
3395 const dst_mcv = try self.byteSwap(inst, src_ty, src_mcv, true);
3396 switch (self.regExtraBits(src_ty)) {
3397 0 => {},
3398 else => |extra| try self.genBinOpMir(
3399 if (src_ty.isSignedInt()) .sar else .shr,
3400 src_ty,
3401 dst_mcv,
3402 .{ .immediate = extra },
3403 ),
3404 }
3405 break :result dst_mcv;
3406 };
3368 const src_ty = self.air.typeOf(ty_op.operand);
3369 const src_mcv = try self.resolveInst(ty_op.operand);
3370
3371 const dst_mcv = try self.byteSwap(inst, src_ty, src_mcv, true);
3372 switch (self.regExtraBits(src_ty)) {
3373 0 => {},
3374 else => |extra| try self.genBinOpMir(
3375 if (src_ty.isSignedInt()) .sar else .shr,
3376 src_ty,
3377 dst_mcv,
3378 .{ .immediate = extra },
3379 ),
3380 }
34073381
3408 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3382 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
34093383}
34103384
34113385fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
34123386 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3413 const result = result: {
3414 if (self.liveness.isUnused(inst)) break :result .dead;
34153387
3416 const src_ty = self.air.typeOf(ty_op.operand);
3417 const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*));
3418 const src_mcv = try self.resolveInst(ty_op.operand);
3388 const src_ty = self.air.typeOf(ty_op.operand);
3389 const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*));
3390 const src_mcv = try self.resolveInst(ty_op.operand);
34193391
3420 const dst_mcv = try self.byteSwap(inst, src_ty, src_mcv, false);
3421 const dst_reg = dst_mcv.register;
3422 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
3423 defer self.register_manager.unlockReg(dst_lock);
3392 const dst_mcv = try self.byteSwap(inst, src_ty, src_mcv, false);
3393 const dst_reg = dst_mcv.register;
3394 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
3395 defer self.register_manager.unlockReg(dst_lock);
34243396
3425 const tmp_reg = try self.register_manager.allocReg(null, gp);
3426 const tmp_lock = self.register_manager.lockReg(tmp_reg);
3427 defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock);
3397 const tmp_reg = try self.register_manager.allocReg(null, gp);
3398 const tmp_lock = self.register_manager.lockReg(tmp_reg);
3399 defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock);
34283400
3429 {
3430 const dst = registerAlias(dst_reg, src_abi_size);
3431 const tmp = registerAlias(tmp_reg, src_abi_size);
3432 const imm = if (src_abi_size > 4)
3433 try self.register_manager.allocReg(null, gp)
3434 else
3435 undefined;
3401 {
3402 const dst = registerAlias(dst_reg, src_abi_size);
3403 const tmp = registerAlias(tmp_reg, src_abi_size);
3404 const imm = if (src_abi_size > 4)
3405 try self.register_manager.allocReg(null, gp)
3406 else
3407 undefined;
34363408
3437 const mask = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - src_abi_size * 8);
3438 const imm_0000_1111 = Immediate.u(mask / 0b0001_0001);
3439 const imm_00_11 = Immediate.u(mask / 0b01_01);
3440 const imm_0_1 = Immediate.u(mask / 0b1_1);
3409 const mask = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - src_abi_size * 8);
3410 const imm_0000_1111 = Immediate.u(mask / 0b0001_0001);
3411 const imm_00_11 = Immediate.u(mask / 0b01_01);
3412 const imm_0_1 = Immediate.u(mask / 0b1_1);
34413413
3442 // dst = temp1 = bswap(operand)
3443 try self.asmRegisterRegister(.mov, tmp, dst);
3444 // tmp = temp1
3445 try self.asmRegisterImmediate(.shr, dst, Immediate.u(4));
3446 // dst = temp1 >> 4
3447 if (src_abi_size > 4) {
3448 try self.asmRegisterImmediate(.mov, imm, imm_0000_1111);
3449 try self.asmRegisterRegister(.@"and", tmp, imm);
3450 try self.asmRegisterRegister(.@"and", dst, imm);
3451 } else {
3452 try self.asmRegisterImmediate(.@"and", tmp, imm_0000_1111);
3453 try self.asmRegisterImmediate(.@"and", dst, imm_0000_1111);
3454 }
3455 // tmp = temp1 & 0x0F...0F
3456 // dst = (temp1 >> 4) & 0x0F...0F
3457 try self.asmRegisterImmediate(.shl, tmp, Immediate.u(4));
3458 // tmp = (temp1 & 0x0F...0F) << 4
3459 try self.asmRegisterRegister(.@"or", dst, tmp);
3460 // dst = temp2 = ((temp1 >> 4) & 0x0F...0F) | ((temp1 & 0x0F...0F) << 4)
3461 try self.asmRegisterRegister(.mov, tmp, dst);
3462 // tmp = temp2
3463 try self.asmRegisterImmediate(.shr, dst, Immediate.u(2));
3464 // dst = temp2 >> 2
3465 if (src_abi_size > 4) {
3466 try self.asmRegisterImmediate(.mov, imm, imm_00_11);
3467 try self.asmRegisterRegister(.@"and", tmp, imm);
3468 try self.asmRegisterRegister(.@"and", dst, imm);
3469 } else {
3470 try self.asmRegisterImmediate(.@"and", tmp, imm_00_11);
3471 try self.asmRegisterImmediate(.@"and", dst, imm_00_11);
3472 }
3473 // tmp = temp2 & 0x33...33
3474 // dst = (temp2 >> 2) & 0x33...33
3475 try self.asmRegisterMemory(
3476 .lea,
3477 if (src_abi_size > 4) tmp.to64() else tmp.to32(),
3478 Memory.sib(.qword, .{
3479 .base = dst.to64(),
3480 .scale_index = .{ .index = tmp.to64(), .scale = 1 << 2 },
3481 }),
3482 );
3483 // tmp = temp3 = ((temp2 >> 2) & 0x33...33) + ((temp2 & 0x33...33) << 2)
3484 try self.asmRegisterRegister(.mov, dst, tmp);
3485 // dst = temp3
3486 try self.asmRegisterImmediate(.shr, tmp, Immediate.u(1));
3487 // tmp = temp3 >> 1
3488 if (src_abi_size > 4) {
3489 try self.asmRegisterImmediate(.mov, imm, imm_0_1);
3490 try self.asmRegisterRegister(.@"and", dst, imm);
3491 try self.asmRegisterRegister(.@"and", tmp, imm);
3492 } else {
3493 try self.asmRegisterImmediate(.@"and", dst, imm_0_1);
3494 try self.asmRegisterImmediate(.@"and", tmp, imm_0_1);
3495 }
3496 // dst = temp3 & 0x55...55
3497 // tmp = (temp3 >> 1) & 0x55...55
3498 try self.asmRegisterMemory(
3499 .lea,
3500 if (src_abi_size > 4) dst.to64() else dst.to32(),
3501 Memory.sib(.qword, .{
3502 .base = tmp.to64(),
3503 .scale_index = .{ .index = dst.to64(), .scale = 1 << 1 },
3504 }),
3505 );
3506 // dst = ((temp3 >> 1) & 0x55...55) + ((temp3 & 0x55...55) << 1)
3414 // dst = temp1 = bswap(operand)
3415 try self.asmRegisterRegister(.mov, tmp, dst);
3416 // tmp = temp1
3417 try self.asmRegisterImmediate(.shr, dst, Immediate.u(4));
3418 // dst = temp1 >> 4
3419 if (src_abi_size > 4) {
3420 try self.asmRegisterImmediate(.mov, imm, imm_0000_1111);
3421 try self.asmRegisterRegister(.@"and", tmp, imm);
3422 try self.asmRegisterRegister(.@"and", dst, imm);
3423 } else {
3424 try self.asmRegisterImmediate(.@"and", tmp, imm_0000_1111);
3425 try self.asmRegisterImmediate(.@"and", dst, imm_0000_1111);
35073426 }
3508
3509 switch (self.regExtraBits(src_ty)) {
3510 0 => {},
3511 else => |extra| try self.genBinOpMir(
3512 if (src_ty.isSignedInt()) .sar else .shr,
3513 src_ty,
3514 dst_mcv,
3515 .{ .immediate = extra },
3516 ),
3427 // tmp = temp1 & 0x0F...0F
3428 // dst = (temp1 >> 4) & 0x0F...0F
3429 try self.asmRegisterImmediate(.shl, tmp, Immediate.u(4));
3430 // tmp = (temp1 & 0x0F...0F) << 4
3431 try self.asmRegisterRegister(.@"or", dst, tmp);
3432 // dst = temp2 = ((temp1 >> 4) & 0x0F...0F) | ((temp1 & 0x0F...0F) << 4)
3433 try self.asmRegisterRegister(.mov, tmp, dst);
3434 // tmp = temp2
3435 try self.asmRegisterImmediate(.shr, dst, Immediate.u(2));
3436 // dst = temp2 >> 2
3437 if (src_abi_size > 4) {
3438 try self.asmRegisterImmediate(.mov, imm, imm_00_11);
3439 try self.asmRegisterRegister(.@"and", tmp, imm);
3440 try self.asmRegisterRegister(.@"and", dst, imm);
3441 } else {
3442 try self.asmRegisterImmediate(.@"and", tmp, imm_00_11);
3443 try self.asmRegisterImmediate(.@"and", dst, imm_00_11);
35173444 }
3518 break :result dst_mcv;
3519 };
3445 // tmp = temp2 & 0x33...33
3446 // dst = (temp2 >> 2) & 0x33...33
3447 try self.asmRegisterMemory(
3448 .lea,
3449 if (src_abi_size > 4) tmp.to64() else tmp.to32(),
3450 Memory.sib(.qword, .{
3451 .base = dst.to64(),
3452 .scale_index = .{ .index = tmp.to64(), .scale = 1 << 2 },
3453 }),
3454 );
3455 // tmp = temp3 = ((temp2 >> 2) & 0x33...33) + ((temp2 & 0x33...33) << 2)
3456 try self.asmRegisterRegister(.mov, dst, tmp);
3457 // dst = temp3
3458 try self.asmRegisterImmediate(.shr, tmp, Immediate.u(1));
3459 // tmp = temp3 >> 1
3460 if (src_abi_size > 4) {
3461 try self.asmRegisterImmediate(.mov, imm, imm_0_1);
3462 try self.asmRegisterRegister(.@"and", dst, imm);
3463 try self.asmRegisterRegister(.@"and", tmp, imm);
3464 } else {
3465 try self.asmRegisterImmediate(.@"and", dst, imm_0_1);
3466 try self.asmRegisterImmediate(.@"and", tmp, imm_0_1);
3467 }
3468 // dst = temp3 & 0x55...55
3469 // tmp = (temp3 >> 1) & 0x55...55
3470 try self.asmRegisterMemory(
3471 .lea,
3472 if (src_abi_size > 4) dst.to64() else dst.to32(),
3473 Memory.sib(.qword, .{
3474 .base = tmp.to64(),
3475 .scale_index = .{ .index = dst.to64(), .scale = 1 << 1 },
3476 }),
3477 );
3478 // dst = ((temp3 >> 1) & 0x55...55) + ((temp3 & 0x55...55) << 1)
3479 }
35203480
3521 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3481 switch (self.regExtraBits(src_ty)) {
3482 0 => {},
3483 else => |extra| try self.genBinOpMir(
3484 if (src_ty.isSignedInt()) .sar else .shr,
3485 src_ty,
3486 dst_mcv,
3487 .{ .immediate = extra },
3488 ),
3489 }
3490
3491 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
35223492}
35233493
35243494fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
35253495 const un_op = self.air.instructions.items(.data)[inst].un_op;
3526 const result: MCValue = if (self.liveness.isUnused(inst))
3527 .dead
3528 else
3529 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
3530 return self.finishAir(inst, result, .{ un_op, .none, .none });
3496 _ = un_op;
3497 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
3498 //return self.finishAir(inst, result, .{ un_op, .none, .none });
35313499}
35323500
35333501fn reuseOperand(
......@@ -3559,10 +3527,7 @@ fn reuseOperand(
35593527
35603528 // Prevent the operand deaths processing code from deallocating it.
35613529 self.liveness.clearOperandDeath(inst, op_index);
3562
3563 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
3564 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
3565 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead);
3530 self.getResolvedInstValue(Air.refToIndex(operand).?).reuse(self);
35663531
35673532 return true;
35683533}
......@@ -3703,9 +3668,6 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
37033668 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);
37043669
37053670 const ptr = try self.resolveInst(ty_op.operand);
3706 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr();
3707 if (self.liveness.isUnused(inst) and !is_volatile) break :result .dead;
3708
37093671 const dst_mcv: MCValue = if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr))
37103672 // The MCValue that holds the pointer can be re-used as the value.
37113673 ptr
......@@ -4002,10 +3964,6 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
40023964}
40033965
40043966fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue {
4005 if (self.liveness.isUnused(inst)) {
4006 return MCValue.dead;
4007 }
4008
40093967 const mcv = try self.resolveInst(operand);
40103968 const ptr_ty = self.air.typeOf(operand);
40113969 const container_ty = ptr_ty.childType();
......@@ -4072,7 +4030,7 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32
40724030fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
40734031 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
40744032 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
4075 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4033 const result: MCValue = result: {
40764034 const operand = extra.struct_operand;
40774035 const index = extra.field_index;
40784036
......@@ -4220,11 +4178,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
42204178
42214179fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
42224180 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4223 const result: MCValue = if (self.liveness.isUnused(inst))
4224 .dead
4225 else
4226 return self.fail("TODO implement airFieldParentPtr for {}", .{self.target.cpu.arch});
4227 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4181 _ = ty_op;
4182 return self.fail("TODO implement airFieldParentPtr for {}", .{self.target.cpu.arch});
4183 //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
42284184}
42294185
42304186fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: Air.Inst.Ref) !MCValue {
......@@ -5443,9 +5399,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
54435399 const src_index = self.air.instructions.items(.data)[inst].arg.src_index;
54445400 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index);
54455401
5446 const result: MCValue = result: {
5447 if (self.liveness.isUnused(inst)) break :result .dead;
5448
5402 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
54495403 const dst_mcv: MCValue = switch (mcv) {
54505404 .register => |reg| blk: {
54515405 self.register_manager.getRegAssumeFree(reg.to64(), inst);
......@@ -5536,23 +5490,17 @@ fn airBreakpoint(self: *Self) !void {
55365490}
55375491
55385492fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
5539 const result = if (self.liveness.isUnused(inst)) .dead else result: {
5540 const dst_mcv = try self.allocRegOrMem(inst, true);
5541 try self.setRegOrMem(Type.usize, dst_mcv, .{
5542 .stack_offset = -@as(i32, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)),
5543 });
5544 break :result dst_mcv;
5545 };
5546 return self.finishAir(inst, result, .{ .none, .none, .none });
5493 const dst_mcv = try self.allocRegOrMem(inst, true);
5494 try self.setRegOrMem(Type.usize, dst_mcv, .{
5495 .stack_offset = -@as(i32, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)),
5496 });
5497 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
55475498}
55485499
55495500fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
5550 const result = if (self.liveness.isUnused(inst)) .dead else result: {
5551 const dst_mcv = try self.allocRegOrMem(inst, true);
5552 try self.setRegOrMem(Type.usize, dst_mcv, .{ .register = .rbp });
5553 break :result dst_mcv;
5554 };
5555 return self.finishAir(inst, result, .{ .none, .none, .none });
5501 const dst_mcv = try self.allocRegOrMem(inst, true);
5502 try self.setRegOrMem(Type.usize, dst_mcv, .{ .register = .rbp });
5503 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
55565504}
55575505
55585506fn airFence(self: *Self, inst: Air.Inst.Index) !void {
......@@ -5749,7 +5697,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
57495697 }
57505698
57515699 const result: MCValue = result: {
5752 if (self.liveness.isUnused(inst)) break :result .dead;
5700 if (self.liveness.isUnused(inst)) break :result .unreach;
57535701
57545702 switch (info.return_value) {
57555703 .register => {
......@@ -5771,12 +5719,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
57715719 std.mem.copy(Air.Inst.Ref, buf[1..], args);
57725720 return self.finishAir(inst, result, buf);
57735721 }
5774 var bt = try self.iterateBigTomb(inst, 1 + args.len);
5775 bt.feed(callee);
5776 for (args) |arg| {
5777 bt.feed(arg);
5778 }
5779 return bt.finishAir(result);
5722 var bt = self.liveness.iterateBigTomb(inst);
5723 self.feed(&bt, callee);
5724 for (args) |arg| self.feed(&bt, arg);
5725 return self.finishAirResult(inst, result);
57805726}
57815727
57825728fn airRet(self: *Self, inst: Air.Inst.Index) !void {
......@@ -5799,7 +5745,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
57995745 // which is available if the jump is 127 bytes or less forward.
58005746 const jmp_reloc = try self.asmJmpReloc(undefined);
58015747 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);
5802 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
5748 return self.finishAir(inst, .unreach, .{ un_op, .none, .none });
58035749}
58045750
58055751fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
......@@ -5829,65 +5775,63 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
58295775 // which is available if the jump is 127 bytes or less forward.
58305776 const jmp_reloc = try self.asmJmpReloc(undefined);
58315777 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);
5832 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
5778 return self.finishAir(inst, .unreach, .{ un_op, .none, .none });
58335779}
58345780
58355781fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
58365782 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5837 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
5838 const ty = self.air.typeOf(bin_op.lhs);
5839 const ty_abi_size = ty.abiSize(self.target.*);
5840 const can_reuse = ty_abi_size <= 8;
5783 const ty = self.air.typeOf(bin_op.lhs);
5784 const ty_abi_size = ty.abiSize(self.target.*);
5785 const can_reuse = ty_abi_size <= 8;
58415786
5842 try self.spillEflagsIfOccupied();
5843 self.eflags_inst = inst;
5787 try self.spillEflagsIfOccupied();
5788 self.eflags_inst = inst;
58445789
5845 const lhs_mcv = try self.resolveInst(bin_op.lhs);
5846 const lhs_lock = switch (lhs_mcv) {
5847 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5848 else => null,
5849 };
5850 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
5790 const lhs_mcv = try self.resolveInst(bin_op.lhs);
5791 const lhs_lock = switch (lhs_mcv) {
5792 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5793 else => null,
5794 };
5795 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
58515796
5852 const rhs_mcv = try self.resolveInst(bin_op.rhs);
5853 const rhs_lock = switch (rhs_mcv) {
5854 .register => |reg| self.register_manager.lockReg(reg),
5855 else => null,
5856 };
5857 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
5858
5859 const dst_mem_ok = !ty.isRuntimeFloat();
5860 var flipped = false;
5861 const dst_mcv: MCValue = if (can_reuse and !lhs_mcv.isImmediate() and
5862 (dst_mem_ok or lhs_mcv.isRegister()) and self.liveness.operandDies(inst, 0))
5863 lhs_mcv
5864 else if (can_reuse and !rhs_mcv.isImmediate() and
5865 (dst_mem_ok or rhs_mcv.isRegister()) and self.liveness.operandDies(inst, 1))
5866 dst: {
5867 flipped = true;
5868 break :dst rhs_mcv;
5869 } else if (dst_mem_ok) dst: {
5870 const dst_mcv = try self.allocTempRegOrMem(ty, true);
5871 try self.setRegOrMem(ty, dst_mcv, lhs_mcv);
5872 break :dst dst_mcv;
5873 } else .{ .register = try self.copyToTmpRegister(ty, lhs_mcv) };
5874 const dst_lock = switch (dst_mcv) {
5875 .register => |reg| self.register_manager.lockReg(reg),
5876 else => null,
5877 };
5878 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
5797 const rhs_mcv = try self.resolveInst(bin_op.rhs);
5798 const rhs_lock = switch (rhs_mcv) {
5799 .register => |reg| self.register_manager.lockReg(reg),
5800 else => null,
5801 };
5802 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
58795803
5880 const src_mcv = if (flipped) lhs_mcv else rhs_mcv;
5881 try self.genBinOpMir(switch (ty.tag()) {
5882 else => .cmp,
5883 .f32 => .ucomiss,
5884 .f64 => .ucomisd,
5885 }, ty, dst_mcv, src_mcv);
5804 const dst_mem_ok = !ty.isRuntimeFloat();
5805 var flipped = false;
5806 const dst_mcv: MCValue = if (can_reuse and !lhs_mcv.isImmediate() and
5807 (dst_mem_ok or lhs_mcv.isRegister()) and self.liveness.operandDies(inst, 0))
5808 lhs_mcv
5809 else if (can_reuse and !rhs_mcv.isImmediate() and
5810 (dst_mem_ok or rhs_mcv.isRegister()) and self.liveness.operandDies(inst, 1))
5811 dst: {
5812 flipped = true;
5813 break :dst rhs_mcv;
5814 } else if (dst_mem_ok) dst: {
5815 const dst_mcv = try self.allocTempRegOrMem(ty, true);
5816 try self.setRegOrMem(ty, dst_mcv, lhs_mcv);
5817 break :dst dst_mcv;
5818 } else .{ .register = try self.copyToTmpRegister(ty, lhs_mcv) };
5819 const dst_lock = switch (dst_mcv) {
5820 .register => |reg| self.register_manager.lockReg(reg),
5821 else => null,
5822 };
5823 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
58865824
5887 const signedness = if (ty.isAbiInt()) ty.intInfo(self.target.*).signedness else .unsigned;
5888 break :result .{
5889 .eflags = Condition.fromCompareOperator(signedness, if (flipped) op.reverse() else op),
5890 };
5825 const src_mcv = if (flipped) lhs_mcv else rhs_mcv;
5826 try self.genBinOpMir(switch (ty.tag()) {
5827 else => .cmp,
5828 .f32 => .ucomiss,
5829 .f64 => .ucomisd,
5830 }, ty, dst_mcv, src_mcv);
5831
5832 const signedness = if (ty.isAbiInt()) ty.intInfo(self.target.*).signedness else .unsigned;
5833 const result = MCValue{
5834 .eflags = Condition.fromCompareOperator(signedness, if (flipped) op.reverse() else op),
58915835 };
58925836 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
58935837}
......@@ -5899,56 +5843,55 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
58995843
59005844fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
59015845 const un_op = self.air.instructions.items(.data)[inst].un_op;
5902 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
5903 const addr_reg = try self.register_manager.allocReg(null, gp);
5904 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
5905 defer self.register_manager.unlockReg(addr_lock);
5906
5907 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
5908 const atom_index = try elf_file.getOrCreateAtomForLazySymbol(
5909 .{ .kind = .const_data, .ty = Type.anyerror },
5910 4, // dword alignment
5911 );
5912 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
5913 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
5914 .base = .ds,
5915 .disp = @intCast(i32, got_addr),
5916 }));
5917 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
5918 const atom_index = try coff_file.getOrCreateAtomForLazySymbol(
5919 .{ .kind = .const_data, .ty = Type.anyerror },
5920 4, // dword alignment
5921 );
5922 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
5923 try self.genSetReg(Type.usize, addr_reg, .{ .load_got = sym_index });
5924 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
5925 const atom_index = try macho_file.getOrCreateAtomForLazySymbol(
5926 .{ .kind = .const_data, .ty = Type.anyerror },
5927 4, // dword alignment
5928 );
5929 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
5930 try self.genSetReg(Type.usize, addr_reg, .{ .load_got = sym_index });
5931 } else {
5932 return self.fail("TODO implement airErrorName for x86_64 {s}", .{@tagName(self.bin_file.tag)});
5933 }
59345846
5935 try self.spillEflagsIfOccupied();
5936 self.eflags_inst = inst;
5847 const addr_reg = try self.register_manager.allocReg(null, gp);
5848 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
5849 defer self.register_manager.unlockReg(addr_lock);
59375850
5938 const op_ty = self.air.typeOf(un_op);
5939 const op_abi_size = @intCast(u32, op_ty.abiSize(self.target.*));
5940 const op_mcv = try self.resolveInst(un_op);
5941 const dst_reg = switch (op_mcv) {
5942 .register => |reg| reg,
5943 else => try self.copyToTmpRegister(op_ty, op_mcv),
5944 };
5945 try self.asmRegisterMemory(
5946 .cmp,
5947 registerAlias(dst_reg, op_abi_size),
5948 Memory.sib(Memory.PtrSize.fromSize(op_abi_size), .{ .base = addr_reg }),
5851 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
5852 const atom_index = try elf_file.getOrCreateAtomForLazySymbol(
5853 .{ .kind = .const_data, .ty = Type.anyerror },
5854 4, // dword alignment
5855 );
5856 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
5857 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
5858 .base = .ds,
5859 .disp = @intCast(i32, got_addr),
5860 }));
5861 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
5862 const atom_index = try coff_file.getOrCreateAtomForLazySymbol(
5863 .{ .kind = .const_data, .ty = Type.anyerror },
5864 4, // dword alignment
59495865 );
5950 break :result .{ .eflags = .b };
5866 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
5867 try self.genSetReg(Type.usize, addr_reg, .{ .load_got = sym_index });
5868 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
5869 const atom_index = try macho_file.getOrCreateAtomForLazySymbol(
5870 .{ .kind = .const_data, .ty = Type.anyerror },
5871 4, // dword alignment
5872 );
5873 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
5874 try self.genSetReg(Type.usize, addr_reg, .{ .load_got = sym_index });
5875 } else {
5876 return self.fail("TODO implement airErrorName for x86_64 {s}", .{@tagName(self.bin_file.tag)});
5877 }
5878
5879 try self.spillEflagsIfOccupied();
5880 self.eflags_inst = inst;
5881
5882 const op_ty = self.air.typeOf(un_op);
5883 const op_abi_size = @intCast(u32, op_ty.abiSize(self.target.*));
5884 const op_mcv = try self.resolveInst(un_op);
5885 const dst_reg = switch (op_mcv) {
5886 .register => |reg| reg,
5887 else => try self.copyToTmpRegister(op_ty, op_mcv),
59515888 };
5889 try self.asmRegisterMemory(
5890 .cmp,
5891 registerAlias(dst_reg, op_abi_size),
5892 Memory.sib(Memory.PtrSize.fromSize(op_abi_size), .{ .base = addr_reg }),
5893 );
5894 const result = MCValue{ .eflags = .b };
59525895 return self.finishAir(inst, result, .{ un_op, .none, .none });
59535896}
59545897
......@@ -5957,9 +5900,8 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {
59575900 const extra = self.air.extraData(Air.Try, pl_op.payload);
59585901 const body = self.air.extra[extra.end..][0..extra.data.body_len];
59595902 const err_union_ty = self.air.typeOf(pl_op.operand);
5960 const err_union = try self.resolveInst(pl_op.operand);
5961 const result = try self.genTry(inst, err_union, body, err_union_ty, false);
5962 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
5903 const result = try self.genTry(inst, pl_op.operand, body, err_union_ty, false);
5904 return self.finishAir(inst, result, .{ .none, .none, .none });
59635905}
59645906
59655907fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
......@@ -5967,15 +5909,14 @@ fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
59675909 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
59685910 const body = self.air.extra[extra.end..][0..extra.data.body_len];
59695911 const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
5970 const err_union_ptr = try self.resolveInst(extra.data.ptr);
5971 const result = try self.genTry(inst, err_union_ptr, body, err_union_ty, true);
5972 return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none });
5912 const result = try self.genTry(inst, extra.data.ptr, body, err_union_ty, true);
5913 return self.finishAir(inst, result, .{ .none, .none, .none });
59735914}
59745915
59755916fn genTry(
59765917 self: *Self,
59775918 inst: Air.Inst.Index,
5978 err_union: MCValue,
5919 err_union: Air.Inst.Ref,
59795920 body: []const Air.Inst.Index,
59805921 err_union_ty: Type,
59815922 operand_is_ptr: bool,
......@@ -5983,14 +5924,37 @@ fn genTry(
59835924 if (operand_is_ptr) {
59845925 return self.fail("TODO genTry for pointers", .{});
59855926 }
5986 const is_err_mcv = try self.isErr(null, err_union_ty, err_union);
5927 const liveness_cond_br = self.liveness.getCondBr(inst);
5928
5929 const err_union_mcv = try self.resolveInst(err_union);
5930 const is_err_mcv = try self.isErr(null, err_union_ty, err_union_mcv);
5931
59875932 const reloc = try self.genCondBrMir(Type.anyerror, is_err_mcv);
5933
5934 if (self.liveness.operandDies(inst, 0)) {
5935 if (Air.refToIndex(err_union)) |err_union_inst| self.processDeath(err_union_inst);
5936 }
5937
5938 self.scope_generation += 1;
5939 const state = try self.saveState();
5940
5941 for (liveness_cond_br.else_deaths) |operand| self.processDeath(operand);
59885942 try self.genBody(body);
5943 try self.restoreState(state, &.{}, .{
5944 .emit_instructions = false,
5945 .update_tracking = true,
5946 .resurrect = true,
5947 .close_scope = true,
5948 });
5949
59895950 try self.performReloc(reloc);
5951
5952 for (liveness_cond_br.then_deaths) |operand| self.processDeath(operand);
5953
59905954 const result = if (self.liveness.isUnused(inst))
5991 .dead
5955 .unreach
59925956 else
5993 try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union);
5957 try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union_mcv);
59945958 return result;
59955959}
59965960
......@@ -6013,12 +5977,12 @@ fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
60135977 const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
60145978 // TODO emit debug info for function change
60155979 _ = function;
6016 return self.finishAir(inst, .dead, .{ .none, .none, .none });
5980 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
60175981}
60185982
60195983fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
60205984 // TODO emit debug info lexical block
6021 return self.finishAir(inst, .dead, .{ .none, .none, .none });
5985 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
60225986}
60235987
60245988fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
......@@ -6034,7 +5998,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
60345998 const tag = self.air.instructions.items(.tag)[inst];
60355999 try self.genVarDbgInfo(tag, ty, mcv, name);
60366000
6037 return self.finishAir(inst, .dead, .{ operand, .none, .none });
6001 return self.finishAir(inst, .unreach, .{ operand, .none, .none });
60386002}
60396003
60406004fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
......@@ -6071,7 +6035,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
60716035 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
60726036 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
60736037 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
6074 const liveness_condbr = self.liveness.getCondBr(inst);
6038 const liveness_cond_br = self.liveness.getCondBr(inst);
60756039
60766040 const reloc = try self.genCondBrMir(cond_ty, cond);
60776041
......@@ -6082,60 +6046,37 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
60826046 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
60836047 }
60846048
6085 // Capture the state of register and stack allocation state so that we can revert to it.
6086 const saved_state = self.captureState();
6087
6049 const outer_state = try self.saveState();
60886050 {
6089 try self.branch_stack.append(.{});
6090 errdefer _ = self.branch_stack.pop();
6051 self.scope_generation += 1;
6052 const inner_state = try self.saveState();
60916053
6092 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
6093 for (liveness_condbr.then_deaths) |operand| {
6094 self.processDeath(operand);
6095 }
6054 for (liveness_cond_br.then_deaths) |operand| self.processDeath(operand);
60966055 try self.genBody(then_body);
6097 }
6098
6099 // Revert to the previous register and stack allocation state.
6100
6101 var then_branch = self.branch_stack.pop();
6102 defer then_branch.deinit(self.gpa);
6103
6104 self.revertState(saved_state);
6105
6106 try self.performReloc(reloc);
6056 try self.restoreState(inner_state, &.{}, .{
6057 .emit_instructions = false,
6058 .update_tracking = true,
6059 .resurrect = true,
6060 .close_scope = true,
6061 });
61076062
6108 {
6109 try self.branch_stack.append(.{});
6110 errdefer _ = self.branch_stack.pop();
6063 try self.performReloc(reloc);
61116064
6112 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
6113 for (liveness_condbr.else_deaths) |operand| {
6114 self.processDeath(operand);
6115 }
6065 for (liveness_cond_br.else_deaths) |operand| self.processDeath(operand);
61166066 try self.genBody(else_body);
6067 try self.restoreState(inner_state, &.{}, .{
6068 .emit_instructions = false,
6069 .update_tracking = true,
6070 .resurrect = true,
6071 .close_scope = true,
6072 });
61176073 }
6118
6119 var else_branch = self.branch_stack.pop();
6120 defer else_branch.deinit(self.gpa);
6121
6122 // At this point, each branch will possibly have conflicting values for where
6123 // each instruction is stored. They agree, however, on which instructions are alive/dead.
6124 // We use the first ("then") branch as canonical, and here emit
6125 // instructions into the second ("else") branch to make it conform.
6126 // We continue respect the data structure semantic guarantees of the else_branch so
6127 // that we can use all the code emitting abstractions. This is why at the bottom we
6128 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
6129 // rather than assigning it.
6130 log.debug("airCondBr: %{d}", .{inst});
6131 log.debug("Upper branches:", .{});
6132 for (self.branch_stack.items) |bs| {
6133 log.debug("{}", .{bs.fmtDebug()});
6134 }
6135 log.debug("Then branch: {}", .{then_branch.fmtDebug()});
6136 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
6137
6138 try self.canonicaliseBranches(true, &then_branch, &else_branch, true, true);
6074 try self.restoreState(outer_state, &.{}, .{
6075 .emit_instructions = false,
6076 .update_tracking = false,
6077 .resurrect = false,
6078 .close_scope = true,
6079 });
61396080
61406081 // We already took care of pl_op.operand earlier, so we're going
61416082 // to pass .none here
......@@ -6309,67 +6250,53 @@ fn isNonErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCVa
63096250
63106251fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
63116252 const un_op = self.air.instructions.items(.data)[inst].un_op;
6312 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
6313 const operand = try self.resolveInst(un_op);
6314 const ty = self.air.typeOf(un_op);
6315 break :result try self.isNull(inst, ty, operand);
6316 };
6253 const operand = try self.resolveInst(un_op);
6254 const ty = self.air.typeOf(un_op);
6255 const result = try self.isNull(inst, ty, operand);
63176256 return self.finishAir(inst, result, .{ un_op, .none, .none });
63186257}
63196258
63206259fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
63216260 const un_op = self.air.instructions.items(.data)[inst].un_op;
6322 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
6323 const operand = try self.resolveInst(un_op);
6324 const ty = self.air.typeOf(un_op);
6325 break :result try self.isNullPtr(inst, ty, operand);
6326 };
6261 const operand = try self.resolveInst(un_op);
6262 const ty = self.air.typeOf(un_op);
6263 const result = try self.isNullPtr(inst, ty, operand);
63276264 return self.finishAir(inst, result, .{ un_op, .none, .none });
63286265}
63296266
63306267fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
63316268 const un_op = self.air.instructions.items(.data)[inst].un_op;
6332 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
6333 const operand = try self.resolveInst(un_op);
6334 const ty = self.air.typeOf(un_op);
6335 break :result switch (try self.isNull(inst, ty, operand)) {
6336 .eflags => |cc| .{ .eflags = cc.negate() },
6337 else => unreachable,
6338 };
6269 const operand = try self.resolveInst(un_op);
6270 const ty = self.air.typeOf(un_op);
6271 const result = switch (try self.isNull(inst, ty, operand)) {
6272 .eflags => |cc| .{ .eflags = cc.negate() },
6273 else => unreachable,
63396274 };
63406275 return self.finishAir(inst, result, .{ un_op, .none, .none });
63416276}
63426277
63436278fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
63446279 const un_op = self.air.instructions.items(.data)[inst].un_op;
6345 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
6346 const operand = try self.resolveInst(un_op);
6347 const ty = self.air.typeOf(un_op);
6348 break :result switch (try self.isNullPtr(inst, ty, operand)) {
6349 .eflags => |cc| .{ .eflags = cc.negate() },
6350 else => unreachable,
6351 };
6280 const operand = try self.resolveInst(un_op);
6281 const ty = self.air.typeOf(un_op);
6282 const result = switch (try self.isNullPtr(inst, ty, operand)) {
6283 .eflags => |cc| .{ .eflags = cc.negate() },
6284 else => unreachable,
63526285 };
63536286 return self.finishAir(inst, result, .{ un_op, .none, .none });
63546287}
63556288
63566289fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
63576290 const un_op = self.air.instructions.items(.data)[inst].un_op;
6358 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
6359 const operand = try self.resolveInst(un_op);
6360 const ty = self.air.typeOf(un_op);
6361 break :result try self.isErr(inst, ty, operand);
6362 };
6291 const operand = try self.resolveInst(un_op);
6292 const ty = self.air.typeOf(un_op);
6293 const result = try self.isErr(inst, ty, operand);
63636294 return self.finishAir(inst, result, .{ un_op, .none, .none });
63646295}
63656296
63666297fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
63676298 const un_op = self.air.instructions.items(.data)[inst].un_op;
63686299
6369 if (self.liveness.isUnused(inst)) {
6370 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
6371 }
6372
63736300 const operand_ptr = try self.resolveInst(un_op);
63746301 const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {
63756302 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
......@@ -6395,21 +6322,15 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
63956322
63966323fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
63976324 const un_op = self.air.instructions.items(.data)[inst].un_op;
6398 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
6399 const operand = try self.resolveInst(un_op);
6400 const ty = self.air.typeOf(un_op);
6401 break :result try self.isNonErr(inst, ty, operand);
6402 };
6325 const operand = try self.resolveInst(un_op);
6326 const ty = self.air.typeOf(un_op);
6327 const result = try self.isNonErr(inst, ty, operand);
64036328 return self.finishAir(inst, result, .{ un_op, .none, .none });
64046329}
64056330
64066331fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
64076332 const un_op = self.air.instructions.items(.data)[inst].un_op;
64086333
6409 if (self.liveness.isUnused(inst)) {
6410 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
6411 }
6412
64136334 const operand_ptr = try self.resolveInst(un_op);
64146335 const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {
64156336 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
......@@ -6439,103 +6360,61 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
64396360 const loop = self.air.extraData(Air.Block, ty_pl.payload);
64406361 const body = self.air.extra[loop.end..][0..loop.data.body_len];
64416362 const jmp_target = @intCast(u32, self.mir_instructions.len);
6442 const liveness_loop = self.liveness.getLoop(inst);
6443
6444 {
6445 try self.branch_stack.append(.{});
6446 errdefer _ = self.branch_stack.pop();
6447
6448 try self.genBody(body);
6449 }
64506363
6451 var branch = self.branch_stack.pop();
6452 defer branch.deinit(self.gpa);
6453
6454 log.debug("airLoop: %{d}", .{inst});
6455 log.debug("Upper branches:", .{});
6456 for (self.branch_stack.items) |bs| {
6457 log.debug("{}", .{bs.fmtDebug()});
6458 }
6459 log.debug("Loop branch: {}", .{branch.fmtDebug()});
6460
6461 var dummy_branch = Branch{};
6462 defer dummy_branch.deinit(self.gpa);
6463 try self.canonicaliseBranches(true, &dummy_branch, &branch, true, false);
6364 self.scope_generation += 1;
6365 const state = try self.saveState();
64646366
6367 try self.genBody(body);
6368 try self.restoreState(state, &.{}, .{
6369 .emit_instructions = true,
6370 .update_tracking = false,
6371 .resurrect = false,
6372 .close_scope = true,
6373 });
64656374 _ = try self.asmJmpReloc(jmp_target);
64666375
6467 try self.ensureProcessDeathCapacity(liveness_loop.deaths.len);
6468 for (liveness_loop.deaths) |operand| {
6469 self.processDeath(operand);
6470 }
6471
64726376 return self.finishAirBookkeeping();
64736377}
64746378
64756379fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
64766380 // A block is a setup to be able to jump to the end.
6477 const branch_depth = @intCast(u32, self.branch_stack.items.len);
6478 try self.blocks.putNoClobber(self.gpa, inst, .{ .branch_depth = branch_depth });
6479 defer {
6480 var block_data = self.blocks.fetchRemove(inst).?.value;
6481 block_data.deinit(self.gpa);
6482 }
6483
6484 const ty = self.air.typeOfIndex(inst);
6485 const unused = !ty.hasRuntimeBitsIgnoreComptime() or self.liveness.isUnused(inst);
6486
6487 {
6488 // Here we use `.none` to represent a null value so that the first break
6489 // instruction will choose a MCValue for the block result and overwrite
6490 // this field. Following break instructions will use that MCValue to put
6491 // their block results.
6492 const result: MCValue = if (unused) .dead else .none;
6493 const branch = &self.branch_stack.items[branch_depth - 1];
6494 try branch.inst_table.putNoClobber(self.gpa, inst, result);
6495 }
6496
6497 {
6498 try self.branch_stack.append(.{});
6499 errdefer _ = self.branch_stack.pop();
6381 self.inst_tracking.putAssumeCapacityNoClobber(inst, InstTracking.init(.unreach));
65006382
6501 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
6502 const extra = self.air.extraData(Air.Block, ty_pl.payload);
6503 const body = self.air.extra[extra.end..][0..extra.data.body_len];
6504 try self.genBody(body);
6505 }
6383 self.scope_generation += 1;
6384 try self.blocks.putNoClobber(self.gpa, inst, .{ .state = self.initRetroactiveState() });
6385 const liveness = self.liveness.getBlock(inst);
65066386
6507 const block_data = self.blocks.getPtr(inst).?;
6508 const target_branch = self.branch_stack.pop();
6387 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
6388 const extra = self.air.extraData(Air.Block, ty_pl.payload);
6389 const body = self.air.extra[extra.end..][0..extra.data.body_len];
6390 try self.genBody(body);
65096391
6510 log.debug("airBlock: %{d}", .{inst});
6511 log.debug("Upper branches:", .{});
6512 for (self.branch_stack.items) |bs| {
6513 log.debug("{}", .{bs.fmtDebug()});
6392 var block_data = self.blocks.fetchRemove(inst).?;
6393 defer block_data.value.deinit(self.gpa);
6394 if (block_data.value.relocs.items.len > 0) {
6395 try self.restoreState(block_data.value.state, liveness.deaths, .{
6396 .emit_instructions = false,
6397 .update_tracking = true,
6398 .resurrect = true,
6399 .close_scope = true,
6400 });
6401 for (block_data.value.relocs.items) |reloc| try self.performReloc(reloc);
65146402 }
6515 log.debug("Block branch: {}", .{block_data.branch.fmtDebug()});
6516 log.debug("Target branch: {}", .{target_branch.fmtDebug()});
6517
6518 try self.canonicaliseBranches(true, &block_data.branch, &target_branch, false, false);
65196403
6520 for (block_data.relocs.items) |reloc| try self.performReloc(reloc);
6521
6522 const result = if (unused) .dead else self.getResolvedInstValue(inst).?.*;
6523 self.getValue(result, inst);
6404 const tracking = self.inst_tracking.getPtr(inst).?;
6405 if (self.liveness.isUnused(inst)) tracking.die(self);
6406 self.getValue(tracking.short, inst);
65246407 self.finishAirBookkeeping();
65256408}
65266409
6527fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
6410fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
65286411 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
65296412 const condition = try self.resolveInst(pl_op.operand);
65306413 const condition_ty = self.air.typeOf(pl_op.operand);
65316414 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
65326415 var extra_index: usize = switch_br.end;
65336416 var case_i: u32 = 0;
6534 const liveness = try self.liveness.getSwitchBr(
6535 self.gpa,
6536 inst,
6537 switch_br.data.cases_len + 1,
6538 );
6417 const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.data.cases_len + 1);
65396418 defer self.gpa.free(liveness.deaths);
65406419
65416420 // If the condition dies here in this switch instruction, process
......@@ -6545,186 +6424,69 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
65456424 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
65466425 }
65476426
6548 log.debug("airSwitch: %{d}", .{inst});
6549 log.debug("Upper branches:", .{});
6550 for (self.branch_stack.items) |bs| {
6551 log.debug("{}", .{bs.fmtDebug()});
6552 }
6553
6554 var prev_branch: ?Branch = null;
6555 defer if (prev_branch) |*branch| branch.deinit(self.gpa);
6556
6557 // Capture the state of register and stack allocation state so that we can revert to it.
6558 const saved_state = self.captureState();
6559
6560 const cases_len = switch_br.data.cases_len + @boolToInt(switch_br.data.else_body_len > 0);
6561 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
6562 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
6563 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
6564 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
6565 extra_index = case.end + items.len + case_body.len;
6566
6567 // Revert to the previous register and stack allocation state.
6568 if (prev_branch) |_| self.revertState(saved_state);
6569
6570 var relocs = try self.gpa.alloc(u32, items.len);
6571 defer self.gpa.free(relocs);
6572
6573 for (items, relocs) |item, *reloc| {
6574 try self.spillEflagsIfOccupied();
6575 const item_mcv = try self.resolveInst(item);
6576 try self.genBinOpMir(.cmp, condition_ty, condition, item_mcv);
6577 reloc.* = try self.asmJccReloc(undefined, .ne);
6578 }
6427 const outer_state = try self.saveState();
6428 {
6429 self.scope_generation += 1;
6430 const inner_state = try self.saveState();
6431
6432 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
6433 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
6434 const items = @ptrCast(
6435 []const Air.Inst.Ref,
6436 self.air.extra[case.end..][0..case.data.items_len],
6437 );
6438 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
6439 extra_index = case.end + items.len + case_body.len;
65796440
6580 {
6581 if (cases_len > 1) try self.branch_stack.append(.{});
6582 errdefer _ = if (cases_len > 1) self.branch_stack.pop();
6441 var relocs = try self.gpa.alloc(u32, items.len);
6442 defer self.gpa.free(relocs);
65836443
6584 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
6585 for (liveness.deaths[case_i]) |operand| {
6586 self.processDeath(operand);
6444 for (items, relocs) |item, *reloc| {
6445 try self.spillEflagsIfOccupied();
6446 const item_mcv = try self.resolveInst(item);
6447 try self.genBinOpMir(.cmp, condition_ty, condition, item_mcv);
6448 reloc.* = try self.asmJccReloc(undefined, .ne);
65876449 }
65886450
6589 try self.genBody(case_body);
6590 }
6451 for (liveness.deaths[case_i]) |operand| self.processDeath(operand);
65916452
6592 // Consolidate returned MCValues between prongs like we do in airCondBr.
6593 if (cases_len > 1) {
6594 var case_branch = self.branch_stack.pop();
6595 errdefer case_branch.deinit(self.gpa);
6453 try self.genBody(case_body);
6454 try self.restoreState(inner_state, &.{}, .{
6455 .emit_instructions = false,
6456 .update_tracking = true,
6457 .resurrect = true,
6458 .close_scope = true,
6459 });
65966460
6597 log.debug("Case-{d} branch: {}", .{ case_i, case_branch.fmtDebug() });
6598 const final = case_i == cases_len - 1;
6599 if (prev_branch) |*canon_branch| {
6600 try self.canonicaliseBranches(final, canon_branch, &case_branch, true, true);
6601 canon_branch.deinit(self.gpa);
6602 }
6603 prev_branch = case_branch;
6461 for (relocs) |reloc| try self.performReloc(reloc);
66046462 }
66056463
6606 for (relocs) |reloc| try self.performReloc(reloc);
6607 }
6608
6609 if (switch_br.data.else_body_len > 0) {
6610 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
6611
6612 // Revert to the previous register and stack allocation state.
6613 if (prev_branch) |_| self.revertState(saved_state);
6614
6615 {
6616 if (cases_len > 1) try self.branch_stack.append(.{});
6617 errdefer _ = if (cases_len > 1) self.branch_stack.pop();
6464 if (switch_br.data.else_body_len > 0) {
6465 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
66186466
66196467 const else_deaths = liveness.deaths.len - 1;
6620 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
6621 for (liveness.deaths[else_deaths]) |operand| {
6622 self.processDeath(operand);
6623 }
6468 for (liveness.deaths[else_deaths]) |operand| self.processDeath(operand);
66246469
66256470 try self.genBody(else_body);
6626 }
6627
6628 // Consolidate returned MCValues between a prong and the else branch like we do in airCondBr.
6629 if (cases_len > 1) {
6630 var else_branch = self.branch_stack.pop();
6631 errdefer else_branch.deinit(self.gpa);
6632
6633 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
6634 if (prev_branch) |*canon_branch| {
6635 try self.canonicaliseBranches(true, canon_branch, &else_branch, true, true);
6636 canon_branch.deinit(self.gpa);
6637 }
6638 prev_branch = else_branch;
6471 try self.restoreState(inner_state, &.{}, .{
6472 .emit_instructions = false,
6473 .update_tracking = true,
6474 .resurrect = true,
6475 .close_scope = true,
6476 });
66396477 }
66406478 }
6479 try self.restoreState(outer_state, &.{}, .{
6480 .emit_instructions = false,
6481 .update_tracking = false,
6482 .resurrect = false,
6483 .close_scope = true,
6484 });
66416485
66426486 // We already took care of pl_op.operand earlier, so we're going to pass .none here
66436487 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
66446488}
66456489
6646fn canonicaliseBranches(
6647 self: *Self,
6648 update_parent: bool,
6649 canon_branch: *Branch,
6650 target_branch: *const Branch,
6651 comptime set_values: bool,
6652 comptime assert_same_deaths: bool,
6653) !void {
6654 var hazard_map = std.AutoHashMap(MCValue, void).init(self.gpa);
6655 defer hazard_map.deinit();
6656
6657 const parent_branch =
6658 if (update_parent) &self.branch_stack.items[self.branch_stack.items.len - 1] else undefined;
6659
6660 if (update_parent) try self.ensureProcessDeathCapacity(target_branch.inst_table.count());
6661 var target_it = target_branch.inst_table.iterator();
6662 while (target_it.next()) |target_entry| {
6663 const target_key = target_entry.key_ptr.*;
6664 const target_value = target_entry.value_ptr.*;
6665 const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: {
6666 // The instruction's MCValue is overridden in both branches.
6667 if (target_value == .dead) {
6668 if (update_parent) {
6669 parent_branch.inst_table.putAssumeCapacity(target_key, .dead);
6670 }
6671 if (assert_same_deaths) assert(canon_entry.value == .dead);
6672 continue;
6673 }
6674 if (update_parent) {
6675 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);
6676 }
6677 break :blk canon_entry.value;
6678 } else blk: {
6679 if (target_value == .dead) {
6680 if (update_parent) {
6681 parent_branch.inst_table.putAssumeCapacity(target_key, .dead);
6682 }
6683 continue;
6684 }
6685 // The instruction is only overridden in the else branch.
6686 // If integer overflow occurs, the question is: why wasn't the instruction marked dead?
6687 break :blk self.getResolvedInstValue(target_key).?.*;
6688 };
6689 log.debug("consolidating target_entry %{d} {}=>{}", .{ target_key, target_value, canon_mcv });
6690 // TODO handle the case where the destination stack offset / register has something
6691 // going on there.
6692 assert(!hazard_map.contains(target_value));
6693 try hazard_map.putNoClobber(canon_mcv, {});
6694 if (set_values) {
6695 try self.setRegOrMem(self.air.typeOfIndex(target_key), canon_mcv, target_value);
6696 } else self.getValue(canon_mcv, target_key);
6697 self.freeValue(target_value);
6698 // TODO track the new register / stack allocation
6699 }
6700
6701 if (update_parent) try self.ensureProcessDeathCapacity(canon_branch.inst_table.count());
6702 var canon_it = canon_branch.inst_table.iterator();
6703 while (canon_it.next()) |canon_entry| {
6704 const canon_key = canon_entry.key_ptr.*;
6705 const canon_value = canon_entry.value_ptr.*;
6706 // We already deleted the items from this table that matched the target_branch.
6707 // So these are all instructions that are only overridden in the canon branch.
6708 const parent_mcv =
6709 if (canon_value != .dead) self.getResolvedInstValue(canon_key).?.* else undefined;
6710 if (canon_value != .dead) {
6711 log.debug("consolidating canon_entry %{d} {}=>{}", .{ canon_key, parent_mcv, canon_value });
6712 // TODO handle the case where the destination stack offset / register has something
6713 // going on there.
6714 assert(!hazard_map.contains(parent_mcv));
6715 try hazard_map.putNoClobber(canon_value, {});
6716 if (set_values) {
6717 try self.setRegOrMem(self.air.typeOfIndex(canon_key), canon_value, parent_mcv);
6718 } else self.getValue(canon_value, canon_key);
6719 self.freeValue(parent_mcv);
6720 // TODO track the new register / stack allocation
6721 }
6722 if (update_parent) {
6723 parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value);
6724 }
6725 }
6726}
6727
67286490fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
67296491 const next_inst = @intCast(u32, self.mir_instructions.len);
67306492 switch (self.mir_instructions.items(.tag)[reloc]) {
......@@ -6740,76 +6502,53 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
67406502
67416503fn airBr(self: *Self, inst: Air.Inst.Index) !void {
67426504 const br = self.air.instructions.items(.data)[inst].br;
6743 const block = br.block_inst;
6744
6745 // The first break instruction encounters `.none` here and chooses a
6746 // machine code value for the block result, populating this field.
6747 // Following break instructions encounter that value and use it for
6748 // the location to store their block results.
6749 if (self.getResolvedInstValue(block)) |dst_mcv| {
6750 const src_mcv = try self.resolveInst(br.operand);
6751 switch (dst_mcv.*) {
6752 .none => {
6753 const result = result: {
6754 if (self.reuseOperand(inst, br.operand, 0, src_mcv)) break :result src_mcv;
6755
6756 const new_mcv = try self.allocRegOrMem(block, true);
6757 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, src_mcv);
6758 break :result new_mcv;
6759 };
6760 dst_mcv.* = result;
6761 self.freeValue(result);
6762 },
6763 else => try self.setRegOrMem(self.air.typeOfIndex(block), dst_mcv.*, src_mcv),
6764 }
6765 }
6505 const src_mcv = try self.resolveInst(br.operand);
6506
6507 const block_ty = self.air.typeOfIndex(br.block_inst);
6508 const block_unused =
6509 !block_ty.hasRuntimeBitsIgnoreComptime() or self.liveness.isUnused(br.block_inst);
6510 const block_tracking = self.inst_tracking.getPtr(br.block_inst).?;
6511 const block_data = self.blocks.getPtr(br.block_inst).?;
6512
6513 if (block_data.relocs.items.len == 0) {
6514 block_tracking.* = InstTracking.init(result: {
6515 if (block_unused) break :result .none;
6516 if (self.reuseOperand(inst, br.operand, 0, src_mcv)) {
6517 // Fix instruction tracking
6518 switch (src_mcv) {
6519 .register => |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
6520 self.register_manager.registers[index] = br.block_inst;
6521 },
6522 else => {},
6523 }
6524 break :result src_mcv;
6525 }
6526
6527 const new_mcv = try self.allocRegOrMem(br.block_inst, true);
6528 try self.setRegOrMem(block_ty, new_mcv, src_mcv);
6529 break :result new_mcv;
6530 });
6531 } else if (!block_unused) try self.setRegOrMem(block_ty, block_tracking.short, src_mcv);
67666532
6767 // Process operand death early so that it is properly accounted for in the Branch below.
6533 // Process operand death so that it is properly accounted for in the State below.
67686534 if (self.liveness.operandDies(inst, 0)) {
67696535 if (Air.refToIndex(br.operand)) |op_inst| self.processDeath(op_inst);
67706536 }
67716537
6772 const block_data = self.blocks.getPtr(block).?;
6773 {
6774 var branch = Branch{};
6775 errdefer branch.deinit(self.gpa);
6776
6777 var branch_i = self.branch_stack.items.len - 1;
6778 while (branch_i >= block_data.branch_depth) : (branch_i -= 1) {
6779 const table = &self.branch_stack.items[branch_i].inst_table;
6780 try branch.inst_table.ensureUnusedCapacity(self.gpa, table.count());
6781 var it = table.iterator();
6782 while (it.next()) |entry| {
6783 // This loop could be avoided by tracking inst depth, which
6784 // will be needed later anyway for reusing loop deaths.
6785 var parent_branch_i = block_data.branch_depth - 1;
6786 while (parent_branch_i > 0) : (parent_branch_i -= 1) {
6787 const parent_table = &self.branch_stack.items[parent_branch_i].inst_table;
6788 if (parent_table.contains(entry.key_ptr.*)) break;
6789 } else continue;
6790 const gop = branch.inst_table.getOrPutAssumeCapacity(entry.key_ptr.*);
6791 if (!gop.found_existing) gop.value_ptr.* = entry.value_ptr.*;
6792 }
6793 }
6794
6795 log.debug("airBr: %{d}", .{inst});
6796 log.debug("Upper branches:", .{});
6797 for (self.branch_stack.items) |bs| {
6798 log.debug("{}", .{bs.fmtDebug()});
6799 }
6800 log.debug("Prev branch: {}", .{block_data.branch.fmtDebug()});
6801 log.debug("Cur branch: {}", .{branch.fmtDebug()});
6802
6803 try self.canonicaliseBranches(false, &block_data.branch, &branch, true, false);
6804 block_data.branch.deinit(self.gpa);
6805 block_data.branch = branch;
6806 }
6538 if (block_data.relocs.items.len == 0) {
6539 try self.saveRetroactiveState(&block_data.state);
6540 block_tracking.die(self);
6541 } else try self.restoreState(block_data.state, &.{}, .{
6542 .emit_instructions = true,
6543 .update_tracking = false,
6544 .resurrect = false,
6545 .close_scope = false,
6546 });
68076547
68086548 // Emit a jump with a relocation. It will be patched up after the block ends.
6809 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
68106549 // Leave the jump offset undefined
68116550 const jmp_reloc = try self.asmJmpReloc(undefined);
6812 block_data.relocs.appendAssumeCapacity(jmp_reloc);
6551 try block_data.relocs.append(self.gpa, jmp_reloc);
68136552
68146553 self.finishAirBookkeeping();
68156554}
......@@ -6817,7 +6556,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {
68176556fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
68186557 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
68196558 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
6820 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
68216559 const clobbers_len = @truncate(u31, extra.data.flags);
68226560 var extra_i: usize = extra.end;
68236561 const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
......@@ -6826,216 +6564,214 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
68266564 extra_i += inputs.len;
68276565
68286566 var result: MCValue = .none;
6829 if (!is_volatile and self.liveness.isUnused(inst)) result = .dead else {
6830 var args = std.StringArrayHashMap(MCValue).init(self.gpa);
6831 try args.ensureTotalCapacity(outputs.len + inputs.len + clobbers_len);
6832 defer {
6833 for (args.values()) |arg| switch (arg) {
6834 .register => |reg| self.register_manager.unlockReg(.{ .register = reg }),
6835 else => {},
6836 };
6837 args.deinit();
6838 }
6567 var args = std.StringArrayHashMap(MCValue).init(self.gpa);
6568 try args.ensureTotalCapacity(outputs.len + inputs.len + clobbers_len);
6569 defer {
6570 for (args.values()) |arg| switch (arg) {
6571 .register => |reg| self.register_manager.unlockReg(.{ .register = reg }),
6572 else => {},
6573 };
6574 args.deinit();
6575 }
6576
6577 if (outputs.len > 1) {
6578 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
6579 }
68396580
6840 if (outputs.len > 1) {
6841 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
6581 for (outputs) |output| {
6582 if (output != .none) {
6583 return self.fail("TODO implement codegen for non-expr asm", .{});
6584 }
6585 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
6586 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
6587 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
6588 // This equation accounts for the fact that even if we have exactly 4 bytes
6589 // for the string, we still use the next u32 for the null terminator.
6590 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
6591
6592 const mcv: MCValue = if (mem.eql(u8, constraint, "=r"))
6593 .{ .register = self.register_manager.tryAllocReg(inst, gp) orelse
6594 return self.fail("ran out of registers lowering inline asm", .{}) }
6595 else if (mem.startsWith(u8, constraint, "={") and mem.endsWith(u8, constraint, "}"))
6596 .{ .register = parseRegName(constraint["={".len .. constraint.len - "}".len]) orelse
6597 return self.fail("unrecognized register constraint: '{s}'", .{constraint}) }
6598 else
6599 return self.fail("unrecognized constraint: '{s}'", .{constraint});
6600 args.putAssumeCapacity(name, mcv);
6601 switch (mcv) {
6602 .register => |reg| _ = if (RegisterManager.indexOfRegIntoTracked(reg)) |_|
6603 self.register_manager.lockRegAssumeUnused(reg),
6604 else => {},
68426605 }
6606 if (output == .none) result = mcv;
6607 }
68436608
6844 for (outputs) |output| {
6845 if (output != .none) {
6846 return self.fail("TODO implement codegen for non-expr asm", .{});
6847 }
6848 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
6849 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
6850 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
6851 // This equation accounts for the fact that even if we have exactly 4 bytes
6852 // for the string, we still use the next u32 for the null terminator.
6853 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
6854
6855 const mcv: MCValue = if (mem.eql(u8, constraint, "=r"))
6856 .{ .register = self.register_manager.tryAllocReg(inst, gp) orelse
6857 return self.fail("ran out of registers lowering inline asm", .{}) }
6858 else if (mem.startsWith(u8, constraint, "={") and mem.endsWith(u8, constraint, "}"))
6859 .{ .register = parseRegName(constraint["={".len .. constraint.len - "}".len]) orelse
6860 return self.fail("unrecognized register constraint: '{s}'", .{constraint}) }
6861 else
6862 return self.fail("unrecognized constraint: '{s}'", .{constraint});
6863 args.putAssumeCapacity(name, mcv);
6864 switch (mcv) {
6865 .register => |reg| _ = if (RegisterManager.indexOfRegIntoTracked(reg)) |_|
6866 self.register_manager.lockRegAssumeUnused(reg),
6867 else => {},
6868 }
6869 if (output == .none) result = mcv;
6609 for (inputs) |input| {
6610 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
6611 const constraint = std.mem.sliceTo(input_bytes, 0);
6612 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
6613 // This equation accounts for the fact that even if we have exactly 4 bytes
6614 // for the string, we still use the next u32 for the null terminator.
6615 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
6616
6617 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
6618 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
68706619 }
6620 const reg_name = constraint[1 .. constraint.len - 1];
6621 const reg = parseRegName(reg_name) orelse
6622 return self.fail("unrecognized register: '{s}'", .{reg_name});
6623
6624 const arg_mcv = try self.resolveInst(input);
6625 try self.register_manager.getReg(reg, null);
6626 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
6627 }
68716628
6872 for (inputs) |input| {
6873 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
6874 const constraint = std.mem.sliceTo(input_bytes, 0);
6875 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
6629 {
6630 var clobber_i: u32 = 0;
6631 while (clobber_i < clobbers_len) : (clobber_i += 1) {
6632 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
68766633 // This equation accounts for the fact that even if we have exactly 4 bytes
68776634 // for the string, we still use the next u32 for the null terminator.
6878 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
6879
6880 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
6881 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
6882 }
6883 const reg_name = constraint[1 .. constraint.len - 1];
6884 const reg = parseRegName(reg_name) orelse
6885 return self.fail("unrecognized register: '{s}'", .{reg_name});
6635 extra_i += clobber.len / 4 + 1;
68866636
6887 const arg_mcv = try self.resolveInst(input);
6888 try self.register_manager.getReg(reg, null);
6889 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
6890 }
6891
6892 {
6893 var clobber_i: u32 = 0;
6894 while (clobber_i < clobbers_len) : (clobber_i += 1) {
6895 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
6896 // This equation accounts for the fact that even if we have exactly 4 bytes
6897 // for the string, we still use the next u32 for the null terminator.
6898 extra_i += clobber.len / 4 + 1;
6899
6900 // TODO honor these
6901 }
6637 // TODO honor these
69026638 }
6639 }
69036640
6904 const asm_source = mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
6905 var line_it = mem.tokenize(u8, asm_source, "\n\r;");
6906 while (line_it.next()) |line| {
6907 var mnem_it = mem.tokenize(u8, line, " \t");
6908 const mnem_str = mnem_it.next() orelse continue;
6909 if (mem.startsWith(u8, mnem_str, "#")) continue;
6910
6911 const mnem_size: ?Memory.PtrSize = if (mem.endsWith(u8, mnem_str, "b"))
6912 .byte
6913 else if (mem.endsWith(u8, mnem_str, "w"))
6914 .word
6915 else if (mem.endsWith(u8, mnem_str, "l"))
6916 .dword
6917 else if (mem.endsWith(u8, mnem_str, "q"))
6918 .qword
6919 else
6920 null;
6921 const mnem = std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse
6922 (if (mnem_size) |_|
6923 std.meta.stringToEnum(Mir.Inst.Tag, mnem_str[0 .. mnem_str.len - 1])
6924 else
6925 null) orelse return self.fail("Invalid mnemonic: '{s}'", .{mnem_str});
6926
6927 var op_it = mem.tokenize(u8, mnem_it.rest(), ",");
6928 var ops = [1]encoder.Instruction.Operand{.none} ** 4;
6929 for (&ops) |*op| {
6930 const op_str = mem.trim(u8, op_it.next() orelse break, " \t");
6931 if (mem.startsWith(u8, op_str, "#")) break;
6932 if (mem.startsWith(u8, op_str, "%%")) {
6933 const colon = mem.indexOfScalarPos(u8, op_str, "%%".len + 2, ':');
6934 const reg = parseRegName(op_str["%%".len .. colon orelse op_str.len]) orelse
6935 return self.fail("Invalid register: '{s}'", .{op_str});
6936 if (colon) |colon_pos| {
6937 const disp = std.fmt.parseInt(i32, op_str[colon_pos + 1 ..], 0) catch
6938 return self.fail("Invalid displacement: '{s}'", .{op_str});
6939 op.* = .{ .mem = Memory.sib(
6940 mnem_size orelse return self.fail("Unknown size: '{s}'", .{op_str}),
6941 .{ .base = reg, .disp = disp },
6942 ) };
6943 } else {
6944 if (mnem_size) |size| if (reg.bitSize() != size.bitSize())
6945 return self.fail("Invalid register size: '{s}'", .{op_str});
6946 op.* = .{ .reg = reg };
6641 const asm_source = mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
6642 var line_it = mem.tokenize(u8, asm_source, "\n\r;");
6643 while (line_it.next()) |line| {
6644 var mnem_it = mem.tokenize(u8, line, " \t");
6645 const mnem_str = mnem_it.next() orelse continue;
6646 if (mem.startsWith(u8, mnem_str, "#")) continue;
6647
6648 const mnem_size: ?Memory.PtrSize = if (mem.endsWith(u8, mnem_str, "b"))
6649 .byte
6650 else if (mem.endsWith(u8, mnem_str, "w"))
6651 .word
6652 else if (mem.endsWith(u8, mnem_str, "l"))
6653 .dword
6654 else if (mem.endsWith(u8, mnem_str, "q"))
6655 .qword
6656 else
6657 null;
6658 const mnem = std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse
6659 (if (mnem_size) |_|
6660 std.meta.stringToEnum(Mir.Inst.Tag, mnem_str[0 .. mnem_str.len - 1])
6661 else
6662 null) orelse return self.fail("Invalid mnemonic: '{s}'", .{mnem_str});
6663
6664 var op_it = mem.tokenize(u8, mnem_it.rest(), ",");
6665 var ops = [1]encoder.Instruction.Operand{.none} ** 4;
6666 for (&ops) |*op| {
6667 const op_str = mem.trim(u8, op_it.next() orelse break, " \t");
6668 if (mem.startsWith(u8, op_str, "#")) break;
6669 if (mem.startsWith(u8, op_str, "%%")) {
6670 const colon = mem.indexOfScalarPos(u8, op_str, "%%".len + 2, ':');
6671 const reg = parseRegName(op_str["%%".len .. colon orelse op_str.len]) orelse
6672 return self.fail("Invalid register: '{s}'", .{op_str});
6673 if (colon) |colon_pos| {
6674 const disp = std.fmt.parseInt(i32, op_str[colon_pos + 1 ..], 0) catch
6675 return self.fail("Invalid displacement: '{s}'", .{op_str});
6676 op.* = .{ .mem = Memory.sib(
6677 mnem_size orelse return self.fail("Unknown size: '{s}'", .{op_str}),
6678 .{ .base = reg, .disp = disp },
6679 ) };
6680 } else {
6681 if (mnem_size) |size| if (reg.bitSize() != size.bitSize())
6682 return self.fail("Invalid register size: '{s}'", .{op_str});
6683 op.* = .{ .reg = reg };
6684 }
6685 } else if (mem.startsWith(u8, op_str, "%[") and mem.endsWith(u8, op_str, "]")) {
6686 switch (args.get(op_str["%[".len .. op_str.len - "]".len]) orelse
6687 return self.fail("No matching constraint: '{s}'", .{op_str})) {
6688 .register => |reg| op.* = .{ .reg = reg },
6689 else => return self.fail("Invalid constraint: '{s}'", .{op_str}),
6690 }
6691 } else if (mem.startsWith(u8, op_str, "$")) {
6692 if (std.fmt.parseInt(i32, op_str["$".len..], 0)) |s| {
6693 if (mnem_size) |size| {
6694 const max = @as(u64, math.maxInt(u64)) >>
6695 @intCast(u6, 64 - (size.bitSize() - 1));
6696 if ((if (s < 0) ~s else s) > max)
6697 return self.fail("Invalid immediate size: '{s}'", .{op_str});
69476698 }
6948 } else if (mem.startsWith(u8, op_str, "%[") and mem.endsWith(u8, op_str, "]")) {
6949 switch (args.get(op_str["%[".len .. op_str.len - "]".len]) orelse
6950 return self.fail("No matching constraint: '{s}'", .{op_str})) {
6951 .register => |reg| op.* = .{ .reg = reg },
6952 else => return self.fail("Invalid constraint: '{s}'", .{op_str}),
6699 op.* = .{ .imm = Immediate.s(s) };
6700 } else |_| if (std.fmt.parseInt(u64, op_str["$".len..], 0)) |u| {
6701 if (mnem_size) |size| {
6702 const max = @as(u64, math.maxInt(u64)) >>
6703 @intCast(u6, 64 - size.bitSize());
6704 if (u > max)
6705 return self.fail("Invalid immediate size: '{s}'", .{op_str});
69536706 }
6954 } else if (mem.startsWith(u8, op_str, "$")) {
6955 if (std.fmt.parseInt(i32, op_str["$".len..], 0)) |s| {
6956 if (mnem_size) |size| {
6957 const max = @as(u64, math.maxInt(u64)) >>
6958 @intCast(u6, 64 - (size.bitSize() - 1));
6959 if ((if (s < 0) ~s else s) > max)
6960 return self.fail("Invalid immediate size: '{s}'", .{op_str});
6961 }
6962 op.* = .{ .imm = Immediate.s(s) };
6963 } else |_| if (std.fmt.parseInt(u64, op_str["$".len..], 0)) |u| {
6964 if (mnem_size) |size| {
6965 const max = @as(u64, math.maxInt(u64)) >>
6966 @intCast(u6, 64 - size.bitSize());
6967 if (u > max)
6968 return self.fail("Invalid immediate size: '{s}'", .{op_str});
6969 }
6970 op.* = .{ .imm = Immediate.u(u) };
6971 } else |_| return self.fail("Invalid immediate: '{s}'", .{op_str});
6972 } else return self.fail("Invalid operand: '{s}'", .{op_str});
6973 } else if (op_it.next()) |op_str| return self.fail("Extra operand: '{s}'", .{op_str});
6974
6975 (switch (ops[0]) {
6976 .none => self.asmOpOnly(mnem),
6977 .reg => |reg0| switch (ops[1]) {
6978 .none => self.asmRegister(mnem, reg0),
6979 .reg => |reg1| switch (ops[2]) {
6980 .none => self.asmRegisterRegister(mnem, reg1, reg0),
6981 .reg => |reg2| switch (ops[3]) {
6982 .none => self.asmRegisterRegisterRegister(mnem, reg2, reg1, reg0),
6983 else => error.InvalidInstruction,
6984 },
6985 .mem => |mem2| switch (ops[3]) {
6986 .none => self.asmMemoryRegisterRegister(mnem, mem2, reg1, reg0),
6987 else => error.InvalidInstruction,
6988 },
6707 op.* = .{ .imm = Immediate.u(u) };
6708 } else |_| return self.fail("Invalid immediate: '{s}'", .{op_str});
6709 } else return self.fail("Invalid operand: '{s}'", .{op_str});
6710 } else if (op_it.next()) |op_str| return self.fail("Extra operand: '{s}'", .{op_str});
6711
6712 (switch (ops[0]) {
6713 .none => self.asmOpOnly(mnem),
6714 .reg => |reg0| switch (ops[1]) {
6715 .none => self.asmRegister(mnem, reg0),
6716 .reg => |reg1| switch (ops[2]) {
6717 .none => self.asmRegisterRegister(mnem, reg1, reg0),
6718 .reg => |reg2| switch (ops[3]) {
6719 .none => self.asmRegisterRegisterRegister(mnem, reg2, reg1, reg0),
69896720 else => error.InvalidInstruction,
69906721 },
6991 .mem => |mem1| switch (ops[2]) {
6992 .none => self.asmMemoryRegister(mnem, mem1, reg0),
6722 .mem => |mem2| switch (ops[3]) {
6723 .none => self.asmMemoryRegisterRegister(mnem, mem2, reg1, reg0),
69936724 else => error.InvalidInstruction,
69946725 },
69956726 else => error.InvalidInstruction,
69966727 },
6997 .mem => |mem0| switch (ops[1]) {
6998 .none => self.asmMemory(mnem, mem0),
6999 .reg => |reg1| switch (ops[2]) {
7000 .none => self.asmRegisterMemory(mnem, reg1, mem0),
7001 else => error.InvalidInstruction,
7002 },
6728 .mem => |mem1| switch (ops[2]) {
6729 .none => self.asmMemoryRegister(mnem, mem1, reg0),
70036730 else => error.InvalidInstruction,
70046731 },
7005 .imm => |imm0| switch (ops[1]) {
7006 .none => self.asmImmediate(mnem, imm0),
7007 .reg => |reg1| switch (ops[2]) {
7008 .none => self.asmRegisterImmediate(mnem, reg1, imm0),
7009 .reg => |reg2| switch (ops[3]) {
7010 .none => self.asmRegisterRegisterImmediate(mnem, reg2, reg1, imm0),
7011 else => error.InvalidInstruction,
7012 },
7013 .mem => |mem2| switch (ops[3]) {
7014 .none => self.asmMemoryRegisterImmediate(mnem, mem2, reg1, imm0),
7015 else => error.InvalidInstruction,
7016 },
6732 else => error.InvalidInstruction,
6733 },
6734 .mem => |mem0| switch (ops[1]) {
6735 .none => self.asmMemory(mnem, mem0),
6736 .reg => |reg1| switch (ops[2]) {
6737 .none => self.asmRegisterMemory(mnem, reg1, mem0),
6738 else => error.InvalidInstruction,
6739 },
6740 else => error.InvalidInstruction,
6741 },
6742 .imm => |imm0| switch (ops[1]) {
6743 .none => self.asmImmediate(mnem, imm0),
6744 .reg => |reg1| switch (ops[2]) {
6745 .none => self.asmRegisterImmediate(mnem, reg1, imm0),
6746 .reg => |reg2| switch (ops[3]) {
6747 .none => self.asmRegisterRegisterImmediate(mnem, reg2, reg1, imm0),
70176748 else => error.InvalidInstruction,
70186749 },
7019 .mem => |mem1| switch (ops[2]) {
7020 .none => self.asmMemoryImmediate(mnem, mem1, imm0),
6750 .mem => |mem2| switch (ops[3]) {
6751 .none => self.asmMemoryRegisterImmediate(mnem, mem2, reg1, imm0),
70216752 else => error.InvalidInstruction,
70226753 },
70236754 else => error.InvalidInstruction,
70246755 },
7025 }) catch |err| switch (err) {
7026 error.InvalidInstruction => return self.fail(
7027 "Invalid instruction: '{s} {s} {s} {s} {s}'",
7028 .{
7029 @tagName(mnem),
7030 @tagName(ops[0]),
7031 @tagName(ops[1]),
7032 @tagName(ops[2]),
7033 @tagName(ops[3]),
7034 },
7035 ),
7036 else => |e| return e,
7037 };
7038 }
6756 .mem => |mem1| switch (ops[2]) {
6757 .none => self.asmMemoryImmediate(mnem, mem1, imm0),
6758 else => error.InvalidInstruction,
6759 },
6760 else => error.InvalidInstruction,
6761 },
6762 }) catch |err| switch (err) {
6763 error.InvalidInstruction => return self.fail(
6764 "Invalid instruction: '{s} {s} {s} {s} {s}'",
6765 .{
6766 @tagName(mnem),
6767 @tagName(ops[0]),
6768 @tagName(ops[1]),
6769 @tagName(ops[2]),
6770 @tagName(ops[3]),
6771 },
6772 ),
6773 else => |e| return e,
6774 };
70396775 }
70406776
70416777 simple: {
......@@ -7052,25 +6788,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
70526788 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
70536789 return self.finishAir(inst, result, buf);
70546790 }
7055 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
7056 for (outputs) |output| {
7057 if (output == .none) continue;
7058
7059 bt.feed(output);
7060 }
7061 for (inputs) |input| {
7062 bt.feed(input);
7063 }
7064 return bt.finishAir(result);
7065}
7066
7067fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
7068 try self.ensureProcessDeathCapacity(operand_count + 1);
7069 return BigTomb{
7070 .function = self,
7071 .inst = inst,
7072 .lbt = self.liveness.iterateBigTomb(inst),
7073 };
6791 var bt = self.liveness.iterateBigTomb(inst);
6792 for (outputs) |output| if (output != .none) self.feed(&bt, output);
6793 for (inputs) |input| self.feed(&bt, input);
6794 return self.finishAirResult(inst, result);
70746795}
70756796
70766797/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
......@@ -7952,7 +7673,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
79527673
79537674fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
79547675 const un_op = self.air.instructions.items(.data)[inst].un_op;
7955 const result = if (self.liveness.isUnused(inst)) .dead else result: {
7676 const result = result: {
79567677 const src_mcv = try self.resolveInst(un_op);
79577678 if (self.reuseOperand(inst, un_op, 0, src_mcv)) break :result src_mcv;
79587679
......@@ -7966,7 +7687,7 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
79667687
79677688fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
79687689 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
7969 const result = if (self.liveness.isUnused(inst)) .dead else result: {
7690 const result = result: {
79707691 const operand = try self.resolveInst(ty_op.operand);
79717692 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand;
79727693
......@@ -7991,28 +7712,24 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
79917712 const ptr = try self.resolveInst(ty_op.operand);
79927713 const array_ty = ptr_ty.childType();
79937714 const array_len = array_ty.arrayLen();
7994 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
7995 const stack_offset = @intCast(i32, try self.allocMem(inst, 16, 16));
7996 try self.genSetStack(ptr_ty, stack_offset, ptr, .{});
7997 try self.genSetStack(Type.u64, stack_offset - 8, .{ .immediate = array_len }, .{});
7998 break :blk .{ .stack_offset = stack_offset };
7999 };
7715
7716 const stack_offset = @intCast(i32, try self.allocMem(inst, 16, 16));
7717 try self.genSetStack(ptr_ty, stack_offset, ptr, .{});
7718 try self.genSetStack(Type.u64, stack_offset - 8, .{ .immediate = array_len }, .{});
7719
7720 const result = MCValue{ .stack_offset = stack_offset };
80007721 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
80017722}
80027723
80037724fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void {
80047725 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8005 const result: MCValue = if (self.liveness.isUnused(inst))
8006 .dead
8007 else
8008 return self.fail("TODO implement airIntToFloat for {}", .{self.target.cpu.arch});
8009 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
7726 _ = ty_op;
7727 return self.fail("TODO implement airIntToFloat for {}", .{self.target.cpu.arch});
7728 //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
80107729}
80117730
80127731fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
80137732 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8014 if (self.liveness.isUnused(inst))
8015 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
80167733
80177734 const src_ty = self.air.typeOf(ty_op.operand);
80187735 const dst_ty = self.air.typeOfIndex(inst);
......@@ -8114,7 +7831,7 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
81147831 }
81157832
81167833 const result: MCValue = result: {
8117 if (self.liveness.isUnused(inst)) break :result .dead;
7834 if (self.liveness.isUnused(inst)) break :result .unreach;
81187835
81197836 if (val_abi_size <= 8) {
81207837 self.eflags_inst = inst;
......@@ -8212,7 +7929,7 @@ fn atomicOp(
82127929 .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),
82137930 } } });
82147931
8215 return if (unused) .none else dst_mcv;
7932 return if (unused) .unreach else dst_mcv;
82167933 },
82177934 .loop => _ = if (val_abi_size <= 8) {
82187935 const tmp_reg = try self.register_manager.allocReg(null, gp);
......@@ -8285,7 +8002,7 @@ fn atomicOp(
82858002 .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),
82868003 } } });
82878004 _ = try self.asmJccReloc(loop, .ne);
8288 return if (unused) .none else .{ .register = .rax };
8005 return if (unused) .unreach else .{ .register = .rax };
82898006 } else {
82908007 try self.asmRegisterMemory(.mov, .rax, Memory.sib(.qword, .{
82918008 .base = ptr_mem.sib.base,
......@@ -8354,7 +8071,7 @@ fn atomicOp(
83548071 } });
83558072 _ = try self.asmJccReloc(loop, .ne);
83568073
8357 if (unused) return .none;
8074 if (unused) return .unreach;
83588075 const dst_mcv = try self.allocTempRegOrMem(val_ty, false);
83598076 try self.asmMemoryRegister(
83608077 .mov,
......@@ -8396,27 +8113,22 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
83968113fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
83978114 const atomic_load = self.air.instructions.items(.data)[inst].atomic_load;
83988115
8399 const result: MCValue = result: {
8400 if (self.liveness.isUnused(inst)) break :result .dead;
8401
8402 const ptr_ty = self.air.typeOf(atomic_load.ptr);
8403 const ptr_mcv = try self.resolveInst(atomic_load.ptr);
8404 const ptr_lock = switch (ptr_mcv) {
8405 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
8406 else => null,
8407 };
8408 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
8116 const ptr_ty = self.air.typeOf(atomic_load.ptr);
8117 const ptr_mcv = try self.resolveInst(atomic_load.ptr);
8118 const ptr_lock = switch (ptr_mcv) {
8119 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
8120 else => null,
8121 };
8122 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
84098123
8410 const dst_mcv =
8411 if (self.reuseOperand(inst, atomic_load.ptr, 0, ptr_mcv))
8412 ptr_mcv
8413 else
8414 try self.allocRegOrMem(inst, true);
8124 const dst_mcv =
8125 if (self.reuseOperand(inst, atomic_load.ptr, 0, ptr_mcv))
8126 ptr_mcv
8127 else
8128 try self.allocRegOrMem(inst, true);
84158129
8416 try self.load(dst_mcv, ptr_mcv, ptr_ty);
8417 break :result dst_mcv;
8418 };
8419 return self.finishAir(inst, result, .{ atomic_load.ptr, .none, .none });
8130 try self.load(dst_mcv, ptr_mcv, ptr_ty);
8131 return self.finishAir(inst, dst_mcv, .{ atomic_load.ptr, .none, .none });
84208132}
84218133
84228134fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {
......@@ -8459,7 +8171,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
84598171
84608172 try self.genInlineMemset(dst_ptr, src_val, len, .{});
84618173
8462 return self.finishAir(inst, .none, .{ pl_op.operand, extra.lhs, extra.rhs });
8174 return self.finishAir(inst, .unreach, .{ pl_op.operand, extra.lhs, extra.rhs });
84638175}
84648176
84658177fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
......@@ -8489,128 +8201,129 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
84898201
84908202 try self.genInlineMemcpy(dst_ptr, src_ptr, len, .{});
84918203
8492 return self.finishAir(inst, .none, .{ pl_op.operand, extra.lhs, extra.rhs });
8204 return self.finishAir(inst, .unreach, .{ pl_op.operand, extra.lhs, extra.rhs });
84938205}
84948206
84958207fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
84968208 const un_op = self.air.instructions.items(.data)[inst].un_op;
84978209 const operand = try self.resolveInst(un_op);
8498 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
8499 _ = operand;
8500 return self.fail("TODO implement airTagName for x86_64", .{});
8501 };
8502 return self.finishAir(inst, result, .{ un_op, .none, .none });
8210 _ = operand;
8211 return self.fail("TODO implement airTagName for x86_64", .{});
8212 //return self.finishAir(inst, result, .{ un_op, .none, .none });
85038213}
85048214
85058215fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
85068216 const un_op = self.air.instructions.items(.data)[inst].un_op;
8507 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
8508 const err_ty = self.air.typeOf(un_op);
8509 const err_mcv = try self.resolveInst(un_op);
8510 const err_reg = try self.copyToTmpRegister(err_ty, err_mcv);
8511 const err_lock = self.register_manager.lockRegAssumeUnused(err_reg);
8512 defer self.register_manager.unlockReg(err_lock);
8513
8514 const addr_reg = try self.register_manager.allocReg(null, gp);
8515 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
8516 defer self.register_manager.unlockReg(addr_lock);
8517
8518 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
8519 const atom_index = try elf_file.getOrCreateAtomForLazySymbol(
8520 .{ .kind = .const_data, .ty = Type.anyerror },
8521 4, // dword alignment
8522 );
8523 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
8524 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
8525 .base = .ds,
8526 .disp = @intCast(i32, got_addr),
8527 }));
8528 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
8529 const atom_index = try coff_file.getOrCreateAtomForLazySymbol(
8530 .{ .kind = .const_data, .ty = Type.anyerror },
8531 4, // dword alignment
8532 );
8533 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
8534 try self.genSetReg(Type.usize, addr_reg, .{ .load_got = sym_index });
8535 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
8536 const atom_index = try macho_file.getOrCreateAtomForLazySymbol(
8537 .{ .kind = .const_data, .ty = Type.anyerror },
8538 4, // dword alignment
8539 );
8540 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
8541 try self.genSetReg(Type.usize, addr_reg, .{ .load_got = sym_index });
8542 } else {
8543 return self.fail("TODO implement airErrorName for x86_64 {s}", .{@tagName(self.bin_file.tag)});
8544 }
8545
8546 const start_reg = try self.register_manager.allocReg(null, gp);
8547 const start_lock = self.register_manager.lockRegAssumeUnused(start_reg);
8548 defer self.register_manager.unlockReg(start_lock);
85498217
8550 const end_reg = try self.register_manager.allocReg(null, gp);
8551 const end_lock = self.register_manager.lockRegAssumeUnused(end_reg);
8552 defer self.register_manager.unlockReg(end_lock);
8218 const err_ty = self.air.typeOf(un_op);
8219 const err_mcv = try self.resolveInst(un_op);
8220 const err_reg = try self.copyToTmpRegister(err_ty, err_mcv);
8221 const err_lock = self.register_manager.lockRegAssumeUnused(err_reg);
8222 defer self.register_manager.unlockReg(err_lock);
85538223
8554 try self.truncateRegister(err_ty, err_reg.to32());
8224 const addr_reg = try self.register_manager.allocReg(null, gp);
8225 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
8226 defer self.register_manager.unlockReg(addr_lock);
85558227
8556 try self.asmRegisterMemory(.mov, start_reg.to32(), Memory.sib(.dword, .{
8557 .base = addr_reg.to64(),
8558 .scale_index = .{ .scale = 4, .index = err_reg.to64() },
8559 .disp = 4,
8560 }));
8561 try self.asmRegisterMemory(.mov, end_reg.to32(), Memory.sib(.dword, .{
8562 .base = addr_reg.to64(),
8563 .scale_index = .{ .scale = 4, .index = err_reg.to64() },
8564 .disp = 8,
8565 }));
8566 try self.asmRegisterRegister(.sub, end_reg.to32(), start_reg.to32());
8567 try self.asmRegisterMemory(.lea, start_reg.to64(), Memory.sib(.byte, .{
8568 .base = addr_reg.to64(),
8569 .scale_index = .{ .scale = 1, .index = start_reg.to64() },
8570 .disp = 0,
8571 }));
8572 try self.asmRegisterMemory(.lea, end_reg.to32(), Memory.sib(.byte, .{
8573 .base = end_reg.to64(),
8574 .disp = -1,
8228 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
8229 const atom_index = try elf_file.getOrCreateAtomForLazySymbol(
8230 .{ .kind = .const_data, .ty = Type.anyerror },
8231 4, // dword alignment
8232 );
8233 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
8234 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
8235 .base = .ds,
8236 .disp = @intCast(i32, got_addr),
85758237 }));
8238 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
8239 const atom_index = try coff_file.getOrCreateAtomForLazySymbol(
8240 .{ .kind = .const_data, .ty = Type.anyerror },
8241 4, // dword alignment
8242 );
8243 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
8244 try self.genSetReg(Type.usize, addr_reg, .{ .load_got = sym_index });
8245 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
8246 const atom_index = try macho_file.getOrCreateAtomForLazySymbol(
8247 .{ .kind = .const_data, .ty = Type.anyerror },
8248 4, // dword alignment
8249 );
8250 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
8251 try self.genSetReg(Type.usize, addr_reg, .{ .load_got = sym_index });
8252 } else {
8253 return self.fail("TODO implement airErrorName for x86_64 {s}", .{@tagName(self.bin_file.tag)});
8254 }
85768255
8577 const dst_mcv = try self.allocRegOrMem(inst, false);
8578 try self.asmMemoryRegister(.mov, Memory.sib(.qword, .{
8579 .base = .rbp,
8580 .disp = 0 - dst_mcv.stack_offset,
8581 }), start_reg.to64());
8582 try self.asmMemoryRegister(.mov, Memory.sib(.qword, .{
8583 .base = .rbp,
8584 .disp = 8 - dst_mcv.stack_offset,
8585 }), end_reg.to64());
8586 break :result dst_mcv;
8587 };
8588 return self.finishAir(inst, result, .{ un_op, .none, .none });
8256 const start_reg = try self.register_manager.allocReg(null, gp);
8257 const start_lock = self.register_manager.lockRegAssumeUnused(start_reg);
8258 defer self.register_manager.unlockReg(start_lock);
8259
8260 const end_reg = try self.register_manager.allocReg(null, gp);
8261 const end_lock = self.register_manager.lockRegAssumeUnused(end_reg);
8262 defer self.register_manager.unlockReg(end_lock);
8263
8264 try self.truncateRegister(err_ty, err_reg.to32());
8265
8266 try self.asmRegisterMemory(.mov, start_reg.to32(), Memory.sib(.dword, .{
8267 .base = addr_reg.to64(),
8268 .scale_index = .{ .scale = 4, .index = err_reg.to64() },
8269 .disp = 4,
8270 }));
8271 try self.asmRegisterMemory(.mov, end_reg.to32(), Memory.sib(.dword, .{
8272 .base = addr_reg.to64(),
8273 .scale_index = .{ .scale = 4, .index = err_reg.to64() },
8274 .disp = 8,
8275 }));
8276 try self.asmRegisterRegister(.sub, end_reg.to32(), start_reg.to32());
8277 try self.asmRegisterMemory(.lea, start_reg.to64(), Memory.sib(.byte, .{
8278 .base = addr_reg.to64(),
8279 .scale_index = .{ .scale = 1, .index = start_reg.to64() },
8280 .disp = 0,
8281 }));
8282 try self.asmRegisterMemory(.lea, end_reg.to32(), Memory.sib(.byte, .{
8283 .base = end_reg.to64(),
8284 .disp = -1,
8285 }));
8286
8287 const dst_mcv = try self.allocRegOrMem(inst, false);
8288 try self.asmMemoryRegister(.mov, Memory.sib(.qword, .{
8289 .base = .rbp,
8290 .disp = 0 - dst_mcv.stack_offset,
8291 }), start_reg.to64());
8292 try self.asmMemoryRegister(.mov, Memory.sib(.qword, .{
8293 .base = .rbp,
8294 .disp = 8 - dst_mcv.stack_offset,
8295 }), end_reg.to64());
8296
8297 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
85898298}
85908299
85918300fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
85928301 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8593 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for x86_64", .{});
8594 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
8302 _ = ty_op;
8303 return self.fail("TODO implement airSplat for x86_64", .{});
8304 //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
85958305}
85968306
85978307fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
85988308 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
85998309 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
8600 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for x86_64", .{});
8601 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });
8310 _ = extra;
8311 return self.fail("TODO implement airSelect for x86_64", .{});
8312 //return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });
86028313}
86038314
86048315fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
86058316 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8606 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for x86_64", .{});
8607 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
8317 _ = ty_op;
8318 return self.fail("TODO implement airShuffle for x86_64", .{});
8319 //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
86088320}
86098321
86108322fn airReduce(self: *Self, inst: Air.Inst.Index) !void {
86118323 const reduce = self.air.instructions.items(.data)[inst].reduce;
8612 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for x86_64", .{});
8613 return self.finishAir(inst, result, .{ reduce.operand, .none, .none });
8324 _ = reduce;
8325 return self.fail("TODO implement airReduce for x86_64", .{});
8326 //return self.finishAir(inst, result, .{ reduce.operand, .none, .none });
86148327}
86158328
86168329fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
......@@ -8620,8 +8333,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
86208333 const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
86218334 const abi_size = @intCast(u32, result_ty.abiSize(self.target.*));
86228335 const abi_align = result_ty.abiAlignment(self.target.*);
8623 const result: MCValue = res: {
8624 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
8336 const result: MCValue = result: {
86258337 switch (result_ty.zigTypeTag()) {
86268338 .Struct => {
86278339 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
......@@ -8712,7 +8424,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
87128424 };
87138425 try self.genSetStack(elem_ty, stack_offset - elem_off, mat_elem_mcv, .{});
87148426 }
8715 break :res .{ .stack_offset = stack_offset };
8427 break :result .{ .stack_offset = stack_offset };
87168428 },
87178429 .Array => {
87188430 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
......@@ -8728,7 +8440,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
87288440 const elem_off = @intCast(i32, elem_size * elem_i);
87298441 try self.genSetStack(elem_ty, stack_offset - elem_off, mat_elem_mcv, .{});
87308442 }
8731 break :res MCValue{ .stack_offset = stack_offset };
8443 break :result MCValue{ .stack_offset = stack_offset };
87328444 },
87338445 .Vector => return self.fail("TODO implement aggregate_init for vectors", .{}),
87348446 else => unreachable,
......@@ -8740,82 +8452,70 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
87408452 std.mem.copy(Air.Inst.Ref, &buf, elements);
87418453 return self.finishAir(inst, result, buf);
87428454 }
8743 var bt = try self.iterateBigTomb(inst, elements.len);
8744 for (elements) |elem| {
8745 bt.feed(elem);
8746 }
8747 return bt.finishAir(result);
8455 var bt = self.liveness.iterateBigTomb(inst);
8456 for (elements) |elem| self.feed(&bt, elem);
8457 return self.finishAirResult(inst, result);
87488458}
87498459
87508460fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
87518461 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
87528462 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
8753 const result: MCValue = res: {
8754 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
8755 return self.fail("TODO implement airAggregateInit for x86_64", .{});
8756 };
8757 return self.finishAir(inst, result, .{ extra.init, .none, .none });
8463 _ = extra;
8464 return self.fail("TODO implement airAggregateInit for x86_64", .{});
8465 //return self.finishAir(inst, result, .{ extra.init, .none, .none });
87588466}
87598467
87608468fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
87618469 const prefetch = self.air.instructions.items(.data)[inst].prefetch;
8762 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
8470 return self.finishAir(inst, .unreach, .{ prefetch.ptr, .none, .none });
87638471}
87648472
87658473fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
87668474 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
87678475 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
8768 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
8769 return self.fail("TODO implement airMulAdd for x86_64", .{});
8770 };
8771 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
8476 _ = extra;
8477 return self.fail("TODO implement airMulAdd for x86_64", .{});
8478 //return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
87728479}
87738480
8774fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
8775 // First section of indexes correspond to a set number of constant values.
8776 const ref_int = @enumToInt(inst);
8777 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
8778 const tv = Air.Inst.Ref.typed_value_map[ref_int];
8779 if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) {
8780 return .none;
8781 }
8782 return self.genTypedValue(tv);
8783 }
8481fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue {
8482 const ty = self.air.typeOf(ref);
87848483
87858484 // If the type has no codegen bits, no need to store it.
8786 const inst_ty = self.air.typeOf(inst);
8787 if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError())
8788 return .none;
8789
8790 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
8791 switch (self.air.instructions.items(.tag)[inst_index]) {
8792 .constant => {
8793 // Constants have static lifetimes, so they are always memoized in the outer most table.
8794 const branch = &self.branch_stack.items[0];
8795 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
8796 if (!gop.found_existing) {
8797 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
8798 gop.value_ptr.* = try self.genTypedValue(.{
8799 .ty = inst_ty,
8800 .val = self.air.values[ty_pl.payload],
8801 });
8802 }
8803 return gop.value_ptr.*;
8804 },
8805 .const_ty => unreachable,
8806 else => return self.getResolvedInstValue(inst_index).?.*,
8485 if (!ty.hasRuntimeBitsIgnoreComptime() and !ty.isError()) return .none;
8486
8487 if (Air.refToIndex(ref)) |inst| {
8488 const mcv = switch (self.air.instructions.items(.tag)[inst]) {
8489 .constant => tracking: {
8490 const gop = try self.const_tracking.getOrPut(self.gpa, inst);
8491 if (!gop.found_existing) gop.value_ptr.* = InstTracking.init(try self.genTypedValue(.{
8492 .ty = ty,
8493 .val = self.air.value(ref).?,
8494 }));
8495 break :tracking gop.value_ptr;
8496 },
8497 .const_ty => unreachable,
8498 else => self.inst_tracking.getPtr(inst).?,
8499 }.short;
8500 switch (mcv) {
8501 .none, .unreach, .dead => unreachable,
8502 else => return mcv,
8503 }
88078504 }
8505
8506 return self.genTypedValue(.{ .ty = ty, .val = self.air.value(ref).? });
88088507}
88098508
8810fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?*MCValue {
8811 // Treat each stack item as a "layer" on top of the previous one.
8812 var i: usize = self.branch_stack.items.len;
8813 while (true) {
8814 i -= 1;
8815 if (self.branch_stack.items[i].inst_table.getPtr(inst)) |mcv| {
8816 return if (mcv.* != .dead) mcv else null;
8817 }
8818 }
8509fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) *InstTracking {
8510 const tracking = switch (self.air.instructions.items(.tag)[inst]) {
8511 .constant => &self.const_tracking,
8512 .const_ty => unreachable,
8513 else => &self.inst_tracking,
8514 }.getPtr(inst).?;
8515 return switch (tracking.short) {
8516 .none, .unreach, .dead => unreachable,
8517 else => tracking,
8518 };
88198519}
88208520
88218521/// If the MCValue is an immediate, and it does not fit within this type,
src/codegen/c.zig+121-411
......@@ -78,7 +78,6 @@ const LoopDepth = u16;
7878const Local = struct {
7979 cty_idx: CType.Index,
8080 alignas: CType.AlignAs,
81 is_in_clone: bool,
8281
8382 pub fn getType(local: Local) LocalType {
8483 return .{ .cty_idx = local.cty_idx, .alignas = local.alignas };
......@@ -275,16 +274,13 @@ pub const Function = struct {
275274 /// All the locals, to be emitted at the top of the function.
276275 locals: std.ArrayListUnmanaged(Local) = .{},
277276 /// Which locals are available for reuse, based on Type.
278 /// Only locals in the last stack entry are available for reuse,
279 /// other entries will become available on loop exit.
280277 free_locals_map: LocalsMap = .{},
281 is_in_clone: bool = false,
282278 /// Locals which will not be freed by Liveness. This is used after a
283279 /// Function body is lowered in order to make `free_locals_map` have
284280 /// 100% of the locals within so that it can be used to render the block
285281 /// of variable declarations at the top of a function, sorted descending
286282 /// by type alignment.
287 /// The value is whether the alloc is static or not.
283 /// The value is whether the alloc needs to be emitted in the header.
288284 allocs: std.AutoArrayHashMapUnmanaged(LocalIndex, bool) = .{},
289285 /// Needed for memory used by the keys of free_locals_map entries.
290286 arena: std.heap.ArenaAllocator,
......@@ -302,7 +298,7 @@ pub const Function = struct {
302298 const alignment = 0;
303299 const decl_c_value = try f.allocLocalValue(ty, alignment);
304300 const gpa = f.object.dg.gpa;
305 try f.allocs.put(gpa, decl_c_value.new_local, true);
301 try f.allocs.put(gpa, decl_c_value.new_local, false);
306302 try writer.writeAll("static ");
307303 try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, Const, alignment, .complete);
308304 try writer.writeAll(" = ");
......@@ -323,14 +319,15 @@ pub const Function = struct {
323319 };
324320 }
325321
326 /// Skips the reuse logic.
322 /// Skips the reuse logic. This function should be used for any persistent allocation, i.e.
323 /// those which go into `allocs`. This function does not add the resulting local into `allocs`;
324 /// that responsibility lies with the caller.
327325 fn allocLocalValue(f: *Function, ty: Type, alignment: u32) !CValue {
328326 const gpa = f.object.dg.gpa;
329327 const target = f.object.dg.module.getTarget();
330328 try f.locals.append(gpa, .{
331329 .cty_idx = try f.typeToIndex(ty, .complete),
332330 .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)),
333 .is_in_clone = f.is_in_clone,
334331 });
335332 return .{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) };
336333 }
......@@ -341,7 +338,8 @@ pub const Function = struct {
341338 return result;
342339 }
343340
344 /// Only allocates the local; does not print anything.
341 /// Only allocates the local; does not print anything. Will attempt to re-use locals, so should
342 /// not be used for persistent locals (i.e. those in `allocs`).
345343 fn allocAlignedLocal(f: *Function, ty: Type, _: CQualifiers, alignment: u32) !CValue {
346344 const target = f.object.dg.module.getTarget();
347345 if (f.free_locals_map.getPtr(.{
......@@ -2586,7 +2584,7 @@ pub fn genFunc(f: *Function) !void {
25862584 f.free_locals_map.clearRetainingCapacity();
25872585
25882586 const main_body = f.air.getMainBody();
2589 try genBody(f, main_body);
2587 try genBodyResolveState(f, undefined, &.{}, main_body, false);
25902588
25912589 try o.indent_writer.insertNewline();
25922590
......@@ -2597,8 +2595,8 @@ pub fn genFunc(f: *Function) !void {
25972595 // alignment, descending.
25982596 const free_locals = &f.free_locals_map;
25992597 assert(f.value_map.count() == 0); // there must not be any unfreed locals
2600 for (f.allocs.keys(), f.allocs.values()) |local_index, value| {
2601 if (value) continue; // static
2598 for (f.allocs.keys(), f.allocs.values()) |local_index, should_emit| {
2599 if (!should_emit) continue;
26022600 const local = f.locals.items[local_index];
26032601 log.debug("inserting local {d} into free_locals", .{local_index});
26042602 const gop = try free_locals.getOrPut(gpa, local.getType());
......@@ -2715,6 +2713,10 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
27152713 }
27162714}
27172715
2716/// Generate code for an entire body which ends with a `noreturn` instruction. The states of
2717/// `value_map` and `free_locals_map` are undefined after the generation, and new locals may not
2718/// have been added to `free_locals_map`. For a version of this function that restores this state,
2719/// see `genBodyResolveState`.
27182720fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
27192721 const writer = f.object.writer();
27202722 if (body.len == 0) {
......@@ -2728,10 +2730,69 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
27282730 }
27292731}
27302732
2733/// Generate code for an entire body which ends with a `noreturn` instruction. The states of
2734/// `value_map` and `free_locals_map` are restored to their original values, and any non-allocated
2735/// locals introduced within the body are correctly added to `free_locals_map`. Operands in
2736/// `leading_deaths` have their deaths processed before the body is generated.
2737/// A scope is introduced (using braces) only if `inner` is `false`.
2738/// If `leading_deaths` is empty, `inst` may be `undefined`.
2739fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) error{ AnalysisFail, OutOfMemory }!void {
2740 if (body.len == 0) {
2741 // Don't go to the expense of cloning everything!
2742 if (!inner) try f.object.writer().writeAll("{}");
2743 return;
2744 }
2745
2746 // TODO: we can probably avoid the copies in some other common cases too.
2747
2748 const gpa = f.object.dg.gpa;
2749
2750 // Save the original value_map and free_locals_map so that we can restore them after the body.
2751 var old_value_map = try f.value_map.clone();
2752 defer old_value_map.deinit();
2753 var old_free_locals = try cloneFreeLocalsMap(gpa, &f.free_locals_map);
2754 defer deinitFreeLocalsMap(gpa, &old_free_locals);
2755
2756 // Remember how many locals there were before entering the body so that we can free any that
2757 // were newly introduced. Any new locals must necessarily be logically free after the then
2758 // branch is complete.
2759 const pre_locals_len = @intCast(LocalIndex, f.locals.items.len);
2760
2761 for (leading_deaths) |death| {
2762 try die(f, inst, Air.indexToRef(death));
2763 }
2764
2765 if (inner) {
2766 try genBodyInner(f, body);
2767 } else {
2768 try genBody(f, body);
2769 }
2770
2771 f.value_map.deinit();
2772 f.value_map = old_value_map.move();
2773 deinitFreeLocalsMap(gpa, &f.free_locals_map);
2774 f.free_locals_map = old_free_locals.move();
2775
2776 // Now, use the lengths we stored earlier to detect any locals the body generated, and free
2777 // them, unless they were used to store allocs.
2778
2779 for (pre_locals_len..f.locals.items.len) |local_i| {
2780 const local_index = @intCast(LocalIndex, local_i);
2781 if (f.allocs.contains(local_index)) {
2782 continue;
2783 }
2784 try freeLocal(f, inst, local_index, 0);
2785 }
2786}
2787
27312788fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
27322789 const air_tags = f.air.instructions.items(.tag);
27332790
27342791 for (body) |inst| {
2792 if (f.liveness.isUnused(inst) and !f.air.mustLower(inst)) {
2793 continue;
2794 }
2795
27352796 const result_value = switch (air_tags[inst]) {
27362797 // zig fmt: off
27372798 .constant => unreachable, // excluded from function bodies
......@@ -3009,11 +3070,6 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
30093070fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: []const u8) !CValue {
30103071 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
30113072
3012 if (f.liveness.isUnused(inst)) {
3013 try reap(f, inst, &.{ty_op.operand});
3014 return .none;
3015 }
3016
30173073 const inst_ty = f.air.typeOfIndex(inst);
30183074 const operand = try f.resolveInst(ty_op.operand);
30193075 try reap(f, inst, &.{ty_op.operand});
......@@ -3032,10 +3088,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [
30323088fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
30333089 const inst_ty = f.air.typeOfIndex(inst);
30343090 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
3035 const ptr_ty = f.air.typeOf(bin_op.lhs);
3036 if ((!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) or
3037 !inst_ty.hasRuntimeBitsIgnoreComptime())
3038 {
3091 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) {
30393092 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
30403093 return .none;
30413094 }
......@@ -3074,11 +3127,6 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
30743127 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
30753128 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
30763129
3077 if (f.liveness.isUnused(inst)) {
3078 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3079 return .none;
3080 }
3081
30823130 const inst_ty = f.air.typeOfIndex(inst);
30833131 const ptr_ty = f.air.typeOf(bin_op.lhs);
30843132 const child_ty = ptr_ty.childType();
......@@ -3116,10 +3164,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
31163164fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
31173165 const inst_ty = f.air.typeOfIndex(inst);
31183166 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
3119 const slice_ty = f.air.typeOf(bin_op.lhs);
3120 if ((!slice_ty.isVolatilePtr() and f.liveness.isUnused(inst)) or
3121 !inst_ty.hasRuntimeBitsIgnoreComptime())
3122 {
3167 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) {
31233168 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
31243169 return .none;
31253170 }
......@@ -3158,11 +3203,6 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
31583203 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
31593204 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
31603205
3161 if (f.liveness.isUnused(inst)) {
3162 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3163 return .none;
3164 }
3165
31663206 const slice_ty = f.air.typeOf(bin_op.lhs);
31673207 const child_ty = slice_ty.elemType2();
31683208 const slice = try f.resolveInst(bin_op.lhs);
......@@ -3188,7 +3228,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
31883228fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
31893229 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
31903230 const inst_ty = f.air.typeOfIndex(inst);
3191 if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBitsIgnoreComptime()) {
3231 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) {
31923232 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
31933233 return .none;
31943234 }
......@@ -3224,40 +3264,34 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
32243264}
32253265
32263266fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
3227 if (f.liveness.isUnused(inst)) return .none;
3228
32293267 const inst_ty = f.air.typeOfIndex(inst);
32303268 const elem_type = inst_ty.elemType();
32313269 if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) return .{ .undef = inst_ty };
32323270
32333271 const target = f.object.dg.module.getTarget();
3234 const local = try f.allocAlignedLocal(
3272 const local = try f.allocLocalValue(
32353273 elem_type,
3236 CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }),
32373274 inst_ty.ptrAlignment(target),
32383275 );
32393276 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
32403277 const gpa = f.object.dg.module.gpa;
3241 try f.allocs.put(gpa, local.new_local, false);
3278 try f.allocs.put(gpa, local.new_local, true);
32423279 return .{ .local_ref = local.new_local };
32433280}
32443281
32453282fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3246 if (f.liveness.isUnused(inst)) return .none;
3247
32483283 const inst_ty = f.air.typeOfIndex(inst);
32493284 const elem_ty = inst_ty.elemType();
32503285 if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return .{ .undef = inst_ty };
32513286
32523287 const target = f.object.dg.module.getTarget();
3253 const local = try f.allocAlignedLocal(
3288 const local = try f.allocLocalValue(
32543289 elem_ty,
3255 CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }),
32563290 inst_ty.ptrAlignment(target),
32573291 );
32583292 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
32593293 const gpa = f.object.dg.module.gpa;
3260 try f.allocs.put(gpa, local.new_local, false);
3294 try f.allocs.put(gpa, local.new_local, true);
32613295 return .{ .local_ref = local.new_local };
32623296}
32633297
......@@ -3293,9 +3327,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
32933327 const ptr_info = ptr_scalar_ty.ptrInfo().data;
32943328 const src_ty = ptr_info.pointee_type;
32953329
3296 if (!src_ty.hasRuntimeBitsIgnoreComptime() or
3297 (!ptr_info.@"volatile" and f.liveness.isUnused(inst)))
3298 {
3330 if (!src_ty.hasRuntimeBitsIgnoreComptime()) {
32993331 try reap(f, inst, &.{ty_op.operand});
33003332 return .none;
33013333 }
......@@ -3442,11 +3474,6 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
34423474fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
34433475 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
34443476
3445 if (f.liveness.isUnused(inst)) {
3446 try reap(f, inst, &.{ty_op.operand});
3447 return .none;
3448 }
3449
34503477 const operand = try f.resolveInst(ty_op.operand);
34513478 try reap(f, inst, &.{ty_op.operand});
34523479
......@@ -3470,10 +3497,6 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
34703497
34713498fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
34723499 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
3473 if (f.liveness.isUnused(inst)) {
3474 try reap(f, inst, &.{ty_op.operand});
3475 return .none;
3476 }
34773500
34783501 const operand = try f.resolveInst(ty_op.operand);
34793502 try reap(f, inst, &.{ty_op.operand});
......@@ -3569,10 +3592,6 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
35693592
35703593fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
35713594 const un_op = f.air.instructions.items(.data)[inst].un_op;
3572 if (f.liveness.isUnused(inst)) {
3573 try reap(f, inst, &.{un_op});
3574 return .none;
3575 }
35763595 const operand = try f.resolveInst(un_op);
35773596 try reap(f, inst, &.{un_op});
35783597 const writer = f.object.writer();
......@@ -3746,11 +3765,6 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
37463765 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
37473766 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
37483767
3749 if (f.liveness.isUnused(inst)) {
3750 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3751 return .none;
3752 }
3753
37543768 const lhs = try f.resolveInst(bin_op.lhs);
37553769 const rhs = try f.resolveInst(bin_op.rhs);
37563770 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
......@@ -3790,11 +3804,6 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
37903804 const scalar_ty = operand_ty.scalarType();
37913805 if (scalar_ty.tag() != .bool) return try airUnBuiltinCall(f, inst, "not", .bits);
37923806
3793 if (f.liveness.isUnused(inst)) {
3794 try reap(f, inst, &.{ty_op.operand});
3795 return .none;
3796 }
3797
37983807 const op = try f.resolveInst(ty_op.operand);
37993808 try reap(f, inst, &.{ty_op.operand});
38003809
......@@ -3829,11 +3838,6 @@ fn airBinOp(
38293838 if ((scalar_ty.isInt() and scalar_ty.bitSize(target) > 64) or scalar_ty.isRuntimeFloat())
38303839 return try airBinBuiltinCall(f, inst, operation, info);
38313840
3832 if (f.liveness.isUnused(inst)) {
3833 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3834 return .none;
3835 }
3836
38373841 const lhs = try f.resolveInst(bin_op.lhs);
38383842 const rhs = try f.resolveInst(bin_op.rhs);
38393843 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
......@@ -3865,11 +3869,6 @@ fn airCmpOp(
38653869 data: anytype,
38663870 operator: std.math.CompareOperator,
38673871) !CValue {
3868 if (f.liveness.isUnused(inst)) {
3869 try reap(f, inst, &.{ data.lhs, data.rhs });
3870 return .none;
3871 }
3872
38733872 const operand_ty = f.air.typeOf(data.lhs);
38743873 const scalar_ty = operand_ty.scalarType();
38753874
......@@ -3918,11 +3917,6 @@ fn airEquality(
39183917) !CValue {
39193918 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
39203919
3921 if (f.liveness.isUnused(inst)) {
3922 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3923 return .none;
3924 }
3925
39263920 const operand_ty = f.air.typeOf(bin_op.lhs);
39273921 const target = f.object.dg.module.getTarget();
39283922 const operand_bits = operand_ty.bitSize(target);
......@@ -3987,11 +3981,6 @@ fn airEquality(
39873981fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {
39883982 const un_op = f.air.instructions.items(.data)[inst].un_op;
39893983
3990 if (f.liveness.isUnused(inst)) {
3991 try reap(f, inst, &.{un_op});
3992 return .none;
3993 }
3994
39953984 const inst_ty = f.air.typeOfIndex(inst);
39963985 const operand = try f.resolveInst(un_op);
39973986 try reap(f, inst, &.{un_op});
......@@ -4008,10 +3997,6 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {
40083997fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
40093998 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
40103999 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
4011 if (f.liveness.isUnused(inst)) {
4012 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
4013 return .none;
4014 }
40154000
40164001 const lhs = try f.resolveInst(bin_op.lhs);
40174002 const rhs = try f.resolveInst(bin_op.rhs);
......@@ -4059,11 +4044,6 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
40594044fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !CValue {
40604045 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
40614046
4062 if (f.liveness.isUnused(inst)) {
4063 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
4064 return .none;
4065 }
4066
40674047 const inst_ty = f.air.typeOfIndex(inst);
40684048 const inst_scalar_ty = inst_ty.scalarType();
40694049
......@@ -4107,11 +4087,6 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
41074087 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
41084088 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
41094089
4110 if (f.liveness.isUnused(inst)) {
4111 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
4112 return .none;
4113 }
4114
41154090 const ptr = try f.resolveInst(bin_op.lhs);
41164091 const len = try f.resolveInst(bin_op.rhs);
41174092 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
......@@ -4316,6 +4291,7 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
43164291 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
43174292 const extra = f.air.extraData(Air.Block, ty_pl.payload);
43184293 const body = f.air.extra[extra.end..][0..extra.data.body_len];
4294 const liveness_block = f.liveness.getBlock(inst);
43194295
43204296 const block_id: usize = f.next_block_index;
43214297 f.next_block_index += 1;
......@@ -4332,7 +4308,15 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
43324308 .result = result,
43334309 });
43344310
4335 try genBodyInner(f, body);
4311 try genBodyResolveState(f, inst, &.{}, body, true);
4312
4313 assert(f.blocks.remove(inst));
4314
4315 // The body might result in some values we had beforehand being killed
4316 for (liveness_block.deaths) |death| {
4317 try die(f, inst, Air.indexToRef(death));
4318 }
4319
43364320 try f.object.indent_writer.insertNewline();
43374321 // label might be unused, add a dummy goto
43384322 // label must be followed by an expression, add an empty one.
......@@ -4366,6 +4350,7 @@ fn lowerTry(
43664350) !CValue {
43674351 const err_union = try f.resolveInst(operand);
43684352 const result_ty = f.air.typeOfIndex(inst);
4353 const liveness_condbr = f.liveness.getCondBr(inst);
43694354 const writer = f.object.writer();
43704355 const payload_ty = err_union_ty.errorUnionPayload();
43714356 const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime();
......@@ -4389,10 +4374,15 @@ fn lowerTry(
43894374 }
43904375 try writer.writeByte(')');
43914376
4392 try genBody(f, body);
4377 try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false);
43934378 try f.object.indent_writer.insertNewline();
43944379 }
43954380
4381 // Now we have the "then branch" (in terms of the liveness data); process any deaths.
4382 for (liveness_condbr.then_deaths) |death| {
4383 try die(f, inst, Air.indexToRef(death));
4384 }
4385
43964386 if (!payload_has_bits) {
43974387 if (!operand_is_ptr) {
43984388 return .none;
......@@ -4466,10 +4456,6 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
44664456fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
44674457 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
44684458 const dest_ty = f.air.typeOfIndex(inst);
4469 if (f.liveness.isUnused(inst)) {
4470 try reap(f, inst, &.{ty_op.operand});
4471 return .none;
4472 }
44734459
44744460 const operand = try f.resolveInst(ty_op.operand);
44754461 try reap(f, inst, &.{ty_op.operand});
......@@ -4593,7 +4579,6 @@ fn airBreakpoint(writer: anytype) !CValue {
45934579}
45944580
45954581fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
4596 if (f.liveness.isUnused(inst)) return .none;
45974582 const writer = f.object.writer();
45984583 const local = try f.allocLocal(inst, Type.usize);
45994584 try f.writeCValue(writer, local, .Other);
......@@ -4604,7 +4589,6 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
46044589}
46054590
46064591fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
4607 if (f.liveness.isUnused(inst)) return .none;
46084592 const writer = f.object.writer();
46094593 const local = try f.allocLocal(inst, Type.usize);
46104594 try f.writeCValue(writer, local, .Other);
......@@ -4637,17 +4621,12 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue {
46374621 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
46384622 const loop = f.air.extraData(Air.Block, ty_pl.payload);
46394623 const body = f.air.extra[loop.end..][0..loop.data.body_len];
4640 const liveness_loop = f.liveness.getLoop(inst);
46414624 const writer = f.object.writer();
46424625
46434626 try writer.writeAll("for (;;) ");
4644 try genBody(f, body);
4627 try genBody(f, body); // no need to restore state, we're noreturn
46454628 try writer.writeByte('\n');
46464629
4647 for (liveness_loop.deaths) |operand| {
4648 try die(f, inst, Air.indexToRef(operand));
4649 }
4650
46514630 return .none;
46524631}
46534632
......@@ -4661,61 +4640,24 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue {
46614640 const liveness_condbr = f.liveness.getCondBr(inst);
46624641 const writer = f.object.writer();
46634642
4664 // Keep using the original for the then branch; use a clone of the value
4665 // map for the else branch.
4666 const gpa = f.object.dg.gpa;
4667 var cloned_map = try f.value_map.clone();
4668 defer cloned_map.deinit();
4669 var cloned_frees = try cloneFreeLocalsMap(gpa, &f.free_locals_map);
4670 defer deinitFreeLocalsMap(gpa, &cloned_frees);
4671
4672 // Remember how many locals there were before entering the then branch so
4673 // that we can notice and use them in the else branch. Any new locals must
4674 // necessarily be free already after the then branch is complete.
4675 const pre_locals_len = @intCast(LocalIndex, f.locals.items.len);
4676 // Remember how many allocs there were before entering the then branch so
4677 // that we can notice and make sure not to use them in the else branch.
4678 // Any new allocs must be removed from the free list.
4679 const pre_allocs_len = @intCast(LocalIndex, f.allocs.count());
4680 const was_in_clone = f.is_in_clone;
4681 f.is_in_clone = true;
4682
4683 for (liveness_condbr.then_deaths) |operand| {
4684 try die(f, inst, Air.indexToRef(operand));
4685 }
4686
46874643 try writer.writeAll("if (");
46884644 try f.writeCValue(writer, cond, .Other);
46894645 try writer.writeAll(") ");
4690 try genBody(f, then_body);
46914646
4692 // TODO: If body ends in goto, elide the else block?
4693 const needs_else = then_body.len <= 0 or f.air.instructions.items(.tag)[then_body[then_body.len - 1]] != .br;
4694 if (needs_else) {
4695 try writer.writeAll(" else ");
4696 } else {
4697 try writer.writeByte('\n');
4698 }
4647 try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false);
46994648
4700 f.value_map.deinit();
4701 f.value_map = cloned_map.move();
4702 const free_locals = &f.free_locals_map;
4703 deinitFreeLocalsMap(gpa, free_locals);
4704 free_locals.* = cloned_frees.move();
4705 f.is_in_clone = was_in_clone;
4706 for (liveness_condbr.else_deaths) |operand| {
4707 try die(f, inst, Air.indexToRef(operand));
4708 }
4709
4710 try noticeBranchFrees(f, pre_locals_len, pre_allocs_len, inst);
4649 // We don't need to use `genBodyResolveState` for the else block, because this instruction is
4650 // noreturn so must terminate a body, therefore we don't need to leave `value_map` or
4651 // `free_locals_map` well defined (our parent is responsible for doing that).
47114652
4712 if (needs_else) {
4713 try genBody(f, else_body);
4714 } else {
4715 try genBodyInner(f, else_body);
4653 for (liveness_condbr.else_deaths) |death| {
4654 try die(f, inst, Air.indexToRef(death));
47164655 }
47174656
4718 try f.object.indent_writer.insertNewline();
4657 // We never actually need an else block, because our branches are noreturn so must (for
4658 // instance) `br` to a block (label).
4659
4660 try genBodyInner(f, else_body);
47194661
47204662 return .none;
47214663}
......@@ -4746,9 +4688,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
47464688 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.data.cases_len + 1);
47474689 defer gpa.free(liveness.deaths);
47484690
4749 // On the final iteration we do not clone the map. This ensures that
4750 // lowering proceeds after the switch_br taking into account the
4751 // mutations to the liveness information.
4691 // On the final iteration we do not need to fix any state. This is because, like in the `else`
4692 // branch of a `cond_br`, our parent has to do it for this entire body anyway.
47524693 const last_case_i = switch_br.data.cases_len - @boolToInt(switch_br.data.else_body_len == 0);
47534694
47544695 var extra_index: usize = switch_br.end;
......@@ -4772,56 +4713,23 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
47724713 try writer.writeByte(' ');
47734714
47744715 if (case_i != last_case_i) {
4775 const old_value_map = f.value_map;
4776 f.value_map = try old_value_map.clone();
4777 var free_locals = &f.free_locals_map;
4778 const old_free_locals = free_locals.*;
4779 free_locals.* = try cloneFreeLocalsMap(gpa, free_locals);
4780
4781 // Remember how many locals there were before entering each branch so that
4782 // we can notice and use them in subsequent branches. Any new locals must
4783 // necessarily be free already after the previous branch is complete.
4784 const pre_locals_len = @intCast(LocalIndex, f.locals.items.len);
4785 // Remember how many allocs there were before entering each branch so that
4786 // we can notice and make sure not to use them in subsequent branches.
4787 // Any new allocs must be removed from the free list.
4788 const pre_allocs_len = @intCast(LocalIndex, f.allocs.count());
4789 const was_in_clone = f.is_in_clone;
4790 f.is_in_clone = true;
4791
4792 {
4793 defer {
4794 f.is_in_clone = was_in_clone;
4795 f.value_map.deinit();
4796 deinitFreeLocalsMap(gpa, free_locals);
4797 f.value_map = old_value_map;
4798 free_locals.* = old_free_locals;
4799 }
4800
4801 for (liveness.deaths[case_i]) |operand| {
4802 try die(f, inst, Air.indexToRef(operand));
4803 }
4804
4805 try genBody(f, case_body);
4806 }
4807
4808 try noticeBranchFrees(f, pre_locals_len, pre_allocs_len, inst);
4716 try genBodyResolveState(f, inst, liveness.deaths[case_i], case_body, false);
48094717 } else {
4810 for (liveness.deaths[case_i]) |operand| {
4811 try die(f, inst, Air.indexToRef(operand));
4718 for (liveness.deaths[case_i]) |death| {
4719 try die(f, inst, Air.indexToRef(death));
48124720 }
48134721 try genBody(f, case_body);
48144722 }
48154723
48164724 // The case body must be noreturn so we don't need to insert a break.
4817
48184725 }
48194726
48204727 const else_body = f.air.extra[extra_index..][0..switch_br.data.else_body_len];
48214728 try f.object.indent_writer.insertNewline();
48224729 if (else_body.len > 0) {
4823 for (liveness.deaths[liveness.deaths.len - 1]) |operand| {
4824 try die(f, inst, Air.indexToRef(operand));
4730 // Note that this must be the last case (i.e. the `last_case_i` case was not hit above)
4731 for (liveness.deaths[liveness.deaths.len - 1]) |death| {
4732 try die(f, inst, Air.indexToRef(death));
48254733 }
48264734 try writer.writeAll("default: ");
48274735 try genBody(f, else_body);
......@@ -4848,6 +4756,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
48484756 const extra = f.air.extraData(Air.Asm, ty_pl.payload);
48494757 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
48504758 const clobbers_len = @truncate(u31, extra.data.flags);
4759 const gpa = f.object.dg.gpa;
48514760 var extra_i: usize = extra.end;
48524761 const outputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]);
48534762 extra_i += outputs.len;
......@@ -4855,8 +4764,6 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
48554764 extra_i += inputs.len;
48564765
48574766 const result = result: {
4858 if (!is_volatile and f.liveness.isUnused(inst)) break :result .none;
4859
48604767 const writer = f.object.writer();
48614768 const inst_ty = f.air.typeOfIndex(inst);
48624769 const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: {
......@@ -4892,6 +4799,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
48924799 try writer.writeAll("register ");
48934800 const alignment = 0;
48944801 const local_value = try f.allocLocalValue(output_ty, alignment);
4802 try f.allocs.put(gpa, local_value.new_local, false);
48954803 try f.object.dg.renderTypeAndName(writer, output_ty, local_value, .{}, alignment, .complete);
48964804 try writer.writeAll(" __asm(\"");
48974805 try writer.writeAll(constraint["={".len .. constraint.len - "}".len]);
......@@ -4924,6 +4832,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
49244832 if (is_reg) try writer.writeAll("register ");
49254833 const alignment = 0;
49264834 const local_value = try f.allocLocalValue(input_ty, alignment);
4835 try f.allocs.put(gpa, local_value.new_local, false);
49274836 try f.object.dg.renderTypeAndName(writer, input_ty, local_value, Const, alignment, .complete);
49284837 if (is_reg) {
49294838 try writer.writeAll(" __asm(\"");
......@@ -5106,11 +5015,6 @@ fn airIsNull(
51065015) !CValue {
51075016 const un_op = f.air.instructions.items(.data)[inst].un_op;
51085017
5109 if (f.liveness.isUnused(inst)) {
5110 try reap(f, inst, &.{un_op});
5111 return .none;
5112 }
5113
51145018 const writer = f.object.writer();
51155019 const operand = try f.resolveInst(un_op);
51165020 try reap(f, inst, &.{un_op});
......@@ -5156,11 +5060,6 @@ fn airIsNull(
51565060fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
51575061 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
51585062
5159 if (f.liveness.isUnused(inst)) {
5160 try reap(f, inst, &.{ty_op.operand});
5161 return .none;
5162 }
5163
51645063 const operand = try f.resolveInst(ty_op.operand);
51655064 try reap(f, inst, &.{ty_op.operand});
51665065 const opt_ty = f.air.typeOf(ty_op.operand);
......@@ -5208,11 +5107,6 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
52085107fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue {
52095108 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
52105109
5211 if (f.liveness.isUnused(inst)) {
5212 try reap(f, inst, &.{ty_op.operand});
5213 return .none;
5214 }
5215
52165110 const writer = f.object.writer();
52175111 const operand = try f.resolveInst(ty_op.operand);
52185112 try reap(f, inst, &.{ty_op.operand});
......@@ -5342,11 +5236,6 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue {
53425236 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
53435237 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
53445238
5345 if (f.liveness.isUnused(inst)) {
5346 try reap(f, inst, &.{extra.struct_operand});
5347 return .none;
5348 }
5349
53505239 const container_ptr_val = try f.resolveInst(extra.struct_operand);
53515240 try reap(f, inst, &.{extra.struct_operand});
53525241 const container_ptr_ty = f.air.typeOf(extra.struct_operand);
......@@ -5356,11 +5245,6 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue {
53565245fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue {
53575246 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
53585247
5359 if (f.liveness.isUnused(inst)) {
5360 try reap(f, inst, &.{ty_op.operand});
5361 return .none;
5362 }
5363
53645248 const container_ptr_val = try f.resolveInst(ty_op.operand);
53655249 try reap(f, inst, &.{ty_op.operand});
53665250 const container_ptr_ty = f.air.typeOf(ty_op.operand);
......@@ -5371,11 +5255,6 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
53715255 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
53725256 const extra = f.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
53735257
5374 if (f.liveness.isUnused(inst)) {
5375 try reap(f, inst, &.{extra.field_ptr});
5376 return .none;
5377 }
5378
53795258 const target = f.object.dg.module.getTarget();
53805259 const container_ptr_ty = f.air.typeOfIndex(inst);
53815260 const container_ty = container_ptr_ty.childType();
......@@ -5494,11 +5373,6 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
54945373 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
54955374 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
54965375
5497 if (f.liveness.isUnused(inst)) {
5498 try reap(f, inst, &.{extra.struct_operand});
5499 return .none;
5500 }
5501
55025376 const inst_ty = f.air.typeOfIndex(inst);
55035377 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) {
55045378 try reap(f, inst, &.{extra.struct_operand});
......@@ -5644,11 +5518,6 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
56445518fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
56455519 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
56465520
5647 if (f.liveness.isUnused(inst)) {
5648 try reap(f, inst, &.{ty_op.operand});
5649 return .none;
5650 }
5651
56525521 const inst_ty = f.air.typeOfIndex(inst);
56535522 const operand = try f.resolveInst(ty_op.operand);
56545523 const operand_ty = f.air.typeOf(ty_op.operand);
......@@ -5681,11 +5550,6 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
56815550fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
56825551 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
56835552
5684 if (f.liveness.isUnused(inst)) {
5685 try reap(f, inst, &.{ty_op.operand});
5686 return .none;
5687 }
5688
56895553 const inst_ty = f.air.typeOfIndex(inst);
56905554 const operand = try f.resolveInst(ty_op.operand);
56915555 try reap(f, inst, &.{ty_op.operand});
......@@ -5723,11 +5587,6 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
57235587fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
57245588 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
57255589
5726 if (f.liveness.isUnused(inst)) {
5727 try reap(f, inst, &.{ty_op.operand});
5728 return .none;
5729 }
5730
57315590 const inst_ty = f.air.typeOfIndex(inst);
57325591 const payload = try f.resolveInst(ty_op.operand);
57335592 try reap(f, inst, &.{ty_op.operand});
......@@ -5769,10 +5628,6 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
57695628
57705629fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
57715630 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
5772 if (f.liveness.isUnused(inst)) {
5773 try reap(f, inst, &.{ty_op.operand});
5774 return .none;
5775 }
57765631
57775632 const writer = f.object.writer();
57785633 const operand = try f.resolveInst(ty_op.operand);
......@@ -5836,7 +5691,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
58365691}
58375692
58385693fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
5839 if (f.liveness.isUnused(inst)) return .none;
5694 _ = inst;
58405695 return f.fail("TODO: C backend: implement airErrReturnTrace", .{});
58415696}
58425697
......@@ -5852,10 +5707,6 @@ fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue {
58525707
58535708fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
58545709 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
5855 if (f.liveness.isUnused(inst)) {
5856 try reap(f, inst, &.{ty_op.operand});
5857 return .none;
5858 }
58595710
58605711 const inst_ty = f.air.typeOfIndex(inst);
58615712 const payload_ty = inst_ty.errorUnionPayload();
......@@ -5890,11 +5741,6 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
58905741fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue {
58915742 const un_op = f.air.instructions.items(.data)[inst].un_op;
58925743
5893 if (f.liveness.isUnused(inst)) {
5894 try reap(f, inst, &.{un_op});
5895 return .none;
5896 }
5897
58985744 const writer = f.object.writer();
58995745 const operand = try f.resolveInst(un_op);
59005746 try reap(f, inst, &.{un_op});
......@@ -5928,11 +5774,6 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const
59285774fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
59295775 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
59305776
5931 if (f.liveness.isUnused(inst)) {
5932 try reap(f, inst, &.{ty_op.operand});
5933 return .none;
5934 }
5935
59365777 const operand = try f.resolveInst(ty_op.operand);
59375778 try reap(f, inst, &.{ty_op.operand});
59385779 const inst_ty = f.air.typeOfIndex(inst);
......@@ -5966,11 +5807,6 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
59665807fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
59675808 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
59685809
5969 if (f.liveness.isUnused(inst)) {
5970 try reap(f, inst, &.{ty_op.operand});
5971 return .none;
5972 }
5973
59745810 const inst_ty = f.air.typeOfIndex(inst);
59755811 const operand = try f.resolveInst(ty_op.operand);
59765812 try reap(f, inst, &.{ty_op.operand});
......@@ -6014,11 +5850,6 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
60145850fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
60155851 const un_op = f.air.instructions.items(.data)[inst].un_op;
60165852
6017 if (f.liveness.isUnused(inst)) {
6018 try reap(f, inst, &.{un_op});
6019 return .none;
6020 }
6021
60225853 const operand = try f.resolveInst(un_op);
60235854 try reap(f, inst, &.{un_op});
60245855 const inst_ty = f.air.typeOfIndex(inst);
......@@ -6042,11 +5873,6 @@ fn airUnBuiltinCall(
60425873) !CValue {
60435874 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
60445875
6045 if (f.liveness.isUnused(inst)) {
6046 try reap(f, inst, &.{ty_op.operand});
6047 return .none;
6048 }
6049
60505876 const operand = try f.resolveInst(ty_op.operand);
60515877 try reap(f, inst, &.{ty_op.operand});
60525878 const inst_ty = f.air.typeOfIndex(inst);
......@@ -6090,11 +5916,6 @@ fn airBinBuiltinCall(
60905916) !CValue {
60915917 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
60925918
6093 if (f.liveness.isUnused(inst)) {
6094 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6095 return .none;
6096 }
6097
60985919 const operand_ty = f.air.typeOf(bin_op.lhs);
60995920 const operand_cty = try f.typeToCType(operand_ty, .complete);
61005921 const is_big = operand_cty.tag() == .array;
......@@ -6147,11 +5968,6 @@ fn airCmpBuiltinCall(
61475968 operation: enum { cmp, operator },
61485969 info: BuiltinInfo,
61495970) !CValue {
6150 if (f.liveness.isUnused(inst)) {
6151 try reap(f, inst, &.{ data.lhs, data.rhs });
6152 return .none;
6153 }
6154
61555971 const lhs = try f.resolveInst(data.lhs);
61565972 const rhs = try f.resolveInst(data.rhs);
61575973 try reap(f, inst, &.{ data.lhs, data.rhs });
......@@ -6322,9 +6138,6 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
63226138 const ptr = try f.resolveInst(atomic_load.ptr);
63236139 try reap(f, inst, &.{atomic_load.ptr});
63246140 const ptr_ty = f.air.typeOf(atomic_load.ptr);
6325 if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) {
6326 return .none;
6327 }
63286141
63296142 const inst_ty = f.air.typeOfIndex(inst);
63306143 const writer = f.object.writer();
......@@ -6468,11 +6281,6 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
64686281fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
64696282 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
64706283
6471 if (f.liveness.isUnused(inst)) {
6472 try reap(f, inst, &.{ty_op.operand});
6473 return .none;
6474 }
6475
64766284 const operand = try f.resolveInst(ty_op.operand);
64776285 try reap(f, inst, &.{ty_op.operand});
64786286
......@@ -6496,11 +6304,6 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
64966304fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
64976305 const un_op = f.air.instructions.items(.data)[inst].un_op;
64986306
6499 if (f.liveness.isUnused(inst)) {
6500 try reap(f, inst, &.{un_op});
6501 return .none;
6502 }
6503
65046307 const inst_ty = f.air.typeOfIndex(inst);
65056308 const enum_ty = f.air.typeOf(un_op);
65066309 const operand = try f.resolveInst(un_op);
......@@ -6521,11 +6324,6 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
65216324fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
65226325 const un_op = f.air.instructions.items(.data)[inst].un_op;
65236326
6524 if (f.liveness.isUnused(inst)) {
6525 try reap(f, inst, &.{un_op});
6526 return .none;
6527 }
6528
65296327 const writer = f.object.writer();
65306328 const inst_ty = f.air.typeOfIndex(inst);
65316329 const operand = try f.resolveInst(un_op);
......@@ -6542,11 +6340,6 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
65426340fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
65436341 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
65446342
6545 if (f.liveness.isUnused(inst)) {
6546 try reap(f, inst, &.{ty_op.operand});
6547 return .none;
6548 }
6549
65506343 const operand = try f.resolveInst(ty_op.operand);
65516344 try reap(f, inst, &.{ty_op.operand});
65526345
......@@ -6578,11 +6371,6 @@ fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {
65786371 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
65796372 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
65806373
6581 if (f.liveness.isUnused(inst)) {
6582 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
6583 return .none;
6584 }
6585
65866374 const pred = try f.resolveInst(pl_op.operand);
65876375 const lhs = try f.resolveInst(extra.lhs);
65886376 const rhs = try f.resolveInst(extra.rhs);
......@@ -6614,11 +6402,6 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {
66146402 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
66156403 const extra = f.air.extraData(Air.Shuffle, ty_pl.payload).data;
66166404
6617 if (f.liveness.isUnused(inst)) {
6618 try reap(f, inst, &.{ extra.a, extra.b });
6619 return .none;
6620 }
6621
66226405 const mask = f.air.values[extra.mask];
66236406 const lhs = try f.resolveInst(extra.a);
66246407 const rhs = try f.resolveInst(extra.b);
......@@ -6660,11 +6443,6 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {
66606443fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
66616444 const reduce = f.air.instructions.items(.data)[inst].reduce;
66626445
6663 if (f.liveness.isUnused(inst)) {
6664 try reap(f, inst, &.{reduce.operand});
6665 return .none;
6666 }
6667
66686446 const target = f.object.dg.module.getTarget();
66696447 const scalar_ty = f.air.typeOfIndex(inst);
66706448 const operand = try f.resolveInst(reduce.operand);
......@@ -6836,8 +6614,6 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
68366614 }
68376615 }
68386616
6839 if (f.liveness.isUnused(inst)) return .none;
6840
68416617 const target = f.object.dg.module.getTarget();
68426618
68436619 const writer = f.object.writer();
......@@ -7004,11 +6780,6 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
70046780 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
70056781 const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data;
70066782
7007 if (f.liveness.isUnused(inst)) {
7008 try reap(f, inst, &.{extra.init});
7009 return .none;
7010 }
7011
70126783 const union_ty = f.air.typeOfIndex(inst);
70136784 const target = f.object.dg.module.getTarget();
70146785 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
......@@ -7075,8 +6846,6 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
70756846}
70766847
70776848fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
7078 if (f.liveness.isUnused(inst)) return .none;
7079
70806849 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
70816850
70826851 const writer = f.object.writer();
......@@ -7109,10 +6878,6 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
71096878
71106879fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {
71116880 const un_op = f.air.instructions.items(.data)[inst].un_op;
7112 if (f.liveness.isUnused(inst)) {
7113 try reap(f, inst, &.{un_op});
7114 return .none;
7115 }
71166881
71176882 const operand = try f.resolveInst(un_op);
71186883 try reap(f, inst, &.{un_op});
......@@ -7138,10 +6903,6 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {
71386903
71396904fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue {
71406905 const un_op = f.air.instructions.items(.data)[inst].un_op;
7141 if (f.liveness.isUnused(inst)) {
7142 try reap(f, inst, &.{un_op});
7143 return .none;
7144 }
71456906
71466907 const operand = try f.resolveInst(un_op);
71476908 try reap(f, inst, &.{un_op});
......@@ -7169,10 +6930,6 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal
71696930
71706931fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue {
71716932 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
7172 if (f.liveness.isUnused(inst)) {
7173 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
7174 return .none;
7175 }
71766933
71776934 const lhs = try f.resolveInst(bin_op.lhs);
71786935 const rhs = try f.resolveInst(bin_op.rhs);
......@@ -7205,10 +6962,6 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa
72056962fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
72066963 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
72076964 const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data;
7208 if (f.liveness.isUnused(inst)) {
7209 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });
7210 return .none;
7211 }
72126965
72136966 const mulend1 = try f.resolveInst(bin_op.lhs);
72146967 const mulend2 = try f.resolveInst(bin_op.rhs);
......@@ -7241,8 +6994,6 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
72416994}
72426995
72436996fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {
7244 if (f.liveness.isUnused(inst)) return .none;
7245
72466997 const inst_ty = f.air.typeOfIndex(inst);
72476998 const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete);
72486999 const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len;
......@@ -7261,10 +7012,6 @@ fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {
72617012
72627013fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue {
72637014 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
7264 if (f.liveness.isUnused(inst)) {
7265 try reap(f, inst, &.{ty_op.operand});
7266 return .none;
7267 }
72687015
72697016 const inst_ty = f.air.typeOfIndex(inst);
72707017 const va_list = try f.resolveInst(ty_op.operand);
......@@ -7296,10 +7043,6 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue {
72967043
72977044fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue {
72987045 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
7299 if (f.liveness.isUnused(inst)) {
7300 try reap(f, inst, &.{ty_op.operand});
7301 return .none;
7302 }
73037046
73047047 const inst_ty = f.air.typeOfIndex(inst);
73057048 const va_list = try f.resolveInst(ty_op.operand);
......@@ -7863,7 +7606,6 @@ fn freeLocal(f: *Function, inst: Air.Inst.Index, local_index: LocalIndex, ref_in
78637606 const gpa = f.object.dg.gpa;
78647607 const local = &f.locals.items[local_index];
78657608 log.debug("%{d}: freeing t{d} (operand %{d})", .{ inst, local_index, ref_inst });
7866 if (f.is_in_clone != local.is_in_clone) return;
78677609 const gop = try f.free_locals_map.getOrPut(gpa, local.getType());
78687610 if (!gop.found_existing) gop.value_ptr.* = .{};
78697611 if (std.debug.runtime_safety) {
......@@ -7921,35 +7663,3 @@ fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void {
79217663 }
79227664 map.deinit(gpa);
79237665}
7924
7925fn noticeBranchFrees(
7926 f: *Function,
7927 pre_locals_len: LocalIndex,
7928 pre_allocs_len: LocalIndex,
7929 inst: Air.Inst.Index,
7930) !void {
7931 for (f.locals.items[pre_locals_len..], pre_locals_len..) |*local, local_i| {
7932 const local_index = @intCast(LocalIndex, local_i);
7933 if (f.allocs.contains(local_index)) {
7934 if (std.debug.runtime_safety) {
7935 // new allocs are no longer freeable, so make sure they aren't in the free list
7936 if (f.free_locals_map.getPtr(local.getType())) |locals_list| {
7937 assert(!locals_list.contains(local_index));
7938 }
7939 }
7940 continue;
7941 }
7942
7943 // free cloned locals from other branches at current cloned-ness
7944 std.debug.assert(local.is_in_clone or !f.is_in_clone);
7945 local.is_in_clone = f.is_in_clone;
7946 try freeLocal(f, inst, local_index, 0);
7947 }
7948
7949 for (f.allocs.keys()[pre_allocs_len..]) |local_i| {
7950 const local_index = @intCast(LocalIndex, local_i);
7951 const local = &f.locals.items[local_index];
7952 // new allocs are no longer freeable, so remove them from the free list
7953 if (f.free_locals_map.getPtr(local.getType())) |locals_list| _ = locals_list.swapRemove(local_index);
7954 }
7955}
src/codegen/llvm.zig+6-187
......@@ -4523,6 +4523,10 @@ pub const FuncGen = struct {
45234523 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) Error!void {
45244524 const air_tags = self.air.instructions.items(.tag);
45254525 for (body, 0..) |inst, i| {
4526 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
4527 continue;
4528 }
4529
45264530 const opt_value: ?*llvm.Value = switch (air_tags[inst]) {
45274531 // zig fmt: off
45284532 .add => try self.airAdd(inst, false),
......@@ -5166,8 +5170,6 @@ pub const FuncGen = struct {
51665170 }
51675171
51685172 fn airCVaArg(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5169 if (self.liveness.isUnused(inst)) return null;
5170
51715173 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
51725174 const list = try self.resolveInst(ty_op.operand);
51735175 const arg_ty = self.air.getRefType(ty_op.ty);
......@@ -5177,8 +5179,6 @@ pub const FuncGen = struct {
51775179 }
51785180
51795181 fn airCVaCopy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5180 if (self.liveness.isUnused(inst)) return null;
5181
51825182 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
51835183 const src_list = try self.resolveInst(ty_op.operand);
51845184 const va_list_ty = self.air.getRefType(ty_op.ty);
......@@ -5226,8 +5226,6 @@ pub const FuncGen = struct {
52265226 }
52275227
52285228 fn airCVaStart(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5229 if (self.liveness.isUnused(inst)) return null;
5230
52315229 const va_list_ty = self.air.typeOfIndex(inst);
52325230 const llvm_va_list_ty = try self.dg.lowerType(va_list_ty);
52335231
......@@ -5254,7 +5252,6 @@ pub const FuncGen = struct {
52545252 }
52555253
52565254 fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator, want_fast_math: bool) !?*llvm.Value {
5257 if (self.liveness.isUnused(inst)) return null;
52585255 self.builder.setFastMath(want_fast_math);
52595256
52605257 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -5266,7 +5263,6 @@ pub const FuncGen = struct {
52665263 }
52675264
52685265 fn airCmpVector(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
5269 if (self.liveness.isUnused(inst)) return null;
52705266 self.builder.setFastMath(want_fast_math);
52715267
52725268 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
......@@ -5281,8 +5277,6 @@ pub const FuncGen = struct {
52815277 }
52825278
52835279 fn airCmpLtErrorsLen(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5284 if (self.liveness.isUnused(inst)) return null;
5285
52865280 const un_op = self.air.instructions.items(.data)[inst].un_op;
52875281 const operand = try self.resolveInst(un_op);
52885282 const llvm_fn = try self.getCmpLtErrorsLenFunction();
......@@ -5650,9 +5644,6 @@ pub const FuncGen = struct {
56505644 }
56515645
56525646 fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5653 if (self.liveness.isUnused(inst))
5654 return null;
5655
56565647 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
56575648 const operand_ty = self.air.typeOf(ty_op.operand);
56585649 const array_ty = operand_ty.childType();
......@@ -5674,9 +5665,6 @@ pub const FuncGen = struct {
56745665 }
56755666
56765667 fn airIntToFloat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5677 if (self.liveness.isUnused(inst))
5678 return null;
5679
56805668 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
56815669
56825670 const operand = try self.resolveInst(ty_op.operand);
......@@ -5733,9 +5721,6 @@ pub const FuncGen = struct {
57335721 }
57345722
57355723 fn airFloatToInt(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
5736 if (self.liveness.isUnused(inst))
5737 return null;
5738
57395724 self.builder.setFastMath(want_fast_math);
57405725
57415726 const target = self.dg.module.getTarget();
......@@ -5792,16 +5777,12 @@ pub const FuncGen = struct {
57925777 }
57935778
57945779 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {
5795 if (self.liveness.isUnused(inst)) return null;
5796
57975780 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
57985781 const operand = try self.resolveInst(ty_op.operand);
57995782 return self.builder.buildExtractValue(operand, index, "");
58005783 }
58015784
58025785 fn airPtrSliceFieldPtr(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {
5803 if (self.liveness.isUnused(inst)) return null;
5804
58055786 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
58065787 const slice_ptr = try self.resolveInst(ty_op.operand);
58075788 const slice_ptr_ty = self.air.typeOf(ty_op.operand);
......@@ -5814,8 +5795,6 @@ pub const FuncGen = struct {
58145795 const inst = body_tail[0];
58155796 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
58165797 const slice_ty = self.air.typeOf(bin_op.lhs);
5817 if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
5818
58195798 const slice = try self.resolveInst(bin_op.lhs);
58205799 const index = try self.resolveInst(bin_op.rhs);
58215800 const elem_ty = slice_ty.childType();
......@@ -5835,7 +5814,6 @@ pub const FuncGen = struct {
58355814 }
58365815
58375816 fn airSliceElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5838 if (self.liveness.isUnused(inst)) return null;
58395817 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
58405818 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
58415819 const slice_ty = self.air.typeOf(bin_op.lhs);
......@@ -5850,7 +5828,6 @@ pub const FuncGen = struct {
58505828
58515829 fn airArrayElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
58525830 const inst = body_tail[0];
5853 if (self.liveness.isUnused(inst)) return null;
58545831
58555832 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
58565833 const array_ty = self.air.typeOf(bin_op.lhs);
......@@ -5881,8 +5858,6 @@ pub const FuncGen = struct {
58815858 const inst = body_tail[0];
58825859 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
58835860 const ptr_ty = self.air.typeOf(bin_op.lhs);
5884 if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
5885
58865861 const elem_ty = ptr_ty.childType();
58875862 const llvm_elem_ty = try self.dg.lowerPtrElemTy(elem_ty);
58885863 const base_ptr = try self.resolveInst(bin_op.lhs);
......@@ -5908,8 +5883,6 @@ pub const FuncGen = struct {
59085883 }
59095884
59105885 fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5911 if (self.liveness.isUnused(inst)) return null;
5912
59135886 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
59145887 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
59155888 const ptr_ty = self.air.typeOf(bin_op.lhs);
......@@ -5934,9 +5907,6 @@ pub const FuncGen = struct {
59345907 }
59355908
59365909 fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5937 if (self.liveness.isUnused(inst))
5938 return null;
5939
59405910 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
59415911 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
59425912 const struct_ptr = try self.resolveInst(struct_field.struct_operand);
......@@ -5949,8 +5919,6 @@ pub const FuncGen = struct {
59495919 inst: Air.Inst.Index,
59505920 field_index: u32,
59515921 ) !?*llvm.Value {
5952 if (self.liveness.isUnused(inst)) return null;
5953
59545922 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
59555923 const struct_ptr = try self.resolveInst(ty_op.operand);
59565924 const struct_ptr_ty = self.air.typeOf(ty_op.operand);
......@@ -5959,8 +5927,6 @@ pub const FuncGen = struct {
59595927
59605928 fn airStructFieldVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
59615929 const inst = body_tail[0];
5962 if (self.liveness.isUnused(inst)) return null;
5963
59645930 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
59655931 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
59665932 const struct_ty = self.air.typeOf(struct_field.struct_operand);
......@@ -6060,8 +6026,6 @@ pub const FuncGen = struct {
60606026 }
60616027
60626028 fn airFieldParentPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6063 if (self.liveness.isUnused(inst)) return null;
6064
60656029 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
60666030 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
60676031
......@@ -6083,9 +6047,6 @@ pub const FuncGen = struct {
60836047 }
60846048
60856049 fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6086 if (self.liveness.isUnused(inst))
6087 return null;
6088
60896050 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
60906051 const operand = try self.resolveInst(ty_op.operand);
60916052
......@@ -6263,8 +6224,6 @@ pub const FuncGen = struct {
62636224 const clobbers_len = @truncate(u31, extra.data.flags);
62646225 var extra_i: usize = extra.end;
62656226
6266 if (!is_volatile and self.liveness.isUnused(inst)) return null;
6267
62686227 const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
62696228 extra_i += outputs.len;
62706229 const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
......@@ -6610,8 +6569,6 @@ pub const FuncGen = struct {
66106569 operand_is_ptr: bool,
66116570 pred: llvm.IntPredicate,
66126571 ) !?*llvm.Value {
6613 if (self.liveness.isUnused(inst)) return null;
6614
66156572 const un_op = self.air.instructions.items(.data)[inst].un_op;
66166573 const operand = try self.resolveInst(un_op);
66176574 const operand_ty = self.air.typeOf(un_op);
......@@ -6659,8 +6616,6 @@ pub const FuncGen = struct {
66596616 op: llvm.IntPredicate,
66606617 operand_is_ptr: bool,
66616618 ) !?*llvm.Value {
6662 if (self.liveness.isUnused(inst)) return null;
6663
66646619 const un_op = self.air.instructions.items(.data)[inst].un_op;
66656620 const operand = try self.resolveInst(un_op);
66666621 const operand_ty = self.air.typeOf(un_op);
......@@ -6701,8 +6656,6 @@ pub const FuncGen = struct {
67016656 }
67026657
67036658 fn airOptionalPayloadPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6704 if (self.liveness.isUnused(inst)) return null;
6705
67066659 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
67076660 const operand = try self.resolveInst(ty_op.operand);
67086661 const optional_ty = self.air.typeOf(ty_op.operand).childType();
......@@ -6756,8 +6709,6 @@ pub const FuncGen = struct {
67566709
67576710 fn airOptionalPayload(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
67586711 const inst = body_tail[0];
6759 if (self.liveness.isUnused(inst)) return null;
6760
67616712 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
67626713 const operand = try self.resolveInst(ty_op.operand);
67636714 const optional_ty = self.air.typeOf(ty_op.operand);
......@@ -6780,8 +6731,6 @@ pub const FuncGen = struct {
67806731 operand_is_ptr: bool,
67816732 ) !?*llvm.Value {
67826733 const inst = body_tail[0];
6783 if (self.liveness.isUnused(inst)) return null;
6784
67856734 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
67866735 const operand = try self.resolveInst(ty_op.operand);
67876736 const operand_ty = self.air.typeOf(ty_op.operand);
......@@ -6817,9 +6766,6 @@ pub const FuncGen = struct {
68176766 inst: Air.Inst.Index,
68186767 operand_is_ptr: bool,
68196768 ) !?*llvm.Value {
6820 if (self.liveness.isUnused(inst))
6821 return null;
6822
68236769 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
68246770 const operand = try self.resolveInst(ty_op.operand);
68256771 const operand_ty = self.air.typeOf(ty_op.operand);
......@@ -6893,8 +6839,6 @@ pub const FuncGen = struct {
68936839 }
68946840
68956841 fn airSaveErrReturnTraceIndex(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6896 if (self.liveness.isUnused(inst)) return null;
6897
68986842 const target = self.dg.module.getTarget();
68996843
69006844 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
......@@ -6911,8 +6855,6 @@ pub const FuncGen = struct {
69116855 }
69126856
69136857 fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6914 if (self.liveness.isUnused(inst)) return null;
6915
69166858 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
69176859 const payload_ty = self.air.typeOf(ty_op.operand);
69186860 const non_null_bit = self.context.intType(8).constInt(1, .False);
......@@ -6943,8 +6885,6 @@ pub const FuncGen = struct {
69436885 }
69446886
69456887 fn airWrapErrUnionPayload(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6946 if (self.liveness.isUnused(inst)) return null;
6947
69486888 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
69496889 const err_un_ty = self.air.typeOfIndex(inst);
69506890 const operand = try self.resolveInst(ty_op.operand);
......@@ -6978,8 +6918,6 @@ pub const FuncGen = struct {
69786918 }
69796919
69806920 fn airWrapErrUnionErr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6981 if (self.liveness.isUnused(inst)) return null;
6982
69836921 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
69846922 const err_un_ty = self.air.typeOfIndex(inst);
69856923 const payload_ty = err_un_ty.errorUnionPayload();
......@@ -7015,8 +6953,6 @@ pub const FuncGen = struct {
70156953 }
70166954
70176955 fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7018 if (self.liveness.isUnused(inst)) return null;
7019
70206956 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
70216957 const index = pl_op.payload;
70226958 const llvm_u32 = self.context.intType(32);
......@@ -7061,8 +6997,6 @@ pub const FuncGen = struct {
70616997 }
70626998
70636999 fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7064 if (self.liveness.isUnused(inst)) return null;
7065
70667000 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
70677001 const lhs = try self.resolveInst(bin_op.lhs);
70687002 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -7074,8 +7008,6 @@ pub const FuncGen = struct {
70747008 }
70757009
70767010 fn airMax(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7077 if (self.liveness.isUnused(inst)) return null;
7078
70797011 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
70807012 const lhs = try self.resolveInst(bin_op.lhs);
70817013 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -7087,8 +7019,6 @@ pub const FuncGen = struct {
70877019 }
70887020
70897021 fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7090 if (self.liveness.isUnused(inst)) return null;
7091
70927022 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
70937023 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
70947024 const ptr = try self.resolveInst(bin_op.lhs);
......@@ -7103,7 +7033,6 @@ pub const FuncGen = struct {
71037033 }
71047034
71057035 fn airAdd(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7106 if (self.liveness.isUnused(inst)) return null;
71077036 self.builder.setFastMath(want_fast_math);
71087037
71097038 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7118,7 +7047,6 @@ pub const FuncGen = struct {
71187047 }
71197048
71207049 fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7121 if (self.liveness.isUnused(inst)) return null;
71227050 self.builder.setFastMath(want_fast_math);
71237051
71247052 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7129,8 +7057,6 @@ pub const FuncGen = struct {
71297057 }
71307058
71317059 fn airAddSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7132 if (self.liveness.isUnused(inst)) return null;
7133
71347060 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
71357061 const lhs = try self.resolveInst(bin_op.lhs);
71367062 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -7144,7 +7070,6 @@ pub const FuncGen = struct {
71447070 }
71457071
71467072 fn airSub(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7147 if (self.liveness.isUnused(inst)) return null;
71487073 self.builder.setFastMath(want_fast_math);
71497074
71507075 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7159,7 +7084,6 @@ pub const FuncGen = struct {
71597084 }
71607085
71617086 fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7162 if (self.liveness.isUnused(inst)) return null;
71637087 self.builder.setFastMath(want_fast_math);
71647088
71657089 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7170,8 +7094,6 @@ pub const FuncGen = struct {
71707094 }
71717095
71727096 fn airSubSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7173 if (self.liveness.isUnused(inst)) return null;
7174
71757097 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
71767098 const lhs = try self.resolveInst(bin_op.lhs);
71777099 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -7184,7 +7106,6 @@ pub const FuncGen = struct {
71847106 }
71857107
71867108 fn airMul(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7187 if (self.liveness.isUnused(inst)) return null;
71887109 self.builder.setFastMath(want_fast_math);
71897110
71907111 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7199,7 +7120,6 @@ pub const FuncGen = struct {
71997120 }
72007121
72017122 fn airMulWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7202 if (self.liveness.isUnused(inst)) return null;
72037123 self.builder.setFastMath(want_fast_math);
72047124
72057125 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7210,8 +7130,6 @@ pub const FuncGen = struct {
72107130 }
72117131
72127132 fn airMulSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7213 if (self.liveness.isUnused(inst)) return null;
7214
72157133 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
72167134 const lhs = try self.resolveInst(bin_op.lhs);
72177135 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -7224,7 +7142,6 @@ pub const FuncGen = struct {
72247142 }
72257143
72267144 fn airDivFloat(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7227 if (self.liveness.isUnused(inst)) return null;
72287145 self.builder.setFastMath(want_fast_math);
72297146
72307147 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7236,7 +7153,6 @@ pub const FuncGen = struct {
72367153 }
72377154
72387155 fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7239 if (self.liveness.isUnused(inst)) return null;
72407156 self.builder.setFastMath(want_fast_math);
72417157
72427158 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7254,7 +7170,6 @@ pub const FuncGen = struct {
72547170 }
72557171
72567172 fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7257 if (self.liveness.isUnused(inst)) return null;
72587173 self.builder.setFastMath(want_fast_math);
72597174
72607175 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7287,7 +7202,6 @@ pub const FuncGen = struct {
72877202 }
72887203
72897204 fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7290 if (self.liveness.isUnused(inst)) return null;
72917205 self.builder.setFastMath(want_fast_math);
72927206
72937207 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7302,7 +7216,6 @@ pub const FuncGen = struct {
73027216 }
73037217
73047218 fn airRem(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7305 if (self.liveness.isUnused(inst)) return null;
73067219 self.builder.setFastMath(want_fast_math);
73077220
73087221 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7317,7 +7230,6 @@ pub const FuncGen = struct {
73177230 }
73187231
73197232 fn airMod(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7320 if (self.liveness.isUnused(inst)) return null;
73217233 self.builder.setFastMath(want_fast_math);
73227234
73237235 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -7347,8 +7259,6 @@ pub const FuncGen = struct {
73477259 }
73487260
73497261 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7350 if (self.liveness.isUnused(inst)) return null;
7351
73527262 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
73537263 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
73547264 const base_ptr = try self.resolveInst(bin_op.lhs);
......@@ -7368,8 +7278,6 @@ pub const FuncGen = struct {
73687278 }
73697279
73707280 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7371 if (self.liveness.isUnused(inst)) return null;
7372
73737281 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
73747282 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
73757283 const base_ptr = try self.resolveInst(bin_op.lhs);
......@@ -7395,9 +7303,6 @@ pub const FuncGen = struct {
73957303 signed_intrinsic: []const u8,
73967304 unsigned_intrinsic: []const u8,
73977305 ) !?*llvm.Value {
7398 if (self.liveness.isUnused(inst))
7399 return null;
7400
74017306 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
74027307 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
74037308
......@@ -7686,8 +7591,6 @@ pub const FuncGen = struct {
76867591 }
76877592
76887593 fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7689 if (self.liveness.isUnused(inst)) return null;
7690
76917594 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
76927595 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
76937596
......@@ -7700,9 +7603,6 @@ pub const FuncGen = struct {
77007603 }
77017604
77027605 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7703 if (self.liveness.isUnused(inst))
7704 return null;
7705
77067606 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
77077607 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
77087608
......@@ -7759,8 +7659,6 @@ pub const FuncGen = struct {
77597659 }
77607660
77617661 fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7762 if (self.liveness.isUnused(inst))
7763 return null;
77647662 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
77657663 const lhs = try self.resolveInst(bin_op.lhs);
77667664 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -7768,8 +7666,6 @@ pub const FuncGen = struct {
77687666 }
77697667
77707668 fn airOr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7771 if (self.liveness.isUnused(inst))
7772 return null;
77737669 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
77747670 const lhs = try self.resolveInst(bin_op.lhs);
77757671 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -7777,8 +7673,6 @@ pub const FuncGen = struct {
77777673 }
77787674
77797675 fn airXor(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7780 if (self.liveness.isUnused(inst))
7781 return null;
77827676 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
77837677 const lhs = try self.resolveInst(bin_op.lhs);
77847678 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -7786,8 +7680,6 @@ pub const FuncGen = struct {
77867680 }
77877681
77887682 fn airShlExact(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7789 if (self.liveness.isUnused(inst)) return null;
7790
77917683 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
77927684
77937685 const lhs = try self.resolveInst(bin_op.lhs);
......@@ -7809,8 +7701,6 @@ pub const FuncGen = struct {
78097701 }
78107702
78117703 fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7812 if (self.liveness.isUnused(inst)) return null;
7813
78147704 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
78157705
78167706 const lhs = try self.resolveInst(bin_op.lhs);
......@@ -7831,8 +7721,6 @@ pub const FuncGen = struct {
78317721 }
78327722
78337723 fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7834 if (self.liveness.isUnused(inst)) return null;
7835
78367724 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
78377725
78387726 const lhs = try self.resolveInst(bin_op.lhs);
......@@ -7876,8 +7764,6 @@ pub const FuncGen = struct {
78767764 }
78777765
78787766 fn airShr(self: *FuncGen, inst: Air.Inst.Index, is_exact: bool) !?*llvm.Value {
7879 if (self.liveness.isUnused(inst)) return null;
7880
78817767 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
78827768
78837769 const lhs = try self.resolveInst(bin_op.lhs);
......@@ -7912,9 +7798,6 @@ pub const FuncGen = struct {
79127798 }
79137799
79147800 fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7915 if (self.liveness.isUnused(inst))
7916 return null;
7917
79187801 const target = self.dg.module.getTarget();
79197802 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
79207803 const dest_ty = self.air.typeOfIndex(inst);
......@@ -7937,8 +7820,6 @@ pub const FuncGen = struct {
79377820 }
79387821
79397822 fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7940 if (self.liveness.isUnused(inst)) return null;
7941
79427823 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
79437824 const operand = try self.resolveInst(ty_op.operand);
79447825 const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));
......@@ -7946,9 +7827,6 @@ pub const FuncGen = struct {
79467827 }
79477828
79487829 fn airFptrunc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7949 if (self.liveness.isUnused(inst))
7950 return null;
7951
79527830 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
79537831 const operand = try self.resolveInst(ty_op.operand);
79547832 const operand_ty = self.air.typeOf(ty_op.operand);
......@@ -7978,9 +7856,6 @@ pub const FuncGen = struct {
79787856 }
79797857
79807858 fn airFpext(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7981 if (self.liveness.isUnused(inst))
7982 return null;
7983
79847859 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
79857860 const operand = try self.resolveInst(ty_op.operand);
79867861 const operand_ty = self.air.typeOf(ty_op.operand);
......@@ -8010,9 +7885,6 @@ pub const FuncGen = struct {
80107885 }
80117886
80127887 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8013 if (self.liveness.isUnused(inst))
8014 return null;
8015
80167888 const un_op = self.air.instructions.items(.data)[inst].un_op;
80177889 const operand = try self.resolveInst(un_op);
80187890 const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));
......@@ -8020,8 +7892,6 @@ pub const FuncGen = struct {
80207892 }
80217893
80227894 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8023 if (self.liveness.isUnused(inst)) return null;
8024
80257895 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
80267896 const operand_ty = self.air.typeOf(ty_op.operand);
80277897 const inst_ty = self.air.typeOfIndex(inst);
......@@ -8137,9 +8007,6 @@ pub const FuncGen = struct {
81378007 }
81388008
81398009 fn airBoolToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8140 if (self.liveness.isUnused(inst))
8141 return null;
8142
81438010 const un_op = self.air.instructions.items(.data)[inst].un_op;
81448011 const operand = try self.resolveInst(un_op);
81458012 return operand;
......@@ -8189,7 +8056,6 @@ pub const FuncGen = struct {
81898056 }
81908057
81918058 fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8192 if (self.liveness.isUnused(inst)) return null;
81938059 const ptr_ty = self.air.typeOfIndex(inst);
81948060 const pointee_type = ptr_ty.childType();
81958061 if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime()) return self.dg.lowerPtrToVoid(ptr_ty);
......@@ -8201,7 +8067,6 @@ pub const FuncGen = struct {
82018067 }
82028068
82038069 fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8204 if (self.liveness.isUnused(inst)) return null;
82058070 const ptr_ty = self.air.typeOfIndex(inst);
82068071 const ret_ty = ptr_ty.childType();
82078072 if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return self.dg.lowerPtrToVoid(ptr_ty);
......@@ -8289,8 +8154,6 @@ pub const FuncGen = struct {
82898154 const ptr = try fg.resolveInst(ty_op.operand);
82908155
82918156 elide: {
8292 if (ptr_info.@"volatile") break :elide;
8293 if (fg.liveness.isUnused(inst)) return null;
82948157 if (!isByRef(ptr_info.pointee_type)) break :elide;
82958158 if (!canElideLoad(fg, body_tail)) break :elide;
82968159 return ptr;
......@@ -8314,8 +8177,7 @@ pub const FuncGen = struct {
83148177 }
83158178
83168179 fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8317 if (self.liveness.isUnused(inst)) return null;
8318
8180 _ = inst;
83198181 const llvm_usize = try self.dg.lowerType(Type.usize);
83208182 const target = self.dg.module.getTarget();
83218183 if (!target_util.supportsReturnAddress(target)) {
......@@ -8331,8 +8193,7 @@ pub const FuncGen = struct {
83318193 }
83328194
83338195 fn airFrameAddress(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8334 if (self.liveness.isUnused(inst)) return null;
8335
8196 _ = inst;
83368197 const llvm_i32 = self.context.intType(32);
83378198 const llvm_fn_name = "llvm.frameaddress.p0";
83388199 const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
......@@ -8462,8 +8323,6 @@ pub const FuncGen = struct {
84628323 const ptr = try self.resolveInst(atomic_load.ptr);
84638324 const ptr_ty = self.air.typeOf(atomic_load.ptr);
84648325 const ptr_info = ptr_ty.ptrInfo().data;
8465 if (!ptr_info.@"volatile" and self.liveness.isUnused(inst))
8466 return null;
84678326 const elem_ty = ptr_info.pointee_type;
84688327 if (!elem_ty.hasRuntimeBitsIgnoreComptime())
84698328 return null;
......@@ -8577,8 +8436,6 @@ pub const FuncGen = struct {
85778436 }
85788437
85798438 fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8580 if (self.liveness.isUnused(inst)) return null;
8581
85828439 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
85838440 const un_ty = self.air.typeOf(ty_op.operand);
85848441 const target = self.dg.module.getTarget();
......@@ -8603,8 +8460,6 @@ pub const FuncGen = struct {
86038460 }
86048461
86058462 fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: FloatOp) !?*llvm.Value {
8606 if (self.liveness.isUnused(inst)) return null;
8607
86088463 const un_op = self.air.instructions.items(.data)[inst].un_op;
86098464 const operand = try self.resolveInst(un_op);
86108465 const operand_ty = self.air.typeOf(un_op);
......@@ -8613,7 +8468,6 @@ pub const FuncGen = struct {
86138468 }
86148469
86158470 fn airNeg(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
8616 if (self.liveness.isUnused(inst)) return null;
86178471 self.builder.setFastMath(want_fast_math);
86188472
86198473 const un_op = self.air.instructions.items(.data)[inst].un_op;
......@@ -8624,8 +8478,6 @@ pub const FuncGen = struct {
86248478 }
86258479
86268480 fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {
8627 if (self.liveness.isUnused(inst)) return null;
8628
86298481 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
86308482 const operand_ty = self.air.typeOf(ty_op.operand);
86318483 const operand = try self.resolveInst(ty_op.operand);
......@@ -8652,8 +8504,6 @@ pub const FuncGen = struct {
86528504 }
86538505
86548506 fn airBitOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {
8655 if (self.liveness.isUnused(inst)) return null;
8656
86578507 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
86588508 const operand_ty = self.air.typeOf(ty_op.operand);
86598509 const operand = try self.resolveInst(ty_op.operand);
......@@ -8679,8 +8529,6 @@ pub const FuncGen = struct {
86798529 }
86808530
86818531 fn airByteSwap(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {
8682 if (self.liveness.isUnused(inst)) return null;
8683
86848532 const target = self.dg.module.getTarget();
86858533 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
86868534 const operand_ty = self.air.typeOf(ty_op.operand);
......@@ -8734,8 +8582,6 @@ pub const FuncGen = struct {
87348582 }
87358583
87368584 fn airErrorSetHasValue(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8737 if (self.liveness.isUnused(inst)) return null;
8738
87398585 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
87408586 const operand = try self.resolveInst(ty_op.operand);
87418587 const error_set_ty = self.air.getRefType(ty_op.ty);
......@@ -8781,8 +8627,6 @@ pub const FuncGen = struct {
87818627 }
87828628
87838629 fn airIsNamedEnumValue(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8784 if (self.liveness.isUnused(inst)) return null;
8785
87868630 const un_op = self.air.instructions.items(.data)[inst].un_op;
87878631 const operand = try self.resolveInst(un_op);
87888632 const enum_ty = self.air.typeOf(un_op);
......@@ -8862,8 +8706,6 @@ pub const FuncGen = struct {
88628706 }
88638707
88648708 fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8865 if (self.liveness.isUnused(inst)) return null;
8866
88678709 const un_op = self.air.instructions.items(.data)[inst].un_op;
88688710 const operand = try self.resolveInst(un_op);
88698711 const enum_ty = self.air.typeOf(un_op);
......@@ -8995,8 +8837,6 @@ pub const FuncGen = struct {
89958837 }
89968838
89978839 fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8998 if (self.liveness.isUnused(inst)) return null;
8999
90008840 const un_op = self.air.instructions.items(.data)[inst].un_op;
90018841 const operand = try self.resolveInst(un_op);
90028842 const slice_ty = self.air.typeOfIndex(inst);
......@@ -9011,8 +8851,6 @@ pub const FuncGen = struct {
90118851 }
90128852
90138853 fn airSplat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9014 if (self.liveness.isUnused(inst)) return null;
9015
90168854 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
90178855 const scalar = try self.resolveInst(ty_op.operand);
90188856 const vector_ty = self.air.typeOfIndex(inst);
......@@ -9021,8 +8859,6 @@ pub const FuncGen = struct {
90218859 }
90228860
90238861 fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9024 if (self.liveness.isUnused(inst)) return null;
9025
90268862 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
90278863 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
90288864 const pred = try self.resolveInst(pl_op.operand);
......@@ -9033,8 +8869,6 @@ pub const FuncGen = struct {
90338869 }
90348870
90358871 fn airShuffle(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9036 if (self.liveness.isUnused(inst)) return null;
9037
90388872 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
90398873 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
90408874 const a = try self.resolveInst(extra.a);
......@@ -9134,7 +8968,6 @@ pub const FuncGen = struct {
91348968 }
91358969
91368970 fn airReduce(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
9137 if (self.liveness.isUnused(inst)) return null;
91388971 self.builder.setFastMath(want_fast_math);
91398972 const target = self.dg.module.getTarget();
91408973
......@@ -9221,8 +9054,6 @@ pub const FuncGen = struct {
92219054 }
92229055
92239056 fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9224 if (self.liveness.isUnused(inst)) return null;
9225
92269057 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
92279058 const result_ty = self.air.typeOfIndex(inst);
92289059 const len = @intCast(usize, result_ty.arrayLen());
......@@ -9360,8 +9191,6 @@ pub const FuncGen = struct {
93609191 }
93619192
93629193 fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9363 if (self.liveness.isUnused(inst)) return null;
9364
93659194 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
93669195 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
93679196 const union_ty = self.air.typeOfIndex(inst);
......@@ -9566,8 +9395,6 @@ pub const FuncGen = struct {
95669395 }
95679396
95689397 fn airAddrSpaceCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9569 if (self.liveness.isUnused(inst)) return null;
9570
95719398 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
95729399 const inst_ty = self.air.typeOfIndex(inst);
95739400 const operand = try self.resolveInst(ty_op.operand);
......@@ -9592,8 +9419,6 @@ pub const FuncGen = struct {
95929419 }
95939420
95949421 fn airWorkItemId(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9595 if (self.liveness.isUnused(inst)) return null;
9596
95979422 const target = self.dg.module.getTarget();
95989423 assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures
95999424
......@@ -9603,8 +9428,6 @@ pub const FuncGen = struct {
96039428 }
96049429
96059430 fn airWorkGroupSize(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9606 if (self.liveness.isUnused(inst)) return null;
9607
96089431 const target = self.dg.module.getTarget();
96099432 assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures
96109433
......@@ -9634,8 +9457,6 @@ pub const FuncGen = struct {
96349457 }
96359458
96369459 fn airWorkGroupId(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9637 if (self.liveness.isUnused(inst)) return null;
9638
96399460 const target = self.dg.module.getTarget();
96409461 assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures
96419462
......@@ -9756,8 +9577,6 @@ pub const FuncGen = struct {
97569577 struct_ptr_ty: Type,
97579578 field_index: u32,
97589579 ) !?*llvm.Value {
9759 if (self.liveness.isUnused(inst)) return null;
9760
97619580 const target = self.dg.object.target;
97629581 const struct_ty = struct_ptr_ty.childType();
97639582 switch (struct_ty.zigTypeTag()) {
src/codegen/spirv.zig+5
......@@ -1507,6 +1507,11 @@ pub const DeclGen = struct {
15071507 }
15081508
15091509 fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void {
1510 // TODO: remove now-redundant isUnused calls from AIR handler functions
1511 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
1512 return;
1513 }
1514
15101515 const air_tags = self.air.instructions.items(.tag);
15111516 const maybe_result_id: ?IdRef = switch (air_tags[inst]) {
15121517 // zig fmt: off
src/print_air.zig+74-29
......@@ -8,16 +8,16 @@ const Type = @import("type.zig").Type;
88const Air = @import("Air.zig");
99const Liveness = @import("Liveness.zig");
1010
11pub fn write(stream: anytype, module: *Module, air: Air, liveness: Liveness) void {
11pub fn write(stream: anytype, module: *Module, air: Air, liveness: ?Liveness) void {
1212 const instruction_bytes = air.instructions.len *
1313 // Here we don't use @sizeOf(Air.Inst.Data) because it would include
1414 // the debug safety tag but we want to measure release size.
1515 (@sizeOf(Air.Inst.Tag) + 8);
1616 const extra_bytes = air.extra.len * @sizeOf(u32);
1717 const values_bytes = air.values.len * @sizeOf(Value);
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;
18 const tomb_bytes = if (liveness) |l| l.tomb_bits.len * @sizeOf(usize) else 0;
19 const liveness_extra_bytes = if (liveness) |l| l.extra.len * @sizeOf(u32) else 0;
20 const liveness_special_bytes = if (liveness) |l| l.special.count() * 8 else 0;
2121 const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes +
2222 values_bytes + @sizeOf(Liveness) + liveness_extra_bytes +
2323 liveness_special_bytes + tomb_bytes;
......@@ -38,8 +38,8 @@ pub fn write(stream: anytype, module: *Module, air: Air, liveness: Liveness) voi
3838 air.extra.len, fmtIntSizeBin(extra_bytes),
3939 air.values.len, fmtIntSizeBin(values_bytes),
4040 fmtIntSizeBin(tomb_bytes),
41 liveness.extra.len, fmtIntSizeBin(liveness_extra_bytes),
42 liveness.special.count(), fmtIntSizeBin(liveness_special_bytes),
41 if (liveness) |l| l.extra.len else 0, fmtIntSizeBin(liveness_extra_bytes),
42 if (liveness) |l| l.special.count() else 0, fmtIntSizeBin(liveness_special_bytes),
4343 }) catch return;
4444 // zig fmt: on
4545
......@@ -61,7 +61,7 @@ pub fn writeInst(
6161 inst: Air.Inst.Index,
6262 module: *Module,
6363 air: Air,
64 liveness: Liveness,
64 liveness: ?Liveness,
6565) void {
6666 var writer: Writer = .{
6767 .module = module,
......@@ -74,11 +74,11 @@ pub fn writeInst(
7474 writer.writeInst(stream, inst) catch return;
7575}
7676
77pub fn dump(module: *Module, air: Air, liveness: Liveness) void {
77pub fn dump(module: *Module, air: Air, liveness: ?Liveness) void {
7878 write(std.io.getStdErr().writer(), module, air, liveness);
7979}
8080
81pub fn dumpInst(inst: Air.Inst.Index, module: *Module, air: Air, liveness: Liveness) void {
81pub fn dumpInst(inst: Air.Inst.Index, module: *Module, air: Air, liveness: ?Liveness) void {
8282 writeInst(std.io.getStdErr().writer(), inst, module, air, liveness);
8383}
8484
......@@ -86,7 +86,7 @@ const Writer = struct {
8686 module: *Module,
8787 gpa: Allocator,
8888 air: Air,
89 liveness: Liveness,
89 liveness: ?Liveness,
9090 indent: usize,
9191 skip_body: bool,
9292
......@@ -109,7 +109,7 @@ const Writer = struct {
109109 try s.writeByteNTimes(' ', w.indent);
110110 try s.print("%{d}{c}= {s}(", .{
111111 inst,
112 @as(u8, if (w.liveness.isUnused(inst)) '!' else ' '),
112 @as(u8, if (if (w.liveness) |liveness| liveness.isUnused(inst) else false) '!' else ' '),
113113 @tagName(tag),
114114 });
115115 switch (tag) {
......@@ -389,6 +389,10 @@ const Writer = struct {
389389 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
390390 const extra = w.air.extraData(Air.Block, ty_pl.payload);
391391 const body = w.air.extra[extra.end..][0..extra.data.body_len];
392 const liveness_block = if (w.liveness) |liveness|
393 liveness.getBlock(inst)
394 else
395 Liveness.BlockSlices{ .deaths = &.{} };
392396
393397 try w.writeType(s, w.air.getRefType(ty_pl.ty));
394398 if (w.skip_body) return s.writeAll(", ...");
......@@ -399,13 +403,16 @@ const Writer = struct {
399403 w.indent = old_indent;
400404 try s.writeByteNTimes(' ', w.indent);
401405 try s.writeAll("}");
406
407 for (liveness_block.deaths) |operand| {
408 try s.print(" %{d}!", .{operand});
409 }
402410 }
403411
404412 fn writeLoop(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
405413 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
406414 const extra = w.air.extraData(Air.Block, ty_pl.payload);
407415 const body = w.air.extra[extra.end..][0..extra.data.body_len];
408 const liveness_loop = w.liveness.getLoop(inst);
409416
410417 try w.writeType(s, w.air.getRefType(ty_pl.ty));
411418 if (w.skip_body) return s.writeAll(", ...");
......@@ -413,14 +420,6 @@ const Writer = struct {
413420 const old_indent = w.indent;
414421 w.indent += 2;
415422 try w.writeBody(s, body);
416 if (liveness_loop.deaths.len != 0) {
417 try s.writeByteNTimes(' ', w.indent);
418 for (liveness_loop.deaths, 0..) |operand, i| {
419 if (i != 0) try s.writeAll(" ");
420 try s.print("%{d}!", .{operand});
421 }
422 try s.writeAll("\n");
423 }
424423 w.indent = old_indent;
425424 try s.writeByteNTimes(' ', w.indent);
426425 try s.writeAll("}");
......@@ -746,22 +745,44 @@ const Writer = struct {
746745 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
747746 const extra = w.air.extraData(Air.Try, pl_op.payload);
748747 const body = w.air.extra[extra.end..][0..extra.data.body_len];
748 const liveness_condbr = if (w.liveness) |liveness|
749 liveness.getCondBr(inst)
750 else
751 Liveness.CondBrSlices{ .then_deaths = &.{}, .else_deaths = &.{} };
749752
750753 try w.writeOperand(s, inst, 0, pl_op.operand);
751754 if (w.skip_body) return s.writeAll(", ...");
752755 try s.writeAll(", {\n");
753756 const old_indent = w.indent;
754757 w.indent += 2;
758
759 if (liveness_condbr.else_deaths.len != 0) {
760 try s.writeByteNTimes(' ', w.indent);
761 for (liveness_condbr.else_deaths, 0..) |operand, i| {
762 if (i != 0) try s.writeAll(" ");
763 try s.print("%{d}!", .{operand});
764 }
765 try s.writeAll("\n");
766 }
755767 try w.writeBody(s, body);
768
756769 w.indent = old_indent;
757770 try s.writeByteNTimes(' ', w.indent);
758771 try s.writeAll("}");
772
773 for (liveness_condbr.then_deaths) |operand| {
774 try s.print(" %{d}!", .{operand});
775 }
759776 }
760777
761778 fn writeTryPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
762779 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
763780 const extra = w.air.extraData(Air.TryPtr, ty_pl.payload);
764781 const body = w.air.extra[extra.end..][0..extra.data.body_len];
782 const liveness_condbr = if (w.liveness) |liveness|
783 liveness.getCondBr(inst)
784 else
785 Liveness.CondBrSlices{ .then_deaths = &.{}, .else_deaths = &.{} };
765786
766787 try w.writeOperand(s, inst, 0, extra.data.ptr);
767788
......@@ -771,10 +792,24 @@ const Writer = struct {
771792 try s.writeAll(", {\n");
772793 const old_indent = w.indent;
773794 w.indent += 2;
795
796 if (liveness_condbr.else_deaths.len != 0) {
797 try s.writeByteNTimes(' ', w.indent);
798 for (liveness_condbr.else_deaths, 0..) |operand, i| {
799 if (i != 0) try s.writeAll(" ");
800 try s.print("%{d}!", .{operand});
801 }
802 try s.writeAll("\n");
803 }
774804 try w.writeBody(s, body);
805
775806 w.indent = old_indent;
776807 try s.writeByteNTimes(' ', w.indent);
777808 try s.writeAll("}");
809
810 for (liveness_condbr.then_deaths) |operand| {
811 try s.print(" %{d}!", .{operand});
812 }
778813 }
779814
780815 fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
......@@ -782,7 +817,10 @@ const Writer = struct {
782817 const extra = w.air.extraData(Air.CondBr, pl_op.payload);
783818 const then_body = w.air.extra[extra.end..][0..extra.data.then_body_len];
784819 const else_body = w.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
785 const liveness_condbr = w.liveness.getCondBr(inst);
820 const liveness_condbr = if (w.liveness) |liveness|
821 liveness.getCondBr(inst)
822 else
823 Liveness.CondBrSlices{ .then_deaths = &.{}, .else_deaths = &.{} };
786824
787825 try w.writeOperand(s, inst, 0, pl_op.operand);
788826 if (w.skip_body) return s.writeAll(", ...");
......@@ -822,8 +860,15 @@ const Writer = struct {
822860 fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
823861 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
824862 const switch_br = w.air.extraData(Air.SwitchBr, pl_op.payload);
825 const liveness = w.liveness.getSwitchBr(w.gpa, inst, switch_br.data.cases_len + 1) catch
826 @panic("out of memory");
863 const liveness = if (w.liveness) |liveness|
864 liveness.getSwitchBr(w.gpa, inst, switch_br.data.cases_len + 1) catch
865 @panic("out of memory")
866 else blk: {
867 const slice = w.gpa.alloc([]const Air.Inst.Index, switch_br.data.cases_len + 1) catch
868 @panic("out of memory");
869 std.mem.set([]const Air.Inst.Index, slice, &.{});
870 break :blk Liveness.SwitchBrTable{ .deaths = slice };
871 };
827872 defer w.gpa.free(liveness.deaths);
828873 var extra_index: usize = switch_br.end;
829874 var case_i: u32 = 0;
......@@ -913,13 +958,13 @@ const Writer = struct {
913958 operand: Air.Inst.Ref,
914959 ) @TypeOf(s).Error!void {
915960 const small_tomb_bits = Liveness.bpi - 1;
916 const dies = if (op_index < small_tomb_bits)
917 w.liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index))
918 else blk: {
919 var extra_index = w.liveness.special.get(inst).?;
961 const dies = if (w.liveness) |liveness| blk: {
962 if (op_index < small_tomb_bits)
963 break :blk liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index));
964 var extra_index = liveness.special.get(inst).?;
920965 var tomb_op_index: usize = small_tomb_bits;
921966 while (true) {
922 const bits = w.liveness.extra[extra_index];
967 const bits = liveness.extra[extra_index];
923968 if (op_index < tomb_op_index + 31) {
924969 break :blk @truncate(u1, bits >> @intCast(u5, op_index - tomb_op_index)) != 0;
925970 }
......@@ -927,7 +972,7 @@ const Writer = struct {
927972 extra_index += 1;
928973 tomb_op_index += 31;
929974 }
930 };
975 } else false;
931976 return w.writeInstRef(s, operand, dies);
932977 }
933978
src/register_manager.zig+4
......@@ -95,6 +95,10 @@ pub fn RegisterManager(
9595 return indexOfReg(tracked_registers, reg);
9696 }
9797
98 pub fn regAtTrackedIndex(index: RegisterBitSet.ShiftInt) Register {
99 return tracked_registers[index];
100 }
101
98102 /// Returns true when this register is not tracked
99103 pub fn isRegFree(self: Self, reg: Register) bool {
100104 const index = indexOfRegIntoTracked(reg) orelse return true;
test/behavior/for.zig-1
......@@ -274,7 +274,6 @@ test "two counters" {
274274test "1-based counter and ptr to array" {
275275 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
276276 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
277 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
278277
279278 var ok: usize = 0;
280279
test/behavior/var_args.zig+26
......@@ -215,3 +215,29 @@ test "copy VaList" {
215215 try std.testing.expectEqual(@as(c_int, 3), S.add(1, @as(c_int, 1)));
216216 try std.testing.expectEqual(@as(c_int, 9), S.add(2, @as(c_int, 1), @as(c_int, 2)));
217217}
218
219test "unused VaList arg" {
220 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
221 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
222 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
223 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
224 if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) {
225 // https://github.com/ziglang/zig/issues/14096
226 return error.SkipZigTest;
227 }
228 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
229
230 const S = struct {
231 fn thirdArg(dummy: c_int, ...) callconv(.C) c_int {
232 _ = dummy;
233
234 var ap = @cVaStart();
235 defer @cVaEnd(&ap);
236
237 _ = @cVaArg(&ap, c_int);
238 return @cVaArg(&ap, c_int);
239 }
240 };
241 const x = S.thirdArg(0, @as(c_int, 1), @as(c_int, 2));
242 try std.testing.expectEqual(@as(c_int, 2), x);
243}
test/cases/aarch64-linux/conditional_branches.0.zig deleted-26
......@@ -1,26 +0,0 @@
1pub fn main() void {
2 foo(123);
3}
4
5fn foo(x: u64) void {
6 if (x > 42) {
7 print();
8 }
9}
10
11fn print() void {
12 asm volatile ("svc #0"
13 :
14 : [number] "{x8}" (64),
15 [arg1] "{x0}" (1),
16 [arg2] "{x1}" (@ptrToInt("Hello, World!\n")),
17 [arg3] "{x2}" ("Hello, World!\n".len),
18 : "memory", "cc"
19 );
20}
21
22// run
23// target=aarch64-linux
24//
25// Hello, World!
26//
test/cases/aarch64-linux/conditional_branches.1.zig deleted-25
......@@ -1,25 +0,0 @@
1pub fn main() void {
2 foo(true);
3}
4
5fn foo(x: bool) void {
6 if (x) {
7 print();
8 }
9}
10
11fn print() void {
12 asm volatile ("svc #0"
13 :
14 : [number] "{x8}" (64),
15 [arg1] "{x0}" (1),
16 [arg2] "{x1}" (@ptrToInt("Hello, World!\n")),
17 [arg3] "{x2}" ("Hello, World!\n".len),
18 : "memory", "cc"
19 );
20}
21
22// run
23//
24// Hello, World!
25//
test/cases/aarch64-linux/hello_world_with_updates.0.zig deleted-31
......@@ -1,31 +0,0 @@
1pub export fn _start() noreturn {
2 print();
3 exit(0);
4}
5
6fn print() void {
7 asm volatile ("svc #0"
8 :
9 : [number] "{x8}" (64),
10 [arg1] "{x0}" (1),
11 [arg2] "{x1}" (@ptrToInt("Hello, World!\n")),
12 [arg3] "{x2}" ("Hello, World!\n".len),
13 : "memory", "cc"
14 );
15}
16
17fn exit(ret: usize) noreturn {
18 asm volatile ("svc #0"
19 :
20 : [number] "{x8}" (93),
21 [arg1] "{x0}" (ret),
22 : "memory", "cc"
23 );
24 unreachable;
25}
26
27// run
28// target=aarch64-linux
29//
30// Hello, World!
31//
test/cases/aarch64-linux/hello_world_with_updates.1.zig deleted-36
......@@ -1,36 +0,0 @@
1pub export fn _start() noreturn {
2 print();
3 print();
4 print();
5 print();
6 exit(0);
7}
8
9fn print() void {
10 asm volatile ("svc #0"
11 :
12 : [number] "{x8}" (64),
13 [arg1] "{x0}" (1),
14 [arg2] "{x1}" (@ptrToInt("Hello, World!\n")),
15 [arg3] "{x2}" ("Hello, World!\n".len),
16 : "memory", "cc"
17 );
18}
19
20fn exit(ret: usize) noreturn {
21 asm volatile ("svc #0"
22 :
23 : [number] "{x8}" (93),
24 [arg1] "{x0}" (ret),
25 : "memory", "cc"
26 );
27 unreachable;
28}
29
30// run
31//
32// Hello, World!
33// Hello, World!
34// Hello, World!
35// Hello, World!
36//
test/cases/aarch64-linux/hello_world_with_updates.2.zig deleted-21
......@@ -1,21 +0,0 @@
1pub fn main() void {
2 print();
3 print();
4}
5
6fn print() void {
7 asm volatile ("svc #0"
8 :
9 : [number] "{x8}" (64),
10 [arg1] "{x0}" (1),
11 [arg2] "{x1}" (@ptrToInt("Hello, World!\n")),
12 [arg3] "{x2}" ("Hello, World!\n".len),
13 : "memory", "cc"
14 );
15}
16
17// run
18//
19// Hello, World!
20// Hello, World!
21//
test/cases/aarch64-macos/hello_world_with_updates.0.zig deleted-5
......@@ -1,5 +0,0 @@
1// error
2// output_mode=Exe
3// target=aarch64-macos
4//
5// :?:?: error: root struct of file 'tmp' has no member named 'main'
test/cases/aarch64-macos/hello_world_with_updates.1.zig deleted-6
......@@ -1,6 +0,0 @@
1pub export fn main() noreturn {}
2
3// error
4//
5// :1:22: error: function declared 'noreturn' implicitly returns
6// :1:32: note: control flow reaches end of body here
test/cases/aarch64-macos/hello_world_with_updates.2.zig deleted-19
......@@ -1,19 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2extern "c" fn exit(usize) noreturn;
3
4pub export fn main() noreturn {
5 print();
6
7 exit(0);
8}
9
10fn print() void {
11 const msg = @ptrToInt("Hello, World!\n");
12 const len = 14;
13 _ = write(1, msg, len);
14}
15
16// run
17//
18// Hello, World!
19//
test/cases/aarch64-macos/hello_world_with_updates.3.zig deleted-16
......@@ -1,16 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 print();
5}
6
7fn print() void {
8 const msg = @ptrToInt("Hello, World!\n");
9 const len = 14;
10 _ = write(1, msg, len);
11}
12
13// run
14//
15// Hello, World!
16//
test/cases/aarch64-macos/hello_world_with_updates.4.zig deleted-22
......@@ -1,22 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 print();
5 print();
6 print();
7 print();
8}
9
10fn print() void {
11 const msg = @ptrToInt("Hello, World!\n");
12 const len = 14;
13 _ = write(1, msg, len);
14}
15
16// run
17//
18// Hello, World!
19// Hello, World!
20// Hello, World!
21// Hello, World!
22//
test/cases/aarch64-macos/hello_world_with_updates.5.zig deleted-16
......@@ -1,16 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 print();
5}
6
7fn print() void {
8 const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
9 const len = 104;
10 _ = write(1, msg, len);
11}
12
13// run
14//
15// What is up? This is a longer message that will force the data to be relocated in virtual address space.
16//
test/cases/aarch64-macos/hello_world_with_updates.6.zig deleted-18
......@@ -1,18 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 print();
5 print();
6}
7
8fn print() void {
9 const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
10 const len = 104;
11 _ = write(1, msg, len);
12}
13
14// run
15//
16// What is up? This is a longer message that will force the data to be relocated in virtual address space.
17// What is up? This is a longer message that will force the data to be relocated in virtual address space.
18//
test/cases/arithmetic_operations.0.zig created+17
......@@ -0,0 +1,17 @@
1const std = @import("std");
2
3pub fn main() void {
4 print(2, 4);
5 print(1, 7);
6}
7
8fn print(a: u32, b: u32) void {
9 const str = "123456789";
10 const len = a + b;
11 _ = std.os.write(1, str[0..len]) catch {};
12}
13
14// run
15// target=x86_64-linux,x86_64-macos
16//
17// 12345612345678
test/cases/arithmetic_operations.1.zig created+16
......@@ -0,0 +1,16 @@
1const std = @import("std");
2
3pub fn main() void {
4 print(10, 5);
5 print(4, 3);
6}
7
8fn print(a: u32, b: u32) void {
9 const str = "123456789";
10 const len = a - b;
11 _ = std.os.write(1, str[0..len]) catch {};
12}
13
14// run
15//
16// 123451
test/cases/arithmetic_operations.2.zig created+16
......@@ -0,0 +1,16 @@
1const std = @import("std");
2
3pub fn main() void {
4 print(8, 9);
5 print(3, 7);
6}
7
8fn print(a: u32, b: u32) void {
9 const str = "123456789";
10 const len = a & b;
11 _ = std.os.write(1, str[0..len]) catch {};
12}
13
14// run
15//
16// 12345678123
test/cases/arithmetic_operations.3.zig created+16
......@@ -0,0 +1,16 @@
1const std = @import("std");
2
3pub fn main() void {
4 print(4, 2);
5 print(3, 7);
6}
7
8fn print(a: u32, b: u32) void {
9 const str = "123456789";
10 const len = a | b;
11 _ = std.os.write(1, str[0..len]) catch {};
12}
13
14// run
15//
16// 1234561234567
test/cases/arithmetic_operations.4.zig created+16
......@@ -0,0 +1,16 @@
1const std = @import("std");
2
3pub fn main() void {
4 print(42, 42);
5 print(3, 5);
6}
7
8fn print(a: u32, b: u32) void {
9 const str = "123456789";
10 const len = a ^ b;
11 _ = std.os.write(1, str[0..len]) catch {};
12}
13
14// run
15//
16// 123456
test/cases/arithmetic_operations.5.zig created+15
......@@ -0,0 +1,15 @@
1pub fn main() void {
2 var x: u32 = 1;
3 assert(x << 1 == 2);
4
5 x <<= 1;
6 assert(x << 2 == 8);
7 assert(x << 3 == 16);
8}
9
10pub fn assert(ok: bool) void {
11 if (!ok) unreachable; // assertion failure
12}
13
14// run
15//
test/cases/arithmetic_operations.6.zig created+21
......@@ -0,0 +1,21 @@
1pub fn main() void {
2 var a: u32 = 1024;
3 assert(a >> 1 == 512);
4
5 a >>= 1;
6 assert(a >> 2 == 128);
7 assert(a >> 3 == 64);
8 assert(a >> 4 == 32);
9 assert(a >> 5 == 16);
10 assert(a >> 6 == 8);
11 assert(a >> 7 == 4);
12 assert(a >> 8 == 2);
13 assert(a >> 9 == 1);
14}
15
16pub fn assert(ok: bool) void {
17 if (!ok) unreachable; // assertion failure
18}
19
20// run
21//
test/cases/arm-linux/arithmetic_operations.0.zig deleted-21
......@@ -1,21 +0,0 @@
1pub fn main() void {
2 print(2, 4);
3 print(1, 7);
4}
5
6fn print(a: u32, b: u32) void {
7 asm volatile ("svc #0"
8 :
9 : [number] "{r7}" (4),
10 [arg3] "{r2}" (a + b),
11 [arg1] "{r0}" (1),
12 [arg2] "{r1}" (@ptrToInt("123456789")),
13 : "memory"
14 );
15 return;
16}
17
18// run
19// target=arm-linux
20//
21// 12345612345678
test/cases/arm-linux/arithmetic_operations.1.zig deleted-20
......@@ -1,20 +0,0 @@
1pub fn main() void {
2 print(10, 5);
3 print(4, 3);
4}
5
6fn print(a: u32, b: u32) void {
7 asm volatile ("svc #0"
8 :
9 : [number] "{r7}" (4),
10 [arg3] "{r2}" (a - b),
11 [arg1] "{r0}" (1),
12 [arg2] "{r1}" (@ptrToInt("123456789")),
13 : "memory"
14 );
15 return;
16}
17
18// run
19//
20// 123451
test/cases/arm-linux/arithmetic_operations.2.zig deleted-20
......@@ -1,20 +0,0 @@
1pub fn main() void {
2 print(8, 9);
3 print(3, 7);
4}
5
6fn print(a: u32, b: u32) void {
7 asm volatile ("svc #0"
8 :
9 : [number] "{r7}" (4),
10 [arg3] "{r2}" (a & b),
11 [arg1] "{r0}" (1),
12 [arg2] "{r1}" (@ptrToInt("123456789")),
13 : "memory"
14 );
15 return;
16}
17
18// run
19//
20// 12345678123
test/cases/arm-linux/arithmetic_operations.3.zig deleted-20
......@@ -1,20 +0,0 @@
1pub fn main() void {
2 print(4, 2);
3 print(3, 7);
4}
5
6fn print(a: u32, b: u32) void {
7 asm volatile ("svc #0"
8 :
9 : [number] "{r7}" (4),
10 [arg3] "{r2}" (a | b),
11 [arg1] "{r0}" (1),
12 [arg2] "{r1}" (@ptrToInt("123456789")),
13 : "memory"
14 );
15 return;
16}
17
18// run
19//
20// 1234561234567
test/cases/arm-linux/arithmetic_operations.4.zig deleted-20
......@@ -1,20 +0,0 @@
1pub fn main() void {
2 print(42, 42);
3 print(3, 5);
4}
5
6fn print(a: u32, b: u32) void {
7 asm volatile ("svc #0"
8 :
9 : [number] "{r7}" (4),
10 [arg3] "{r2}" (a ^ b),
11 [arg1] "{r0}" (1),
12 [arg2] "{r1}" (@ptrToInt("123456789")),
13 : "memory"
14 );
15 return;
16}
17
18// run
19//
20// 123456
test/cases/arm-linux/arithmetic_operations.5.zig deleted-15
......@@ -1,15 +0,0 @@
1pub fn main() void {
2 var x: u32 = 1;
3 assert(x << 1 == 2);
4
5 x <<= 1;
6 assert(x << 2 == 8);
7 assert(x << 3 == 16);
8}
9
10pub fn assert(ok: bool) void {
11 if (!ok) unreachable; // assertion failure
12}
13
14// run
15//
test/cases/arm-linux/arithmetic_operations.6.zig deleted-21
......@@ -1,21 +0,0 @@
1pub fn main() void {
2 var a: u32 = 1024;
3 assert(a >> 1 == 512);
4
5 a >>= 1;
6 assert(a >> 2 == 128);
7 assert(a >> 3 == 64);
8 assert(a >> 4 == 32);
9 assert(a >> 5 == 16);
10 assert(a >> 6 == 8);
11 assert(a >> 7 == 4);
12 assert(a >> 8 == 2);
13 assert(a >> 9 == 1);
14}
15
16pub fn assert(ok: bool) void {
17 if (!ok) unreachable; // assertion failure
18}
19
20// run
21//
test/cases/arm-linux/errors.0.zig deleted-21
......@@ -1,21 +0,0 @@
1pub fn main() void {
2 foo() catch print();
3}
4
5fn foo() anyerror!void {}
6
7fn print() void {
8 asm volatile ("svc #0"
9 :
10 : [number] "{r7}" (4),
11 [arg1] "{r0}" (1),
12 [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
13 [arg3] "{r2}" ("Hello, World!\n".len),
14 : "memory"
15 );
16 return;
17}
18
19// run
20// target=arm-linux
21//
test/cases/arm-linux/errors.1.zig deleted-24
......@@ -1,24 +0,0 @@
1pub fn main() void {
2 foo() catch print();
3}
4
5fn foo() anyerror!void {
6 return error.Test;
7}
8
9fn print() void {
10 asm volatile ("svc #0"
11 :
12 : [number] "{r7}" (4),
13 [arg1] "{r0}" (1),
14 [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
15 [arg3] "{r2}" ("Hello, World!\n".len),
16 : "memory"
17 );
18 return;
19}
20
21// run
22//
23// Hello, World!
24//
test/cases/arm-linux/errors.2.zig deleted-27
......@@ -1,27 +0,0 @@
1pub fn main() void {
2 foo() catch |err| {
3 assert(err == error.Foo);
4 assert(err != error.Bar);
5 assert(err != error.Baz);
6 };
7 bar() catch |err| {
8 assert(err != error.Foo);
9 assert(err == error.Bar);
10 assert(err != error.Baz);
11 };
12}
13
14fn assert(ok: bool) void {
15 if (!ok) unreachable;
16}
17
18fn foo() anyerror!void {
19 return error.Foo;
20}
21
22fn bar() anyerror!void {
23 return error.Bar;
24}
25
26// run
27//
test/cases/arm-linux/errors.3.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() void {
2 foo() catch unreachable;
3}
4
5fn foo() anyerror!void {
6 try bar();
7}
8
9fn bar() anyerror!void {}
10
11// run
12//
test/cases/arm-linux/function_pointers.zig deleted-44
......@@ -1,44 +0,0 @@
1const PrintFn = *const fn () void;
2
3pub fn main() void {
4 var printFn: PrintFn = stopSayingThat;
5 var i: u32 = 0;
6 while (i < 4) : (i += 1) printFn();
7
8 printFn = moveEveryZig;
9 printFn();
10}
11
12fn stopSayingThat() void {
13 asm volatile ("svc #0"
14 :
15 : [number] "{r7}" (4),
16 [arg1] "{r0}" (1),
17 [arg2] "{r1}" (@ptrToInt("Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n")),
18 [arg3] "{r2}" ("Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n".len),
19 : "memory"
20 );
21 return;
22}
23
24fn moveEveryZig() void {
25 asm volatile ("svc #0"
26 :
27 : [number] "{r7}" (4),
28 [arg1] "{r0}" (1),
29 [arg2] "{r1}" (@ptrToInt("All your codebase are belong to us\n")),
30 [arg3] "{r2}" ("All your codebase are belong to us\n".len),
31 : "memory"
32 );
33 return;
34}
35
36// run
37// target=arm-linux
38//
39// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
40// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
41// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
42// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
43// All your codebase are belong to us
44//
test/cases/arm-linux/hello_world_with_updates.0.zig deleted-32
......@@ -1,32 +0,0 @@
1pub export fn _start() noreturn {
2 print();
3 exit();
4}
5
6fn print() void {
7 asm volatile ("svc #0"
8 :
9 : [number] "{r7}" (4),
10 [arg1] "{r0}" (1),
11 [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
12 [arg3] "{r2}" (14),
13 : "memory"
14 );
15 return;
16}
17
18fn exit() noreturn {
19 asm volatile ("svc #0"
20 :
21 : [number] "{r7}" (1),
22 [arg1] "{r0}" (0),
23 : "memory"
24 );
25 unreachable;
26}
27
28// run
29// target=arm-linux
30//
31// Hello, World!
32//
test/cases/arm-linux/hello_world_with_updates.1.zig deleted-37
......@@ -1,37 +0,0 @@
1pub export fn _start() noreturn {
2 print();
3 print();
4 print();
5 print();
6 exit();
7}
8
9fn print() void {
10 asm volatile ("svc #0"
11 :
12 : [number] "{r7}" (4),
13 [arg1] "{r0}" (1),
14 [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
15 [arg3] "{r2}" (14),
16 : "memory"
17 );
18 return;
19}
20
21fn exit() noreturn {
22 asm volatile ("svc #0"
23 :
24 : [number] "{r7}" (1),
25 [arg1] "{r0}" (0),
26 : "memory"
27 );
28 unreachable;
29}
30
31// run
32//
33// Hello, World!
34// Hello, World!
35// Hello, World!
36// Hello, World!
37//
test/cases/arm-linux/hello_world_with_updates.2.zig deleted-22
......@@ -1,22 +0,0 @@
1pub fn main() void {
2 print();
3 print();
4}
5
6fn print() void {
7 asm volatile ("svc #0"
8 :
9 : [number] "{r7}" (4),
10 [arg1] "{r0}" (1),
11 [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
12 [arg3] "{r2}" (14),
13 : "memory"
14 );
15 return;
16}
17
18// run
19//
20// Hello, World!
21// Hello, World!
22//
test/cases/arm-linux/parameters_and_return_values.0.zig deleted-28
......@@ -1,28 +0,0 @@
1pub fn main() void {
2 print(id(14));
3}
4
5fn id(x: u32) u32 {
6 return x;
7}
8
9// TODO: The parameters to the asm statement in print() had to
10// be in a specific order because otherwise the write to r0
11// would overwrite the len parameter which resides in r0
12fn print(len: u32) void {
13 asm volatile ("svc #0"
14 :
15 : [number] "{r7}" (4),
16 [arg3] "{r2}" (len),
17 [arg1] "{r0}" (1),
18 [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
19 : "memory"
20 );
21 return;
22}
23
24// run
25// target=arm-linux
26//
27// Hello, World!
28//
test/cases/arm-linux/parameters_and_return_values.1.zig deleted-14
......@@ -1,14 +0,0 @@
1pub fn main() void {
2 assert(add(1, 2, 3, 4, 5, 6) == 21);
3}
4
5fn add(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32) u32 {
6 return a + b + c + d + e + f;
7}
8
9pub fn assert(ok: bool) void {
10 if (!ok) unreachable; // assertion failure
11}
12
13// run
14//
test/cases/arm-linux/print_u32s.zig deleted-40
......@@ -1,40 +0,0 @@
1pub fn main() void {
2 printNumberHex(0x00000000);
3 printNumberHex(0xaaaaaaaa);
4 printNumberHex(0xdeadbeef);
5 printNumberHex(0x31415926);
6}
7
8fn printNumberHex(x: u32) void {
9 var i: u5 = 28;
10 while (true) : (i -= 4) {
11 const digit = (x >> i) & 0xf;
12 asm volatile ("svc #0"
13 :
14 : [number] "{r7}" (4),
15 [arg1] "{r0}" (1),
16 [arg2] "{r1}" (@ptrToInt("0123456789abcdef") + digit),
17 [arg3] "{r2}" (1),
18 : "memory"
19 );
20
21 if (i == 0) break;
22 }
23 asm volatile ("svc #0"
24 :
25 : [number] "{r7}" (4),
26 [arg1] "{r0}" (1),
27 [arg2] "{r1}" (@ptrToInt("\n")),
28 [arg3] "{r2}" (1),
29 : "memory"
30 );
31}
32
33// run
34// target=arm-linux
35//
36// 00000000
37// aaaaaaaa
38// deadbeef
39// 31415926
40//
test/cases/arm-linux/spilling_registers.0.zig deleted-38
......@@ -1,38 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 791);
3}
4
5fn add(a: u32, b: u32) u32 {
6 const x: u32 = blk: {
7 const c = a + b; // 7
8 const d = a + c; // 10
9 const e = d + b; // 14
10 const f = d + e; // 24
11 const g = e + f; // 38
12 const h = f + g; // 62
13 const i = g + h; // 100
14 const j = i + d; // 110
15 const k = i + j; // 210
16 const l = k + c; // 217
17 const m = l + d; // 227
18 const n = m + e; // 241
19 const o = n + f; // 265
20 const p = o + g; // 303
21 const q = p + h; // 365
22 const r = q + i; // 465
23 const s = r + j; // 575
24 const t = s + k; // 785
25 break :blk t;
26 };
27 const y = x + a; // 788
28 const z = y + a; // 791
29 return z;
30}
31
32fn assert(ok: bool) void {
33 if (!ok) unreachable;
34}
35
36// run
37// target=arm-linux
38//
test/cases/arm-linux/spilling_registers.1.zig deleted-37
......@@ -1,37 +0,0 @@
1pub fn main() void {
2 assert(addMul(3, 4) == 357747496);
3}
4
5fn addMul(a: u32, b: u32) u32 {
6 const x: u32 = blk: {
7 const c = a + b; // 7
8 const d = a + c; // 10
9 const e = d + b; // 14
10 const f = d + e; // 24
11 const g = e + f; // 38
12 const h = f + g; // 62
13 const i = g + h; // 100
14 const j = i + d; // 110
15 const k = i + j; // 210
16 const l = k + c; // 217
17 const m = l * d; // 2170
18 const n = m + e; // 2184
19 const o = n * f; // 52416
20 const p = o + g; // 52454
21 const q = p * h; // 3252148
22 const r = q + i; // 3252248
23 const s = r * j; // 357747280
24 const t = s + k; // 357747490
25 break :blk t;
26 };
27 const y = x + a; // 357747493
28 const z = y + a; // 357747496
29 return z;
30}
31
32fn assert(ok: bool) void {
33 if (!ok) unreachable;
34}
35
36// run
37//
test/cases/assert_function.0.zig created+15
......@@ -0,0 +1,15 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 assert(a + b == 7);
7}
8
9pub fn assert(ok: bool) void {
10 if (!ok) unreachable; // assertion failure
11}
12
13// run
14// target=x86_64-macos,x86_64-linux
15// link_libc=true
test/cases/assert_function.1.zig created+17
......@@ -0,0 +1,17 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 const c = a + b; // 7
7 const d = a + c; // 10
8 const e = d + b; // 14
9 assert(e == 14);
10}
11
12pub fn assert(ok: bool) void {
13 if (!ok) unreachable; // assertion failure
14}
15
16// run
17//
test/cases/assert_function.10.zig created+27
......@@ -0,0 +1,27 @@
1pub fn main() void {
2 assert(add(3, 4) == 116);
3}
4
5fn add(a: u32, b: u32) u32 {
6 const x: u32 = blk: {
7 const c = a + b; // 7
8 const d = a + c; // 10
9 const e = d + b; // 14
10 const f = d + e; // 24
11 const g = e + f; // 38
12 const h = f + g; // 62
13 const i = g + h; // 100
14 const j = i + d; // 110
15 break :blk j;
16 };
17 const y = x + a; // 113
18 const z = y + a; // 116
19 return z;
20}
21
22pub fn assert(ok: bool) void {
23 if (!ok) unreachable; // assertion failure
24}
25
26// run
27//
test/cases/assert_function.11.zig created+66
......@@ -0,0 +1,66 @@
1pub fn main() void {
2 assert(add(3, 4) == 1221);
3 assert(mul(3, 4) == 21609);
4}
5
6fn add(a: u32, b: u32) u32 {
7 const x: u32 = blk: {
8 const c = a + b; // 7
9 const d = a + c; // 10
10 const e = d + b; // 14
11 const f = d + e; // 24
12 const g = e + f; // 38
13 const h = f + g; // 62
14 const i = g + h; // 100
15 const j = i + d; // 110
16 const k = i + j; // 210
17 const l = j + k; // 320
18 const m = l + c; // 327
19 const n = m + d; // 337
20 const o = n + e; // 351
21 const p = o + f; // 375
22 const q = p + g; // 413
23 const r = q + h; // 475
24 const s = r + i; // 575
25 const t = s + j; // 685
26 const u = t + k; // 895
27 const v = u + l; // 1215
28 break :blk v;
29 };
30 const y = x + a; // 1218
31 const z = y + a; // 1221
32 return z;
33}
34
35fn mul(a: u32, b: u32) u32 {
36 const x: u32 = blk: {
37 const c = a * a * a * a; // 81
38 const d = a * a * a * b; // 108
39 const e = a * a * b * a; // 108
40 const f = a * a * b * b; // 144
41 const g = a * b * a * a; // 108
42 const h = a * b * a * b; // 144
43 const i = a * b * b * a; // 144
44 const j = a * b * b * b; // 192
45 const k = b * a * a * a; // 108
46 const l = b * a * a * b; // 144
47 const m = b * a * b * a; // 144
48 const n = b * a * b * b; // 192
49 const o = b * b * a * a; // 144
50 const p = b * b * a * b; // 192
51 const q = b * b * b * a; // 192
52 const r = b * b * b * b; // 256
53 const s = c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r; // 2401
54 break :blk s;
55 };
56 const y = x * a; // 7203
57 const z = y * a; // 21609
58 return z;
59}
60
61pub fn assert(ok: bool) void {
62 if (!ok) unreachable; // assertion failure
63}
64
65// run
66//
test/cases/assert_function.12.zig created+47
......@@ -0,0 +1,47 @@
1pub fn main() void {
2 assert(add(3, 4) == 791);
3 assert(add(4, 3) == 79);
4}
5
6fn add(a: u32, b: u32) u32 {
7 const x: u32 = if (a < b) blk: {
8 const c = a + b; // 7
9 const d = a + c; // 10
10 const e = d + b; // 14
11 const f = d + e; // 24
12 const g = e + f; // 38
13 const h = f + g; // 62
14 const i = g + h; // 100
15 const j = i + d; // 110
16 const k = i + j; // 210
17 const l = k + c; // 217
18 const m = l + d; // 227
19 const n = m + e; // 241
20 const o = n + f; // 265
21 const p = o + g; // 303
22 const q = p + h; // 365
23 const r = q + i; // 465
24 const s = r + j; // 575
25 const t = s + k; // 785
26 break :blk t;
27 } else blk: {
28 const t = b + b + a; // 10
29 const c = a + t; // 14
30 const d = c + t; // 24
31 const e = d + t; // 34
32 const f = e + t; // 44
33 const g = f + t; // 54
34 const h = c + g; // 68
35 break :blk h + b; // 71
36 };
37 const y = x + a; // 788, 75
38 const z = y + a; // 791, 79
39 return z;
40}
41
42pub fn assert(ok: bool) void {
43 if (!ok) unreachable; // assertion failure
44}
45
46// run
47//
test/cases/assert_function.13.zig created+19
......@@ -0,0 +1,19 @@
1pub fn main() void {
2 const ignore =
3 \\ cool thx
4 \\
5 ;
6 _ = ignore;
7 add('ぁ', '\x03');
8}
9
10fn add(a: u32, b: u32) void {
11 assert(a + b == 12356);
12}
13
14pub fn assert(ok: bool) void {
15 if (!ok) unreachable; // assertion failure
16}
17
18// run
19//
test/cases/assert_function.14.zig created+17
......@@ -0,0 +1,17 @@
1pub fn main() void {
2 add(aa, bb);
3}
4
5const aa = 'ぁ';
6const bb = '\x03';
7
8fn add(a: u32, b: u32) void {
9 assert(a + b == 12356);
10}
11
12pub fn assert(ok: bool) void {
13 if (!ok) unreachable; // assertion failure
14}
15
16// run
17//
test/cases/assert_function.15.zig created+10
......@@ -0,0 +1,10 @@
1pub fn main() void {
2 assert("hello"[0] == 'h');
3}
4
5pub fn assert(ok: bool) void {
6 if (!ok) unreachable; // assertion failure
7}
8
9// run
10//
test/cases/assert_function.16.zig created+11
......@@ -0,0 +1,11 @@
1const hello = "hello".*;
2pub fn main() void {
3 assert(hello[1] == 'e');
4}
5
6pub fn assert(ok: bool) void {
7 if (!ok) unreachable; // assertion failure
8}
9
10// run
11//
test/cases/assert_function.17.zig created+11
......@@ -0,0 +1,11 @@
1pub fn main() void {
2 var i: u64 = 0xFFEEDDCCBBAA9988;
3 assert(i == 0xFFEEDDCCBBAA9988);
4}
5
6pub fn assert(ok: bool) void {
7 if (!ok) unreachable; // assertion failure
8}
9
10// run
11//
test/cases/assert_function.18.zig created+20
......@@ -0,0 +1,20 @@
1const builtin = @import("builtin");
2
3extern "c" fn write(c_int, usize, usize) usize;
4
5pub fn main() void {
6 for ("hello") |_| print();
7}
8
9fn print() void {
10 _ = write(1, @ptrToInt("hello\n"), 6);
11}
12
13// run
14//
15// hello
16// hello
17// hello
18// hello
19// hello
20//
test/cases/assert_function.2.zig created+21
......@@ -0,0 +1,21 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 const c = a + b; // 7
7 const d = a + c; // 10
8 const e = d + b; // 14
9 const f = d + e; // 24
10 const g = e + f; // 38
11 const h = f + g; // 62
12 const i = g + h; // 100
13 assert(i == 100);
14}
15
16pub fn assert(ok: bool) void {
17 if (!ok) unreachable; // assertion failure
18}
19
20// run
21//
test/cases/assert_function.3.zig created+22
......@@ -0,0 +1,22 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 const c = a + b; // 7
7 const d = a + c; // 10
8 const e = d + b; // 14
9 const f = d + e; // 24
10 const g = e + f; // 38
11 const h = f + g; // 62
12 const i = g + h; // 100
13 const j = i + d; // 110
14 assert(j == 110);
15}
16
17pub fn assert(ok: bool) void {
18 if (!ok) unreachable; // assertion failure
19}
20
21// run
22//
test/cases/assert_function.4.zig created+15
......@@ -0,0 +1,15 @@
1pub fn main() void {
2 assert(add(3, 4) == 7);
3 assert(add(20, 10) == 30);
4}
5
6fn add(a: u32, b: u32) u32 {
7 return a + b;
8}
9
10pub fn assert(ok: bool) void {
11 if (!ok) unreachable; // assertion failure
12}
13
14// run
15//
test/cases/assert_function.5.zig created+19
......@@ -0,0 +1,19 @@
1pub fn main() void {
2 assert(add(3, 4) == 7);
3 assert(add(20, 10) == 30);
4}
5
6fn add(a: u32, b: u32) u32 {
7 var x: u32 = undefined;
8 x = 0;
9 x += a;
10 x += b;
11 return x;
12}
13
14pub fn assert(ok: bool) void {
15 if (!ok) unreachable; // assertion failure
16}
17
18// run
19//
test/cases/assert_function.6.zig created+9
......@@ -0,0 +1,9 @@
1pub fn main() void {
2 const a: u32 = 2;
3 const b: ?u32 = a;
4 const c = b.?;
5 if (c != 2) unreachable;
6}
7
8// run
9//
test/cases/assert_function.7.zig created+23
......@@ -0,0 +1,23 @@
1extern "c" fn write(c_int, usize, usize) usize;
2
3pub fn main() void {
4 var i: u32 = 0;
5 while (i < 4) : (i += 1) print();
6 assert(i == 4);
7}
8
9fn print() void {
10 _ = write(1, @ptrToInt("hello\n"), 6);
11}
12
13pub fn assert(ok: bool) void {
14 if (!ok) unreachable; // assertion failure
15}
16
17// run
18//
19// hello
20// hello
21// hello
22// hello
23//
test/cases/assert_function.8.zig created+20
......@@ -0,0 +1,20 @@
1extern "c" fn write(c_int, usize, usize) usize;
2
3pub fn main() void {
4 var i: u32 = 0;
5 inline while (i < 4) : (i += 1) print();
6 assert(i == 4);
7}
8
9fn print() void {
10 _ = write(1, @ptrToInt("hello\n"), 6);
11}
12
13pub fn assert(ok: bool) void {
14 if (!ok) unreachable; // assertion failure
15}
16
17// error
18//
19// :5:21: error: unable to resolve comptime value
20// :5:21: note: condition in comptime branch must be comptime-known
test/cases/assert_function.9.zig created+22
......@@ -0,0 +1,22 @@
1pub fn main() void {
2 assert(add(3, 4) == 20);
3}
4
5fn add(a: u32, b: u32) u32 {
6 const x: u32 = blk: {
7 const c = a + b; // 7
8 const d = a + c; // 10
9 const e = d + b; // 14
10 break :blk e;
11 };
12 const y = x + a; // 17
13 const z = y + a; // 20
14 return z;
15}
16
17pub fn assert(ok: bool) void {
18 if (!ok) unreachable; // assertion failure
19}
20
21// run
22//
test/cases/break_continue.0.zig+1-1
......@@ -5,5 +5,5 @@ pub fn main() void {
55}
66
77// run
8// target=x86_64-linux,x86_64-macos,aarch64-linux,aarch64-macos
8// target=x86_64-linux,x86_64-macos
99//
test/cases/comptime_var.0.zig created+13
......@@ -0,0 +1,13 @@
1pub fn main() void {
2 var a: u32 = 0;
3 comptime var b: u32 = 0;
4 if (a == 0) b = 3;
5}
6
7// error
8// output_mode=Exe
9// target=x86_64-macos,x86_64-linux
10// link_libc=true
11//
12// :4:19: error: store to comptime variable depends on runtime condition
13// :4:11: note: runtime condition here
test/cases/comptime_var.1.zig created+13
......@@ -0,0 +1,13 @@
1pub fn main() void {
2 var a: u32 = 0;
3 comptime var b: u32 = 0;
4 switch (a) {
5 0 => {},
6 else => b = 3,
7 }
8}
9
10// error
11//
12// :6:19: error: store to comptime variable depends on runtime condition
13// :4:13: note: runtime condition here
test/cases/comptime_var.2.zig created+17
......@@ -0,0 +1,17 @@
1extern "c" fn write(c_int, usize, usize) usize;
2
3pub fn main() void {
4 comptime var len: u32 = 5;
5 print(len);
6 len += 9;
7 print(len);
8}
9
10fn print(len: usize) void {
11 _ = write(1, @ptrToInt("Hello, World!\n"), len);
12}
13
14// run
15//
16// HelloHello, World!
17//
test/cases/comptime_var.3.zig created+10
......@@ -0,0 +1,10 @@
1comptime {
2 var x: i32 = 1;
3 x += 1;
4 if (x != 1) unreachable;
5}
6pub fn main() void {}
7
8// error
9//
10// :4:17: error: reached unreachable code
test/cases/comptime_var.4.zig created+9
......@@ -0,0 +1,9 @@
1pub fn main() void {
2 comptime var i: u64 = 0;
3 while (i < 5) : (i += 1) {}
4}
5
6// error
7//
8// :3:24: error: cannot store to comptime variable in non-inline loop
9// :3:5: note: non-inline loop here
test/cases/comptime_var.5.zig created+15
......@@ -0,0 +1,15 @@
1pub fn main() void {
2 var a: u32 = 0;
3 if (a == 0) {
4 comptime var b: u32 = 0;
5 b = 1;
6 }
7}
8comptime {
9 var x: i32 = 1;
10 x += 1;
11 if (x != 2) unreachable;
12}
13
14// run
15//
test/cases/comptime_var.6.zig created+15
......@@ -0,0 +1,15 @@
1extern "c" fn write(c_int, usize, usize) usize;
2
3pub fn main() void {
4 comptime var i: u64 = 2;
5 inline while (i < 6) : (i += 1) {
6 print(i);
7 }
8}
9fn print(len: usize) void {
10 _ = write(1, @ptrToInt("Hello"), len);
11}
12
13// run
14//
15// HeHelHellHello
test/cases/conditional_branches.0.zig created+23
......@@ -0,0 +1,23 @@
1extern "c" fn write(c_int, usize, usize) usize;
2
3pub fn main() void {
4 foo(123);
5}
6
7fn foo(x: u64) void {
8 if (x > 42) {
9 print();
10 }
11}
12
13fn print() void {
14 const str = "Hello, World!\n";
15 _ = write(1, @ptrToInt(str.ptr), ptr.len);
16}
17
18// run
19// target=x86_64-linux,x86_64-macos
20// link_libc=true
21//
22// Hello, World!
23//
test/cases/conditional_branches.1.zig created+25
......@@ -0,0 +1,25 @@
1extern "c" fn write(c_int, usize, usize) usize;
2
3pub fn main() void {
4 foo(true);
5}
6
7fn foo(x: bool) void {
8 if (x) {
9 print();
10 print();
11 } else {
12 print();
13 }
14}
15
16fn print() void {
17 const str = "Hello, World!\n";
18 _ = write(1, @ptrToInt(str.ptr), ptr.len);
19}
20
21// run
22//
23// Hello, World!
24// Hello, World!
25//
test/cases/conditions.0.zig created+11
......@@ -0,0 +1,11 @@
1pub fn main() u8 {
2 var i: u8 = 5;
3 if (i > @as(u8, 4)) {
4 i += 10;
5 }
6 return i - 15;
7}
8
9// run
10// target=wasm32-wasi
11//
test/cases/conditions.1.zig created+12
......@@ -0,0 +1,12 @@
1pub fn main() u8 {
2 var i: u8 = 5;
3 if (i < @as(u8, 4)) {
4 i += 10;
5 } else {
6 i = 2;
7 }
8 return i - 2;
9}
10
11// run
12//
test/cases/conditions.2.zig created+12
......@@ -0,0 +1,12 @@
1pub fn main() u8 {
2 var i: u8 = 5;
3 if (i < @as(u8, 4)) {
4 i += 10;
5 } else if (i == @as(u8, 5)) {
6 i = 20;
7 }
8 return i - 20;
9}
10
11// run
12//
test/cases/conditions.3.zig created+16
......@@ -0,0 +1,16 @@
1pub fn main() u8 {
2 var i: u8 = 11;
3 if (i < @as(u8, 4)) {
4 i += 10;
5 } else {
6 if (i > @as(u8, 10)) {
7 i += 20;
8 } else {
9 i = 20;
10 }
11 }
12 return i - 31;
13}
14
15// run
16//
test/cases/conditions.4.zig created+15
......@@ -0,0 +1,15 @@
1pub fn main() void {
2 assert(foo(true) != @as(i32, 30));
3}
4
5fn assert(ok: bool) void {
6 if (!ok) unreachable;
7}
8
9fn foo(ok: bool) i32 {
10 const x = if (ok) @as(i32, 20) else @as(i32, 10);
11 return x;
12}
13
14// run
15//
test/cases/conditions.5.zig created+20
......@@ -0,0 +1,20 @@
1pub fn main() void {
2 assert(foo(false) == @as(i32, 20));
3 assert(foo(true) == @as(i32, 30));
4}
5
6fn assert(ok: bool) void {
7 if (!ok) unreachable;
8}
9
10fn foo(ok: bool) i32 {
11 const val: i32 = blk: {
12 var x: i32 = 1;
13 if (!ok) break :blk x + @as(i32, 9);
14 break :blk x + @as(i32, 19);
15 };
16 return val + 10;
17}
18
19// run
20//
test/cases/error_unions.0.zig created+15
......@@ -0,0 +1,15 @@
1pub fn main() void {
2 var e1 = error.Foo;
3 var e2 = error.Bar;
4 assert(e1 != e2);
5 assert(e1 == error.Foo);
6 assert(e2 == error.Bar);
7}
8
9fn assert(b: bool) void {
10 if (!b) unreachable;
11}
12
13// run
14// target=wasm32-wasi
15//
test/cases/error_unions.1.zig created+8
......@@ -0,0 +1,8 @@
1pub fn main() u8 {
2 var e: anyerror!u8 = 5;
3 const i = e catch 10;
4 return i - 5;
5}
6
7// run
8//
test/cases/error_unions.2.zig created+8
......@@ -0,0 +1,8 @@
1pub fn main() u8 {
2 var e: anyerror!u8 = error.Foo;
3 const i = e catch 10;
4 return i - 10;
5}
6
7// run
8//
test/cases/error_unions.3.zig created+12
......@@ -0,0 +1,12 @@
1pub fn main() u8 {
2 var e = foo();
3 const i = e catch 69;
4 return i - 5;
5}
6
7fn foo() anyerror!u8 {
8 return 5;
9}
10
11// run
12//
test/cases/error_unions.4.zig created+12
......@@ -0,0 +1,12 @@
1pub fn main() u8 {
2 var e = foo();
3 const i = e catch 69;
4 return i - 69;
5}
6
7fn foo() anyerror!u8 {
8 return error.Bruh;
9}
10
11// run
12//
test/cases/error_unions.5.zig created+12
......@@ -0,0 +1,12 @@
1pub fn main() u8 {
2 var e = foo();
3 const i = e catch 42;
4 return i - 42;
5}
6
7fn foo() anyerror!u8 {
8 return error.Dab;
9}
10
11// run
12//
test/cases/errors.0.zig created+15
......@@ -0,0 +1,15 @@
1const std = @import("std");
2
3pub fn main() void {
4 foo() catch print();
5}
6
7fn foo() anyerror!void {}
8
9fn print() void {
10 _ = std.os.write(1, "Hello, World!\n") catch {};
11}
12
13// run
14// target=x86_64-macos
15//
test/cases/errors.1.zig created+18
......@@ -0,0 +1,18 @@
1const std = @import("std");
2
3pub fn main() void {
4 foo() catch print();
5}
6
7fn foo() anyerror!void {
8 return error.Test;
9}
10
11fn print() void {
12 _ = std.os.write(1, "Hello, World!\n") catch {};
13}
14
15// run
16//
17// Hello, World!
18//
test/cases/errors.2.zig created+27
......@@ -0,0 +1,27 @@
1pub fn main() void {
2 foo() catch |err| {
3 assert(err == error.Foo);
4 assert(err != error.Bar);
5 assert(err != error.Baz);
6 };
7 bar() catch |err| {
8 assert(err != error.Foo);
9 assert(err == error.Bar);
10 assert(err != error.Baz);
11 };
12}
13
14fn assert(ok: bool) void {
15 if (!ok) unreachable;
16}
17
18fn foo() anyerror!void {
19 return error.Foo;
20}
21
22fn bar() anyerror!void {
23 return error.Bar;
24}
25
26// run
27//
test/cases/errors.3.zig created+12
......@@ -0,0 +1,12 @@
1pub fn main() void {
2 foo() catch unreachable;
3}
4
5fn foo() anyerror!void {
6 try bar();
7}
8
9fn bar() anyerror!void {}
10
11// run
12//
test/cases/exit.zig created+5
......@@ -0,0 +1,5 @@
1pub fn main() void {}
2
3// run
4// target=x86_64-linux,x86_64-macos,x86_64-windows,x86_64-plan9
5//
test/cases/function_pointers.zig created+30
......@@ -0,0 +1,30 @@
1const std = @import("std");
2
3const PrintFn = *const fn () void;
4
5pub fn main() void {
6 var printFn: PrintFn = stopSayingThat;
7 var i: u32 = 0;
8 while (i < 4) : (i += 1) printFn();
9
10 printFn = moveEveryZig;
11 printFn();
12}
13
14fn stopSayingThat() void {
15 _ = std.os.write(1, "Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n") catch {};
16}
17
18fn moveEveryZig() void {
19 _ = std.os.write(1, "All your codebase are belong to us\n") catch {};
20}
21
22// run
23// target=x86_64-macos
24//
25// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
26// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
27// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
28// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
29// All your codebase are belong to us
30//
test/cases/hello_world_with_updates.0.zig created+6
......@@ -0,0 +1,6 @@
1// error
2// output_mode=Exe
3// target=x86_64-linux,x86_64-macos
4// link_libc=true
5//
6// :?:?: error: root struct of file 'tmp' has no member named 'main'
test/cases/hello_world_with_updates.1.zig created+6
......@@ -0,0 +1,6 @@
1pub export fn main() noreturn {}
2
3// error
4//
5// :1:22: error: function declared 'noreturn' implicitly returns
6// :1:32: note: control flow reaches end of body here
test/cases/hello_world_with_updates.2.zig created+19
......@@ -0,0 +1,19 @@
1extern "c" fn write(c_int, usize, usize) usize;
2extern "c" fn exit(c_int) noreturn;
3
4pub export fn main() noreturn {
5 print();
6
7 exit(0);
8}
9
10fn print() void {
11 const msg = @ptrToInt("Hello, World!\n");
12 const len = 14;
13 _ = write(1, msg, len);
14}
15
16// run
17//
18// Hello, World!
19//
test/cases/hello_world_with_updates.3.zig created+16
......@@ -0,0 +1,16 @@
1extern "c" fn write(c_int, usize, usize) usize;
2
3pub fn main() void {
4 print();
5}
6
7fn print() void {
8 const msg = @ptrToInt("Hello, World!\n");
9 const len = 14;
10 _ = write(1, msg, len);
11}
12
13// run
14//
15// Hello, World!
16//
test/cases/hello_world_with_updates.4.zig created+22
......@@ -0,0 +1,22 @@
1extern "c" fn write(c_int, usize, usize) usize;
2
3pub fn main() void {
4 print();
5 print();
6 print();
7 print();
8}
9
10fn print() void {
11 const msg = @ptrToInt("Hello, World!\n");
12 const len = 14;
13 _ = write(1, msg, len);
14}
15
16// run
17//
18// Hello, World!
19// Hello, World!
20// Hello, World!
21// Hello, World!
22//
test/cases/hello_world_with_updates.5.zig created+16
......@@ -0,0 +1,16 @@
1extern "c" fn write(c_int, usize, usize) usize;
2
3pub fn main() void {
4 print();
5}
6
7fn print() void {
8 const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
9 const len = 104;
10 _ = write(1, msg, len);
11}
12
13// run
14//
15// What is up? This is a longer message that will force the data to be relocated in virtual address space.
16//
test/cases/hello_world_with_updates.6.zig created+20
......@@ -0,0 +1,20 @@
1const builtin = @import("builtin");
2
3extern "c" fn write(c_int, usize, usize) usize;
4
5pub fn main() void {
6 print();
7 print();
8}
9
10fn print() void {
11 const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
12 const len = 104;
13 _ = write(1, msg, len);
14}
15
16// run
17//
18// What is up? This is a longer message that will force the data to be relocated in virtual address space.
19// What is up? This is a longer message that will force the data to be relocated in virtual address space.
20//
test/cases/large_add_function.zig+3-1
......@@ -33,6 +33,8 @@ fn assert(ok: bool) void {
3333 if (!ok) unreachable;
3434}
3535
36// TODO: enable this for native backend
37
3638// run
39// backend=llvm
3740// target=aarch64-linux,aarch64-macos
38//
test/cases/locals.0.zig created+14
......@@ -0,0 +1,14 @@
1pub fn main() void {
2 var i: u8 = 5;
3 var y: f32 = 42.0;
4 var x: u8 = 10;
5 if (false) {
6 y;
7 x;
8 }
9 if (i != 5) unreachable;
10}
11
12// run
13// target=wasm32-wasi
14//
test/cases/locals.1.zig created+17
......@@ -0,0 +1,17 @@
1pub fn main() void {
2 var i: u8 = 5;
3 var y: f32 = 42.0;
4 _ = y;
5 var x: u8 = 10;
6 foo(i, x);
7 i = x;
8 if (i != 10) unreachable;
9}
10fn foo(x: u8, y: u8) void {
11 _ = y;
12 var i: u8 = 10;
13 i = x;
14}
15
16// run
17//
test/cases/only_1_function_and_it_gets_updated.0.zig created+7
......@@ -0,0 +1,7 @@
1pub export fn _start() noreturn {
2 while (true) {}
3}
4
5// run
6// target=x86_64-linux,x86_64-macos
7//
test/cases/only_1_function_and_it_gets_updated.1.zig created+8
......@@ -0,0 +1,8 @@
1pub export fn _start() noreturn {
2 var dummy: u32 = 10;
3 _ = dummy;
4 while (true) {}
5}
6
7// run
8//
test/cases/optionals.0.zig created+12
......@@ -0,0 +1,12 @@
1pub fn main() u8 {
2 var x: ?u8 = 5;
3 var y: u8 = 0;
4 if (x) |val| {
5 y = val;
6 }
7 return y - 5;
8}
9
10// run
11// target=wasm32-wasi
12//
test/cases/optionals.1.zig created+11
......@@ -0,0 +1,11 @@
1pub fn main() u8 {
2 var x: ?u8 = null;
3 var y: u8 = 0;
4 if (x) |val| {
5 y = val;
6 }
7 return y;
8}
9
10// run
11//
test/cases/optionals.2.zig created+7
......@@ -0,0 +1,7 @@
1pub fn main() u8 {
2 var x: ?u8 = 5;
3 return x.? - 5;
4}
5
6// run
7//
test/cases/optionals.3.zig created+8
......@@ -0,0 +1,8 @@
1pub fn main() u8 {
2 var x: u8 = 5;
3 var y: ?u8 = x;
4 return y.? - 5;
5}
6
7// run
8//
test/cases/optionals.4.zig created+13
......@@ -0,0 +1,13 @@
1pub fn main() u8 {
2 var val: ?u8 = 5;
3 while (val) |*v| {
4 v.* -= 1;
5 if (v.* == 2) {
6 val = null;
7 }
8 }
9 return 0;
10}
11
12// run
13//
test/cases/parameters_and_return_values.0.zig created+20
......@@ -0,0 +1,20 @@
1const std = @import("std");
2
3pub fn main() void {
4 print(id(14));
5}
6
7fn id(x: u32) u32 {
8 return x;
9}
10
11fn print(len: u32) void {
12 const str = "Hello, World!\n";
13 _ = std.os.write(1, str[0..len]) catch {};
14}
15
16// run
17// target=x86_64-macos
18//
19// Hello, World!
20//
test/cases/parameters_and_return_values.1.zig created+14
......@@ -0,0 +1,14 @@
1pub fn main() void {
2 assert(add(1, 2, 3, 4, 5, 6) == 21);
3}
4
5fn add(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32) u32 {
6 return a + b + c + d + e + f;
7}
8
9pub fn assert(ok: bool) void {
10 if (!ok) unreachable; // assertion failure
11}
12
13// run
14//
test/cases/plan9/exit.zig deleted-5
......@@ -1,5 +0,0 @@
1pub fn main() void {}
2
3// run
4// target=x86_64-plan9
5//
test/cases/plan9/hello_world_with_updates.0.zig deleted-28
......@@ -1,28 +0,0 @@
1pub fn main() void {
2 const str = "Hello World!\n";
3 asm volatile (
4 \\push $0
5 \\push %%r10
6 \\push %%r11
7 \\push $1
8 \\push $0
9 \\syscall
10 \\pop %%r11
11 \\pop %%r11
12 \\pop %%r11
13 \\pop %%r11
14 \\pop %%r11
15 :
16 // pwrite
17 : [syscall_number] "{rbp}" (51),
18 [hey] "{r11}" (@ptrToInt(str)),
19 [strlen] "{r10}" (str.len),
20 : "rcx", "rbp", "r11", "memory"
21 );
22}
23
24// run
25// target=x86_64-plan9
26//
27// Hello World
28//
test/cases/plan9/hello_world_with_updates.1.zig deleted-11
......@@ -1,11 +0,0 @@
1const std = @import("std");
2pub fn main() void {
3 const str = "Hello World!\n";
4 _ = std.os.plan9.pwrite(1, str, str.len, 0);
5}
6
7// run
8// target=x86_64-plan9
9//
10// Hello World
11//
test/cases/pointers.0.zig created+14
......@@ -0,0 +1,14 @@
1pub fn main() u8 {
2 var x: u8 = 0;
3
4 foo(&x);
5 return x - 2;
6}
7
8fn foo(x: *u8) void {
9 x.* = 2;
10}
11
12// run
13// target=wasm32-wasi
14//
test/cases/pointers.1.zig created+18
......@@ -0,0 +1,18 @@
1pub fn main() u8 {
2 var x: u8 = 0;
3
4 foo(&x);
5 bar(&x);
6 return x - 4;
7}
8
9fn foo(x: *u8) void {
10 x.* = 2;
11}
12
13fn bar(x: *u8) void {
14 x.* += 2;
15}
16
17// run
18//
test/cases/print_u32s.zig created+28
......@@ -0,0 +1,28 @@
1const std = @import("std");
2
3pub fn main() void {
4 printNumberHex(0x00000000);
5 printNumberHex(0xaaaaaaaa);
6 printNumberHex(0xdeadbeef);
7 printNumberHex(0x31415926);
8}
9
10fn printNumberHex(x: u32) void {
11 const digit_chars = "0123456789abcdef";
12 var i: u5 = 28;
13 while (true) : (i -= 4) {
14 const digit = (x >> i) & 0xf;
15 _ = std.os.write(1, &.{digit_chars[digit]}) catch {};
16 if (i == 0) break;
17 }
18 _ = std.os.write(1, "\n") catch {};
19}
20
21// run
22// target=x86_64-macos
23//
24// 00000000
25// aaaaaaaa
26// deadbeef
27// 31415926
28//
test/cases/recursive_fibonacci.zig+1-1
......@@ -20,5 +20,5 @@ fn assert(ok: bool) void {
2020}
2121
2222// run
23// target=x86_64-linux,x86_64-macos,arm-linux,wasm32-wasi
23// target=x86_64-linux,x86_64-macos,wasm32-wasi
2424//
test/cases/spilling_registers.0.zig created+38
......@@ -0,0 +1,38 @@
1pub fn main() void {
2 assert(add(3, 4) == 791);
3}
4
5fn add(a: u32, b: u32) u32 {
6 const x: u32 = blk: {
7 const c = a + b; // 7
8 const d = a + c; // 10
9 const e = d + b; // 14
10 const f = d + e; // 24
11 const g = e + f; // 38
12 const h = f + g; // 62
13 const i = g + h; // 100
14 const j = i + d; // 110
15 const k = i + j; // 210
16 const l = k + c; // 217
17 const m = l + d; // 227
18 const n = m + e; // 241
19 const o = n + f; // 265
20 const p = o + g; // 303
21 const q = p + h; // 365
22 const r = q + i; // 465
23 const s = r + j; // 575
24 const t = s + k; // 785
25 break :blk t;
26 };
27 const y = x + a; // 788
28 const z = y + a; // 791
29 return z;
30}
31
32fn assert(ok: bool) void {
33 if (!ok) unreachable;
34}
35
36// run
37// target=x86_64-linux,x86_64-macos
38//
test/cases/spilling_registers.1.zig created+37
......@@ -0,0 +1,37 @@
1pub fn main() void {
2 assert(addMul(3, 4) == 357747496);
3}
4
5fn addMul(a: u32, b: u32) u32 {
6 const x: u32 = blk: {
7 const c = a + b; // 7
8 const d = a + c; // 10
9 const e = d + b; // 14
10 const f = d + e; // 24
11 const g = e + f; // 38
12 const h = f + g; // 62
13 const i = g + h; // 100
14 const j = i + d; // 110
15 const k = i + j; // 210
16 const l = k + c; // 217
17 const m = l * d; // 2170
18 const n = m + e; // 2184
19 const o = n * f; // 52416
20 const p = o + g; // 52454
21 const q = p * h; // 3252148
22 const r = q + i; // 3252248
23 const s = r * j; // 357747280
24 const t = s + k; // 357747490
25 break :blk t;
26 };
27 const y = x + a; // 357747493
28 const z = y + a; // 357747496
29 return z;
30}
31
32fn assert(ok: bool) void {
33 if (!ok) unreachable;
34}
35
36// run
37//
test/cases/structs.0.zig created+10
......@@ -0,0 +1,10 @@
1const Example = struct { x: u8 };
2
3pub fn main() u8 {
4 var example: Example = .{ .x = 5 };
5 return example.x - 5;
6}
7
8// run
9// target=wasm32-wasi
10//
test/cases/structs.1.zig created+10
......@@ -0,0 +1,10 @@
1const Example = struct { x: u8 };
2
3pub fn main() u8 {
4 var example: Example = .{ .x = 5 };
5 example.x = 10;
6 return example.x - 10;
7}
8
9// run
10//
test/cases/structs.2.zig created+9
......@@ -0,0 +1,9 @@
1const Example = struct { x: u8, y: u8 };
2
3pub fn main() u8 {
4 var example: Example = .{ .x = 5, .y = 10 };
5 return example.y + example.x - 15;
6}
7
8// run
9//
test/cases/structs.3.zig created+12
......@@ -0,0 +1,12 @@
1const Example = struct { x: u8, y: u8 };
2
3pub fn main() u8 {
4 var example: Example = .{ .x = 5, .y = 10 };
5 var example2: Example = .{ .x = 10, .y = 20 };
6
7 example = example2;
8 return example.y + example.x - 30;
9}
10
11// run
12//
test/cases/structs.4.zig created+11
......@@ -0,0 +1,11 @@
1const Example = struct { x: u8, y: u8 };
2
3pub fn main() u8 {
4 var example: Example = .{ .x = 5, .y = 10 };
5
6 example = .{ .x = 10, .y = 20 };
7 return example.y + example.x - 30;
8}
9
10// run
11//
test/cases/switch.0.zig created+15
......@@ -0,0 +1,15 @@
1pub fn main() u8 {
2 var val: u8 = 1;
3 var a: u8 = switch (val) {
4 0, 1 => 2,
5 2 => 3,
6 3 => 4,
7 else => 5,
8 };
9
10 return a - 2;
11}
12
13// run
14// target=wasm32-wasi
15//
test/cases/switch.1.zig created+14
......@@ -0,0 +1,14 @@
1pub fn main() u8 {
2 var val: u8 = 2;
3 var a: u8 = switch (val) {
4 0, 1 => 2,
5 2 => 3,
6 3 => 4,
7 else => 5,
8 };
9
10 return a - 3;
11}
12
13// run
14//
test/cases/switch.2.zig created+14
......@@ -0,0 +1,14 @@
1pub fn main() u8 {
2 var val: u8 = 10;
3 var a: u8 = switch (val) {
4 0, 1 => 2,
5 2 => 3,
6 3 => 4,
7 else => 5,
8 };
9
10 return a - 5;
11}
12
13// run
14//
test/cases/switch.3.zig created+15
......@@ -0,0 +1,15 @@
1const MyEnum = enum { One, Two, Three };
2
3pub fn main() u8 {
4 var val: MyEnum = .Two;
5 var a: u8 = switch (val) {
6 .One => 1,
7 .Two => 2,
8 .Three => 3,
9 };
10
11 return a - 2;
12}
13
14// run
15//
test/cases/wasm-wasi/conditions.0.zig deleted-11
......@@ -1,11 +0,0 @@
1pub fn main() u8 {
2 var i: u8 = 5;
3 if (i > @as(u8, 4)) {
4 i += 10;
5 }
6 return i - 15;
7}
8
9// run
10// target=wasm32-wasi
11//
test/cases/wasm-wasi/conditions.1.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() u8 {
2 var i: u8 = 5;
3 if (i < @as(u8, 4)) {
4 i += 10;
5 } else {
6 i = 2;
7 }
8 return i - 2;
9}
10
11// run
12//
test/cases/wasm-wasi/conditions.2.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() u8 {
2 var i: u8 = 5;
3 if (i < @as(u8, 4)) {
4 i += 10;
5 } else if (i == @as(u8, 5)) {
6 i = 20;
7 }
8 return i - 20;
9}
10
11// run
12//
test/cases/wasm-wasi/conditions.3.zig deleted-16
......@@ -1,16 +0,0 @@
1pub fn main() u8 {
2 var i: u8 = 11;
3 if (i < @as(u8, 4)) {
4 i += 10;
5 } else {
6 if (i > @as(u8, 10)) {
7 i += 20;
8 } else {
9 i = 20;
10 }
11 }
12 return i - 31;
13}
14
15// run
16//
test/cases/wasm-wasi/conditions.4.zig deleted-15
......@@ -1,15 +0,0 @@
1pub fn main() void {
2 assert(foo(true) != @as(i32, 30));
3}
4
5fn assert(ok: bool) void {
6 if (!ok) unreachable;
7}
8
9fn foo(ok: bool) i32 {
10 const x = if (ok) @as(i32, 20) else @as(i32, 10);
11 return x;
12}
13
14// run
15//
test/cases/wasm-wasi/conditions.5.zig deleted-20
......@@ -1,20 +0,0 @@
1pub fn main() void {
2 assert(foo(false) == @as(i32, 20));
3 assert(foo(true) == @as(i32, 30));
4}
5
6fn assert(ok: bool) void {
7 if (!ok) unreachable;
8}
9
10fn foo(ok: bool) i32 {
11 const val: i32 = blk: {
12 var x: i32 = 1;
13 if (!ok) break :blk x + @as(i32, 9);
14 break :blk x + @as(i32, 19);
15 };
16 return val + 10;
17}
18
19// run
20//
test/cases/wasm-wasi/error_unions.0.zig deleted-15
......@@ -1,15 +0,0 @@
1pub fn main() void {
2 var e1 = error.Foo;
3 var e2 = error.Bar;
4 assert(e1 != e2);
5 assert(e1 == error.Foo);
6 assert(e2 == error.Bar);
7}
8
9fn assert(b: bool) void {
10 if (!b) unreachable;
11}
12
13// run
14// target=wasm32-wasi
15//
test/cases/wasm-wasi/error_unions.1.zig deleted-8
......@@ -1,8 +0,0 @@
1pub fn main() u8 {
2 var e: anyerror!u8 = 5;
3 const i = e catch 10;
4 return i - 5;
5}
6
7// run
8//
test/cases/wasm-wasi/error_unions.2.zig deleted-8
......@@ -1,8 +0,0 @@
1pub fn main() u8 {
2 var e: anyerror!u8 = error.Foo;
3 const i = e catch 10;
4 return i - 10;
5}
6
7// run
8//
test/cases/wasm-wasi/error_unions.3.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() u8 {
2 var e = foo();
3 const i = e catch 69;
4 return i - 5;
5}
6
7fn foo() anyerror!u8 {
8 return 5;
9}
10
11// run
12//
test/cases/wasm-wasi/error_unions.4.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() u8 {
2 var e = foo();
3 const i = e catch 69;
4 return i - 69;
5}
6
7fn foo() anyerror!u8 {
8 return error.Bruh;
9}
10
11// run
12//
test/cases/wasm-wasi/error_unions.5.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() u8 {
2 var e = foo();
3 const i = e catch 42;
4 return i - 42;
5}
6
7fn foo() anyerror!u8 {
8 return error.Dab;
9}
10
11// run
12//
test/cases/wasm-wasi/locals.0.zig deleted-14
......@@ -1,14 +0,0 @@
1pub fn main() void {
2 var i: u8 = 5;
3 var y: f32 = 42.0;
4 var x: u8 = 10;
5 if (false) {
6 y;
7 x;
8 }
9 if (i != 5) unreachable;
10}
11
12// run
13// target=wasm32-wasi
14//
test/cases/wasm-wasi/locals.1.zig deleted-17
......@@ -1,17 +0,0 @@
1pub fn main() void {
2 var i: u8 = 5;
3 var y: f32 = 42.0;
4 _ = y;
5 var x: u8 = 10;
6 foo(i, x);
7 i = x;
8 if (i != 10) unreachable;
9}
10fn foo(x: u8, y: u8) void {
11 _ = y;
12 var i: u8 = 10;
13 i = x;
14}
15
16// run
17//
test/cases/wasm-wasi/optionals.0.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() u8 {
2 var x: ?u8 = 5;
3 var y: u8 = 0;
4 if (x) |val| {
5 y = val;
6 }
7 return y - 5;
8}
9
10// run
11// target=wasm32-wasi
12//
test/cases/wasm-wasi/optionals.1.zig deleted-11
......@@ -1,11 +0,0 @@
1pub fn main() u8 {
2 var x: ?u8 = null;
3 var y: u8 = 0;
4 if (x) |val| {
5 y = val;
6 }
7 return y;
8}
9
10// run
11//
test/cases/wasm-wasi/optionals.2.zig deleted-7
......@@ -1,7 +0,0 @@
1pub fn main() u8 {
2 var x: ?u8 = 5;
3 return x.? - 5;
4}
5
6// run
7//
test/cases/wasm-wasi/optionals.3.zig deleted-8
......@@ -1,8 +0,0 @@
1pub fn main() u8 {
2 var x: u8 = 5;
3 var y: ?u8 = x;
4 return y.? - 5;
5}
6
7// run
8//
test/cases/wasm-wasi/optionals.4.zig deleted-13
......@@ -1,13 +0,0 @@
1pub fn main() u8 {
2 var val: ?u8 = 5;
3 while (val) |*v| {
4 v.* -= 1;
5 if (v.* == 2) {
6 val = null;
7 }
8 }
9 return 0;
10}
11
12// run
13//
test/cases/wasm-wasi/pointers.0.zig deleted-14
......@@ -1,14 +0,0 @@
1pub fn main() u8 {
2 var x: u8 = 0;
3
4 foo(&x);
5 return x - 2;
6}
7
8fn foo(x: *u8) void {
9 x.* = 2;
10}
11
12// run
13// target=wasm32-wasi
14//
test/cases/wasm-wasi/pointers.1.zig deleted-18
......@@ -1,18 +0,0 @@
1pub fn main() u8 {
2 var x: u8 = 0;
3
4 foo(&x);
5 bar(&x);
6 return x - 4;
7}
8
9fn foo(x: *u8) void {
10 x.* = 2;
11}
12
13fn bar(x: *u8) void {
14 x.* += 2;
15}
16
17// run
18//
test/cases/wasm-wasi/structs.0.zig deleted-10
......@@ -1,10 +0,0 @@
1const Example = struct { x: u8 };
2
3pub fn main() u8 {
4 var example: Example = .{ .x = 5 };
5 return example.x - 5;
6}
7
8// run
9// target=wasm32-wasi
10//
test/cases/wasm-wasi/structs.1.zig deleted-10
......@@ -1,10 +0,0 @@
1const Example = struct { x: u8 };
2
3pub fn main() u8 {
4 var example: Example = .{ .x = 5 };
5 example.x = 10;
6 return example.x - 10;
7}
8
9// run
10//
test/cases/wasm-wasi/structs.2.zig deleted-9
......@@ -1,9 +0,0 @@
1const Example = struct { x: u8, y: u8 };
2
3pub fn main() u8 {
4 var example: Example = .{ .x = 5, .y = 10 };
5 return example.y + example.x - 15;
6}
7
8// run
9//
test/cases/wasm-wasi/structs.3.zig deleted-12
......@@ -1,12 +0,0 @@
1const Example = struct { x: u8, y: u8 };
2
3pub fn main() u8 {
4 var example: Example = .{ .x = 5, .y = 10 };
5 var example2: Example = .{ .x = 10, .y = 20 };
6
7 example = example2;
8 return example.y + example.x - 30;
9}
10
11// run
12//
test/cases/wasm-wasi/structs.4.zig deleted-11
......@@ -1,11 +0,0 @@
1const Example = struct { x: u8, y: u8 };
2
3pub fn main() u8 {
4 var example: Example = .{ .x = 5, .y = 10 };
5
6 example = .{ .x = 10, .y = 20 };
7 return example.y + example.x - 30;
8}
9
10// run
11//
test/cases/wasm-wasi/switch.0.zig deleted-15
......@@ -1,15 +0,0 @@
1pub fn main() u8 {
2 var val: u8 = 1;
3 var a: u8 = switch (val) {
4 0, 1 => 2,
5 2 => 3,
6 3 => 4,
7 else => 5,
8 };
9
10 return a - 2;
11}
12
13// run
14// target=wasm32-wasi
15//
test/cases/wasm-wasi/switch.1.zig deleted-14
......@@ -1,14 +0,0 @@
1pub fn main() u8 {
2 var val: u8 = 2;
3 var a: u8 = switch (val) {
4 0, 1 => 2,
5 2 => 3,
6 3 => 4,
7 else => 5,
8 };
9
10 return a - 3;
11}
12
13// run
14//
test/cases/wasm-wasi/switch.2.zig deleted-14
......@@ -1,14 +0,0 @@
1pub fn main() u8 {
2 var val: u8 = 10;
3 var a: u8 = switch (val) {
4 0, 1 => 2,
5 2 => 3,
6 3 => 4,
7 else => 5,
8 };
9
10 return a - 5;
11}
12
13// run
14//
test/cases/wasm-wasi/switch.3.zig deleted-15
......@@ -1,15 +0,0 @@
1const MyEnum = enum { One, Two, Three };
2
3pub fn main() u8 {
4 var val: MyEnum = .Two;
5 var a: u8 = switch (val) {
6 .One => 1,
7 .Two => 2,
8 .Three => 3,
9 };
10
11 return a - 2;
12}
13
14// run
15//
test/cases/wasm-wasi/while_loops.0.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() u8 {
2 var i: u8 = 0;
3 while (i < @as(u8, 5)) {
4 i += 1;
5 }
6
7 return i - 5;
8}
9
10// run
11// target=wasm32-wasi
12//
test/cases/wasm-wasi/while_loops.1.zig deleted-11
......@@ -1,11 +0,0 @@
1pub fn main() u8 {
2 var i: u8 = 0;
3 while (i < @as(u8, 10)) {
4 var x: u8 = 1;
5 i += x;
6 }
7 return i - 10;
8}
9
10// run
11//
test/cases/wasm-wasi/while_loops.2.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() u8 {
2 var i: u8 = 0;
3 while (i < @as(u8, 10)) {
4 var x: u8 = 1;
5 i += x;
6 if (i == @as(u8, 5)) break;
7 }
8 return i - 5;
9}
10
11// run
12//
test/cases/while_loops.0.zig created+12
......@@ -0,0 +1,12 @@
1pub fn main() u8 {
2 var i: u8 = 0;
3 while (i < @as(u8, 5)) {
4 i += 1;
5 }
6
7 return i - 5;
8}
9
10// run
11// target=wasm32-wasi
12//
test/cases/while_loops.1.zig created+11
......@@ -0,0 +1,11 @@
1pub fn main() u8 {
2 var i: u8 = 0;
3 while (i < @as(u8, 10)) {
4 var x: u8 = 1;
5 i += x;
6 }
7 return i - 10;
8}
9
10// run
11//
test/cases/while_loops.2.zig created+12
......@@ -0,0 +1,12 @@
1pub fn main() u8 {
2 var i: u8 = 0;
3 while (i < @as(u8, 10)) {
4 var x: u8 = 1;
5 i += x;
6 if (i == @as(u8, 5)) break;
7 }
8 return i - 5;
9}
10
11// run
12//
test/cases/x86_64-linux/assert_function.0.zig deleted-15
......@@ -1,15 +0,0 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 assert(a + b == 7);
7}
8
9pub fn assert(ok: bool) void {
10 if (!ok) unreachable; // assertion failure
11}
12
13// run
14// target=x86_64-linux
15//
test/cases/x86_64-linux/assert_function.1.zig deleted-17
......@@ -1,17 +0,0 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 const c = a + b; // 7
7 const d = a + c; // 10
8 const e = d + b; // 14
9 assert(e == 14);
10}
11
12pub fn assert(ok: bool) void {
13 if (!ok) unreachable; // assertion failure
14}
15
16// run
17//
test/cases/x86_64-linux/assert_function.10.zig deleted-27
......@@ -1,27 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 116);
3}
4
5fn add(a: u32, b: u32) u32 {
6 const x: u32 = blk: {
7 const c = a + b; // 7
8 const d = a + c; // 10
9 const e = d + b; // 14
10 const f = d + e; // 24
11 const g = e + f; // 38
12 const h = f + g; // 62
13 const i = g + h; // 100
14 const j = i + d; // 110
15 break :blk j;
16 };
17 const y = x + a; // 113
18 const z = y + a; // 116
19 return z;
20}
21
22pub fn assert(ok: bool) void {
23 if (!ok) unreachable; // assertion failure
24}
25
26// run
27//
test/cases/x86_64-linux/assert_function.11.zig deleted-66
......@@ -1,66 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 1221);
3 assert(mul(3, 4) == 21609);
4}
5
6fn add(a: u32, b: u32) u32 {
7 const x: u32 = blk: {
8 const c = a + b; // 7
9 const d = a + c; // 10
10 const e = d + b; // 14
11 const f = d + e; // 24
12 const g = e + f; // 38
13 const h = f + g; // 62
14 const i = g + h; // 100
15 const j = i + d; // 110
16 const k = i + j; // 210
17 const l = j + k; // 320
18 const m = l + c; // 327
19 const n = m + d; // 337
20 const o = n + e; // 351
21 const p = o + f; // 375
22 const q = p + g; // 413
23 const r = q + h; // 475
24 const s = r + i; // 575
25 const t = s + j; // 685
26 const u = t + k; // 895
27 const v = u + l; // 1215
28 break :blk v;
29 };
30 const y = x + a; // 1218
31 const z = y + a; // 1221
32 return z;
33}
34
35fn mul(a: u32, b: u32) u32 {
36 const x: u32 = blk: {
37 const c = a * a * a * a; // 81
38 const d = a * a * a * b; // 108
39 const e = a * a * b * a; // 108
40 const f = a * a * b * b; // 144
41 const g = a * b * a * a; // 108
42 const h = a * b * a * b; // 144
43 const i = a * b * b * a; // 144
44 const j = a * b * b * b; // 192
45 const k = b * a * a * a; // 108
46 const l = b * a * a * b; // 144
47 const m = b * a * b * a; // 144
48 const n = b * a * b * b; // 192
49 const o = b * b * a * a; // 144
50 const p = b * b * a * b; // 192
51 const q = b * b * b * a; // 192
52 const r = b * b * b * b; // 256
53 const s = c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r; // 2401
54 break :blk s;
55 };
56 const y = x * a; // 7203
57 const z = y * a; // 21609
58 return z;
59}
60
61pub fn assert(ok: bool) void {
62 if (!ok) unreachable; // assertion failure
63}
64
65// run
66//
test/cases/x86_64-linux/assert_function.12.zig deleted-47
......@@ -1,47 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 791);
3 assert(add(4, 3) == 79);
4}
5
6fn add(a: u32, b: u32) u32 {
7 const x: u32 = if (a < b) blk: {
8 const c = a + b; // 7
9 const d = a + c; // 10
10 const e = d + b; // 14
11 const f = d + e; // 24
12 const g = e + f; // 38
13 const h = f + g; // 62
14 const i = g + h; // 100
15 const j = i + d; // 110
16 const k = i + j; // 210
17 const l = k + c; // 217
18 const m = l + d; // 227
19 const n = m + e; // 241
20 const o = n + f; // 265
21 const p = o + g; // 303
22 const q = p + h; // 365
23 const r = q + i; // 465
24 const s = r + j; // 575
25 const t = s + k; // 785
26 break :blk t;
27 } else blk: {
28 const t = b + b + a; // 10
29 const c = a + t; // 14
30 const d = c + t; // 24
31 const e = d + t; // 34
32 const f = e + t; // 44
33 const g = f + t; // 54
34 const h = c + g; // 68
35 break :blk h + b; // 71
36 };
37 const y = x + a; // 788, 75
38 const z = y + a; // 791, 79
39 return z;
40}
41
42pub fn assert(ok: bool) void {
43 if (!ok) unreachable; // assertion failure
44}
45
46// run
47//
test/cases/x86_64-linux/assert_function.13.zig deleted-19
......@@ -1,19 +0,0 @@
1pub fn main() void {
2 const ignore =
3 \\ cool thx
4 \\
5 ;
6 _ = ignore;
7 add('ぁ', '\x03');
8}
9
10fn add(a: u32, b: u32) void {
11 assert(a + b == 12356);
12}
13
14pub fn assert(ok: bool) void {
15 if (!ok) unreachable; // assertion failure
16}
17
18// run
19//
test/cases/x86_64-linux/assert_function.14.zig deleted-17
......@@ -1,17 +0,0 @@
1pub fn main() void {
2 add(aa, bb);
3}
4
5const aa = 'ぁ';
6const bb = '\x03';
7
8fn add(a: u32, b: u32) void {
9 assert(a + b == 12356);
10}
11
12pub fn assert(ok: bool) void {
13 if (!ok) unreachable; // assertion failure
14}
15
16// run
17//
test/cases/x86_64-linux/assert_function.15.zig deleted-10
......@@ -1,10 +0,0 @@
1pub fn main() void {
2 assert("hello"[0] == 'h');
3}
4
5pub fn assert(ok: bool) void {
6 if (!ok) unreachable; // assertion failure
7}
8
9// run
10//
test/cases/x86_64-linux/assert_function.16.zig deleted-11
......@@ -1,11 +0,0 @@
1const hello = "hello".*;
2pub fn main() void {
3 assert(hello[1] == 'e');
4}
5
6pub fn assert(ok: bool) void {
7 if (!ok) unreachable; // assertion failure
8}
9
10// run
11//
test/cases/x86_64-linux/assert_function.17.zig deleted-11
......@@ -1,11 +0,0 @@
1pub fn main() void {
2 var i: u64 = 0xFFEEDDCCBBAA9988;
3 assert(i == 0xFFEEDDCCBBAA9988);
4}
5
6pub fn assert(ok: bool) void {
7 if (!ok) unreachable; // assertion failure
8}
9
10// run
11//
test/cases/x86_64-linux/assert_function.18.zig deleted-35
......@@ -1,35 +0,0 @@
1const builtin = @import("builtin");
2
3extern "c" fn write(usize, usize, usize) usize;
4
5pub fn main() void {
6 for ("hello") |_| print();
7}
8
9fn print() void {
10 switch (builtin.os.tag) {
11 .linux => {
12 asm volatile ("syscall"
13 :
14 : [number] "{rax}" (1),
15 [arg1] "{rdi}" (1),
16 [arg2] "{rsi}" (@ptrToInt("hello\n")),
17 [arg3] "{rdx}" (6),
18 : "rcx", "r11", "memory"
19 );
20 },
21 .macos => {
22 _ = write(1, @ptrToInt("hello\n"), 6);
23 },
24 else => unreachable,
25 }
26}
27
28// run
29//
30// hello
31// hello
32// hello
33// hello
34// hello
35//
test/cases/x86_64-linux/assert_function.2.zig deleted-21
......@@ -1,21 +0,0 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 const c = a + b; // 7
7 const d = a + c; // 10
8 const e = d + b; // 14
9 const f = d + e; // 24
10 const g = e + f; // 38
11 const h = f + g; // 62
12 const i = g + h; // 100
13 assert(i == 100);
14}
15
16pub fn assert(ok: bool) void {
17 if (!ok) unreachable; // assertion failure
18}
19
20// run
21//
test/cases/x86_64-linux/assert_function.3.zig deleted-22
......@@ -1,22 +0,0 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 const c = a + b; // 7
7 const d = a + c; // 10
8 const e = d + b; // 14
9 const f = d + e; // 24
10 const g = e + f; // 38
11 const h = f + g; // 62
12 const i = g + h; // 100
13 const j = i + d; // 110
14 assert(j == 110);
15}
16
17pub fn assert(ok: bool) void {
18 if (!ok) unreachable; // assertion failure
19}
20
21// run
22//
test/cases/x86_64-linux/assert_function.4.zig deleted-15
......@@ -1,15 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 7);
3 assert(add(20, 10) == 30);
4}
5
6fn add(a: u32, b: u32) u32 {
7 return a + b;
8}
9
10pub fn assert(ok: bool) void {
11 if (!ok) unreachable; // assertion failure
12}
13
14// run
15//
test/cases/x86_64-linux/assert_function.5.zig deleted-19
......@@ -1,19 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 7);
3 assert(add(20, 10) == 30);
4}
5
6fn add(a: u32, b: u32) u32 {
7 var x: u32 = undefined;
8 x = 0;
9 x += a;
10 x += b;
11 return x;
12}
13
14pub fn assert(ok: bool) void {
15 if (!ok) unreachable; // assertion failure
16}
17
18// run
19//
test/cases/x86_64-linux/assert_function.6.zig deleted-9
......@@ -1,9 +0,0 @@
1pub fn main() void {
2 const a: u32 = 2;
3 const b: ?u32 = a;
4 const c = b.?;
5 if (c != 2) unreachable;
6}
7
8// run
9//
test/cases/x86_64-linux/assert_function.7.zig deleted-28
......@@ -1,28 +0,0 @@
1pub fn main() void {
2 var i: u32 = 0;
3 while (i < 4) : (i += 1) print();
4 assert(i == 4);
5}
6
7fn print() void {
8 asm volatile ("syscall"
9 :
10 : [number] "{rax}" (1),
11 [arg1] "{rdi}" (1),
12 [arg2] "{rsi}" (@ptrToInt("hello\n")),
13 [arg3] "{rdx}" (6),
14 : "rcx", "r11", "memory"
15 );
16}
17
18pub fn assert(ok: bool) void {
19 if (!ok) unreachable; // assertion failure
20}
21
22// run
23//
24// hello
25// hello
26// hello
27// hello
28//
test/cases/x86_64-linux/assert_function.8.zig deleted-25
......@@ -1,25 +0,0 @@
1pub fn main() void {
2 var i: u32 = 0;
3 inline while (i < 4) : (i += 1) print();
4 assert(i == 4);
5}
6
7fn print() void {
8 asm volatile ("syscall"
9 :
10 : [number] "{rax}" (1),
11 [arg1] "{rdi}" (1),
12 [arg2] "{rsi}" (@ptrToInt("hello\n")),
13 [arg3] "{rdx}" (6),
14 : "rcx", "r11", "memory"
15 );
16}
17
18pub fn assert(ok: bool) void {
19 if (!ok) unreachable; // assertion failure
20}
21
22// error
23//
24// :3:21: error: unable to resolve comptime value
25// :3:21: note: condition in comptime branch must be comptime-known
test/cases/x86_64-linux/assert_function.9.zig deleted-22
......@@ -1,22 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 20);
3}
4
5fn add(a: u32, b: u32) u32 {
6 const x: u32 = blk: {
7 const c = a + b; // 7
8 const d = a + c; // 10
9 const e = d + b; // 14
10 break :blk e;
11 };
12 const y = x + a; // 17
13 const z = y + a; // 20
14 return z;
15}
16
17pub fn assert(ok: bool) void {
18 if (!ok) unreachable; // assertion failure
19}
20
21// run
22//
test/cases/x86_64-linux/comptime_var.0.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() void {
2 var a: u32 = 0;
3 comptime var b: u32 = 0;
4 if (a == 0) b = 3;
5}
6
7// error
8// output_mode=Exe
9// target=x86_64-linux
10//
11// :4:19: error: store to comptime variable depends on runtime condition
12// :4:11: note: runtime condition here
test/cases/x86_64-linux/comptime_var.1.zig deleted-13
......@@ -1,13 +0,0 @@
1pub fn main() void {
2 var a: u32 = 0;
3 comptime var b: u32 = 0;
4 switch (a) {
5 0 => {},
6 else => b = 3,
7 }
8}
9
10// error
11//
12// :6:19: error: store to comptime variable depends on runtime condition
13// :4:13: note: runtime condition here
test/cases/x86_64-linux/comptime_var.2.zig deleted-22
......@@ -1,22 +0,0 @@
1pub fn main() void {
2 comptime var len: u32 = 5;
3 print(len);
4 len += 9;
5 print(len);
6}
7
8fn print(len: usize) void {
9 asm volatile ("syscall"
10 :
11 : [number] "{rax}" (1),
12 [arg1] "{rdi}" (1),
13 [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
14 [arg3] "{rdx}" (len),
15 : "rcx", "r11", "memory"
16 );
17}
18
19// run
20//
21// HelloHello, World!
22//
test/cases/x86_64-linux/comptime_var.3.zig deleted-10
......@@ -1,10 +0,0 @@
1comptime {
2 var x: i32 = 1;
3 x += 1;
4 if (x != 1) unreachable;
5}
6pub fn main() void {}
7
8// error
9//
10// :4:17: error: reached unreachable code
test/cases/x86_64-linux/comptime_var.4.zig deleted-9
......@@ -1,9 +0,0 @@
1pub fn main() void {
2 comptime var i: u64 = 0;
3 while (i < 5) : (i += 1) {}
4}
5
6// error
7//
8// :3:24: error: cannot store to comptime variable in non-inline loop
9// :3:5: note: non-inline loop here
test/cases/x86_64-linux/comptime_var.5.zig deleted-15
......@@ -1,15 +0,0 @@
1pub fn main() void {
2 var a: u32 = 0;
3 if (a == 0) {
4 comptime var b: u32 = 0;
5 b = 1;
6 }
7}
8comptime {
9 var x: i32 = 1;
10 x += 1;
11 if (x != 2) unreachable;
12}
13
14// run
15//
test/cases/x86_64-linux/comptime_var.6.zig deleted-20
......@@ -1,20 +0,0 @@
1pub fn main() void {
2 comptime var i: u64 = 2;
3 inline while (i < 6) : (i += 1) {
4 print(i);
5 }
6}
7fn print(len: usize) void {
8 asm volatile ("syscall"
9 :
10 : [number] "{rax}" (1),
11 [arg1] "{rdi}" (1),
12 [arg2] "{rsi}" (@ptrToInt("Hello")),
13 [arg3] "{rdx}" (len),
14 : "rcx", "r11", "memory"
15 );
16}
17
18// run
19//
20// HeHelHellHello
test/cases/x86_64-linux/hello_world_with_updates.0.zig deleted-8
......@@ -1,8 +0,0 @@
1// error
2// output_mode=Exe
3// target=x86_64-linux
4//
5// :?:?: error: root struct of file 'tmp' has no member named 'main'
6// :?:?: note: called from here
7// :?:?: note: called from here
8// :?:?: note: called from here
test/cases/x86_64-linux/hello_world_with_updates.1.zig deleted-6
......@@ -1,6 +0,0 @@
1pub export fn main() noreturn {}
2
3// error
4//
5// :1:22: error: function declared 'noreturn' implicitly returns
6// :1:32: note: control flow reaches end of body here
test/cases/x86_64-linux/hello_world_with_updates.2.zig deleted-32
......@@ -1,32 +0,0 @@
1pub export fn _start() noreturn {
2 print();
3
4 exit();
5}
6
7fn print() void {
8 asm volatile ("syscall"
9 :
10 : [number] "{rax}" (1),
11 [arg1] "{rdi}" (1),
12 [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
13 [arg3] "{rdx}" (14),
14 : "rcx", "r11", "memory"
15 );
16 return;
17}
18
19fn exit() noreturn {
20 asm volatile ("syscall"
21 :
22 : [number] "{rax}" (231),
23 [arg1] "{rdi}" (0),
24 : "rcx", "r11", "memory"
25 );
26 unreachable;
27}
28
29// run
30//
31// Hello, World!
32//
test/cases/x86_64-linux/hello_world_with_updates.3.zig deleted-20
......@@ -1,20 +0,0 @@
1pub fn main() void {
2 print();
3}
4
5fn print() void {
6 asm volatile ("syscall"
7 :
8 : [number] "{rax}" (1),
9 [arg1] "{rdi}" (1),
10 [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
11 [arg3] "{rdx}" (14),
12 : "rcx", "r11", "memory"
13 );
14 return;
15}
16
17// run
18//
19// Hello, World!
20//
test/cases/x86_64-linux/hello_world_with_updates.4.zig deleted-20
......@@ -1,20 +0,0 @@
1pub fn main() void {
2 print();
3}
4
5fn print() void {
6 asm volatile ("syscall"
7 :
8 : [number] "{rax}" (1),
9 [arg1] "{rdi}" (1),
10 [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
11 [arg3] "{rdx}" (104),
12 : "rcx", "r11", "memory"
13 );
14 return;
15}
16
17// run
18//
19// What is up? This is a longer message that will force the data to be relocated in virtual address space.
20//
test/cases/x86_64-linux/hello_world_with_updates.5.zig deleted-22
......@@ -1,22 +0,0 @@
1pub fn main() void {
2 print();
3 print();
4}
5
6fn print() void {
7 asm volatile ("syscall"
8 :
9 : [number] "{rax}" (1),
10 [arg1] "{rdi}" (1),
11 [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
12 [arg3] "{rdx}" (104),
13 : "rcx", "r11", "memory"
14 );
15 return;
16}
17
18// run
19//
20// What is up? This is a longer message that will force the data to be relocated in virtual address space.
21// What is up? This is a longer message that will force the data to be relocated in virtual address space.
22//
test/cases/x86_64-linux/only_1_function_and_it_gets_updated.0.zig deleted-13
......@@ -1,13 +0,0 @@
1pub export fn _start() noreturn {
2 asm volatile ("syscall"
3 :
4 : [number] "{rax}" (60), // exit
5 [arg1] "{rdi}" (0),
6 : "rcx", "r11", "memory"
7 );
8 unreachable;
9}
10
11// run
12// target=x86_64-linux
13//
test/cases/x86_64-linux/only_1_function_and_it_gets_updated.1.zig deleted-12
......@@ -1,12 +0,0 @@
1pub export fn _start() noreturn {
2 asm volatile ("syscall"
3 :
4 : [number] "{rax}" (231), // exit_group
5 [arg1] "{rdi}" (0),
6 : "rcx", "r11", "memory"
7 );
8 unreachable;
9}
10
11// run
12//
test/cases/x86_64-macos/assert_function.0.zig deleted-15
......@@ -1,15 +0,0 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 assert(a + b == 7);
7}
8
9pub fn assert(ok: bool) void {
10 if (!ok) unreachable; // assertion failure
11}
12
13// run
14// target=x86_64-macos
15//
test/cases/x86_64-macos/assert_function.1.zig deleted-17
......@@ -1,17 +0,0 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 const c = a + b; // 7
7 const d = a + c; // 10
8 const e = d + b; // 14
9 assert(e == 14);
10}
11
12pub fn assert(ok: bool) void {
13 if (!ok) unreachable; // assertion failure
14}
15
16// run
17//
test/cases/x86_64-macos/assert_function.10.zig deleted-27
......@@ -1,27 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 116);
3}
4
5fn add(a: u32, b: u32) u32 {
6 const x: u32 = blk: {
7 const c = a + b; // 7
8 const d = a + c; // 10
9 const e = d + b; // 14
10 const f = d + e; // 24
11 const g = e + f; // 38
12 const h = f + g; // 62
13 const i = g + h; // 100
14 const j = i + d; // 110
15 break :blk j;
16 };
17 const y = x + a; // 113
18 const z = y + a; // 116
19 return z;
20}
21
22pub fn assert(ok: bool) void {
23 if (!ok) unreachable; // assertion failure
24}
25
26// run
27//
test/cases/x86_64-macos/assert_function.11.zig deleted-66
......@@ -1,66 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 1221);
3 assert(mul(3, 4) == 21609);
4}
5
6fn add(a: u32, b: u32) u32 {
7 const x: u32 = blk: {
8 const c = a + b; // 7
9 const d = a + c; // 10
10 const e = d + b; // 14
11 const f = d + e; // 24
12 const g = e + f; // 38
13 const h = f + g; // 62
14 const i = g + h; // 100
15 const j = i + d; // 110
16 const k = i + j; // 210
17 const l = j + k; // 320
18 const m = l + c; // 327
19 const n = m + d; // 337
20 const o = n + e; // 351
21 const p = o + f; // 375
22 const q = p + g; // 413
23 const r = q + h; // 475
24 const s = r + i; // 575
25 const t = s + j; // 685
26 const u = t + k; // 895
27 const v = u + l; // 1215
28 break :blk v;
29 };
30 const y = x + a; // 1218
31 const z = y + a; // 1221
32 return z;
33}
34
35fn mul(a: u32, b: u32) u32 {
36 const x: u32 = blk: {
37 const c = a * a * a * a; // 81
38 const d = a * a * a * b; // 108
39 const e = a * a * b * a; // 108
40 const f = a * a * b * b; // 144
41 const g = a * b * a * a; // 108
42 const h = a * b * a * b; // 144
43 const i = a * b * b * a; // 144
44 const j = a * b * b * b; // 192
45 const k = b * a * a * a; // 108
46 const l = b * a * a * b; // 144
47 const m = b * a * b * a; // 144
48 const n = b * a * b * b; // 192
49 const o = b * b * a * a; // 144
50 const p = b * b * a * b; // 192
51 const q = b * b * b * a; // 192
52 const r = b * b * b * b; // 256
53 const s = c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r; // 2401
54 break :blk s;
55 };
56 const y = x * a; // 7203
57 const z = y * a; // 21609
58 return z;
59}
60
61pub fn assert(ok: bool) void {
62 if (!ok) unreachable; // assertion failure
63}
64
65// run
66//
test/cases/x86_64-macos/assert_function.12.zig deleted-47
......@@ -1,47 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 791);
3 assert(add(4, 3) == 79);
4}
5
6fn add(a: u32, b: u32) u32 {
7 const x: u32 = if (a < b) blk: {
8 const c = a + b; // 7
9 const d = a + c; // 10
10 const e = d + b; // 14
11 const f = d + e; // 24
12 const g = e + f; // 38
13 const h = f + g; // 62
14 const i = g + h; // 100
15 const j = i + d; // 110
16 const k = i + j; // 210
17 const l = k + c; // 217
18 const m = l + d; // 227
19 const n = m + e; // 241
20 const o = n + f; // 265
21 const p = o + g; // 303
22 const q = p + h; // 365
23 const r = q + i; // 465
24 const s = r + j; // 575
25 const t = s + k; // 785
26 break :blk t;
27 } else blk: {
28 const t = b + b + a; // 10
29 const c = a + t; // 14
30 const d = c + t; // 24
31 const e = d + t; // 34
32 const f = e + t; // 44
33 const g = f + t; // 54
34 const h = c + g; // 68
35 break :blk h + b; // 71
36 };
37 const y = x + a; // 788, 75
38 const z = y + a; // 791, 79
39 return z;
40}
41
42pub fn assert(ok: bool) void {
43 if (!ok) unreachable; // assertion failure
44}
45
46// run
47//
test/cases/x86_64-macos/assert_function.13.zig deleted-19
......@@ -1,19 +0,0 @@
1pub fn main() void {
2 const ignore =
3 \\ cool thx
4 \\
5 ;
6 _ = ignore;
7 add('ぁ', '\x03');
8}
9
10fn add(a: u32, b: u32) void {
11 assert(a + b == 12356);
12}
13
14pub fn assert(ok: bool) void {
15 if (!ok) unreachable; // assertion failure
16}
17
18// run
19//
test/cases/x86_64-macos/assert_function.14.zig deleted-17
......@@ -1,17 +0,0 @@
1pub fn main() void {
2 add(aa, bb);
3}
4
5const aa = 'ぁ';
6const bb = '\x03';
7
8fn add(a: u32, b: u32) void {
9 assert(a + b == 12356);
10}
11
12pub fn assert(ok: bool) void {
13 if (!ok) unreachable; // assertion failure
14}
15
16// run
17//
test/cases/x86_64-macos/assert_function.15.zig deleted-10
......@@ -1,10 +0,0 @@
1pub fn main() void {
2 assert("hello"[0] == 'h');
3}
4
5pub fn assert(ok: bool) void {
6 if (!ok) unreachable; // assertion failure
7}
8
9// run
10//
test/cases/x86_64-macos/assert_function.16.zig deleted-11
......@@ -1,11 +0,0 @@
1const hello = "hello".*;
2pub fn main() void {
3 assert(hello[1] == 'e');
4}
5
6pub fn assert(ok: bool) void {
7 if (!ok) unreachable; // assertion failure
8}
9
10// run
11//
test/cases/x86_64-macos/assert_function.17.zig deleted-11
......@@ -1,11 +0,0 @@
1pub fn main() void {
2 var i: u64 = 0xFFEEDDCCBBAA9988;
3 assert(i == 0xFFEEDDCCBBAA9988);
4}
5
6pub fn assert(ok: bool) void {
7 if (!ok) unreachable; // assertion failure
8}
9
10// run
11//
test/cases/x86_64-macos/assert_function.18.zig deleted-35
......@@ -1,35 +0,0 @@
1const builtin = @import("builtin");
2
3extern "c" fn write(usize, usize, usize) usize;
4
5pub fn main() void {
6 for ("hello") |_| print();
7}
8
9fn print() void {
10 switch (builtin.os.tag) {
11 .linux => {
12 asm volatile ("syscall"
13 :
14 : [number] "{rax}" (1),
15 [arg1] "{rdi}" (1),
16 [arg2] "{rsi}" (@ptrToInt("hello\n")),
17 [arg3] "{rdx}" (6),
18 : "rcx", "r11", "memory"
19 );
20 },
21 .macos => {
22 _ = write(1, @ptrToInt("hello\n"), 6);
23 },
24 else => unreachable,
25 }
26}
27
28// run
29//
30// hello
31// hello
32// hello
33// hello
34// hello
35//
test/cases/x86_64-macos/assert_function.2.zig deleted-21
......@@ -1,21 +0,0 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 const c = a + b; // 7
7 const d = a + c; // 10
8 const e = d + b; // 14
9 const f = d + e; // 24
10 const g = e + f; // 38
11 const h = f + g; // 62
12 const i = g + h; // 100
13 assert(i == 100);
14}
15
16pub fn assert(ok: bool) void {
17 if (!ok) unreachable; // assertion failure
18}
19
20// run
21//
test/cases/x86_64-macos/assert_function.3.zig deleted-22
......@@ -1,22 +0,0 @@
1pub fn main() void {
2 add(3, 4);
3}
4
5fn add(a: u32, b: u32) void {
6 const c = a + b; // 7
7 const d = a + c; // 10
8 const e = d + b; // 14
9 const f = d + e; // 24
10 const g = e + f; // 38
11 const h = f + g; // 62
12 const i = g + h; // 100
13 const j = i + d; // 110
14 assert(j == 110);
15}
16
17pub fn assert(ok: bool) void {
18 if (!ok) unreachable; // assertion failure
19}
20
21// run
22//
test/cases/x86_64-macos/assert_function.4.zig deleted-15
......@@ -1,15 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 7);
3 assert(add(20, 10) == 30);
4}
5
6fn add(a: u32, b: u32) u32 {
7 return a + b;
8}
9
10pub fn assert(ok: bool) void {
11 if (!ok) unreachable; // assertion failure
12}
13
14// run
15//
test/cases/x86_64-macos/assert_function.5.zig deleted-19
......@@ -1,19 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 7);
3 assert(add(20, 10) == 30);
4}
5
6fn add(a: u32, b: u32) u32 {
7 var x: u32 = undefined;
8 x = 0;
9 x += a;
10 x += b;
11 return x;
12}
13
14pub fn assert(ok: bool) void {
15 if (!ok) unreachable; // assertion failure
16}
17
18// run
19//
test/cases/x86_64-macos/assert_function.6.zig deleted-9
......@@ -1,9 +0,0 @@
1pub fn main() void {
2 const a: u32 = 2;
3 const b: ?u32 = a;
4 const c = b.?;
5 if (c != 2) unreachable;
6}
7
8// run
9//
test/cases/x86_64-macos/assert_function.7.zig deleted-23
......@@ -1,23 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 var i: u32 = 0;
5 while (i < 4) : (i += 1) print();
6 assert(i == 4);
7}
8
9fn print() void {
10 _ = write(1, @ptrToInt("hello\n"), 6);
11}
12
13pub fn assert(ok: bool) void {
14 if (!ok) unreachable; // assertion failure
15}
16
17// run
18//
19// hello
20// hello
21// hello
22// hello
23//
test/cases/x86_64-macos/assert_function.8.zig deleted-20
......@@ -1,20 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 var i: u32 = 0;
5 inline while (i < 4) : (i += 1) print();
6 assert(i == 4);
7}
8
9fn print() void {
10 _ = write(1, @ptrToInt("hello\n"), 6);
11}
12
13pub fn assert(ok: bool) void {
14 if (!ok) unreachable; // assertion failure
15}
16
17// error
18//
19// :5:21: error: unable to resolve comptime value
20// :5:21: note: condition in comptime branch must be comptime-known
test/cases/x86_64-macos/assert_function.9.zig deleted-22
......@@ -1,22 +0,0 @@
1pub fn main() void {
2 assert(add(3, 4) == 20);
3}
4
5fn add(a: u32, b: u32) u32 {
6 const x: u32 = blk: {
7 const c = a + b; // 7
8 const d = a + c; // 10
9 const e = d + b; // 14
10 break :blk e;
11 };
12 const y = x + a; // 17
13 const z = y + a; // 20
14 return z;
15}
16
17pub fn assert(ok: bool) void {
18 if (!ok) unreachable; // assertion failure
19}
20
21// run
22//
test/cases/x86_64-macos/comptime_var.0.zig deleted-12
......@@ -1,12 +0,0 @@
1pub fn main() void {
2 var a: u32 = 0;
3 comptime var b: u32 = 0;
4 if (a == 0) b = 3;
5}
6
7// error
8// output_mode=Exe
9// target=x86_64-macos
10//
11// :4:19: error: store to comptime variable depends on runtime condition
12// :4:11: note: runtime condition here
test/cases/x86_64-macos/comptime_var.1.zig deleted-13
......@@ -1,13 +0,0 @@
1pub fn main() void {
2 var a: u32 = 0;
3 comptime var b: u32 = 0;
4 switch (a) {
5 0 => {},
6 else => b = 3,
7 }
8}
9
10// error
11//
12// :6:19: error: store to comptime variable depends on runtime condition
13// :4:13: note: runtime condition here
test/cases/x86_64-macos/comptime_var.2.zig deleted-17
......@@ -1,17 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 comptime var len: u32 = 5;
5 print(len);
6 len += 9;
7 print(len);
8}
9
10fn print(len: usize) void {
11 _ = write(1, @ptrToInt("Hello, World!\n"), len);
12}
13
14// run
15//
16// HelloHello, World!
17//
test/cases/x86_64-macos/comptime_var.3.zig deleted-10
......@@ -1,10 +0,0 @@
1comptime {
2 var x: i32 = 1;
3 x += 1;
4 if (x != 1) unreachable;
5}
6pub fn main() void {}
7
8// error
9//
10// :4:17: error: reached unreachable code
test/cases/x86_64-macos/comptime_var.4.zig deleted-9
......@@ -1,9 +0,0 @@
1pub fn main() void {
2 comptime var i: u64 = 0;
3 while (i < 5) : (i += 1) {}
4}
5
6// error
7//
8// :3:24: error: cannot store to comptime variable in non-inline loop
9// :3:5: note: non-inline loop here
test/cases/x86_64-macos/comptime_var.5.zig deleted-15
......@@ -1,15 +0,0 @@
1pub fn main() void {
2 var a: u32 = 0;
3 if (a == 0) {
4 comptime var b: u32 = 0;
5 b = 1;
6 }
7}
8comptime {
9 var x: i32 = 1;
10 x += 1;
11 if (x != 2) unreachable;
12}
13
14// run
15//
test/cases/x86_64-macos/comptime_var.6.zig deleted-15
......@@ -1,15 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 comptime var i: u64 = 2;
5 inline while (i < 6) : (i += 1) {
6 print(i);
7 }
8}
9fn print(len: usize) void {
10 _ = write(1, @ptrToInt("Hello"), len);
11}
12
13// run
14//
15// HeHelHellHello
test/cases/x86_64-macos/hello_world_with_updates.0.zig deleted-5
......@@ -1,5 +0,0 @@
1// error
2// output_mode=Exe
3// target=x86_64-macos
4//
5// :?:?: error: root struct of file 'tmp' has no member named 'main'
test/cases/x86_64-macos/hello_world_with_updates.1.zig deleted-6
......@@ -1,6 +0,0 @@
1pub export fn main() noreturn {}
2
3// error
4//
5// :1:22: error: function declared 'noreturn' implicitly returns
6// :1:32: note: control flow reaches end of body here
test/cases/x86_64-macos/hello_world_with_updates.2.zig deleted-19
......@@ -1,19 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2extern "c" fn exit(usize) noreturn;
3
4pub export fn main() noreturn {
5 print();
6
7 exit(0);
8}
9
10fn print() void {
11 const msg = @ptrToInt("Hello, World!\n");
12 const len = 14;
13 _ = write(1, msg, len);
14}
15
16// run
17//
18// Hello, World!
19//
test/cases/x86_64-macos/hello_world_with_updates.3.zig deleted-16
......@@ -1,16 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 print();
5}
6
7fn print() void {
8 const msg = @ptrToInt("Hello, World!\n");
9 const len = 14;
10 _ = write(1, msg, len);
11}
12
13// run
14//
15// Hello, World!
16//
test/cases/x86_64-macos/hello_world_with_updates.4.zig deleted-22
......@@ -1,22 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 print();
5 print();
6 print();
7 print();
8}
9
10fn print() void {
11 const msg = @ptrToInt("Hello, World!\n");
12 const len = 14;
13 _ = write(1, msg, len);
14}
15
16// run
17//
18// Hello, World!
19// Hello, World!
20// Hello, World!
21// Hello, World!
22//
test/cases/x86_64-macos/hello_world_with_updates.5.zig deleted-16
......@@ -1,16 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 print();
5}
6
7fn print() void {
8 const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
9 const len = 104;
10 _ = write(1, msg, len);
11}
12
13// run
14//
15// What is up? This is a longer message that will force the data to be relocated in virtual address space.
16//
test/cases/x86_64-macos/hello_world_with_updates.6.zig deleted-18
......@@ -1,18 +0,0 @@
1extern "c" fn write(usize, usize, usize) usize;
2
3pub fn main() void {
4 print();
5 print();
6}
7
8fn print() void {
9 const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
10 const len = 104;
11 _ = write(1, msg, len);
12}
13
14// run
15//
16// What is up? This is a longer message that will force the data to be relocated in virtual address space.
17// What is up? This is a longer message that will force the data to be relocated in virtual address space.
18//
test/cases/x86_64-windows/hello_world_with_updates.0.zig deleted-7
......@@ -1,7 +0,0 @@
1// error
2// output_mode=Exe
3// target=x86_64-windows
4//
5// :?:?: error: root struct of file 'tmp' has no member named 'main'
6// :?:?: note: called from here
7// :?:?: note: called from here
test/cases/x86_64-windows/hello_world_with_updates.1.zig deleted-6
......@@ -1,6 +0,0 @@
1pub export fn main() noreturn {}
2
3// error
4//
5// :1:22: error: function declared 'noreturn' implicitly returns
6// :1:32: note: control flow reaches end of body here
test/cases/x86_64-windows/hello_world_with_updates.2.zig deleted-16
......@@ -1,16 +0,0 @@
1const std = @import("std");
2
3pub fn main() void {
4 print();
5}
6
7fn print() void {
8 const msg = "Hello, World!\n";
9 const stdout = std.io.getStdOut();
10 stdout.writeAll(msg) catch unreachable;
11}
12
13// run
14//
15// Hello, World!
16//
test/tests.zig+19-17
......@@ -94,14 +94,15 @@ const test_targets = blk: {
9494 .use_llvm = false,
9595 .use_lld = false,
9696 },
97 .{
98 .target = .{
99 .cpu_arch = .aarch64,
100 .os_tag = .linux,
101 },
102 .use_llvm = false,
103 .use_lld = false,
104 },
97 // Doesn't support new liveness
98 //.{
99 // .target = .{
100 // .cpu_arch = .aarch64,
101 // .os_tag = .linux,
102 // },
103 // .use_llvm = false,
104 // .use_lld = false,
105 //},
105106 .{
106107 .target = .{
107108 .cpu_arch = .wasm32,
......@@ -128,15 +129,16 @@ const test_targets = blk: {
128129 // .use_llvm = false,
129130 // .use_lld = false,
130131 //},
131 .{
132 .target = .{
133 .cpu_arch = .aarch64,
134 .os_tag = .macos,
135 .abi = .none,
136 },
137 .use_llvm = false,
138 .use_lld = false,
139 },
132 // Doesn't support new liveness
133 //.{
134 // .target = .{
135 // .cpu_arch = .aarch64,
136 // .os_tag = .macos,
137 // .abi = .none,
138 // },
139 // .use_llvm = false,
140 // .use_lld = false,
141 //},
140142 .{
141143 .target = .{
142144 .cpu_arch = .x86_64,