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);...@@ -13,7 +13,7 @@ var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
1313
14pub fn main() void {14pub fn main() void {
15 if (builtin.zig_backend == .stage2_wasm or15 if (builtin.zig_backend == .stage2_wasm or
16 builtin.zig_backend == .stage2_x86_64 or16 (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag != .linux) or
17 builtin.zig_backend == .stage2_aarch64)17 builtin.zig_backend == .stage2_aarch64)
18 {18 {
19 return mainSimple() catch @panic("test failure");19 return mainSimple() catch @panic("test failure");
src/Air.zig+214
...@@ -1375,3 +1375,217 @@ pub fn nullTerminatedString(air: Air, index: usize) [:0]const u8 {...@@ -1375,3 +1375,217 @@ pub fn nullTerminatedString(air: Air, index: usize) [:0]const u8 {
1375 }1375 }
1376 return bytes[0..end :0];1376 return bytes[0..end :0];
1377}1377}
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...@@ -3095,6 +3095,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
30953095
3096 .file_failure,3096 .file_failure,
3097 .sema_failure,3097 .sema_failure,
3098 .liveness_failure,
3098 .codegen_failure,3099 .codegen_failure,
3099 .dependency_failure,3100 .dependency_failure,
3100 .sema_failure_retryable,3101 .sema_failure_retryable,
...@@ -3145,7 +3146,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v...@@ -3145,7 +3146,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
31453146
3146 // emit-h only requires semantic analysis of the Decl to be complete,3147 // emit-h only requires semantic analysis of the Decl to be complete,
3147 // it does not depend on machine code generation to succeed.3148 // 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 => {
3149 const named_frame = tracy.namedFrame("emit_h_decl");3150 const named_frame = tracy.namedFrame("emit_h_decl");
3150 defer named_frame.end();3151 defer named_frame.end();
31513152
src/Liveness.zig+978-863
...@@ -14,6 +14,8 @@ const Allocator = std.mem.Allocator;...@@ -14,6 +14,8 @@ const Allocator = std.mem.Allocator;
14const Air = @import("Air.zig");14const Air = @import("Air.zig");
15const Log2Int = std.math.Log2Int;15const Log2Int = std.math.Log2Int;
1616
17pub const Verify = @import("Liveness/Verify.zig");
18
17/// This array is split into sets of 4 bits per AIR instruction.19/// This array is split into sets of 4 bits per AIR instruction.
18/// The MSB (0bX000) is whether the instruction is unreferenced.20/// The MSB (0bX000) is whether the instruction is unreferenced.
19/// The LSB (0b000X) is the first operand, and so on, up to 3 operands. A set bit means the21/// 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,...@@ -24,8 +26,10 @@ tomb_bits: []usize,
24/// Sparse table of specially handled instructions. The value is an index into the `extra`26/// Sparse table of specially handled instructions. The value is an index into the `extra`
25/// array. The meaning of the data depends on the AIR tag.27/// array. The meaning of the data depends on the AIR tag.
26/// * `cond_br` - points to a `CondBr` in `extra` at this index.28/// * `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".
27/// * `switch_br` - points to a `SwitchBr` in `extra` at this index.31/// * `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.
29/// * `asm`, `call`, `aggregate_init` - the value is a set of bits which are the extra tomb33/// * `asm`, `call`, `aggregate_init` - the value is a set of bits which are the extra tomb
30/// bits of operands.34/// bits of operands.
31/// The main tomb bits are still used and the extra ones are starting with the lsb of the35/// 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 {...@@ -52,11 +56,88 @@ pub const SwitchBr = struct {
52 else_death_count: u32,56 else_death_count: u32,
53};57};
5458
55/// Trailing is the set of instructions whose lifetimes end at the end of the loop body.59/// Trailing is the set of instructions which die in the block. Note that these are not additional
56pub const Loop = struct {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 {
57 death_count: u32,63 death_count: u32,
58};64};
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
60pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {141pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {
61 const tracy = trace(@src());142 const tracy = trace(@src());
62 defer tracy.end();143 defer tracy.end();
...@@ -64,7 +145,6 @@ pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {...@@ -64,7 +145,6 @@ pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {
64 var a: Analysis = .{145 var a: Analysis = .{
65 .gpa = gpa,146 .gpa = gpa,
66 .air = air,147 .air = air,
67 .table = .{},
68 .tomb_bits = try gpa.alloc(148 .tomb_bits = try gpa.alloc(
69 usize,149 usize,
70 (air.instructions.len * bpi + @bitSizeOf(usize) - 1) / @bitSizeOf(usize),150 (air.instructions.len * bpi + @bitSizeOf(usize) - 1) / @bitSizeOf(usize),
...@@ -75,19 +155,27 @@ pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {...@@ -75,19 +155,27 @@ pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {
75 errdefer gpa.free(a.tomb_bits);155 errdefer gpa.free(a.tomb_bits);
76 errdefer a.special.deinit(gpa);156 errdefer a.special.deinit(gpa);
77 defer a.extra.deinit(gpa);157 defer a.extra.deinit(gpa);
78 defer a.table.deinit(gpa);
79158
80 std.mem.set(usize, a.tomb_bits, 0);159 std.mem.set(usize, a.tomb_bits, 0);
81160
82 const main_body = air.getMainBody();161 const main_body = air.getMainBody();
83 try a.table.ensureTotalCapacity(gpa, @intCast(u32, main_body.len));162
84 try analyzeWithContext(&a, null, main_body);163 {
164 var data: LivenessPassData(.loop_analysis) = .{};
165 defer data.deinit(gpa);
166 try analyzeBody(&a, .loop_analysis, &data, main_body);
167 }
168
85 {169 {
86 var to_remove: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{};170 var data: LivenessPassData(.main_analysis) = .{};
87 defer to_remove.deinit(gpa);171 defer data.deinit(gpa);
88 try removeDeaths(&a, &to_remove, main_body);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);
89 }176 }
90 return Liveness{177
178 return .{
91 .tomb_bits = a.tomb_bits,179 .tomb_bits = a.tomb_bits,
92 .special = a.special,180 .special = a.special,
93 .extra = try a.extra.toOwnedSlice(gpa),181 .extra = try a.extra.toOwnedSlice(gpa),
...@@ -661,18 +749,27 @@ pub fn getSwitchBr(l: Liveness, gpa: Allocator, inst: Air.Inst.Index, cases_len:...@@ -661,18 +749,27 @@ pub fn getSwitchBr(l: Liveness, gpa: Allocator, inst: Air.Inst.Index, cases_len:
661 };749 };
662}750}
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 {
665 deaths: []const Air.Inst.Index,755 deaths: []const Air.Inst.Index,
666};756};
667757
668pub fn getLoop(l: Liveness, inst: Air.Inst.Index) LoopSlice {758pub fn getBlock(l: Liveness, inst: Air.Inst.Index) BlockSlices {
669 const index: usize = l.special.get(inst) orelse return .{759 const index: usize = l.special.get(inst) orelse return .{
670 .deaths = &.{},760 .deaths = &.{},
671 };761 };
672 const death_count = l.extra[index];762 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 };
674}767}
675768
769pub const LoopSlice = struct {
770 deaths: []const Air.Inst.Index,
771};
772
676pub fn deinit(l: *Liveness, gpa: Allocator) void {773pub fn deinit(l: *Liveness, gpa: Allocator) void {
677 gpa.free(l.tomb_bits);774 gpa.free(l.tomb_bits);
678 gpa.free(l.extra);775 gpa.free(l.extra);
...@@ -687,6 +784,7 @@ pub fn iterateBigTomb(l: Liveness, inst: Air.Inst.Index) BigTomb {...@@ -687,6 +784,7 @@ pub fn iterateBigTomb(l: Liveness, inst: Air.Inst.Index) BigTomb {
687 .extra_offset = 0,784 .extra_offset = 0,
688 .extra = l.extra,785 .extra = l.extra,
689 .bit_index = 0,786 .bit_index = 0,
787 .reached_end = false,
690 };788 };
691}789}
692790
...@@ -702,13 +800,16 @@ pub const BigTomb = struct {...@@ -702,13 +800,16 @@ pub const BigTomb = struct {
702 extra_start: u32,800 extra_start: u32,
703 extra_offset: u32,801 extra_offset: u32,
704 extra: []const u32,802 extra: []const u32,
803 reached_end: bool,
705804
706 /// Returns whether the next operand dies.805 /// Returns whether the next operand dies.
707 pub fn feed(bt: *BigTomb) bool {806 pub fn feed(bt: *BigTomb) bool {
807 if (bt.reached_end) return false;
808
708 const this_bit_index = bt.bit_index;809 const this_bit_index = bt.bit_index;
709 bt.bit_index += 1;810 bt.bit_index += 1;
710811
711 const small_tombs = Liveness.bpi - 1;812 const small_tombs = bpi - 1;
712 if (this_bit_index < small_tombs) {813 if (this_bit_index < small_tombs) {
713 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;814 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
714 return dies;815 return dies;
...@@ -716,6 +817,10 @@ pub const BigTomb = struct {...@@ -716,6 +817,10 @@ pub const BigTomb = struct {
716817
717 const big_bit_index = this_bit_index - small_tombs;818 const big_bit_index = this_bit_index - small_tombs;
718 while (big_bit_index - bt.extra_offset * 31 >= 31) {819 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 }
719 bt.extra_offset += 1;824 bt.extra_offset += 1;
720 }825 }
721 const dies = @truncate(u1, bt.extra[bt.extra_start + bt.extra_offset] >>826 const dies = @truncate(u1, bt.extra[bt.extra_start + bt.extra_offset] >>
...@@ -728,7 +833,6 @@ pub const BigTomb = struct {...@@ -728,7 +833,6 @@ pub const BigTomb = struct {
728const Analysis = struct {833const Analysis = struct {
729 gpa: Allocator,834 gpa: Allocator,
730 air: Air,835 air: Air,
731 table: std.AutoHashMapUnmanaged(Air.Inst.Index, void),
732 tomb_bits: []usize,836 tomb_bits: []usize,
733 special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),837 special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
734 extra: std.ArrayListUnmanaged(u32),838 extra: std.ArrayListUnmanaged(u32),
...@@ -758,46 +862,70 @@ const Analysis = struct {...@@ -758,46 +862,70 @@ const Analysis = struct {
758 }862 }
759};863};
760864
761fn analyzeWithContext(865fn analyzeBody(
762 a: *Analysis,866 a: *Analysis,
763 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),867 comptime pass: LivenessPass,
868 data: *LivenessPassData(pass),
764 body: []const Air.Inst.Index,869 body: []const Air.Inst.Index,
765) Allocator.Error!void {870) Allocator.Error!void {
766 var i: usize = body.len;871 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| {879const ControlBranchInfo = struct {
769 // We are only interested in doing this for instructions which are born880 branch_deaths: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
770 // before a conditional branch, so after obtaining the new set for881 live_set: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
771 // each branch we prune the instructions which were born within.882};
772 while (i != 0) {883
773 i -= 1;884/// Helper function for running `analyzeBody`, but resetting `branch_deaths` and `live_set` to their
774 const inst = body[i];885/// original states before returning, returning the modified versions of them. Only makes sense in
775 _ = ns.remove(inst);886/// the `main_analysis` pass.
776 try analyzeInst(a, new_set, inst);887fn analyzeBodyResetBranch(
777 }888 a: *Analysis,
778 } else {889 comptime pass: LivenessPass,
779 while (i != 0) {890 data: *LivenessPassData(pass),
780 i -= 1;891 body: []const Air.Inst.Index,
781 const inst = body[i];892) !ControlBranchInfo {
782 try analyzeInst(a, new_set, inst);893 switch (pass) {
783 }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;
784 }910 }
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 };
785}918}
786919
787fn analyzeInst(920fn analyzeInst(
788 a: *Analysis,921 a: *Analysis,
789 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),922 comptime pass: LivenessPass,
923 data: *LivenessPassData(pass),
790 inst: Air.Inst.Index,924 inst: Air.Inst.Index,
791) Allocator.Error!void {925) Allocator.Error!void {
792 const gpa = a.gpa;
793 const table = &a.table;
794 const inst_tags = a.air.instructions.items(.tag);926 const inst_tags = a.air.instructions.items(.tag);
795 const inst_datas = a.air.instructions.items(.data);927 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
801 switch (inst_tags[inst]) {929 switch (inst_tags[inst]) {
802 .add,930 .add,
803 .add_optimized,931 .add_optimized,
...@@ -861,28 +989,24 @@ fn analyzeInst(...@@ -861,28 +989,24 @@ fn analyzeInst(
861 .max,989 .max,
862 => {990 => {
863 const o = inst_datas[inst].bin_op;991 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 });
865 },993 },
866994
867 .vector_store_elem => {995 .vector_store_elem => {
868 const o = inst_datas[inst].vector_store_elem;996 const o = inst_datas[inst].vector_store_elem;
869 const extra = a.air.extraData(Air.Bin, o.payload).data;997 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 });
871 },999 },
8721000
873 .arg,1001 .arg,
874 .alloc,1002 .alloc,
875 .ret_ptr,1003 .ret_ptr,
876 .constant,
877 .const_ty,
878 .trap,
879 .breakpoint,1004 .breakpoint,
880 .dbg_stmt,1005 .dbg_stmt,
881 .dbg_inline_begin,1006 .dbg_inline_begin,
882 .dbg_inline_end,1007 .dbg_inline_end,
883 .dbg_block_begin,1008 .dbg_block_begin,
884 .dbg_block_end,1009 .dbg_block_end,
885 .unreach,
886 .fence,1010 .fence,
887 .ret_addr,1011 .ret_addr,
888 .frame_addr,1012 .frame_addr,
...@@ -893,7 +1017,15 @@ fn analyzeInst(...@@ -893,7 +1017,15 @@ fn analyzeInst(
893 .work_item_id,1017 .work_item_id,
894 .work_group_size,1018 .work_group_size,
895 .work_group_id,1019 .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
898 .not,1030 .not,
899 .bitcast,1031 .bitcast,
...@@ -938,7 +1070,7 @@ fn analyzeInst(...@@ -938,7 +1070,7 @@ fn analyzeInst(
938 .c_va_copy,1070 .c_va_copy,
939 => {1071 => {
940 const o = inst_datas[inst].ty_op;1072 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 });
942 },1074 },
9431075
944 .is_null,1076 .is_null,
...@@ -951,8 +1083,6 @@ fn analyzeInst(...@@ -951,8 +1083,6 @@ fn analyzeInst(
951 .is_non_err_ptr,1083 .is_non_err_ptr,
952 .ptrtoint,1084 .ptrtoint,
953 .bool_to_int,1085 .bool_to_int,
954 .ret,
955 .ret_load,
956 .is_named_enum_value,1086 .is_named_enum_value,
957 .tag_name,1087 .tag_name,
958 .error_name,1088 .error_name,
...@@ -977,7 +1107,14 @@ fn analyzeInst(...@@ -977,7 +1107,14 @@ fn analyzeInst(
977 .c_va_end,1107 .c_va_end,
978 => {1108 => {
979 const operand = inst_datas[inst].un_op;1109 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 });
981 },1118 },
9821119
983 .add_with_overflow,1120 .add_with_overflow,
...@@ -992,19 +1129,19 @@ fn analyzeInst(...@@ -992,19 +1129,19 @@ fn analyzeInst(
992 => {1129 => {
993 const ty_pl = inst_datas[inst].ty_pl;1130 const ty_pl = inst_datas[inst].ty_pl;
994 const extra = a.air.extraData(Air.Bin, ty_pl.payload).data;1131 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 });
996 },1133 },
9971134
998 .dbg_var_ptr,1135 .dbg_var_ptr,
999 .dbg_var_val,1136 .dbg_var_val,
1000 => {1137 => {
1001 const operand = inst_datas[inst].pl_op.operand;1138 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 });
1003 },1140 },
10041141
1005 .prefetch => {1142 .prefetch => {
1006 const prefetch = inst_datas[inst].prefetch;1143 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 });
1008 },1145 },
10091146
1010 .call, .call_always_tail, .call_never_tail, .call_never_inline => {1147 .call, .call_always_tail, .call_never_tail, .call_never_inline => {
...@@ -1016,37 +1153,35 @@ fn analyzeInst(...@@ -1016,37 +1153,35 @@ fn analyzeInst(
1016 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);1153 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
1017 buf[0] = callee;1154 buf[0] = callee;
1018 std.mem.copy(Air.Inst.Ref, buf[1..], args);1155 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);
1020 }1157 }
1021 var extra_tombs: ExtraTombs = .{1158
1022 .analysis = a,1159 var big = try AnalyzeBigOperands(pass).init(a, data, inst, args.len + 1);
1023 .new_set = new_set,1160 defer big.deinit();
1024 .inst = inst,1161 var i: usize = args.len;
1025 .main_tomb = main_tomb,1162 while (i > 0) {
1026 };1163 i -= 1;
1027 defer extra_tombs.deinit();1164 try big.feed(args[i]);
1028 try extra_tombs.feed(callee);
1029 for (args) |arg| {
1030 try extra_tombs.feed(arg);
1031 }1165 }
1032 return extra_tombs.finish();1166 try big.feed(callee);
1167 return big.finish();
1033 },1168 },
1034 .select => {1169 .select => {
1035 const pl_op = inst_datas[inst].pl_op;1170 const pl_op = inst_datas[inst].pl_op;
1036 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;1171 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 });
1038 },1173 },
1039 .shuffle => {1174 .shuffle => {
1040 const extra = a.air.extraData(Air.Shuffle, inst_datas[inst].ty_pl.payload).data;1175 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 });
1042 },1177 },
1043 .reduce, .reduce_optimized => {1178 .reduce, .reduce_optimized => {
1044 const reduce = inst_datas[inst].reduce;1179 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 });
1046 },1181 },
1047 .cmp_vector, .cmp_vector_optimized => {1182 .cmp_vector, .cmp_vector_optimized => {
1048 const extra = a.air.extraData(Air.VectorCmp, inst_datas[inst].ty_pl.payload).data;1183 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 });
1050 },1185 },
1051 .aggregate_init => {1186 .aggregate_init => {
1052 const ty_pl = inst_datas[inst].ty_pl;1187 const ty_pl = inst_datas[inst].ty_pl;
...@@ -1057,62 +1192,58 @@ fn analyzeInst(...@@ -1057,62 +1192,58 @@ fn analyzeInst(
1057 if (elements.len <= bpi - 1) {1192 if (elements.len <= bpi - 1) {
1058 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);1193 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
1059 std.mem.copy(Air.Inst.Ref, &buf, elements);1194 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);
1061 }1196 }
1062 var extra_tombs: ExtraTombs = .{1197
1063 .analysis = a,1198 var big = try AnalyzeBigOperands(pass).init(a, data, inst, elements.len);
1064 .new_set = new_set,1199 defer big.deinit();
1065 .inst = inst,1200 var i: usize = elements.len;
1066 .main_tomb = main_tomb,1201 while (i > 0) {
1067 };1202 i -= 1;
1068 defer extra_tombs.deinit();1203 try big.feed(elements[i]);
1069 for (elements) |elem| {
1070 try extra_tombs.feed(elem);
1071 }1204 }
1072 return extra_tombs.finish();1205 return big.finish();
1073 },1206 },
1074 .union_init => {1207 .union_init => {
1075 const extra = a.air.extraData(Air.UnionInit, inst_datas[inst].ty_pl.payload).data;1208 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 });
1077 },1210 },
1078 .struct_field_ptr, .struct_field_val => {1211 .struct_field_ptr, .struct_field_val => {
1079 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;1212 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 });
1081 },1214 },
1082 .field_parent_ptr => {1215 .field_parent_ptr => {
1083 const extra = a.air.extraData(Air.FieldParentPtr, inst_datas[inst].ty_pl.payload).data;1216 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 });
1085 },1218 },
1086 .cmpxchg_strong, .cmpxchg_weak => {1219 .cmpxchg_strong, .cmpxchg_weak => {
1087 const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data;1220 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 });
1089 },1222 },
1090 .mul_add => {1223 .mul_add => {
1091 const pl_op = inst_datas[inst].pl_op;1224 const pl_op = inst_datas[inst].pl_op;
1092 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;1225 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 });
1094 },1227 },
1095 .atomic_load => {1228 .atomic_load => {
1096 const ptr = inst_datas[inst].atomic_load.ptr;1229 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 });
1098 },1231 },
1099 .atomic_rmw => {1232 .atomic_rmw => {
1100 const pl_op = inst_datas[inst].pl_op;1233 const pl_op = inst_datas[inst].pl_op;
1101 const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;1234 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 });
1103 },1236 },
1104 .memset,1237 .memset,
1105 .memcpy,1238 .memcpy,
1106 => {1239 => {
1107 const pl_op = inst_datas[inst].pl_op;1240 const pl_op = inst_datas[inst].pl_op;
1108 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;1241 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 });
1110 },1243 },
11111244
1112 .br => {1245 .br => return analyzeInstBr(a, pass, data, inst),
1113 const br = inst_datas[inst].br;1246
1114 return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none });
1115 },
1116 .assembly => {1247 .assembly => {
1117 const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload);1248 const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload);
1118 var extra_i: usize = extra.end;1249 var extra_i: usize = extra.end;
...@@ -1121,912 +1252,896 @@ fn analyzeInst(...@@ -1121,912 +1252,896 @@ fn analyzeInst(
1121 const inputs = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]);1252 const inputs = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]);
1122 extra_i += inputs.len;1253 extra_i += inputs.len;
11231254
1124 simple: {1255 const num_operands = simple: {
1125 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);1256 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
1126 var buf_index: usize = 0;1257 var buf_index: usize = 0;
1127 for (outputs) |output| {1258 for (outputs) |output| {
1128 if (output != .none) {1259 if (output != .none) {
1129 if (buf_index >= buf.len) break :simple;1260 if (buf_index < buf.len) buf[buf_index] = output;
1130 buf[buf_index] = output;
1131 buf_index += 1;1261 buf_index += 1;
1132 }1262 }
1133 }1263 }
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 }
1135 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);1267 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
1136 return trackOperands(a, new_set, inst, main_tomb, buf);1268 return analyzeOperands(a, pass, data, inst, buf);
1137 }
1138 var extra_tombs: ExtraTombs = .{
1139 .analysis = a,
1140 .new_set = new_set,
1141 .inst = inst,
1142 .main_tomb = main_tomb,
1143 };1269 };
1144 defer extra_tombs.deinit();1270
1145 for (outputs) |output| {1271 var big = try AnalyzeBigOperands(pass).init(a, data, inst, num_operands);
1146 if (output != .none) {1272 defer big.deinit();
1147 try extra_tombs.feed(output);1273 var i: usize = inputs.len;
1148 }1274 while (i > 0) {
1275 i -= 1;
1276 try big.feed(inputs[i]);
1149 }1277 }
1150 for (inputs) |input| {1278 i = outputs.len;
1151 try extra_tombs.feed(input);1279 while (i > 0) {
1280 i -= 1;
1281 if (outputs[i] != .none) {
1282 try big.feed(outputs[i]);
1283 }
1152 }1284 }
1153 return extra_tombs.finish();1285 return big.finish();
1154 },1286 },
1155 .block => {1287
1156 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);1288 .block => return analyzeInstBlock(a, pass, data, inst),
1157 const body = a.air.extra[extra.end..][0..extra.data.body_len];1289 .loop => return analyzeInstLoop(a, pass, data, inst),
1158 try analyzeWithContext(a, new_set, body);1290
1159 return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none });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 });
1160 },1299 },
1161 .loop => {1300 }
1162 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);1301}
1163 const body = a.air.extra[extra.end..][0..extra.data.body_len];
11641302
1165 var body_table: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{};1303/// Every instruction should hit this (after handling any nested bodies), in every pass. In the
1166 defer body_table.deinit(gpa);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 loop1316 switch (pass) {
1169 // iterations may occur. Track deaths from the loop body - we'll remove all of these1317 .loop_analysis => {
1170 // retroactively, and add them to our extra data.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| {1323 // Don't compute any liveness for constants
1175 try ns.ensureUnusedCapacity(gpa, body_table.count());1324 switch (inst_tags[operand]) {
1176 var it = body_table.keyIterator();1325 .constant, .const_ty => continue,
1177 while (it.next()) |key| {1326 else => {},
1178 _ = ns.putAssumeCapacity(key.*, {});
1179 }1327 }
1328
1329 _ = try data.live_set.put(gpa, operand, {});
1180 }1330 }
1331 },
11811332
1182 try a.extra.ensureUnusedCapacity(gpa, std.meta.fields(Loop).len + body_table.count());1333 .main_analysis => {
1183 const extra_index = a.addExtraAssumeCapacity(Loop{1334 const usize_index = (inst * bpi) / @bitSizeOf(usize);
1184 .death_count = body_table.count(),1335
1185 });1336 // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`.
1186 {1337 var immediate_death = false;
1187 var it = body_table.keyIterator();1338 if (data.branch_deaths.remove(inst)) {
1188 while (it.next()) |key| {1339 log.debug("[{}] %{}: resolved branch death to birth (immediate death)", .{ pass, inst });
1189 a.extra.appendAssumeCapacity(key.*);1340 immediate_death = true;
1190 }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;
1191 }1347 }
1192 try a.special.put(gpa, inst, extra_index);
11931348
1194 // We'll remove invalid deaths in a separate pass after main liveness analysis. See1349 var tomb_bits: Bpi = @as(Bpi, @boolToInt(immediate_death)) << (bpi - 1);
1195 // removeDeaths for more details.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.1371 if ((try data.live_set.fetchPut(gpa, operand, {})) == null) {
1198 },1372 log.debug("[{}] %{}: added %{} to live set (operand dies here)", .{ pass, inst, operand });
1199 .@"try" => {1373 tomb_bits |= mask;
1200 const pl_op = inst_datas[inst].pl_op;1374 if (data.branch_deaths.remove(operand)) {
1201 const extra = a.air.extraData(Air.Try, pl_op.payload);1375 log.debug("[{}] %{}: resolved branch death of %{} to this usage", .{ pass, inst, operand });
1202 const body = a.air.extra[extra.end..][0..extra.data.body_len];1376 }
1203 try analyzeWithContext(a, new_set, body);1377 }
1204 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, .none, .none });1378 }
1379 }
1380
1381 a.tomb_bits[usize_index] |= @as(usize, tomb_bits) <<
1382 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi);
1205 },1383 },
1206 .try_ptr => {1384 }
1207 const extra = a.air.extraData(Air.TryPtr, inst_datas[inst].ty_pl.payload);1385}
1208 const body = a.air.extra[extra.end..][0..extra.data.body_len];1386
1209 try analyzeWithContext(a, new_set, body);1387/// Like `analyzeOperands`, but for an instruction which returns from a function, so should
1210 return trackOperands(a, new_set, inst, main_tomb, .{ extra.data.ptr, .none, .none });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.
1211 },1400 },
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) = .{};1402 .main_analysis => {
1222 defer then_table.deinit(gpa);1403 const gpa = a.gpa;
1223 try analyzeWithContext(a, &then_table, then_body);
12241404
1225 // Reset the table back to its state from before the branch.1405 // Note that we preserve previous branch deaths - anything that needs to die in our
1226 {1406 // "parent" branch also needs to die for us.
1227 var it = then_table.keyIterator();1407
1228 while (it.next()) |key| {1408 try data.branch_deaths.ensureUnusedCapacity(gpa, data.live_set.count());
1229 assert(table.remove(key.*));1409 var it = data.live_set.keyIterator();
1230 }1410 while (it.next()) |key| {
1411 const alive = key.*;
1412 data.branch_deaths.putAssumeCapacity(alive, {});
1231 }1413 }
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) = .{};1431 switch (pass) {
1234 defer else_table.deinit(gpa);1432 .loop_analysis => {
1235 try analyzeWithContext(a, &else_table, else_body);1433 try data.breaks.put(gpa, br.block_inst, {});
1434 },
12361435
1237 var then_entry_deaths = std.ArrayList(Air.Inst.Index).init(gpa);1436 .main_analysis => {
1238 defer then_entry_deaths.deinit();1437 const block_scope = data.block_scopes.get(br.block_inst).?; // we should always be breaking from an enclosing block
1239 var else_entry_deaths = std.ArrayList(Air.Inst.Index).init(gpa);
1240 defer else_entry_deaths.deinit();
12411438
1242 {1439 // We mostly preserve previous branch deaths - anything that should die for our
1243 var it = else_table.keyIterator();1440 // enclosing branch should die for us too. However, if our break target requires such an
1244 while (it.next()) |key| {1441 // operand to be alive, it's actually not something we want to kill, since its "last
1245 const else_death = key.*;1442 // use" (i.e. the point at which it should die) is outside of our scope.
1246 if (!then_table.contains(else_death)) {1443 var it = block_scope.live_set.keyIterator();
1247 try then_entry_deaths.append(else_death);1444 while (it.next()) |key| {
1248 }1445 const alive = key.*;
1249 }1446 _ = data.branch_deaths.remove(alive);
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 }
1262 }1447 }
1263 // Now we have to correctly populate new_set.1448 log.debug("[{}] %{}: preserved branch deaths are {}", .{ pass, inst, fmtInstSet(&data.branch_deaths) });
1264 if (new_set) |ns| {1449
1265 try ns.ensureUnusedCapacity(gpa, @intCast(u32, then_table.count() + else_table.count()));1450 // Anything that's currently alive but our target doesn't need becomes a branch death.
1266 var it = then_table.keyIterator();1451 it = data.live_set.keyIterator();
1267 while (it.next()) |key| {1452 while (it.next()) |key| {
1268 _ = ns.putAssumeCapacity(key.*, {});1453 const alive = key.*;
1269 }1454 if (!block_scope.live_set.contains(alive)) {
1270 it = else_table.keyIterator();1455 _ = try data.branch_deaths.put(gpa, alive, {});
1271 while (it.next()) |key| {1456 log.debug("[{}] %{}: added branch death of {}", .{ pass, inst, alive });
1272 _ = ns.putAssumeCapacity(key.*, {});
1273 }1457 }
1274 }1458 }
1275 const then_death_count = @intCast(u32, then_entry_deaths.items.len);1459 const new_live_set = try block_scope.live_set.clone(gpa);
1276 const else_death_count = @intCast(u32, else_entry_deaths.items.len);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 +1465 return analyzeOperands(a, pass, data, inst, .{ br.operand, .none, .none });
1279 then_death_count + else_death_count);1466}
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);
12871467
1288 // Continue on with the instruction analysis. The following code will find the condition1468fn analyzeInstBlock(
1289 // instruction, and the deaths flag for the CondBr instruction will indicate whether the1469 a: *Analysis,
1290 // condition's lifetime ends immediately before entering any branch.1470 comptime pass: LivenessPass,
1291 return trackOperands(a, new_set, inst, main_tomb, .{ condition, .none, .none });1471 data: *LivenessPassData(pass),
1292 },1472 inst: Air.Inst.Index,
1293 .switch_br => {1473) !void {
1294 const pl_op = inst_datas[inst].pl_op;1474 const inst_datas = a.air.instructions.items(.data);
1295 const condition = pl_op.operand;1475 const ty_pl = inst_datas[inst].ty_pl;
1296 const switch_br = a.air.extraData(Air.SwitchBr, pl_op.payload);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);1479 const gpa = a.gpa;
1299 const case_tables = try gpa.alloc(Table, switch_br.data.cases_len + 1); // +1 for else
1300 defer gpa.free(case_tables);
13011480
1302 std.mem.set(Table, case_tables, .{});1481 // We actually want to do `analyzeOperands` *first*, since our result logically doesn't
1303 defer for (case_tables) |*ct| ct.deinit(gpa);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;1485 switch (pass) {
1306 for (case_tables[0..switch_br.data.cases_len]) |*case_table| {1486 .loop_analysis => {
1307 const case = a.air.extraData(Air.SwitchBr.Case, air_extra_index);1487 try analyzeBody(a, pass, data, body);
1308 const case_body = a.air.extra[case.end + case.data.items_len ..][0..case.data.body_len];1488 _ = data.breaks.remove(inst);
1309 air_extra_index = case.end + case.data.items_len + case_body.len;1489 },
1310 try analyzeWithContext(a, case_table, case_body);
13111490
1312 // Reset the table back to its state from before the case.1491 .main_analysis => {
1313 var it = case_table.keyIterator();1492 log.debug("[{}] %{}: block live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });
1314 while (it.next()) |key| {1493 try data.block_scopes.put(gpa, inst, .{
1315 assert(table.remove(key.*));1494 .live_set = try data.live_set.clone(gpa),
1316 }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);
1317 }1500 }
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.1502 log.debug("[{}] %{}: pushed new block scope", .{ pass, inst });
1324 var it = else_table.keyIterator();1503 try analyzeBody(a, pass, data, body);
1325 while (it.next()) |key| {
1326 assert(table.remove(key.*));
1327 }
1328 }
13291504
1330 const List = std.ArrayListUnmanaged(Air.Inst.Index);1505 // If the block is noreturn, block deaths not only aren't useful, they're impossible to
1331 const case_deaths = try gpa.alloc(List, case_tables.len); // includes else1506 // find: there could be more stuff alive after the block than before it!
1332 defer gpa.free(case_deaths);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, .{});1512 try a.extra.ensureUnusedCapacity(gpa, num_deaths + std.meta.fields(Block).len);
1335 defer for (case_deaths) |*cd| cd.deinit(gpa);1513 const extra_index = a.addExtraAssumeCapacity(Block{
1514 .death_count = num_deaths,
1515 });
13361516
1337 var total_deaths: u32 = 0;1517 var measured_num: u32 = 0;
1338 for (case_tables, 0..) |*ct, i| {1518 var it = data.live_set.keyIterator();
1339 total_deaths += ct.count();
1340 var it = ct.keyIterator();
1341 while (it.next()) |key| {1519 while (it.next()) |key| {
1342 const case_death = key.*;1520 const alive = key.*;
1343 for (case_tables, 0..) |*ct_inner, j| {1521 if (!block_scope.live_set.contains(alive)) {
1344 if (i == j) continue;1522 // Dies in block
1345 if (!ct_inner.contains(case_death)) {1523 a.extra.appendAssumeCapacity(alive);
1346 // instruction is not referenced in this case1524 measured_num += 1;
1347 try case_deaths[j].append(gpa, case_death);
1348 }
1349 }1525 }
1350 // undo resetting the table
1351 try table.put(gpa, case_death, {});
1352 }1526 }
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 });
1353 }1534 }
1535 },
1536 }
1537}
13541538
1355 // Now we have to correctly populate new_set.1539fn analyzeInstLoop(
1356 if (new_set) |ns| {1540 a: *Analysis,
1357 try ns.ensureUnusedCapacity(gpa, total_deaths);1541 comptime pass: LivenessPass,
1358 for (case_tables) |*ct| {1542 data: *LivenessPassData(pass),
1359 var it = ct.keyIterator();1543 inst: Air.Inst.Index,
1360 while (it.next()) |key| {1544) !void {
1361 _ = ns.putAssumeCapacity(key.*, {});1545 const inst_datas = a.air.instructions.items(.data);
1362 }1546 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
1363 }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);
1364 }1572 }
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);1575 // Now we put the live operands from the loop body in too
1367 const extra_index = try a.addExtra(SwitchBr{1576 const num_live = data.live_set.count();
1368 .else_death_count = else_death_count,1577 try a.extra.ensureUnusedCapacity(gpa, 1 + num_live);
1369 });1578
1370 for (case_deaths[0 .. case_deaths.len - 1]) |*cd| {1579 a.extra.appendAssumeCapacity(num_live);
1371 const case_death_count = @intCast(u32, cd.items.len);1580 it = data.live_set.keyIterator();
1372 try a.extra.ensureUnusedCapacity(gpa, 1 + case_death_count + else_death_count);1581 while (it.next()) |key| {
1373 a.extra.appendAssumeCapacity(case_death_count);1582 const alive = key.*;
1374 a.extra.appendSliceAssumeCapacity(cd.items);1583 a.extra.appendAssumeCapacity(alive);
1375 }1584 }
1376 a.extra.appendSliceAssumeCapacity(case_deaths[case_deaths.len - 1].items);1585 log.debug("[{}] %{}: maintain liveness of {}", .{ pass, inst, fmtInstSet(&data.live_set) });
1586
1377 try a.special.put(gpa, inst, extra_index);1587 try a.special.put(gpa, inst, extra_index);
13781588
1379 return trackOperands(a, new_set, inst, main_tomb, .{ condition, .none, .none });1589 // Add back operands which were previously alive
1380 },1590 it = old_live.keyIterator();
1381 .wasm_memory_grow => {1591 while (it.next()) |key| {
1382 const pl_op = inst_datas[inst].pl_op;1592 const alive = key.*;
1383 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, .none, .none });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 }
1384 },1602 },
1385 }
1386}
13871603
1388fn trackOperands(1604 .main_analysis => {
1389 a: *Analysis,1605 const extra_idx = a.special.fetchRemove(inst).?.value; // remove because this data does not exist after 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;
13971606
1398 var tomb_bits: Bpi = @boolToInt(main_tomb);1607 const num_breaks = data.old_extra.items[extra_idx];
1399 var i = operands.len;1608 const breaks = data.old_extra.items[extra_idx + 1 ..][0..num_breaks];
14001609
1401 while (i > 0) {1610 const num_loop_live = data.old_extra.items[extra_idx + num_breaks + 1];
1402 i -= 1;1611 const loop_live = data.old_extra.items[extra_idx + num_breaks + 2 ..][0..num_loop_live];
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}
14161612
1417const ExtraTombs = struct {1613 // This is necessarily not in the same control flow branch, because loops are noreturn
1418 analysis: *Analysis,1614 data.live_set.clearRetainingCapacity();
1419 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),1615
1420 inst: Air.Inst.Index,1616 try data.live_set.ensureUnusedCapacity(gpa, @intCast(u32, loop_live.len));
1421 main_tomb: bool,1617 for (loop_live) |alive| {
1422 bit_index: usize = 0,1618 data.live_set.putAssumeCapacity(alive, {});
1423 tomb_bits: Bpi = 0,1619 // If the loop requires a branch death operand to be alive, it's not something we
1424 big_tomb_bits: u32 = 0,1620 // want to kill: its "last use" (i.e. the point at which it should die) is the loop
1425 big_tomb_bits_extra: std.ArrayListUnmanaged(u32) = .{},1621 // body itself.
14261622 _ = data.branch_deaths.remove(alive);
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 }
1449 }1623 }
1450 }
1451 }
14521624
1453 fn finish(et: *ExtraTombs) !void {1625 log.debug("[{}] %{}: block live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });
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 }
14651626
1466 fn deinit(et: *ExtraTombs) void {1627 for (breaks) |block_inst| {
1467 et.big_tomb_bits_extra.deinit(et.analysis.gpa);1628 // We might break to this block, so include every operand that the block needs alive
1468 }1629 const block_scope = data.block_scopes.get(block_inst).?;
1469};
14701630
1471/// Remove any deaths invalidated by the deaths from an enclosing `loop`. Reshuffling deaths stored1631 var it = block_scope.live_set.keyIterator();
1472/// in `extra` causes it to become non-dense, but that's fine - we won't remove too much data.1632 while (it.next()) |key| {
1473/// Making it dense would be a lot more work - it'd require recomputing every index in `special`.1633 const alive = key.*;
1474fn removeDeaths(1634 try data.live_set.put(gpa, alive, {});
1475 a: *Analysis,1635 }
1476 to_remove: *std.AutoHashMapUnmanaged(Air.Inst.Index, void),1636 }
1477 body: []const Air.Inst.Index,1637
1478) error{OutOfMemory}!void {1638 try analyzeBody(a, pass, data, body);
1479 for (body) |inst| {1639 },
1480 try removeInstDeaths(a, to_remove, inst);
1481 }1640 }
1482}1641}
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(
1485 a: *Analysis,1647 a: *Analysis,
1486 to_remove: *std.AutoHashMapUnmanaged(Air.Inst.Index, void),1648 comptime pass: LivenessPass,
1649 data: *LivenessPassData(pass),
1487 inst: Air.Inst.Index,1650 inst: Air.Inst.Index,
1651 comptime inst_type: enum { cond_br, @"try", try_ptr },
1488) !void {1652) !void {
1489 const inst_tags = a.air.instructions.items(.tag);
1490 const inst_datas = a.air.instructions.items(.data);1653 const inst_datas = a.air.instructions.items(.data);
1654 const gpa = a.gpa;
14911655
1492 switch (inst_tags[inst]) {1656 const extra = switch (inst_type) {
1493 .add,1657 .cond_br => a.air.extraData(Air.CondBr, inst_datas[inst].pl_op.payload),
1494 .add_optimized,1658 .@"try" => a.air.extraData(Air.Try, inst_datas[inst].pl_op.payload),
1495 .addwrap,1659 .try_ptr => a.air.extraData(Air.TryPtr, inst_datas[inst].ty_pl.payload),
1496 .addwrap_optimized,1660 };
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 },
15571661
1558 .vector_store_elem => {1662 const condition = switch (inst_type) {
1559 const o = inst_datas[inst].vector_store_elem;1663 .cond_br, .@"try" => inst_datas[inst].pl_op.operand,
1560 const extra = a.air.extraData(Air.Bin, o.payload).data;1664 .try_ptr => extra.data.ptr,
1561 removeOperandDeaths(a, to_remove, inst, .{ o.vector_ptr, extra.lhs, extra.rhs });1665 };
1562 },
15631666
1564 .arg,1667 const then_body = switch (inst_type) {
1565 .alloc,1668 .cond_br => a.air.extra[extra.end..][0..extra.data.then_body_len],
1566 .ret_ptr,1669 else => {}, // we won't use this
1567 .constant,1670 };
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 => {},
15881671
1589 .not,1672 const else_body = switch (inst_type) {
1590 .bitcast,1673 .cond_br => a.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len],
1591 .load,1674 .@"try", .try_ptr => a.air.extra[extra.end..][0..extra.data.body_len],
1592 .fpext,1675 };
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 },
16341676
1635 .is_null,1677 switch (pass) {
1636 .is_non_null,1678 .loop_analysis => {
1637 .is_null_ptr,1679 switch (inst_type) {
1638 .is_non_null_ptr,1680 .cond_br => try analyzeBody(a, pass, data, then_body),
1639 .is_err,1681 .@"try", .try_ptr => {},
1640 .is_non_err,1682 }
1641 .is_err_ptr,1683 try analyzeBody(a, pass, data, else_body);
1642 .is_non_err_ptr,1684 },
1643 .ptrtoint,1685
1644 .bool_to_int,1686 .main_analysis => {
1645 .ret,1687 var then_info: ControlBranchInfo = switch (inst_type) {
1646 .ret_load,1688 .cond_br => try analyzeBodyResetBranch(a, pass, data, then_body),
1647 .is_named_enum_value,1689 .@"try", .try_ptr => blk: {
1648 .tag_name,1690 var branch_deaths = try data.branch_deaths.clone(gpa);
1649 .error_name,1691 errdefer branch_deaths.deinit(gpa);
1650 .sqrt,1692 var live_set = try data.live_set.clone(gpa);
1651 .sin,1693 errdefer live_set.deinit(gpa);
1652 .cos,1694 break :blk .{
1653 .tan,1695 .branch_deaths = branch_deaths,
1654 .exp,1696 .live_set = live_set,
1655 .exp2,1697 };
1656 .log,1698 },
1657 .log2,1699 };
1658 .log10,1700 defer then_info.branch_deaths.deinit(gpa);
1659 .fabs,1701 defer then_info.live_set.deinit(gpa);
1660 .floor,1702
1661 .ceil,1703 // If this is a `try`, the "then body" (rest of the branch) might have referenced our
1662 .round,1704 // result. If so, we want to avoid this value being considered live while analyzing the
1663 .trunc_float,1705 // else branch.
1664 .neg,1706 switch (inst_type) {
1665 .neg_optimized,1707 .cond_br => {},
1666 .cmp_lt_errors_len,1708 .@"try", .try_ptr => _ = data.live_set.remove(inst),
1667 .set_err_return_trace,1709 }
1668 .c_va_end,
1669 => {
1670 const operand = inst_datas[inst].un_op;
1671 removeOperandDeaths(a, to_remove, inst, .{ operand, .none, .none });
1672 },
16731710
1674 .add_with_overflow,1711 try analyzeBody(a, pass, data, else_body);
1675 .sub_with_overflow,1712 var else_info: ControlBranchInfo = .{
1676 .mul_with_overflow,1713 .branch_deaths = data.branch_deaths.move(),
1677 .shl_with_overflow,1714 .live_set = data.live_set.move(),
1678 .ptr_add,1715 };
1679 .ptr_sub,1716 defer else_info.branch_deaths.deinit(gpa);
1680 .ptr_elem_ptr,1717 defer else_info.live_set.deinit(gpa);
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 },
16881718
1689 .dbg_var_ptr,1719 // Any queued deaths shared between both branches can be queued for us instead
1690 .dbg_var_val,1720 {
1691 => {1721 var it = then_info.branch_deaths.keyIterator();
1692 const operand = inst_datas[inst].pl_op.operand;1722 while (it.next()) |key| {
1693 removeOperandDeaths(a, to_remove, inst, .{ operand, .none, .none });1723 const death = key.*;
1694 },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 => {1737 log.debug("[{}] %{}: remaining 'then' branch deaths are {}", .{ pass, inst, fmtInstSet(&then_info.branch_deaths) });
1697 const prefetch = inst_datas[inst].prefetch;1738 log.debug("[{}] %{}: remaining 'else' branch deaths are {}", .{ pass, inst, fmtInstSet(&else_info.branch_deaths) });
1698 removeOperandDeaths(a, to_remove, inst, .{ prefetch.ptr, .none, .none });
1699 },
17001739
1701 .call, .call_always_tail, .call_never_tail, .call_never_inline => {1740 // Deaths that occur in one branch but not another need to be made to occur at the start
1702 const inst_data = inst_datas[inst].pl_op;1741 // of the other branch.
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]);
17061742
1707 var death_remover = BigTombDeathRemover.init(a, to_remove, inst);1743 var then_mirrored_deaths: std.ArrayListUnmanaged(Air.Inst.Index) = .{};
1708 death_remover.feed(callee);1744 defer then_mirrored_deaths.deinit(gpa);
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]);
17361745
1737 var death_remover = BigTombDeathRemover.init(a, to_remove, inst);1746 var else_mirrored_deaths: std.ArrayListUnmanaged(Air.Inst.Index) = .{};
1738 for (elements) |elem| {1747 defer else_mirrored_deaths.deinit(gpa);
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 },
17801748
1781 .br => {1749 // Note: this invalidates `else_info.live_set`, but expands `then_info.live_set` to
1782 const br = inst_datas[inst].br;1750 // be their union
1783 removeOperandDeaths(a, to_remove, inst, .{ br.operand, .none, .none });1751 {
1784 },1752 var it = then_info.live_set.keyIterator();
1785 .assembly => {1753 while (it.next()) |key| {
1786 const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload);1754 const death = key.*;
1787 var extra_i: usize = extra.end;1755 if (else_info.live_set.remove(death)) continue; // removing makes the loop below faster
1788 const outputs = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]);1756 if (else_info.branch_deaths.contains(death)) continue;
1789 extra_i += outputs.len;1757
1790 const inputs = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]);1758 // If this is a `try`, the "then body" (rest of the branch) might have
1791 extra_i += inputs.len;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);1766 try else_mirrored_deaths.append(gpa, death);
1794 for (outputs) |output| {1767 }
1795 if (output != .none) {1768 // Since we removed common stuff above, `else_info.live_set` is now only operands
1796 death_remover.feed(output);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, {});
1797 }1778 }
1798 }1779 }
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 {1781 log.debug("[{}] %{}: 'then' branch mirrored deaths are {}", .{ pass, inst, fmtInstList(then_mirrored_deaths.items) });
1814 try removeDeaths(a, to_remove, body);1782 log.debug("[{}] %{}: 'else' branch mirrored deaths are {}", .{ pass, inst, fmtInstList(else_mirrored_deaths.items) });
1815 return;
1816 };
18171783
1818 const death_count = a.extra.items[liveness_extra_idx];1784 data.live_set.deinit(gpa);
1819 var deaths = a.extra.items[liveness_extra_idx + 1 ..][0..death_count];1785 data.live_set = then_info.live_set.move();
18201786
1821 // Remove any deaths in `to_remove` from this loop's deaths1787 log.debug("[{}] %{}: new live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });
1822 deaths.len = removeExtraDeaths(to_remove, deaths);
1823 a.extra.items[liveness_extra_idx] = @intCast(u32, deaths.len);
18241788
1825 // Temporarily add any deaths of ours to `to_remove`1789 // Write the branch deaths to `extra`
1826 try to_remove.ensureUnusedCapacity(a.gpa, @intCast(u32, deaths.len));1790 const then_death_count = then_info.branch_deaths.count() + @intCast(u32, then_mirrored_deaths.items.len);
1827 for (deaths) |d| {1791 const else_death_count = else_info.branch_deaths.count() + @intCast(u32, else_mirrored_deaths.items.len);
1828 to_remove.putAssumeCapacity(d, {});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.*);
1829 }1802 }
1830 try removeDeaths(a, to_remove, body);1803 a.extra.appendSliceAssumeCapacity(else_mirrored_deaths.items);
1831 for (deaths) |d| {1804 {
1832 _ = to_remove.remove(d);1805 var it = else_info.branch_deaths.keyIterator();
1806 while (it.next()) |key| a.extra.appendAssumeCapacity(key.*);
1833 }1807 }
1808 try a.special.put(gpa, inst, extra_index);
1834 },1809 },
1835 .@"try" => {1810 }
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 }
18741811
1875 try removeDeaths(a, to_remove, then_body);1812 try analyzeOperands(a, pass, data, inst, .{ condition, .none, .none });
1876 try removeDeaths(a, to_remove, else_body);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 }
1879 },1841 },
1880 .switch_br => {1842
1881 const pl_op = inst_datas[inst].pl_op;1843 .main_analysis => {
1882 const condition = pl_op.operand;1844 // This is, all in all, just a messier version of the `cond_br` logic. If you're trying
1883 const switch_br = a.air.extraData(Air.SwitchBr, pl_op.payload);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
1885 var air_extra_index: usize = switch_br.end;1859 var air_extra_index: usize = switch_br.end;
1886 for (0..switch_br.data.cases_len) |_| {1860 for (case_infos[0..ncases]) |*info| {
1887 const case = a.air.extraData(Air.SwitchBr.Case, air_extra_index);1861 const case = a.air.extraData(Air.SwitchBr.Case, air_extra_index);
1888 const case_body = a.air.extra[case.end + case.data.items_len ..][0..case.data.body_len];1862 const case_body = a.air.extra[case.end + case.data.items_len ..][0..case.data.body_len];
1889 air_extra_index = case.end + case.data.items_len + case_body.len;1863 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);
1891 }1865 }
1892 { // else1866 { // else
1893 const else_body = a.air.extra[air_extra_index..][0..switch_br.data.else_body_len];1867 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 };
1895 }1873 }
18961874
1897 if (a.special.get(inst)) |liveness_extra_idx| {1875 // Queued deaths common to all cases can be bubbled up
1898 const else_death_count = a.extra.items[liveness_extra_idx];1876 {
1899 var read_idx = liveness_extra_idx + 1;1877 // We can't remove from the set we're iterating over, so we'll store the shared deaths here
1900 var write_idx = read_idx; // write_idx <= read_idx always1878 // temporarily to remove them
1901 for (0..switch_br.data.cases_len) |_| {1879 var shared_deaths: DeathSet = .{};
1902 const case_death_count = a.extra.items[read_idx];1880 defer shared_deaths.deinit(gpa);
1903 const case_deaths = a.extra.items[read_idx + 1 ..][0..case_death_count];1881
1904 const new_death_count = removeExtraDeaths(to_remove, case_deaths);1882 var it = case_infos[0].branch_deaths.keyIterator();
1905 a.extra.items[write_idx] = new_death_count;1883 while (it.next()) |key| {
1906 if (write_idx < read_idx) {1884 const death = key.*;
1907 std.mem.copy(u32, a.extra.items[write_idx + 1 ..], a.extra.items[read_idx + 1 ..][0..new_death_count]);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);
1908 }1899 }
1909 read_idx += 1 + case_death_count;
1910 write_idx += 1 + new_death_count;
1911 }1900 }
1912 const else_deaths = a.extra.items[read_idx..][0..else_death_count];1901
1913 const new_else_death_count = removeExtraDeaths(to_remove, else_deaths);1902 for (case_infos, 0..) |*info, i| {
1914 a.extra.items[liveness_extra_idx] = new_else_death_count;1903 log.debug("[{}] %{}: case {} remaining branch deaths are {}", .{ pass, inst, i, fmtInstSet(&info.branch_deaths) });
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]);
1917 }1904 }
1918 }1905 }
19191906
1920 removeOperandDeaths(a, to_remove, inst, .{ condition, .none, .none });1907 const mirrored_deaths = try gpa.alloc(DeathList, ncases + 1);
1921 },1908 defer gpa.free(mirrored_deaths);
1922 .wasm_memory_grow => {1909
1923 const pl_op = inst_datas[inst].pl_op;1910 std.mem.set(DeathList, mirrored_deaths, .{});
1924 removeOperandDeaths(a, to_remove, inst, .{ pl_op.operand, .none, .none });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);
1925 },1969 },
1926 }1970 }
1971
1972 try analyzeOperands(a, pass, data, inst, .{ condition, .none, .none });
1927}1973}
19281974
1929fn removeOperandDeaths(1975fn AnalyzeBigOperands(comptime pass: LivenessPass) type {
1930 a: *Analysis,1976 return struct {
1931 to_remove: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void),1977 a: *Analysis,
1932 inst: Air.Inst.Index,1978 data: *LivenessPassData(pass),
1933 operands: [bpi - 1]Air.Inst.Ref,1979 inst: Air.Inst.Index,
1934) void {1980
1935 const usize_index = (inst * bpi) / @bitSizeOf(usize);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] >>2005 std.mem.set(u32, extra_tombs, 0);
1938 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi));
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| {2012 return .{
1943 const mask = @as(Bpi, 1) << @intCast(OperandInt, i);2013 .a = a,
1944 const op_int = @enumToInt(op_ref);2014 .data = data,
1945 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;2015 .inst = inst,
1946 const operand: Air.Inst.Index = op_int - @intCast(u32, Air.Inst.Ref.typed_value_map.len);2016 .operands_remaining = @intCast(u32, total_operands),
1947 if ((cur_tomb & mask) != 0 and to_remove.contains(operand)) {2017 .extra_tombs = extra_tombs,
1948 log.debug("remove death of %{} in %{}", .{ operand, inst });2018 .will_die_immediately = will_die_immediately,
1949 toggle_bits ^= mask;2019 };
1950 }2020 }
1951 }
19522021
1953 a.tomb_bits[usize_index] ^= @as(usize, toggle_bits) <<2022 /// Must be called with operands in reverse order.
1954 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi);2023 fn feed(big: *Self, op_ref: Air.Inst.Ref) !void {
1955}2024 // Note that after this, `operands_remaining` becomes the index of the current operand
2025 big.operands_remaining -= 1;
19562026
1957fn removeExtraDeaths(2027 if (big.operands_remaining < bpi - 1) {
1958 to_remove: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void),2028 big.small[big.operands_remaining] = op_ref;
1959 deaths: []Air.Inst.Index,2029 return;
1960) u32 {2030 }
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}
19742031
1975const BigTombDeathRemover = struct {2032 const operand = Air.refToIndex(op_ref) orelse return;
1976 a: *Analysis,
1977 to_remove: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void),
1978 inst: Air.Inst.Index,
19792033
1980 operands: [bpi - 1]Air.Inst.Ref = .{.none} ** (bpi - 1),2034 // Don't compute any liveness for constants
1981 next_oper: OperandInt = 0,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,2041 // If our result is unused and the instruction doesn't need to be lowered, backends will
1984 // Initialized once we finish the small tomb operands: see `feed`2042 // skip the lowering of this instruction, so we don't want to record uses of operands.
1985 extra_start: u32 = undefined,2043 // That way, we can mark as many instructions as possible unused.
1986 extra_offset: u32 = 0,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 {2046 const extra_byte = (big.operands_remaining - (bpi - 1)) / 31;
1989 return .{2047 const extra_bit = @intCast(u5, big.operands_remaining - (bpi - 1) - extra_byte * 31);
1990 .a = a,2048
1991 .to_remove = to_remove,2049 const gpa = big.a.gpa;
1992 .inst = inst,2050
1993 };2051 switch (pass) {
1994 }2052 .loop_analysis => {
2053 _ = try big.data.live_set.put(gpa, operand, {});
2054 },
19952055
1996 fn feed(dr: *BigTombDeathRemover, operand: Air.Inst.Ref) void {2056 .main_analysis => {
1997 if (dr.next_oper < bpi - 1) {2057 if ((try big.data.live_set.fetchPut(gpa, operand, {})) == null) {
1998 dr.operands[dr.next_oper] = operand;2058 log.debug("[{}] %{}: added %{} to live set (operand dies here)", .{ pass, big.inst, operand });
1999 dr.next_oper += 1;2059 big.extra_tombs[extra_byte] |= @as(u32, 1) << extra_bit;
2000 if (dr.next_oper == bpi - 1) {2060 if (big.data.branch_deaths.remove(operand)) {
2001 removeOperandDeaths(dr.a, dr.to_remove, dr.inst, dr.operands);2061 log.debug("[{}] %{}: resolved branch death of %{} to this usage", .{ pass, big.inst, operand });
2002 if (dr.a.special.get(dr.inst)) |idx| dr.extra_start = idx;2062 }
2063 }
2064 },
2003 }2065 }
2004 return;
2005 }2066 }
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);2093 const extra_tombs = big.extra_tombs[0..num];
2010 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
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) {2101 try analyzeOperands(big.a, pass, big.data, big.inst, big.small);
2015 dr.extra_offset += 1;
2016 }2102 }
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)) {2104 fn deinit(big: *Self) void {
2021 log.debug("remove big death of %{}", .{op_inst});2105 big.a.gpa.free(big.extra_tombs);
2022 dr.a.extra.items[dr.extra_start + dr.extra_offset] ^=2106 }
2023 (@as(u32, 1) << @intCast(u5, dr.bit_index - dr.extra_offset * 31));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.*});
2024 }2126 }
2025 }2127 }
2128};
2129
2130fn fmtInstList(list: []const Air.Inst.Index) FmtInstList {
2131 return .{ .list = list };
2132}
20262133
2027 fn finish(dr: *BigTombDeathRemover) void {2134const FmtInstList = struct {
2028 if (dr.next_oper < bpi) {2135 list: []const Air.Inst.Index,
2029 removeOperandDeaths(dr.a, dr.to_remove, dr.inst, dr.operands);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});
2030 }2145 }
2031 }2146 }
2032};2147};
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 {...@@ -483,6 +483,8 @@ pub const Decl = struct {
483 /// and attempting semantic analysis again may succeed.483 /// and attempting semantic analysis again may succeed.
484 sema_failure_retryable,484 sema_failure_retryable,
485 /// There will be a corresponding ErrorMsg in Module.failed_decls.485 /// There will be a corresponding ErrorMsg in Module.failed_decls.
486 liveness_failure,
487 /// There will be a corresponding ErrorMsg in Module.failed_decls.
486 codegen_failure,488 codegen_failure,
487 /// There will be a corresponding ErrorMsg in Module.failed_decls.489 /// There will be a corresponding ErrorMsg in Module.failed_decls.
488 /// This indicates the failure was something like running out of disk space,490 /// 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 {...@@ -4129,6 +4131,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
4129 .file_failure,4131 .file_failure,
4130 .sema_failure,4132 .sema_failure,
4131 .sema_failure_retryable,4133 .sema_failure_retryable,
4134 .liveness_failure,
4132 .codegen_failure,4135 .codegen_failure,
4133 .dependency_failure,4136 .dependency_failure,
4134 .codegen_failure_retryable,4137 .codegen_failure_retryable,
...@@ -4222,6 +4225,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {...@@ -4222,6 +4225,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
4222 .dependency_failure,4225 .dependency_failure,
4223 .sema_failure,4226 .sema_failure,
4224 .sema_failure_retryable,4227 .sema_failure_retryable,
4228 .liveness_failure,
4225 .codegen_failure,4229 .codegen_failure,
4226 .codegen_failure_retryable,4230 .codegen_failure_retryable,
4227 .complete,4231 .complete,
...@@ -4247,6 +4251,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {...@@ -4247,6 +4251,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
42474251
4248 .file_failure,4252 .file_failure,
4249 .sema_failure,4253 .sema_failure,
4254 .liveness_failure,
4250 .codegen_failure,4255 .codegen_failure,
4251 .dependency_failure,4256 .dependency_failure,
4252 .sema_failure_retryable,4257 .sema_failure_retryable,
...@@ -4306,6 +4311,33 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {...@@ -4306,6 +4311,33 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
4306 std.debug.print("# End Function AIR: {s}\n\n", .{fqn});4311 std.debug.print("# End Function AIR: {s}\n\n", .{fqn});
4307 }4312 }
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
4309 if (no_bin_file and !dump_llvm_ir) return;4341 if (no_bin_file and !dump_llvm_ir) return;
43104342
4311 comp.bin_file.updateFunc(mod, func, air, liveness) catch |err| switch (err) {4343 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 {...@@ -4349,6 +4381,7 @@ pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void {
4349 .dependency_failure,4381 .dependency_failure,
4350 .sema_failure,4382 .sema_failure,
4351 .sema_failure_retryable,4383 .sema_failure_retryable,
4384 .liveness_failure,
4352 .codegen_failure,4385 .codegen_failure,
4353 .codegen_failure_retryable,4386 .codegen_failure_retryable,
4354 .complete,4387 .complete,
src/arch/aarch64/CodeGen.zig+5-6
...@@ -655,6 +655,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -655,6 +655,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
655 const air_tags = self.air.instructions.items(.tag);655 const air_tags = self.air.instructions.items(.tag);
656656
657 for (body) |inst| {657 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
658 const old_air_bookkeeping = self.air_bookkeeping;663 const old_air_bookkeeping = self.air_bookkeeping;
659 try self.ensureProcessDeathCapacity(Liveness.bpi);664 try self.ensureProcessDeathCapacity(Liveness.bpi);
660665
...@@ -5000,17 +5005,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {...@@ -5000,17 +5005,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
5000 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;5005 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5001 const loop = self.air.extraData(Air.Block, ty_pl.payload);5006 const loop = self.air.extraData(Air.Block, ty_pl.payload);
5002 const body = self.air.extra[loop.end..][0..loop.data.body_len];5007 const body = self.air.extra[loop.end..][0..loop.data.body_len];
5003 const liveness_loop = self.liveness.getLoop(inst);
5004 const start_index = @intCast(u32, self.mir_instructions.len);5008 const start_index = @intCast(u32, self.mir_instructions.len);
50055009
5006 try self.genBody(body);5010 try self.genBody(body);
5007 try self.jump(start_index);5011 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
5014 return self.finishAirBookkeeping();5013 return self.finishAirBookkeeping();
5015}5014}
50165015
src/arch/arm/CodeGen.zig+5-6
...@@ -639,6 +639,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -639,6 +639,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
639 const air_tags = self.air.instructions.items(.tag);639 const air_tags = self.air.instructions.items(.tag);
640640
641 for (body) |inst| {641 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
642 const old_air_bookkeeping = self.air_bookkeeping;647 const old_air_bookkeeping = self.air_bookkeeping;
643 try self.ensureProcessDeathCapacity(Liveness.bpi);648 try self.ensureProcessDeathCapacity(Liveness.bpi);
644649
...@@ -4923,17 +4928,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {...@@ -4923,17 +4928,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
4923 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;4928 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4924 const loop = self.air.extraData(Air.Block, ty_pl.payload);4929 const loop = self.air.extraData(Air.Block, ty_pl.payload);
4925 const body = self.air.extra[loop.end..][0..loop.data.body_len];4930 const body = self.air.extra[loop.end..][0..loop.data.body_len];
4926 const liveness_loop = self.liveness.getLoop(inst);
4927 const start_index = @intCast(Mir.Inst.Index, self.mir_instructions.len);4931 const start_index = @intCast(Mir.Inst.Index, self.mir_instructions.len);
49284932
4929 try self.genBody(body);4933 try self.genBody(body);
4930 try self.jump(start_index);4934 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
4937 return self.finishAirBookkeeping();4936 return self.finishAirBookkeeping();
4938}4937}
49394938
src/arch/riscv64/CodeGen.zig+5
...@@ -473,6 +473,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -473,6 +473,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
473 const air_tags = self.air.instructions.items(.tag);473 const air_tags = self.air.instructions.items(.tag);
474474
475 for (body) |inst| {475 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
476 const old_air_bookkeeping = self.air_bookkeeping;481 const old_air_bookkeeping = self.air_bookkeeping;
477 try self.ensureProcessDeathCapacity(Liveness.bpi);482 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 {...@@ -489,6 +489,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
489 const air_tags = self.air.instructions.items(.tag);489 const air_tags = self.air.instructions.items(.tag);
490490
491 for (body) |inst| {491 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
492 const old_air_bookkeeping = self.air_bookkeeping;497 const old_air_bookkeeping = self.air_bookkeeping;
493 try self.ensureProcessDeathCapacity(Liveness.bpi);498 try self.ensureProcessDeathCapacity(Liveness.bpi);
494499
...@@ -1750,17 +1755,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {...@@ -1750,17 +1755,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
1750 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1755 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1751 const loop = self.air.extraData(Air.Block, ty_pl.payload);1756 const loop = self.air.extraData(Air.Block, ty_pl.payload);
1752 const body = self.air.extra[loop.end .. loop.end + loop.data.body_len];1757 const body = self.air.extra[loop.end .. loop.end + loop.data.body_len];
1753 const liveness_loop = self.liveness.getLoop(inst);
1754 const start = @intCast(u32, self.mir_instructions.len);1758 const start = @intCast(u32, self.mir_instructions.len);
17551759
1756 try self.genBody(body);1760 try self.genBody(body);
1757 try self.jump(start);1761 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
1764 return self.finishAirBookkeeping();1763 return self.finishAirBookkeeping();
1765}1764}
17661765
src/arch/wasm/CodeGen.zig+11-88
...@@ -2009,9 +2009,11 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2009,9 +2009,11 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
20092009
2010fn genBody(func: *CodeGen, body: []const Air.Inst.Index) InnerError!void {2010fn genBody(func: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
2011 for (body) |inst| {2011 for (body) |inst| {
2012 if (func.liveness.isUnused(inst) and !func.air.mustLower(inst)) {
2013 continue;
2014 }
2012 const old_bookkeeping_value = func.air_bookkeeping;2015 const old_bookkeeping_value = func.air_bookkeeping;
2013 // TODO: Determine why we need to pre-allocate an extra 4 possible values here.2016 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi);
2014 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi + 4);
2015 try func.genInst(inst);2017 try func.genInst(inst);
20162018
2017 if (builtin.mode == .Debug and func.air_bookkeeping < old_bookkeeping_value + 1) {2019 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...@@ -2185,7 +2187,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
2185 }2187 }
21862188
2187 const result_value = result_value: {2189 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()) {
2189 break :result_value WValue{ .none = {} };2191 break :result_value WValue{ .none = {} };
2190 } else if (ret_ty.isNoReturn()) {2192 } else if (ret_ty.isNoReturn()) {
2191 try func.addTag(.@"unreachable");2193 try func.addTag(.@"unreachable");
...@@ -2494,7 +2496,6 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2494,7 +2496,6 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
24942496
2495fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {2497fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
2496 const bin_op = func.air.instructions.items(.data)[inst].bin_op;2498 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 });
2498 const lhs = try func.resolveInst(bin_op.lhs);2499 const lhs = try func.resolveInst(bin_op.lhs);
2499 const rhs = try func.resolveInst(bin_op.rhs);2500 const rhs = try func.resolveInst(bin_op.rhs);
2500 const ty = func.air.typeOf(bin_op.lhs);2501 const ty = func.air.typeOf(bin_op.lhs);
...@@ -2649,7 +2650,6 @@ const FloatOp = enum {...@@ -2649,7 +2650,6 @@ const FloatOp = enum {
26492650
2650fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void {2651fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void {
2651 const un_op = func.air.instructions.items(.data)[inst].un_op;2652 const un_op = func.air.instructions.items(.data)[inst].un_op;
2652 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
2653 const operand = try func.resolveInst(un_op);2653 const operand = try func.resolveInst(un_op);
2654 const ty = func.air.typeOf(un_op);2654 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...@@ -2723,7 +2723,6 @@ fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) In
27232723
2724fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {2724fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
2725 const bin_op = func.air.instructions.items(.data)[inst].bin_op;2725 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
2728 const lhs = try func.resolveInst(bin_op.lhs);2727 const lhs = try func.resolveInst(bin_op.lhs);
2729 const rhs = try func.resolveInst(bin_op.rhs);2728 const rhs = try func.resolveInst(bin_op.rhs);
...@@ -3183,7 +3182,6 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3183,7 +3182,6 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3183 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;3182 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
3184 const loop = func.air.extraData(Air.Block, ty_pl.payload);3183 const loop = func.air.extraData(Air.Block, ty_pl.payload);
3185 const body = func.air.extra[loop.end..][0..loop.data.body_len];3184 const body = func.air.extra[loop.end..][0..loop.data.body_len];
3186 const liveness_loop = func.liveness.getLoop(inst);
31873185
3188 // result type of loop is always 'noreturn', meaning we can always3186 // result type of loop is always 'noreturn', meaning we can always
3189 // emit the wasm type 'block_empty'.3187 // emit the wasm type 'block_empty'.
...@@ -3194,11 +3192,6 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3194,11 +3192,6 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3194 try func.addLabel(.br, 0);3192 try func.addLabel(.br, 0);
3195 try func.endBlock();3193 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
3202 func.finishAir(inst, .none, &.{});3195 func.finishAir(inst, .none, &.{});
3203}3196}
32043197
...@@ -3224,9 +3217,6 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3224,9 +3217,6 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
32243217
3225 func.branches.appendAssumeCapacity(.{});3218 func.branches.appendAssumeCapacity(.{});
3226 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.else_deaths.len));3219 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 }
3230 try func.genBody(else_body);3220 try func.genBody(else_body);
3231 try func.endBlock();3221 try func.endBlock();
3232 var else_stack = func.branches.pop();3222 var else_stack = func.branches.pop();
...@@ -3235,9 +3225,6 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3235,9 +3225,6 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3235 // Outer block that matches the condition3225 // Outer block that matches the condition
3236 func.branches.appendAssumeCapacity(.{});3226 func.branches.appendAssumeCapacity(.{});
3237 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.then_deaths.len));3227 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 }
3241 try func.genBody(then_body);3228 try func.genBody(then_body);
3242 var then_stack = func.branches.pop();3229 var then_stack = func.branches.pop();
3243 defer then_stack.deinit(func.gpa);3230 defer then_stack.deinit(func.gpa);
...@@ -3255,7 +3242,7 @@ fn mergeBranch(func: *CodeGen, branch: *const Branch) !void {...@@ -3255,7 +3242,7 @@ fn mergeBranch(func: *CodeGen, branch: *const Branch) !void {
3255 const target_keys = target_slice.items(.key);3242 const target_keys = target_slice.items(.key);
3256 const target_values = target_slice.items(.value);3243 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());
3259 for (target_keys, 0..) |key, index| {3246 for (target_keys, 0..) |key, index| {
3260 // TODO: process deaths from branches3247 // TODO: process deaths from branches
3261 parent.values.putAssumeCapacity(key, target_values[index]);3248 parent.values.putAssumeCapacity(key, target_values[index]);
...@@ -3264,7 +3251,6 @@ fn mergeBranch(func: *CodeGen, branch: *const Branch) !void {...@@ -3264,7 +3251,6 @@ fn mergeBranch(func: *CodeGen, branch: *const Branch) !void {
32643251
3265fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {3252fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {
3266 const bin_op = func.air.instructions.items(.data)[inst].bin_op;3253 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
3269 const lhs = try func.resolveInst(bin_op.lhs);3255 const lhs = try func.resolveInst(bin_op.lhs);
3270 const rhs = try func.resolveInst(bin_op.rhs);3256 const rhs = try func.resolveInst(bin_op.rhs);
...@@ -3381,7 +3367,6 @@ fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3381,7 +3367,6 @@ fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
33813367
3382fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3368fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3383 const ty_op = func.air.instructions.items(.data)[inst].ty_op;3369 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
3386 const operand = try func.resolveInst(ty_op.operand);3371 const operand = try func.resolveInst(ty_op.operand);
3387 const operand_ty = func.air.typeOf(ty_op.operand);3372 const operand_ty = func.air.typeOf(ty_op.operand);
...@@ -3447,7 +3432,7 @@ fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3447,7 +3432,7 @@ fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
34473432
3448fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3433fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3449 const ty_op = func.air.instructions.items(.data)[inst].ty_op;3434 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3450 const result = if (!func.liveness.isUnused(inst)) result: {3435 const result = result: {
3451 const operand = try func.resolveInst(ty_op.operand);3436 const operand = try func.resolveInst(ty_op.operand);
3452 const wanted_ty = func.air.typeOfIndex(inst);3437 const wanted_ty = func.air.typeOfIndex(inst);
3453 const given_ty = func.air.typeOf(ty_op.operand);3438 const given_ty = func.air.typeOf(ty_op.operand);
...@@ -3456,7 +3441,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3456,7 +3441,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3456 break :result try bitcast_result.toLocal(func, wanted_ty);3441 break :result try bitcast_result.toLocal(func, wanted_ty);
3457 }3442 }
3458 break :result func.reuseOperand(ty_op.operand, operand);3443 break :result func.reuseOperand(ty_op.operand, operand);
3459 } else WValue{ .none = {} };3444 };
3460 func.finishAir(inst, result, &.{ty_op.operand});3445 func.finishAir(inst, result, &.{ty_op.operand});
3461}3446}
34623447
...@@ -3480,7 +3465,6 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn...@@ -3480,7 +3465,6 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn
3480fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3465fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3481 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;3466 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
3482 const extra = func.air.extraData(Air.StructField, ty_pl.payload);3467 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
3485 const struct_ptr = try func.resolveInst(extra.data.struct_operand);3469 const struct_ptr = try func.resolveInst(extra.data.struct_operand);
3486 const struct_ty = func.air.typeOf(extra.data.struct_operand).childType();3470 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 {...@@ -3490,7 +3474,6 @@ fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
34903474
3491fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void {3475fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void {
3492 const ty_op = func.air.instructions.items(.data)[inst].ty_op;3476 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});
3494 const struct_ptr = try func.resolveInst(ty_op.operand);3477 const struct_ptr = try func.resolveInst(ty_op.operand);
3495 const struct_ty = func.air.typeOf(ty_op.operand).childType();3478 const struct_ty = func.air.typeOf(ty_op.operand).childType();
34963479
...@@ -3535,7 +3518,6 @@ fn structFieldPtr(...@@ -3535,7 +3518,6 @@ fn structFieldPtr(
3535fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3518fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3536 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;3519 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
3537 const struct_field = func.air.extraData(Air.StructField, ty_pl.payload).data;3520 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
3540 const struct_ty = func.air.typeOf(struct_field.struct_operand);3522 const struct_ty = func.air.typeOf(struct_field.struct_operand);
3541 const operand = try func.resolveInst(struct_field.struct_operand);3523 const operand = try func.resolveInst(struct_field.struct_operand);
...@@ -3801,7 +3783,6 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3801,7 +3783,6 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
38013783
3802fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void {3784fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void {
3803 const un_op = func.air.instructions.items(.data)[inst].un_op;3785 const un_op = func.air.instructions.items(.data)[inst].un_op;
3804 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
3805 const operand = try func.resolveInst(un_op);3786 const operand = try func.resolveInst(un_op);
3806 const err_union_ty = func.air.typeOf(un_op);3787 const err_union_ty = func.air.typeOf(un_op);
3807 const pl_ty = err_union_ty.errorUnionPayload();3788 const pl_ty = err_union_ty.errorUnionPayload();
...@@ -3836,7 +3817,6 @@ fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerErro...@@ -3836,7 +3817,6 @@ fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerErro
38363817
3837fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {3818fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
3838 const ty_op = func.air.instructions.items(.data)[inst].ty_op;3819 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
3841 const operand = try func.resolveInst(ty_op.operand);3821 const operand = try func.resolveInst(ty_op.operand);
3842 const op_ty = func.air.typeOf(ty_op.operand);3822 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...@@ -3859,7 +3839,6 @@ fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: boo
38593839
3860fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {3840fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
3861 const ty_op = func.air.instructions.items(.data)[inst].ty_op;3841 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
3864 const operand = try func.resolveInst(ty_op.operand);3843 const operand = try func.resolveInst(ty_op.operand);
3865 const op_ty = func.air.typeOf(ty_op.operand);3844 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)...@@ -3883,7 +3862,6 @@ fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool)
38833862
3884fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3863fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3885 const ty_op = func.air.instructions.items(.data)[inst].ty_op;3864 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
3888 const operand = try func.resolveInst(ty_op.operand);3866 const operand = try func.resolveInst(ty_op.operand);
3889 const err_ty = func.air.typeOfIndex(inst);3867 const err_ty = func.air.typeOfIndex(inst);
...@@ -3910,7 +3888,6 @@ fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void...@@ -3910,7 +3888,6 @@ fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void
39103888
3911fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3889fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3912 const ty_op = func.air.instructions.items(.data)[inst].ty_op;3890 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
3915 const operand = try func.resolveInst(ty_op.operand);3892 const operand = try func.resolveInst(ty_op.operand);
3916 const err_ty = func.air.getRefType(ty_op.ty);3893 const err_ty = func.air.getRefType(ty_op.ty);
...@@ -3937,7 +3914,6 @@ fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3937,7 +3914,6 @@ fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
39373914
3938fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3915fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3939 const ty_op = func.air.instructions.items(.data)[inst].ty_op;3916 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
3942 const ty = func.air.getRefType(ty_op.ty);3918 const ty = func.air.getRefType(ty_op.ty);
3943 const operand = try func.resolveInst(ty_op.operand);3919 const operand = try func.resolveInst(ty_op.operand);
...@@ -4004,7 +3980,6 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro...@@ -4004,7 +3980,6 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
40043980
4005fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {3981fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
4006 const un_op = func.air.instructions.items(.data)[inst].un_op;3982 const un_op = func.air.instructions.items(.data)[inst].un_op;
4007 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
4008 const operand = try func.resolveInst(un_op);3983 const operand = try func.resolveInst(un_op);
40093984
4010 const op_ty = func.air.typeOf(un_op);3985 const op_ty = func.air.typeOf(un_op);
...@@ -4049,7 +4024,7 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4049,7 +4024,7 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4049 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4024 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4050 const opt_ty = func.air.typeOf(ty_op.operand);4025 const opt_ty = func.air.typeOf(ty_op.operand);
4051 const payload_ty = func.air.typeOfIndex(inst);4026 const payload_ty = func.air.typeOfIndex(inst);
4052 if (func.liveness.isUnused(inst) or !payload_ty.hasRuntimeBitsIgnoreComptime()) {4027 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
4053 return func.finishAir(inst, .none, &.{ty_op.operand});4028 return func.finishAir(inst, .none, &.{ty_op.operand});
4054 }4029 }
40554030
...@@ -4069,7 +4044,6 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4069,7 +4044,6 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
40694044
4070fn airOptionalPayloadPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4045fn airOptionalPayloadPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4071 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4046 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});
4073 const operand = try func.resolveInst(ty_op.operand);4047 const operand = try func.resolveInst(ty_op.operand);
4074 const opt_ty = func.air.typeOf(ty_op.operand).childType();4048 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...@@ -4114,7 +4088,6 @@ fn airOptionalPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi
41144088
4115fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4089fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4116 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4090 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});
4118 const payload_ty = func.air.typeOf(ty_op.operand);4091 const payload_ty = func.air.typeOf(ty_op.operand);
41194092
4120 const result = result: {4093 const result = result: {
...@@ -4153,7 +4126,6 @@ fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4153,7 +4126,6 @@ fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4153fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4126fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4154 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;4127 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4155 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;4128 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
4158 const lhs = try func.resolveInst(bin_op.lhs);4130 const lhs = try func.resolveInst(bin_op.lhs);
4159 const rhs = try func.resolveInst(bin_op.rhs);4131 const rhs = try func.resolveInst(bin_op.rhs);
...@@ -4168,7 +4140,6 @@ fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4168,7 +4140,6 @@ fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41684140
4169fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4141fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4170 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4142 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
4173 const operand = try func.resolveInst(ty_op.operand);4144 const operand = try func.resolveInst(ty_op.operand);
4174 const len = try func.load(operand, Type.usize, func.ptrSize());4145 const len = try func.load(operand, Type.usize, func.ptrSize());
...@@ -4178,7 +4149,6 @@ fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4178,7 +4149,6 @@ fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41784149
4179fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4150fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4180 const bin_op = func.air.instructions.items(.data)[inst].bin_op;4151 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
4183 const slice_ty = func.air.typeOf(bin_op.lhs);4153 const slice_ty = func.air.typeOf(bin_op.lhs);
4184 const slice = try func.resolveInst(bin_op.lhs);4154 const slice = try func.resolveInst(bin_op.lhs);
...@@ -4209,7 +4179,6 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4209,7 +4179,6 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4209fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4179fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4210 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;4180 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4211 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;4181 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
4214 const elem_ty = func.air.getRefType(ty_pl.ty).childType();4183 const elem_ty = func.air.getRefType(ty_pl.ty).childType();
4215 const elem_size = elem_ty.abiSize(func.target);4184 const elem_size = elem_ty.abiSize(func.target);
...@@ -4232,7 +4201,6 @@ fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4232,7 +4201,6 @@ fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42324201
4233fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4202fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4234 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4203 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});
4236 const operand = try func.resolveInst(ty_op.operand);4204 const operand = try func.resolveInst(ty_op.operand);
4237 const ptr = try func.load(operand, Type.usize, 0);4205 const ptr = try func.load(operand, Type.usize, 0);
4238 const result = try ptr.toLocal(func, Type.usize);4206 const result = try ptr.toLocal(func, Type.usize);
...@@ -4241,7 +4209,6 @@ fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4241,7 +4209,6 @@ fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
42414209
4242fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4210fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4243 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4211 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
4246 const operand = try func.resolveInst(ty_op.operand);4213 const operand = try func.resolveInst(ty_op.operand);
4247 const wanted_ty = func.air.getRefType(ty_op.ty);4214 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...@@ -4270,19 +4237,14 @@ fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) Inner
42704237
4271fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4238fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4272 const un_op = func.air.instructions.items(.data)[inst].un_op;4239 const un_op = func.air.instructions.items(.data)[inst].un_op;
4273 const result = if (func.liveness.isUnused(inst))4240 const operand = try func.resolveInst(un_op);
4274 WValue{ .none = {} }4241 const result = func.reuseOperand(un_op, operand);
4275 else result: {
4276 const operand = try func.resolveInst(un_op);
4277 break :result func.reuseOperand(un_op, operand);
4278 };
42794242
4280 func.finishAir(inst, result, &.{un_op});4243 func.finishAir(inst, result, &.{un_op});
4281}4244}
42824245
4283fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4246fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4284 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4247 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
4287 const operand = try func.resolveInst(ty_op.operand);4249 const operand = try func.resolveInst(ty_op.operand);
4288 const array_ty = func.air.typeOf(ty_op.operand).childType();4250 const array_ty = func.air.typeOf(ty_op.operand).childType();
...@@ -4305,7 +4267,6 @@ fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4305,7 +4267,6 @@ fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43054267
4306fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4268fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4307 const un_op = func.air.instructions.items(.data)[inst].un_op;4269 const un_op = func.air.instructions.items(.data)[inst].un_op;
4308 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
4309 const operand = try func.resolveInst(un_op);4270 const operand = try func.resolveInst(un_op);
43104271
4311 const result = switch (operand) {4272 const result = switch (operand) {
...@@ -4318,7 +4279,6 @@ fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4318,7 +4279,6 @@ fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43184279
4319fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4280fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4320 const bin_op = func.air.instructions.items(.data)[inst].bin_op;4281 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
4323 const ptr_ty = func.air.typeOf(bin_op.lhs);4283 const ptr_ty = func.air.typeOf(bin_op.lhs);
4324 const ptr = try func.resolveInst(bin_op.lhs);4284 const ptr = try func.resolveInst(bin_op.lhs);
...@@ -4356,7 +4316,6 @@ fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4356,7 +4316,6 @@ fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4356fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4316fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4357 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;4317 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4358 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;4318 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
4361 const ptr_ty = func.air.typeOf(bin_op.lhs);4320 const ptr_ty = func.air.typeOf(bin_op.lhs);
4362 const elem_ty = func.air.getRefType(ty_pl.ty).childType();4321 const elem_ty = func.air.getRefType(ty_pl.ty).childType();
...@@ -4386,7 +4345,6 @@ fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4386,7 +4345,6 @@ fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4386fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {4345fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
4387 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;4346 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4388 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;4347 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
4391 const ptr = try func.resolveInst(bin_op.lhs);4349 const ptr = try func.resolveInst(bin_op.lhs);
4392 const offset = try func.resolveInst(bin_op.rhs);4350 const offset = try func.resolveInst(bin_op.rhs);
...@@ -4510,7 +4468,6 @@ fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!vo...@@ -4510,7 +4468,6 @@ fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!vo
45104468
4511fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4469fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4512 const bin_op = func.air.instructions.items(.data)[inst].bin_op;4470 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
4515 const array_ty = func.air.typeOf(bin_op.lhs);4472 const array_ty = func.air.typeOf(bin_op.lhs);
4516 const array = try func.resolveInst(bin_op.lhs);4473 const array = try func.resolveInst(bin_op.lhs);
...@@ -4579,7 +4536,6 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4579,7 +4536,6 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
45794536
4580fn airFloatToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4537fn airFloatToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4581 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4538 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
4584 const operand = try func.resolveInst(ty_op.operand);4540 const operand = try func.resolveInst(ty_op.operand);
4585 const dest_ty = func.air.typeOfIndex(inst);4541 const dest_ty = func.air.typeOfIndex(inst);
...@@ -4604,7 +4560,6 @@ fn airFloatToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4604,7 +4560,6 @@ fn airFloatToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
46044560
4605fn airIntToFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4561fn airIntToFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4606 const ty_op = func.air.instructions.items(.data)[inst].ty_op;4562 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
4609 const operand = try func.resolveInst(ty_op.operand);4564 const operand = try func.resolveInst(ty_op.operand);
4610 const dest_ty = func.air.typeOfIndex(inst);4565 const dest_ty = func.air.typeOfIndex(inst);
...@@ -4719,10 +4674,6 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4719,10 +4674,6 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4719 const child_ty = inst_ty.childType();4674 const child_ty = inst_ty.childType();
4720 const elem_size = child_ty.abiSize(func.target);4675 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
4726 const module = func.bin_file.base.options.module.?;4677 const module = func.bin_file.base.options.module.?;
4727 // TODO: One of them could be by ref; handle in loop4678 // TODO: One of them could be by ref; handle in loop
4728 if (isByRef(func.air.typeOf(extra.a), func.target) or isByRef(inst_ty, func.target)) {4679 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 {...@@ -4788,7 +4739,6 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4788 const elements = @ptrCast([]const Air.Inst.Ref, func.air.extra[ty_pl.payload..][0..len]);4739 const elements = @ptrCast([]const Air.Inst.Ref, func.air.extra[ty_pl.payload..][0..len]);
47894740
4790 const result: WValue = result_value: {4741 const result: WValue = result_value: {
4791 if (func.liveness.isUnused(inst)) break :result_value WValue.none;
4792 switch (result_ty.zigTypeTag()) {4742 switch (result_ty.zigTypeTag()) {
4793 .Array => {4743 .Array => {
4794 const result = try func.allocStack(result_ty);4744 const result = try func.allocStack(result_ty);
...@@ -4894,7 +4844,6 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4894,7 +4844,6 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4894fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4844fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4895 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;4845 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4896 const extra = func.air.extraData(Air.UnionInit, ty_pl.payload).data;4846 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
4899 const result = result: {4848 const result = result: {
4900 const union_ty = func.air.typeOfIndex(inst);4849 const union_ty = func.air.typeOfIndex(inst);
...@@ -4933,7 +4882,6 @@ fn airPrefetch(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4933,7 +4882,6 @@ fn airPrefetch(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
49334882
4934fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4883fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4935 const pl_op = func.air.instructions.items(.data)[inst].pl_op;4884 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
4938 const result = try func.allocLocal(func.air.typeOfIndex(inst));4886 const result = try func.allocLocal(func.air.typeOfIndex(inst));
4939 try func.addLabel(.memory_size, pl_op.payload);4887 try func.addLabel(.memory_size, pl_op.payload);
...@@ -4943,7 +4891,6 @@ fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4943,7 +4891,6 @@ fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
49434891
4944fn airWasmMemoryGrow(func: *CodeGen, inst: Air.Inst.Index) !void {4892fn airWasmMemoryGrow(func: *CodeGen, inst: Air.Inst.Index) !void {
4945 const pl_op = func.air.instructions.items(.data)[inst].pl_op;4893 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
4948 const operand = try func.resolveInst(pl_op.operand);4895 const operand = try func.resolveInst(pl_op.operand);
4949 const result = try func.allocLocal(func.air.typeOfIndex(inst));4896 const result = try func.allocLocal(func.air.typeOfIndex(inst));
...@@ -5055,7 +5002,6 @@ fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5055,7 +5002,6 @@ fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
50555002
5056fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5003fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5057 const ty_op = func.air.instructions.items(.data)[inst].ty_op;5004 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
5060 const un_ty = func.air.typeOf(ty_op.operand);5006 const un_ty = func.air.typeOf(ty_op.operand);
5061 const tag_ty = func.air.typeOfIndex(inst);5007 const tag_ty = func.air.typeOfIndex(inst);
...@@ -5075,7 +5021,6 @@ fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5075,7 +5021,6 @@ fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
50755021
5076fn airFpext(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5022fn airFpext(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5077 const ty_op = func.air.instructions.items(.data)[inst].ty_op;5023 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
5080 const dest_ty = func.air.typeOfIndex(inst);5025 const dest_ty = func.air.typeOfIndex(inst);
5081 const operand = try func.resolveInst(ty_op.operand);5026 const operand = try func.resolveInst(ty_op.operand);
...@@ -5121,7 +5066,6 @@ fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!...@@ -5121,7 +5066,6 @@ fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!
51215066
5122fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5067fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5123 const ty_op = func.air.instructions.items(.data)[inst].ty_op;5068 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
5126 const dest_ty = func.air.typeOfIndex(inst);5070 const dest_ty = func.air.typeOfIndex(inst);
5127 const operand = try func.resolveInst(ty_op.operand);5071 const operand = try func.resolveInst(ty_op.operand);
...@@ -5162,7 +5106,6 @@ fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro...@@ -5162,7 +5106,6 @@ fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
51625106
5163fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5107fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5164 const ty_op = func.air.instructions.items(.data)[inst].ty_op;5108 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
5167 const err_set_ty = func.air.typeOf(ty_op.operand).childType();5110 const err_set_ty = func.air.typeOf(ty_op.operand).childType();
5168 const payload_ty = err_set_ty.errorUnionPayload();5111 const payload_ty = err_set_ty.errorUnionPayload();
...@@ -5177,8 +5120,6 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi...@@ -5177,8 +5120,6 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi
5177 );5120 );
51785121
5179 const result = result: {5122 const result = result: {
5180 if (func.liveness.isUnused(inst)) break :result WValue{ .none = {} };
5181
5182 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {5123 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
5183 break :result func.reuseOperand(ty_op.operand, operand);5124 break :result func.reuseOperand(ty_op.operand, operand);
5184 }5125 }
...@@ -5191,7 +5132,6 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi...@@ -5191,7 +5132,6 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi
5191fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5132fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5192 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;5133 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
5193 const extra = func.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;5134 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
5196 const field_ptr = try func.resolveInst(extra.field_ptr);5136 const field_ptr = try func.resolveInst(extra.field_ptr);
5197 const parent_ty = func.air.getRefType(ty_pl.ty).childType();5137 const parent_ty = func.air.getRefType(ty_pl.ty).childType();
...@@ -5231,7 +5171,6 @@ fn airRetAddr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5231,7 +5171,6 @@ fn airRetAddr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52315171
5232fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5172fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5233 const ty_op = func.air.instructions.items(.data)[inst].ty_op;5173 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
5236 const operand = try func.resolveInst(ty_op.operand);5175 const operand = try func.resolveInst(ty_op.operand);
5237 const op_ty = func.air.typeOf(ty_op.operand);5176 const op_ty = func.air.typeOf(ty_op.operand);
...@@ -5276,7 +5215,6 @@ fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5276,7 +5215,6 @@ fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52765215
5277fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5216fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5278 const un_op = func.air.instructions.items(.data)[inst].un_op;5217 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
5281 const operand = try func.resolveInst(un_op);5219 const operand = try func.resolveInst(un_op);
5282 // First retrieve the symbol index to the error name table5220 // First retrieve the symbol index to the error name table
...@@ -5318,7 +5256,6 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5318,7 +5256,6 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
53185256
5319fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void {5257fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void {
5320 const ty_op = func.air.instructions.items(.data)[inst].ty_op;5258 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});
5322 const slice_ptr = try func.resolveInst(ty_op.operand);5259 const slice_ptr = try func.resolveInst(ty_op.operand);
5323 const result = try func.buildPointerOffset(slice_ptr, offset, .new);5260 const result = try func.buildPointerOffset(slice_ptr, offset, .new);
5324 func.finishAir(inst, result, &.{ty_op.operand});5261 func.finishAir(inst, result, &.{ty_op.operand});
...@@ -5328,7 +5265,6 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro...@@ -5328,7 +5265,6 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
5328 assert(op == .add or op == .sub);5265 assert(op == .add or op == .sub);
5329 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;5266 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
5330 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;5267 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
5333 const lhs_op = try func.resolveInst(extra.lhs);5269 const lhs_op = try func.resolveInst(extra.lhs);
5334 const rhs_op = try func.resolveInst(extra.rhs);5270 const rhs_op = try func.resolveInst(extra.rhs);
...@@ -5471,7 +5407,6 @@ fn addSubWithOverflowBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type,...@@ -5471,7 +5407,6 @@ fn addSubWithOverflowBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type,
5471fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5407fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5472 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;5408 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
5473 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;5409 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
5476 const lhs = try func.resolveInst(extra.lhs);5411 const lhs = try func.resolveInst(extra.lhs);
5477 const rhs = try func.resolveInst(extra.rhs);5412 const rhs = try func.resolveInst(extra.rhs);
...@@ -5519,7 +5454,6 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5519,7 +5454,6 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5519fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5454fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5520 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;5455 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
5521 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;5456 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
5524 const lhs = try func.resolveInst(extra.lhs);5458 const lhs = try func.resolveInst(extra.lhs);
5525 const rhs = try func.resolveInst(extra.rhs);5459 const rhs = try func.resolveInst(extra.rhs);
...@@ -5605,7 +5539,6 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5605,7 +5539,6 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56055539
5606fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerError!void {5540fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerError!void {
5607 const bin_op = func.air.instructions.items(.data)[inst].bin_op;5541 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
5610 const ty = func.air.typeOfIndex(inst);5543 const ty = func.air.typeOfIndex(inst);
5611 if (ty.zigTypeTag() == .Vector) {5544 if (ty.zigTypeTag() == .Vector) {
...@@ -5637,8 +5570,6 @@ fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerE...@@ -5637,8 +5570,6 @@ fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerE
5637fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5570fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5638 const pl_op = func.air.instructions.items(.data)[inst].pl_op;5571 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
5639 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;5572 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
5643 const ty = func.air.typeOfIndex(inst);5574 const ty = func.air.typeOfIndex(inst);
5644 if (ty.zigTypeTag() == .Vector) {5575 if (ty.zigTypeTag() == .Vector) {
...@@ -5671,7 +5602,6 @@ fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5671,7 +5602,6 @@ fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56715602
5672fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5603fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5673 const ty_op = func.air.instructions.items(.data)[inst].ty_op;5604 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
5676 const ty = func.air.typeOf(ty_op.operand);5606 const ty = func.air.typeOf(ty_op.operand);
5677 const result_ty = func.air.typeOfIndex(inst);5607 const result_ty = func.air.typeOfIndex(inst);
...@@ -5724,7 +5654,6 @@ fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5724,7 +5654,6 @@ fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
57245654
5725fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5655fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5726 const ty_op = func.air.instructions.items(.data)[inst].ty_op;5656 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
5729 const ty = func.air.typeOf(ty_op.operand);5658 const ty = func.air.typeOf(ty_op.operand);
5730 const result_ty = func.air.typeOfIndex(inst);5659 const result_ty = func.air.typeOfIndex(inst);
...@@ -5892,7 +5821,6 @@ fn lowerTry(...@@ -5892,7 +5821,6 @@ fn lowerTry(
58925821
5893fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5822fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5894 const ty_op = func.air.instructions.items(.data)[inst].ty_op;5823 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
5897 const ty = func.air.typeOfIndex(inst);5825 const ty = func.air.typeOfIndex(inst);
5898 const operand = try func.resolveInst(ty_op.operand);5826 const operand = try func.resolveInst(ty_op.operand);
...@@ -5963,7 +5891,6 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5963,7 +5891,6 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59635891
5964fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5892fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5965 const bin_op = func.air.instructions.items(.data)[inst].bin_op;5893 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
5968 const ty = func.air.typeOfIndex(inst);5895 const ty = func.air.typeOfIndex(inst);
5969 const lhs = try func.resolveInst(bin_op.lhs);5896 const lhs = try func.resolveInst(bin_op.lhs);
...@@ -5978,7 +5905,6 @@ fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5978,7 +5905,6 @@ fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59785905
5979fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {5906fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5980 const bin_op = func.air.instructions.items(.data)[inst].bin_op;5907 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
5983 const ty = func.air.typeOfIndex(inst);5909 const ty = func.air.typeOfIndex(inst);
5984 const lhs = try func.resolveInst(bin_op.lhs);5910 const lhs = try func.resolveInst(bin_op.lhs);
...@@ -6127,7 +6053,6 @@ fn signAbsValue(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {...@@ -6127,7 +6053,6 @@ fn signAbsValue(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
6127fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {6053fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
6128 assert(op == .add or op == .sub);6054 assert(op == .add or op == .sub);
6129 const bin_op = func.air.instructions.items(.data)[inst].bin_op;6055 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
6132 const ty = func.air.typeOfIndex(inst);6057 const ty = func.air.typeOfIndex(inst);
6133 const lhs = try func.resolveInst(bin_op.lhs);6058 const lhs = try func.resolveInst(bin_op.lhs);
...@@ -6240,7 +6165,6 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type,...@@ -6240,7 +6165,6 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type,
62406165
6241fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6166fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6242 const bin_op = func.air.instructions.items(.data)[inst].bin_op;6167 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
6245 const ty = func.air.typeOfIndex(inst);6169 const ty = func.air.typeOfIndex(inst);
6246 const int_info = ty.intInfo(func.target);6170 const int_info = ty.intInfo(func.target);
...@@ -6399,7 +6323,6 @@ fn callIntrinsic(...@@ -6399,7 +6323,6 @@ fn callIntrinsic(
63996323
6400fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6324fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6401 const un_op = func.air.instructions.items(.data)[inst].un_op;6325 const un_op = func.air.instructions.items(.data)[inst].un_op;
6402 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
6403 const operand = try func.resolveInst(un_op);6326 const operand = try func.resolveInst(un_op);
6404 const enum_ty = func.air.typeOf(un_op);6327 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,...@@ -79,14 +79,8 @@ end_di_column: u32,
79/// which is a relative jump, based on the address following the reloc.79/// which is a relative jump, based on the address following the reloc.
80exitlude_jump_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},80exitlude_jump_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
8181
82/// Whenever there is a runtime branch, we push a Branch onto this stack,82const_tracking: InstTrackingMap = .{},
83/// and pop it off when the runtime branch joins. This provides an "overlay"83inst_tracking: InstTrackingMap = .{},
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),
9084
91// Key is the block instruction85// Key is the block instruction
92blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},86blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
...@@ -95,6 +89,9 @@ register_manager: RegisterManager = .{},...@@ -95,6 +89,9 @@ register_manager: RegisterManager = .{},
95/// Maps offset to what is stored there.89/// Maps offset to what is stored there.
96stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},90stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
9791
92/// Generation of the current scope, increments by 1 for every entered scope.
93scope_generation: u32 = 0,
94
98/// Offset from the stack base, representing the end of the stack frame.95/// Offset from the stack base, representing the end of the stack frame.
99max_end_stack: u32 = 0,96max_end_stack: u32 = 0,
100/// Represents the current end stack offset. If there is no existing slot97/// Represents the current end stack offset. If there is no existing slot
...@@ -105,10 +102,12 @@ next_stack_offset: u32 = 0,...@@ -105,10 +102,12 @@ next_stack_offset: u32 = 0,
105air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,102air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
106103
107/// For mir debug info, maps a mir index to a air index104/// 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
110const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};107const 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
112pub const MCValue = union(enum) {111pub const MCValue = union(enum) {
113 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.112 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
114 /// TODO Look into deleting this tag and using `dead` instead, since every use113 /// TODO Look into deleting this tag and using `dead` instead, since every use
...@@ -117,7 +116,8 @@ pub const MCValue = union(enum) {...@@ -117,7 +116,8 @@ pub const MCValue = union(enum) {
117 /// Control flow will not allow this value to be observed.116 /// Control flow will not allow this value to be observed.
118 unreach,117 unreach,
119 /// No more references to this value remain.118 /// 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,
121 /// The value is undefined.121 /// The value is undefined.
122 undef,122 undef,
123 /// A pointer-sized integer that fits in a register.123 /// A pointer-sized integer that fits in a register.
...@@ -183,47 +183,95 @@ pub const MCValue = union(enum) {...@@ -183,47 +183,95 @@ pub const MCValue = union(enum) {
183 }183 }
184};184};
185185
186const Branch = struct {186const InstTrackingMap = std.AutoArrayHashMapUnmanaged(Air.Inst.Index, InstTracking);
187 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},187const InstTracking = struct {
188 long: MCValue,
189 short: MCValue,
188190
189 fn deinit(self: *Branch, gpa: Allocator) void {191 fn init(result: MCValue) InstTracking {
190 self.inst_table.deinit(gpa);192 return .{ .long = result, .short = result };
191 self.* = undefined;
192 }193 }
193194
194 const FormatContext = struct {195 fn getReg(self: InstTracking) ?Register {
195 insts: []const Air.Inst.Index,196 return switch (self.short) {
196 mcvs: []const MCValue,197 .register => |reg| reg,
197 };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(238 fn trackSpill(self: *InstTracking, function: *Self) void {
200 ctx: FormatContext,239 if (self.getReg()) |reg| function.register_manager.freeReg(reg);
201 comptime unused_format_string: []const u8,240 switch (self.short) {
202 options: std.fmt.FormatOptions,241 .none, .dead, .unreach => unreachable,
203 writer: anytype,242 else => {},
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 });
210 }243 }
211 try writer.writeAll("}");244 self.short = self.long;
212 }245 }
213246
214 fn format(branch: Branch, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {247 fn materialize(self: *InstTracking, function: *Self, inst: Air.Inst.Index, reg: Register) !void {
215 _ = branch;248 const ty = function.air.typeOfIndex(inst);
216 _ = unused_format_string;249 try function.genSetReg(ty, reg, self.long);
217 _ = options;
218 _ = writer;
219 @compileError("do not format Branch directly; use ty.fmtDebug()");
220 }250 }
221251
222 fn fmtDebug(self: @This()) std.fmt.Formatter(fmt) {252 fn trackMaterialize(self: *InstTracking, function: *Self, inst: Air.Inst.Index, reg: Register) void {
223 return .{ .data = .{253 assert(inst == function.register_manager.registers[
224 .insts = self.inst_table.keys(),254 RegisterManager.indexOfRegIntoTracked(reg).?
225 .mcvs = self.inst_table.values(),255 ]);
226 } };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 };
227 }275 }
228};276};
229277
...@@ -235,39 +283,14 @@ const StackAllocation = struct {...@@ -235,39 +283,14 @@ const StackAllocation = struct {
235283
236const BlockData = struct {284const BlockData = struct {
237 relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},285 relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
238 branch: Branch = .{},286 state: State,
239 branch_depth: u32,
240287
241 fn deinit(self: *BlockData, gpa: Allocator) void {288 fn deinit(self: *BlockData, gpa: Allocator) void {
242 self.branch.deinit(gpa);
243 self.relocs.deinit(gpa);289 self.relocs.deinit(gpa);
244 self.* = undefined;290 self.* = undefined;
245 }291 }
246};292};
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
271const Self = @This();294const Self = @This();
272295
273pub fn generate(296pub fn generate(
...@@ -294,19 +317,9 @@ pub fn generate(...@@ -294,19 +317,9 @@ pub fn generate(
294 stderr.writeAll(":\n") catch {};317 stderr.writeAll(":\n") catch {};
295 }318 }
296319
297 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);320 const gpa = 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
308 var function = Self{321 var function = Self{
309 .gpa = bin_file.allocator,322 .gpa = gpa,
310 .air = air,323 .air = air,
311 .liveness = liveness,324 .liveness = liveness,
312 .target = &bin_file.options.target,325 .target = &bin_file.options.target,
...@@ -318,21 +331,23 @@ pub fn generate(...@@ -318,21 +331,23 @@ pub fn generate(
318 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`331 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
319 .fn_type = fn_type,332 .fn_type = fn_type,
320 .arg_index = 0,333 .arg_index = 0,
321 .branch_stack = &branch_stack,
322 .src_loc = src_loc,334 .src_loc = src_loc,
323 .stack_align = undefined,335 .stack_align = undefined,
324 .end_di_line = module_fn.rbrace_line,336 .end_di_line = module_fn.rbrace_line,
325 .end_di_column = module_fn.rbrace_column,337 .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 {},
329 };338 };
330 defer function.stack.deinit(bin_file.allocator);339 defer {
331 defer function.blocks.deinit(bin_file.allocator);340 function.stack.deinit(gpa);
332 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);341 var block_it = function.blocks.valueIterator();
333 defer function.mir_instructions.deinit(bin_file.allocator);342 while (block_it.next()) |block| block.deinit(gpa);
334 defer function.mir_extra.deinit(bin_file.allocator);343 function.blocks.deinit(gpa);
335 defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit();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
337 var call_info = function.resolveCallingConventionValues(fn_type, &.{}) catch |err| switch (err) {352 var call_info = function.resolveCallingConventionValues(fn_type, &.{}) catch |err| switch (err) {
338 error.CodegenFail => return Result{ .fail = function.err_msg.? },353 error.CodegenFail => return Result{ .fail = function.err_msg.? },
...@@ -905,11 +920,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -905,11 +920,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
905 const air_tags = self.air.instructions.items(.tag);920 const air_tags = self.air.instructions.items(.tag);
906921
907 for (body) |inst| {922 for (body) |inst| {
908 const old_air_bookkeeping = self.air_bookkeeping;
909 try self.ensureProcessDeathCapacity(Liveness.bpi);
910 if (builtin.mode == .Debug) {923 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);
912 }926 }
927
928 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) continue;
913 if (debug_wip_mir) @import("../../print_air.zig").dumpInst(929 if (debug_wip_mir) @import("../../print_air.zig").dumpInst(
914 inst,930 inst,
915 self.bin_file.options.module.?,931 self.bin_file.options.module.?,
...@@ -917,6 +933,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -917,6 +933,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
917 self.liveness,933 self.liveness,
918 );934 );
919935
936 const old_air_bookkeeping = self.air_bookkeeping;
937 try self.inst_tracking.ensureUnusedCapacity(self.gpa, 1);
920 switch (air_tags[inst]) {938 switch (air_tags[inst]) {
921 // zig fmt: off939 // zig fmt: off
922 .not,940 .not,
...@@ -1080,7 +1098,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -1080,7 +1098,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
10801098
1081 .field_parent_ptr => try self.airFieldParentPtr(inst),1099 .field_parent_ptr => try self.airFieldParentPtr(inst),
10821100
1083 .switch_br => try self.airSwitch(inst),1101 .switch_br => try self.airSwitchBr(inst),
1084 .slice_ptr => try self.airSlicePtr(inst),1102 .slice_ptr => try self.airSlicePtr(inst),
1085 .slice_len => try self.airSliceLen(inst),1103 .slice_len => try self.airSliceLen(inst),
10861104
...@@ -1166,8 +1184,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -1166,8 +1184,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1166 var it = self.register_manager.free_registers.iterator(.{ .kind = .unset });1184 var it = self.register_manager.free_registers.iterator(.{ .kind = .unset });
1167 while (it.next()) |index| {1185 while (it.next()) |index| {
1168 const tracked_inst = self.register_manager.registers[index];1186 const tracked_inst = self.register_manager.registers[index];
1169 const tracked_mcv = self.getResolvedInstValue(tracked_inst).?.*;1187 const tracking = self.getResolvedInstValue(tracked_inst);
1170 assert(RegisterManager.indexOfRegIntoTracked(switch (tracked_mcv) {1188 assert(RegisterManager.indexOfRegIntoTracked(switch (tracking.short) {
1171 .register => |reg| reg,1189 .register => |reg| reg,
1172 .register_overflow => |ro| ro.reg,1190 .register_overflow => |ro| ro.reg,
1173 else => unreachable,1191 else => unreachable,
...@@ -1205,16 +1223,16 @@ fn freeValue(self: *Self, value: MCValue) void {...@@ -1205,16 +1223,16 @@ fn freeValue(self: *Self, value: MCValue) void {
1205 }1223 }
1206}1224}
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
1208/// Asserts there is already capacity to insert into top branch inst_table.1230/// Asserts there is already capacity to insert into top branch inst_table.
1209fn processDeath(self: *Self, inst: Air.Inst.Index) void {1231fn processDeath(self: *Self, inst: Air.Inst.Index) void {
1210 const air_tags = self.air.instructions.items(.tag);1232 const air_tags = self.air.instructions.items(.tag);
1211 if (air_tags[inst] == .constant) return; // Constants are immortal.1233 if (air_tags[inst] == .constant) return;
1212 const prev_value = (self.getResolvedInstValue(inst) orelse return).*;
1213 log.debug("%{d} => {}", .{ inst, MCValue.dead });1234 log.debug("%{d} => {}", .{ inst, MCValue.dead });
1214 // When editing this function, note that the logic must synchronize with `reuseOperand`.1235 self.inst_tracking.getPtr(inst).?.die(self);
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);
1218}1236}
12191237
1220/// Called when there are no operands, and the instruction is always unreferenced.1238/// Called when there are no operands, and the instruction is always unreferenced.
...@@ -1224,6 +1242,21 @@ fn finishAirBookkeeping(self: *Self) void {...@@ -1224,6 +1242,21 @@ fn finishAirBookkeeping(self: *Self) void {
1224 }1242 }
1225}1243}
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
1227fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {1260fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
1228 var tomb_bits = self.liveness.getTombBits(inst);1261 var tomb_bits = self.liveness.getTombBits(inst);
1229 for (operands) |op| {1262 for (operands) |op| {
...@@ -1235,26 +1268,7 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live...@@ -1235,26 +1268,7 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
1235 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);1268 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
1236 self.processDeath(op_index);1269 self.processDeath(op_index);
1237 }1270 }
1238 const is_used = @truncate(u1, tomb_bits) == 0;1271 self.finishAirResult(inst, result);
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);
1258}1272}
12591273
1260fn allocMem(self: *Self, inst: ?Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {1274fn 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_...@@ -1339,66 +1353,115 @@ fn allocRegOrMemAdvanced(self: *Self, elem_ty: Type, inst: ?Air.Inst.Index, reg_
1339}1353}
13401354
1341const State = struct {1355const State = struct {
1342 registers: abi.RegisterManager.TrackedRegisters,1356 registers: RegisterManager.TrackedRegisters,
1343 free_registers: abi.RegisterManager.RegisterBitSet,1357 free_registers: RegisterManager.RegisterBitSet,
1344 eflags_inst: ?Air.Inst.Index,1358 inst_tracking_len: u32,
1359 scope_generation: u32,
1345};1360};
13461361
1347fn captureState(self: *Self) State {1362fn initRetroactiveState(self: *Self) State {
1348 return State{1363 var state: State = undefined;
1349 .registers = self.register_manager.registers,1364 state.inst_tracking_len = @intCast(u32, self.inst_tracking.count());
1350 .free_registers = self.register_manager.free_registers,1365 state.scope_generation = self.scope_generation;
1351 .eflags_inst = self.eflags_inst,1366 return state;
1352 };
1353}1367}
13541368
1355fn revertState(self: *Self, state: State) void {1369fn saveRetroactiveState(self: *Self, state: *State) !void {
1356 self.eflags_inst = state.eflags_inst;1370 try self.spillEflagsIfOccupied();
1357 self.register_manager.free_registers = state.free_registers;1371 state.registers = self.register_manager.registers;
1358 self.register_manager.registers = state.registers;1372 state.free_registers = self.register_manager.free_registers;
1359}1373}
13601374
1361pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {1375fn saveState(self: *Self) !State {
1362 const stack_mcv = try self.allocRegOrMem(inst, false);1376 var state = self.initRetroactiveState();
1363 log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv });1377 try self.saveRetroactiveState(&state);
1364 const reg_mcv = self.getResolvedInstValue(inst).?.*;1378 return state;
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, .{});
1377}1379}
13781380
1379pub fn spillEflagsIfOccupied(self: *Self) !void {1381fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, comptime opts: struct {
1380 if (self.eflags_inst) |inst_to_save| {1382 emit_instructions: bool,
1381 const mcv = self.getResolvedInstValue(inst_to_save).?.*;1383 update_tracking: bool,
1382 const new_mcv = switch (mcv) {1384 resurrect: bool,
1383 .register_overflow => try self.allocRegOrMem(inst_to_save, false),1385 close_scope: bool,
1384 .eflags => try self.allocRegOrMem(inst_to_save, true),1386}) !void {
1385 else => unreachable,1387 if (opts.close_scope) {
1386 };1388 for (self.inst_tracking.values()[state.inst_tracking_len..]) |*tracking| tracking.die(self);
13871389 self.inst_tracking.shrinkRetainingCapacity(state.inst_tracking_len);
1388 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);1390 }
1389 log.debug("spilling %{d} to mcv {any}", .{ inst_to_save, new_mcv });
13901391
1391 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1392 if (opts.resurrect) for (self.inst_tracking.values()[0..state.inst_tracking_len]) |*tracking|
1392 branch.inst_table.putAssumeCapacity(inst_to_save, new_mcv);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| {
1394 self.eflags_inst = null;1438 self.eflags_inst = null;
1439 self.inst_tracking.getPtr(inst).?.trackSpill(self);
1440 };
13951441
1396 // TODO consolidate with register manager and spillInstruction1442 if (opts.update_tracking and std.debug.runtime_safety) {
1397 // this call should really belong in the register manager!1443 assert(self.eflags_inst == null);
1398 switch (mcv) {1444 assert(self.register_manager.free_registers.eql(state.free_registers));
1399 .register_overflow => |ro| self.register_manager.freeReg(ro.reg),1445 var used_reg_it = state.free_registers.iterator(.{ .kind = .unset });
1400 else => {},1446 while (used_reg_it.next()) |index|
1401 }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);
1402 }1465 }
1403}1466}
14041467
...@@ -1442,22 +1505,14 @@ fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Ty...@@ -1442,22 +1505,14 @@ fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Ty
1442}1505}
14431506
1444fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {1507fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
1445 const result: MCValue = result: {1508 const stack_offset = try self.allocMemPtr(inst);
1446 if (self.liveness.isUnused(inst)) break :result .dead;1509 const result = MCValue{ .ptr_stack_offset = @intCast(i32, stack_offset) };
1447
1448 const stack_offset = try self.allocMemPtr(inst);
1449 break :result .{ .ptr_stack_offset = @intCast(i32, stack_offset) };
1450 };
1451 return self.finishAir(inst, result, .{ .none, .none, .none });1510 return self.finishAir(inst, result, .{ .none, .none, .none });
1452}1511}
14531512
1454fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {1513fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
1455 const result: MCValue = result: {1514 const stack_offset = try self.allocMemPtr(inst);
1456 if (self.liveness.isUnused(inst)) break :result .dead;1515 const result = MCValue{ .ptr_stack_offset = @intCast(i32, stack_offset) };
1457
1458 const stack_offset = try self.allocMemPtr(inst);
1459 break :result .{ .ptr_stack_offset = @intCast(i32, stack_offset) };
1460 };
1461 return self.finishAir(inst, result, .{ .none, .none, .none });1516 return self.finishAir(inst, result, .{ .none, .none, .none });
1462}1517}
14631518
...@@ -1477,126 +1532,125 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void {...@@ -1477,126 +1532,125 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
14771532
1478fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {1533fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
1479 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1534 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;1536 const src_ty = self.air.typeOf(ty_op.operand);
1501 const signedness: std.builtin.Signedness = if (dst_int_info.signedness == .signed and1537 const src_int_info = src_ty.intInfo(self.target.*);
1502 src_int_info.signedness == .signed) .signed else .unsigned;1538 const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*));
1503 switch (dst_mcv) {1539 const src_mcv = try self.resolveInst(ty_op.operand);
1504 .register => |dst_reg| {1540 const src_lock = switch (src_mcv) {
1505 const min_abi_size = @min(dst_abi_size, src_abi_size);1541 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1506 const tag: Mir.Inst.Tag = switch (signedness) {1542 else => null,
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;
1552 };1543 };
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 });
1554}1607}
15551608
1556fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {1609fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
1557 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1610 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))1612 const dst_ty = self.air.typeOfIndex(inst);
1573 src_mcv1613 const dst_abi_size = dst_ty.abiSize(self.target.*);
1574 else1614 if (dst_abi_size > 8) {
1575 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv);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 result1618 const src_mcv = try self.resolveInst(ty_op.operand);
1578 // have to be removed. this only happens if the dst if not a power-of-two size.1619 const src_lock = switch (src_mcv) {
1579 if (self.regExtraBits(dst_ty) > 0) try self.truncateRegister(dst_ty, dst_mcv.register.to64());1620 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1580 break :result dst_mcv;1621 else => null,
1581 };1622 };
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 });
1583}1635}
15841636
1585fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {1637fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
1586 const un_op = self.air.instructions.items(.data)[inst].un_op;1638 const un_op = self.air.instructions.items(.data)[inst].un_op;
1639 const ty = self.air.typeOfIndex(inst);
1640
1587 const operand = try self.resolveInst(un_op);1641 const operand = try self.resolveInst(un_op);
1588 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;1642 const dst_mcv = if (self.reuseOperand(inst, un_op, 0, operand))
1589 return self.finishAir(inst, result, .{ un_op, .none, .none });1643 operand
1644 else
1645 try self.copyToRegisterWithInstTracking(inst, ty, operand);
1646
1647 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
1590}1648}
15911649
1592fn airSlice(self: *Self, inst: Air.Inst.Index) !void {1650fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
1593 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1651 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1594 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1652 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
1600 const ptr = try self.resolveInst(bin_op.lhs);1654 const ptr = try self.resolveInst(bin_op.lhs);
1601 const ptr_ty = self.air.typeOf(bin_op.lhs);1655 const ptr_ty = self.air.typeOf(bin_op.lhs);
1602 const len = try self.resolveInst(bin_op.rhs);1656 const len = try self.resolveInst(bin_op.rhs);
...@@ -1612,33 +1666,21 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {...@@ -1612,33 +1666,21 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
16121666
1613fn airUnOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {1667fn airUnOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1614 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1668 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
16151669 const dst_mcv = try self.genUnOp(inst, tag, ty_op.operand);
1616 const result = if (self.liveness.isUnused(inst))1670 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
1617 .dead
1618 else
1619 try self.genUnOp(inst, tag, ty_op.operand);
1620 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1621}1671}
16221672
1623fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {1673fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1624 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1674 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
16251675 const dst_mcv = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
1626 const result = if (self.liveness.isUnused(inst))1676 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
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 });
1631}1677}
16321678
1633fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {1679fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1634 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1680 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1635 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1681 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
16361682 const dst_mcv = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
1637 const result = if (self.liveness.isUnused(inst))1683 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
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 });
1642}1684}
16431685
1644fn activeIntBits(self: *Self, dst_air: Air.Inst.Ref) u16 {1686fn activeIntBits(self: *Self, dst_air: Air.Inst.Ref) u16 {
...@@ -1678,7 +1720,7 @@ fn activeIntBits(self: *Self, dst_air: Air.Inst.Ref) u16 {...@@ -1678,7 +1720,7 @@ fn activeIntBits(self: *Self, dst_air: Air.Inst.Ref) u16 {
16781720
1679fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {1721fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
1680 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1722 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: {
1682 const tag = self.air.instructions.items(.tag)[inst];1724 const tag = self.air.instructions.items(.tag)[inst];
1683 const dst_ty = self.air.typeOfIndex(inst);1725 const dst_ty = self.air.typeOfIndex(inst);
1684 if (dst_ty.zigTypeTag() == .Float)1726 if (dst_ty.zigTypeTag() == .Float)
...@@ -1709,168 +1751,162 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {...@@ -1709,168 +1751,162 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
17091751
1710fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {1752fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
1711 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1753 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1712 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1754 const ty = self.air.typeOf(bin_op.lhs);
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);
17231755
1724 const rhs_mcv = try self.resolveInst(bin_op.rhs);1756 const lhs_mcv = try self.resolveInst(bin_op.lhs);
1725 const rhs_lock = switch (rhs_mcv) {1757 const dst_mcv = if (lhs_mcv.isRegister() and self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv))
1726 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),1758 lhs_mcv
1727 else => null,1759 else
1728 };1760 try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv);
1729 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);1761 const dst_reg = dst_mcv.register;
17301762 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
1731 const limit_reg = try self.register_manager.allocReg(null, gp);1763 defer self.register_manager.unlockReg(dst_lock);
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);
17511764
1752 const cmov_abi_size = @max(@intCast(u32, ty.abiSize(self.target.*)), 2);1765 const rhs_mcv = try self.resolveInst(bin_op.rhs);
1753 try self.asmCmovccRegisterRegister(1766 const rhs_lock = switch (rhs_mcv) {
1754 registerAlias(dst_reg, cmov_abi_size),1767 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1755 registerAlias(limit_reg, cmov_abi_size),1768 else => null,
1756 cc,
1757 );
1758 break :result dst_mcv;
1759 };1769 };
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 });
1761}1801}
17621802
1763fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {1803fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
1764 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1804 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1765 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1805 const ty = self.air.typeOf(bin_op.lhs);
1766 const ty = self.air.typeOf(bin_op.lhs);
17671806
1768 const lhs_mcv = try self.resolveInst(bin_op.lhs);1807 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))1808 const dst_mcv = if (lhs_mcv.isRegister() and self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv))
1770 lhs_mcv1809 lhs_mcv
1771 else1810 else
1772 try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv);1811 try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv);
1773 const dst_reg = dst_mcv.register;1812 const dst_reg = dst_mcv.register;
1774 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);1813 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
1775 defer self.register_manager.unlockReg(dst_lock);1814 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);
18021815
1803 const cmov_abi_size = @max(@intCast(u32, ty.abiSize(self.target.*)), 2);1816 const rhs_mcv = try self.resolveInst(bin_op.rhs);
1804 try self.asmCmovccRegisterRegister(1817 const rhs_lock = switch (rhs_mcv) {
1805 registerAlias(dst_reg, cmov_abi_size),1818 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1806 registerAlias(limit_reg, cmov_abi_size),1819 else => null,
1807 cc,
1808 );
1809 break :result dst_mcv;
1810 };1820 };
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 });
1812}1850}
18131851
1814fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {1852fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
1815 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1853 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1816 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1854 const ty = self.air.typeOf(bin_op.lhs);
1817 const ty = self.air.typeOf(bin_op.lhs);
18181855
1819 try self.spillRegisters(&.{ .rax, .rdx });1856 try self.spillRegisters(&.{ .rax, .rdx });
1820 const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx });1857 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);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);1860 const lhs_mcv = try self.resolveInst(bin_op.lhs);
1824 const lhs_lock = switch (lhs_mcv) {1861 const lhs_lock = switch (lhs_mcv) {
1825 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),1862 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1826 else => null,1863 else => null,
1827 };1864 };
1828 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);1865 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
18291866
1830 const rhs_mcv = try self.resolveInst(bin_op.rhs);1867 const rhs_mcv = try self.resolveInst(bin_op.rhs);
1831 const rhs_lock = switch (rhs_mcv) {1868 const rhs_lock = switch (rhs_mcv) {
1832 .register => |reg| self.register_manager.lockReg(reg),1869 .register => |reg| self.register_manager.lockReg(reg),
1833 else => null,1870 else => null,
1834 };1871 };
1835 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);1872 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 };
18571873
1858 const dst_mcv = try self.genMulDivBinOp(.mul, inst, ty, ty, lhs_mcv, rhs_mcv);1874 const limit_reg = try self.register_manager.allocReg(null, gp);
1859 const cmov_abi_size = @max(@intCast(u32, ty.abiSize(self.target.*)), 2);1875 const limit_mcv = MCValue{ .register = limit_reg };
1860 try self.asmCmovccRegisterRegister(1876 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg);
1861 registerAlias(dst_mcv.register, cmov_abi_size),1877 defer self.register_manager.unlockReg(limit_lock);
1862 registerAlias(limit_reg, cmov_abi_size),1878
1863 cc,1879 const reg_bits = self.regBitSize(ty);
1864 );1880 const cc: Condition = if (ty.isSignedInt()) cc: {
1865 break :result dst_mcv;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;
1866 };1893 };
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 });
1868}1904}
18691905
1870fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1906fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1871 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1907 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1872 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1908 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: {
1874 const tag = self.air.instructions.items(.tag)[inst];1910 const tag = self.air.instructions.items(.tag)[inst];
1875 const ty = self.air.typeOf(bin_op.lhs);1911 const ty = self.air.typeOf(bin_op.lhs);
1876 switch (ty.zigTypeTag()) {1912 switch (ty.zigTypeTag()) {
...@@ -1929,7 +1965,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1929,7 +1965,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1929fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1965fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1930 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1966 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1931 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1967 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: {
1933 const lhs_ty = self.air.typeOf(bin_op.lhs);1969 const lhs_ty = self.air.typeOf(bin_op.lhs);
1934 const rhs_ty = self.air.typeOf(bin_op.rhs);1970 const rhs_ty = self.air.typeOf(bin_op.rhs);
1935 switch (lhs_ty.zigTypeTag()) {1971 switch (lhs_ty.zigTypeTag()) {
...@@ -2051,7 +2087,7 @@ fn genSetStackTruncatedOverflowCompare(...@@ -2051,7 +2087,7 @@ fn genSetStackTruncatedOverflowCompare(
2051fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {2087fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2052 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2088 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2053 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;2089 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: {
2055 const dst_ty = self.air.typeOf(bin_op.lhs);2091 const dst_ty = self.air.typeOf(bin_op.lhs);
2056 switch (dst_ty.zigTypeTag()) {2092 switch (dst_ty.zigTypeTag()) {
2057 .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}),2093 .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...@@ -2240,10 +2276,6 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa
2240fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void {2276fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void {
2241 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2277 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
2247 try self.spillRegisters(&.{.rcx});2279 try self.spillRegisters(&.{.rcx});
22482280
2249 const tag = self.air.instructions.items(.tag)[inst];2281 const tag = self.air.instructions.items(.tag)[inst];
...@@ -2260,18 +2292,14 @@ fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void {...@@ -2260,18 +2292,14 @@ fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void {
22602292
2261fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {2293fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
2262 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2294 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2263 const result: MCValue = if (self.liveness.isUnused(inst))2295 _ = bin_op;
2264 .dead2296 return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
2265 else2297 //return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
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 });
2268}2298}
22692299
2270fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {2300fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
2271 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2301 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2272 const result: MCValue = result: {2302 const result: MCValue = result: {
2273 if (self.liveness.isUnused(inst)) break :result .none;
2274
2275 const pl_ty = self.air.typeOfIndex(inst);2303 const pl_ty = self.air.typeOfIndex(inst);
2276 const opt_mcv = try self.resolveInst(ty_op.operand);2304 const opt_mcv = try self.resolveInst(ty_op.operand);
22772305
...@@ -2296,18 +2324,15 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -2296,18 +2324,15 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
22962324
2297fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {2325fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
2298 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2326 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);2328 const dst_ty = self.air.typeOfIndex(inst);
2303 const opt_mcv = try self.resolveInst(ty_op.operand);2329 const opt_mcv = try self.resolveInst(ty_op.operand);
23042330
2305 break :result if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv))2331 const dst_mcv = if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv))
2306 opt_mcv2332 opt_mcv
2307 else2333 else
2308 try self.copyToRegisterWithInstTracking(inst, dst_ty, opt_mcv);2334 try self.copyToRegisterWithInstTracking(inst, dst_ty, opt_mcv);
2309 };2335 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
2310 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2311}2336}
23122337
2313fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {2338fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
...@@ -2320,7 +2345,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -2320,7 +2345,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
23202345
2321 if (opt_ty.optionalReprIsPayload()) {2346 if (opt_ty.optionalReprIsPayload()) {
2322 break :result if (self.liveness.isUnused(inst))2347 break :result if (self.liveness.isUnused(inst))
2323 .dead2348 .unreach
2324 else if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))2349 else if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2325 src_mcv2350 src_mcv
2326 else2351 else
...@@ -2339,16 +2364,13 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -2339,16 +2364,13 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
2339 Memory.sib(.byte, .{ .base = dst_mcv.register, .disp = pl_abi_size }),2364 Memory.sib(.byte, .{ .base = dst_mcv.register, .disp = pl_abi_size }),
2340 Immediate.u(1),2365 Immediate.u(1),
2341 );2366 );
2342 break :result if (self.liveness.isUnused(inst)) .dead else dst_mcv;2367 break :result if (self.liveness.isUnused(inst)) .unreach else dst_mcv;
2343 };2368 };
2344 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2369 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2345}2370}
23462371
2347fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {2372fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
2348 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2373 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 }
2352 const err_union_ty = self.air.typeOf(ty_op.operand);2374 const err_union_ty = self.air.typeOf(ty_op.operand);
2353 const err_ty = err_union_ty.errorUnionSet();2375 const err_ty = err_union_ty.errorUnionSet();
2354 const payload_ty = err_union_ty.errorUnionPayload();2376 const payload_ty = err_union_ty.errorUnionPayload();
...@@ -2391,9 +2413,6 @@ fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2391,9 +2413,6 @@ fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
23912413
2392fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {2414fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
2393 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2415 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 }
2397 const err_union_ty = self.air.typeOf(ty_op.operand);2416 const err_union_ty = self.air.typeOf(ty_op.operand);
2398 const operand = try self.resolveInst(ty_op.operand);2417 const operand = try self.resolveInst(ty_op.operand);
2399 const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, operand);2418 const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, operand);
...@@ -2444,72 +2463,68 @@ fn genUnwrapErrorUnionPayloadMir(...@@ -2444,72 +2463,68 @@ fn genUnwrapErrorUnionPayloadMir(
2444// *(E!T) -> E2463// *(E!T) -> E
2445fn airUnwrapErrUnionErrPtr(self: *Self, inst: Air.Inst.Index) !void {2464fn airUnwrapErrUnionErrPtr(self: *Self, inst: Air.Inst.Index) !void {
2446 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2465 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);2467 const src_ty = self.air.typeOf(ty_op.operand);
2451 const src_mcv = try self.resolveInst(ty_op.operand);2468 const src_mcv = try self.resolveInst(ty_op.operand);
2452 const src_reg = switch (src_mcv) {2469 const src_reg = switch (src_mcv) {
2453 .register => |reg| reg,2470 .register => |reg| reg,
2454 else => try self.copyToTmpRegister(src_ty, src_mcv),2471 else => try self.copyToTmpRegister(src_ty, src_mcv),
2455 };2472 };
2456 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);2473 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
2457 defer self.register_manager.unlockReg(src_lock);2474 defer self.register_manager.unlockReg(src_lock);
24582475
2459 const dst_reg = try self.register_manager.allocReg(inst, gp);2476 const dst_reg = try self.register_manager.allocReg(inst, gp);
2460 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);2477 const dst_mcv = MCValue{ .register = dst_reg };
2461 defer self.register_manager.unlockReg(dst_lock);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();2481 const eu_ty = src_ty.childType();
2464 const pl_ty = eu_ty.errorUnionPayload();2482 const pl_ty = eu_ty.errorUnionPayload();
2465 const err_ty = eu_ty.errorUnionSet();2483 const err_ty = eu_ty.errorUnionSet();
2466 const err_off = @intCast(i32, errUnionErrorOffset(pl_ty, self.target.*));2484 const err_off = @intCast(i32, errUnionErrorOffset(pl_ty, self.target.*));
2467 const err_abi_size = @intCast(u32, err_ty.abiSize(self.target.*));2485 const err_abi_size = @intCast(u32, err_ty.abiSize(self.target.*));
2468 try self.asmRegisterMemory(2486 try self.asmRegisterMemory(
2469 .mov,2487 .mov,
2470 registerAlias(dst_reg, err_abi_size),2488 registerAlias(dst_reg, err_abi_size),
2471 Memory.sib(Memory.PtrSize.fromSize(err_abi_size), .{ .base = src_reg, .disp = err_off }),2489 Memory.sib(Memory.PtrSize.fromSize(err_abi_size), .{ .base = src_reg, .disp = err_off }),
2472 );2490 );
2473 break :result .{ .register = dst_reg };2491
2474 };2492 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
2475 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2476}2493}
24772494
2478// *(E!T) -> *T2495// *(E!T) -> *T
2479fn airUnwrapErrUnionPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {2496fn airUnwrapErrUnionPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
2480 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2497 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);2499 const src_ty = self.air.typeOf(ty_op.operand);
2485 const src_mcv = try self.resolveInst(ty_op.operand);2500 const src_mcv = try self.resolveInst(ty_op.operand);
2486 const src_reg = switch (src_mcv) {2501 const src_reg = switch (src_mcv) {
2487 .register => |reg| reg,2502 .register => |reg| reg,
2488 else => try self.copyToTmpRegister(src_ty, src_mcv),2503 else => try self.copyToTmpRegister(src_ty, src_mcv),
2489 };2504 };
2490 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);2505 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
2491 defer self.register_manager.unlockReg(src_lock);2506 defer self.register_manager.unlockReg(src_lock);
24922507
2493 const dst_ty = self.air.typeOfIndex(inst);2508 const dst_ty = self.air.typeOfIndex(inst);
2494 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))2509 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2495 src_reg2510 src_reg
2496 else2511 else
2497 try self.register_manager.allocReg(inst, gp);2512 try self.register_manager.allocReg(inst, gp);
2498 const dst_lock = self.register_manager.lockReg(dst_reg);2513 const dst_mcv = MCValue{ .register = dst_reg };
2499 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);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();2517 const eu_ty = src_ty.childType();
2502 const pl_ty = eu_ty.errorUnionPayload();2518 const pl_ty = eu_ty.errorUnionPayload();
2503 const pl_off = @intCast(i32, errUnionPayloadOffset(pl_ty, self.target.*));2519 const pl_off = @intCast(i32, errUnionPayloadOffset(pl_ty, self.target.*));
2504 const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));2520 const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
2505 try self.asmRegisterMemory(2521 try self.asmRegisterMemory(
2506 .lea,2522 .lea,
2507 registerAlias(dst_reg, dst_abi_size),2523 registerAlias(dst_reg, dst_abi_size),
2508 Memory.sib(.qword, .{ .base = src_reg, .disp = pl_off }),2524 Memory.sib(.qword, .{ .base = src_reg, .disp = pl_off }),
2509 );2525 );
2510 break :result .{ .register = dst_reg };2526
2511 };2527 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
2512 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2513}2528}
25142529
2515fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {2530fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
...@@ -2535,7 +2550,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -2535,7 +2550,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
2535 Immediate.u(0),2550 Immediate.u(0),
2536 );2551 );
25372552
2538 if (self.liveness.isUnused(inst)) break :result .dead;2553 if (self.liveness.isUnused(inst)) break :result .unreach;
25392554
2540 const dst_ty = self.air.typeOfIndex(inst);2555 const dst_ty = self.air.typeOfIndex(inst);
2541 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))2556 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 {...@@ -2558,11 +2573,9 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
2558}2573}
25592574
2560fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {2575fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
2561 const result: MCValue = if (self.liveness.isUnused(inst))2576 _ = inst;
2562 .dead2577 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
2563 else2578 //return self.finishAir(inst, result, .{ .none, .none, .none });
2564 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
2565 return self.finishAir(inst, result, .{ .none, .none, .none });
2566}2579}
25672580
2568fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {2581fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
...@@ -2578,8 +2591,6 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {...@@ -2578,8 +2591,6 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
2578fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {2591fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
2579 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2592 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2580 const result: MCValue = result: {2593 const result: MCValue = result: {
2581 if (self.liveness.isUnused(inst)) break :result .dead;
2582
2583 const pl_ty = self.air.typeOf(ty_op.operand);2594 const pl_ty = self.air.typeOf(ty_op.operand);
2584 if (!pl_ty.hasRuntimeBits()) break :result .{ .immediate = 1 };2595 if (!pl_ty.hasRuntimeBits()) break :result .{ .immediate = 1 };
25852596
...@@ -2624,10 +2635,6 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {...@@ -2624,10 +2635,6 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
2624fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {2635fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
2625 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2636 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
2631 const error_union_ty = self.air.getRefType(ty_op.ty);2638 const error_union_ty = self.air.getRefType(ty_op.ty);
2632 const payload_ty = error_union_ty.errorUnionPayload();2639 const payload_ty = error_union_ty.errorUnionPayload();
2633 const operand = try self.resolveInst(ty_op.operand);2640 const operand = try self.resolveInst(ty_op.operand);
...@@ -2654,9 +2661,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -2654,9 +2661,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
2654/// E to E!T2661/// E to E!T
2655fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {2662fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
2656 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2663 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2657 if (self.liveness.isUnused(inst)) {2664
2658 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
2659 }
2660 const error_union_ty = self.air.getRefType(ty_op.ty);2665 const error_union_ty = self.air.getRefType(ty_op.ty);
2661 const payload_ty = error_union_ty.errorUnionPayload();2666 const payload_ty = error_union_ty.errorUnionPayload();
2662 const operand = try self.resolveInst(ty_op.operand);2667 const operand = try self.resolveInst(ty_op.operand);
...@@ -2682,7 +2687,7 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2682,7 +2687,7 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
26822687
2683fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {2688fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
2684 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2689 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: {
2686 const src_mcv = try self.resolveInst(ty_op.operand);2691 const src_mcv = try self.resolveInst(ty_op.operand);
2687 if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result src_mcv;2692 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 {...@@ -2696,72 +2701,65 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
26962701
2697fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {2702fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
2698 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2703 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2699 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2704
2700 const operand = try self.resolveInst(ty_op.operand);2705 const operand = try self.resolveInst(ty_op.operand);
2701 const dst_mcv: MCValue = blk: {2706 const dst_mcv: MCValue = blk: {
2702 switch (operand) {2707 switch (operand) {
2703 .stack_offset => |off| {2708 .stack_offset => |off| {
2704 break :blk MCValue{ .stack_offset = off - 8 };2709 break :blk MCValue{ .stack_offset = off - 8 };
2705 },2710 },
2706 else => return self.fail("TODO implement slice_len for {}", .{operand}),2711 else => return self.fail("TODO implement slice_len for {}", .{operand}),
2707 }2712 }
2708 };
2709 break :result dst_mcv;
2710 };2713 };
2711 log.debug("airSliceLen(%{d}): {}", .{ inst, result });2714
2712 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2715 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
2713}2716}
27142717
2715fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {2718fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
2716 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2719 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_ty = self.air.typeOf(ty_op.operand);
2721 const src_mcv = try self.resolveInst(ty_op.operand);2722 const src_mcv = try self.resolveInst(ty_op.operand);
2722 const src_reg = switch (src_mcv) {2723 const src_reg = switch (src_mcv) {
2723 .register => |reg| reg,2724 .register => |reg| reg,
2724 else => try self.copyToTmpRegister(src_ty, src_mcv),2725 else => try self.copyToTmpRegister(src_ty, src_mcv),
2725 };2726 };
2726 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);2727 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
2727 defer self.register_manager.unlockReg(src_lock);2728 defer self.register_manager.unlockReg(src_lock);
27282729
2729 const dst_ty = self.air.typeOfIndex(inst);2730 const dst_ty = self.air.typeOfIndex(inst);
2730 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))2731 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
2731 src_reg2732 src_reg
2732 else2733 else
2733 try self.register_manager.allocReg(inst, gp);2734 try self.register_manager.allocReg(inst, gp);
2734 const dst_lock = self.register_manager.lockReg(dst_reg);2735 const dst_mcv = MCValue{ .register = dst_reg };
2735 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);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.*));2739 const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
2738 try self.asmRegisterMemory(2740 try self.asmRegisterMemory(
2739 .lea,2741 .lea,
2740 registerAlias(dst_reg, dst_abi_size),2742 registerAlias(dst_reg, dst_abi_size),
2741 Memory.sib(.qword, .{2743 Memory.sib(.qword, .{
2742 .base = src_reg,2744 .base = src_reg,
2743 .disp = @divExact(self.target.cpu.arch.ptrBitWidth(), 8),2745 .disp = @divExact(self.target.cpu.arch.ptrBitWidth(), 8),
2744 }),2746 }),
2745 );2747 );
2746 break :result .{ .register = dst_reg };2748
2747 };2749 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
2748 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2749}2750}
27502751
2751fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {2752fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
2752 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2753 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);2755 const dst_ty = self.air.typeOfIndex(inst);
2757 const opt_mcv = try self.resolveInst(ty_op.operand);2756 const opt_mcv = try self.resolveInst(ty_op.operand);
27582757
2759 break :result if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv))2758 const dst_mcv = if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv))
2760 opt_mcv2759 opt_mcv
2761 else2760 else
2762 try self.copyToRegisterWithInstTracking(inst, dst_ty, opt_mcv);2761 try self.copyToRegisterWithInstTracking(inst, dst_ty, opt_mcv);
2763 };2762 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
2764 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2765}2763}
27662764
2767fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Register {2765fn 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 {...@@ -2829,34 +2827,26 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
2829fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {2827fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
2830 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2828 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2831 const slice_ty = self.air.typeOf(bin_op.lhs);2829 const slice_ty = self.air.typeOf(bin_op.lhs);
2832 const result = if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: {2830
2833 var buf: Type.SlicePtrFieldTypeBuffer = undefined;2831 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
2834 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);2832 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
2835 const elem_ptr = try self.genSliceElemPtr(bin_op.lhs, bin_op.rhs);2833 const elem_ptr = try self.genSliceElemPtr(bin_op.lhs, bin_op.rhs);
2836 const dst_mcv = try self.allocRegOrMem(inst, false);2834 const dst_mcv = try self.allocRegOrMem(inst, false);
2837 try self.load(dst_mcv, elem_ptr, slice_ptr_field_type);2835 try self.load(dst_mcv, elem_ptr, slice_ptr_field_type);
2838 break :result dst_mcv;2836
2839 };2837 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
2840 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2841}2838}
28422839
2843fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {2840fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
2844 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2841 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2845 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2842 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2846 const result: MCValue = if (self.liveness.isUnused(inst))2843 const dst_mcv = try self.genSliceElemPtr(extra.lhs, extra.rhs);
2847 .dead2844 return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none });
2848 else
2849 try self.genSliceElemPtr(extra.lhs, extra.rhs);
2850 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2851}2845}
28522846
2853fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {2847fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
2854 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2848 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
2860 const array_ty = self.air.typeOf(bin_op.lhs);2850 const array_ty = self.air.typeOf(bin_op.lhs);
2861 const array = try self.resolveInst(bin_op.lhs);2851 const array = try self.resolveInst(bin_op.lhs);
2862 const array_lock: ?RegisterLock = switch (array) {2852 const array_lock: ?RegisterLock = switch (array) {
...@@ -2923,77 +2913,74 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2923,77 +2913,74 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
2923fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {2913fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
2924 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2914 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2925 const ptr_ty = self.air.typeOf(bin_op.lhs);2915 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);2917 // this is identical to the `airPtrElemPtr` codegen expect here an
2941 const offset_lock = self.register_manager.lockRegAssumeUnused(offset_reg);2918 // additional `mov` is needed at the end to get the actual value
2942 defer self.register_manager.unlockReg(offset_lock);
29432919
2944 const ptr_mcv = try self.resolveInst(bin_op.lhs);2920 const elem_ty = ptr_ty.elemType2();
2945 const elem_ptr_reg = if (ptr_mcv.isRegister() and self.liveness.operandDies(inst, 0))2921 const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*));
2946 ptr_mcv.register2922 const index_ty = self.air.typeOf(bin_op.rhs);
2947 else2923 const index_mcv = try self.resolveInst(bin_op.rhs);
2948 try self.copyToTmpRegister(ptr_ty, ptr_mcv);2924 const index_lock = switch (index_mcv) {
2949 const elem_ptr_lock = self.register_manager.lockRegAssumeUnused(elem_ptr_reg);2925 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2950 defer self.register_manager.unlockReg(elem_ptr_lock);2926 else => null,
2951 try self.asmRegisterRegister(.add, elem_ptr_reg, offset_reg);2927 };
2928 defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
29522929
2953 const dst_mcv = try self.allocRegOrMem(inst, true);2930 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_abi_size);
2954 const dst_lock = switch (dst_mcv) {2931 const offset_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
2955 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),2932 defer self.register_manager.unlockReg(offset_lock);
2956 else => null,2933
2957 };2934 const ptr_mcv = try self.resolveInst(bin_op.lhs);
2958 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);2935 const elem_ptr_reg = if (ptr_mcv.isRegister() and self.liveness.operandDies(inst, 0))
2959 try self.load(dst_mcv, .{ .register = elem_ptr_reg }, ptr_ty);2936 ptr_mcv.register
2960 break :result dst_mcv;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,
2961 };2947 };
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 });
2963}2952}
29642953
2965fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {2954fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
2966 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2955 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2967 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2956 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
29682957
2969 const result = if (self.liveness.isUnused(inst)) .dead else result: {2958 const ptr_ty = self.air.typeOf(extra.lhs);
2970 const ptr_ty = self.air.typeOf(extra.lhs);2959 const ptr = try self.resolveInst(extra.lhs);
2971 const ptr = try self.resolveInst(extra.lhs);2960 const ptr_lock: ?RegisterLock = switch (ptr) {
2972 const ptr_lock: ?RegisterLock = switch (ptr) {2961 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2973 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),2962 else => null,
2974 else => null,2963 };
2975 };2964 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
2976 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
29772965
2978 const elem_ty = ptr_ty.elemType2();2966 const elem_ty = ptr_ty.elemType2();
2979 const elem_abi_size = elem_ty.abiSize(self.target.*);2967 const elem_abi_size = elem_ty.abiSize(self.target.*);
2980 const index_ty = self.air.typeOf(extra.rhs);2968 const index_ty = self.air.typeOf(extra.rhs);
2981 const index = try self.resolveInst(extra.rhs);2969 const index = try self.resolveInst(extra.rhs);
2982 const index_lock: ?RegisterLock = switch (index) {2970 const index_lock: ?RegisterLock = switch (index) {
2983 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),2971 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2984 else => null,2972 else => null,
2985 };2973 };
2986 defer if (index_lock) |lock| self.register_manager.unlockReg(lock);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);2980 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr);
2989 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);2981 try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
2990 defer self.register_manager.unlockReg(offset_reg_lock);
29912982
2992 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr);2983 return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none });
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 });
2997}2984}
29982985
2999fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {2986fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
...@@ -3035,9 +3022,6 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {...@@ -3035,9 +3022,6 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
30353022
3036fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {3023fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
3037 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3024 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
3042 const tag_ty = self.air.typeOfIndex(inst);3026 const tag_ty = self.air.typeOfIndex(inst);
3043 const union_ty = self.air.typeOf(ty_op.operand);3027 const union_ty = self.air.typeOf(ty_op.operand);
...@@ -3089,8 +3073,6 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {...@@ -3089,8 +3073,6 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
3089fn airClz(self: *Self, inst: Air.Inst.Index) !void {3073fn airClz(self: *Self, inst: Air.Inst.Index) !void {
3090 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3074 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3091 const result = result: {3075 const result = result: {
3092 if (self.liveness.isUnused(inst)) break :result .dead;
3093
3094 const dst_ty = self.air.typeOfIndex(inst);3076 const dst_ty = self.air.typeOfIndex(inst);
3095 const src_ty = self.air.typeOf(ty_op.operand);3077 const src_ty = self.air.typeOf(ty_op.operand);
30963078
...@@ -3158,8 +3140,6 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {...@@ -3158,8 +3140,6 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {
3158fn airCtz(self: *Self, inst: Air.Inst.Index) !void {3140fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
3159 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3141 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3160 const result = result: {3142 const result = result: {
3161 if (self.liveness.isUnused(inst)) break :result .dead;
3162
3163 const dst_ty = self.air.typeOfIndex(inst);3143 const dst_ty = self.air.typeOfIndex(inst);
3164 const src_ty = self.air.typeOf(ty_op.operand);3144 const src_ty = self.air.typeOf(ty_op.operand);
3165 const src_bits = src_ty.bitSize(self.target.*);3145 const src_bits = src_ty.bitSize(self.target.*);
...@@ -3216,8 +3196,6 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {...@@ -3216,8 +3196,6 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
3216fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {3196fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
3217 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3197 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3218 const result: MCValue = result: {3198 const result: MCValue = result: {
3219 if (self.liveness.isUnused(inst)) break :result .dead;
3220
3221 const src_ty = self.air.typeOf(ty_op.operand);3199 const src_ty = self.air.typeOf(ty_op.operand);
3222 const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*));3200 const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*));
3223 const src_mcv = try self.resolveInst(ty_op.operand);3201 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...@@ -3386,148 +3364,138 @@ fn byteSwap(self: *Self, inst: Air.Inst.Index, src_ty: Type, src_mcv: MCValue, m
33863364
3387fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {3365fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
3388 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3366 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);3368 const src_ty = self.air.typeOf(ty_op.operand);
3396 switch (self.regExtraBits(src_ty)) {3369 const src_mcv = try self.resolveInst(ty_op.operand);
3397 0 => {},3370
3398 else => |extra| try self.genBinOpMir(3371 const dst_mcv = try self.byteSwap(inst, src_ty, src_mcv, true);
3399 if (src_ty.isSignedInt()) .sar else .shr,3372 switch (self.regExtraBits(src_ty)) {
3400 src_ty,3373 0 => {},
3401 dst_mcv,3374 else => |extra| try self.genBinOpMir(
3402 .{ .immediate = extra },3375 if (src_ty.isSignedInt()) .sar else .shr,
3403 ),3376 src_ty,
3404 }3377 dst_mcv,
3405 break :result dst_mcv;3378 .{ .immediate = extra },
3406 };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 });
3409}3383}
34103384
3411fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {3385fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
3412 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3386 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);3388 const src_ty = self.air.typeOf(ty_op.operand);
3417 const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*));3389 const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*));
3418 const src_mcv = try self.resolveInst(ty_op.operand);3390 const src_mcv = try self.resolveInst(ty_op.operand);
34193391
3420 const dst_mcv = try self.byteSwap(inst, src_ty, src_mcv, false);3392 const dst_mcv = try self.byteSwap(inst, src_ty, src_mcv, false);
3421 const dst_reg = dst_mcv.register;3393 const dst_reg = dst_mcv.register;
3422 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);3394 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
3423 defer self.register_manager.unlockReg(dst_lock);3395 defer self.register_manager.unlockReg(dst_lock);
34243396
3425 const tmp_reg = try self.register_manager.allocReg(null, gp);3397 const tmp_reg = try self.register_manager.allocReg(null, gp);
3426 const tmp_lock = self.register_manager.lockReg(tmp_reg);3398 const tmp_lock = self.register_manager.lockReg(tmp_reg);
3427 defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock);3399 defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock);
34283400
3429 {3401 {
3430 const dst = registerAlias(dst_reg, src_abi_size);3402 const dst = registerAlias(dst_reg, src_abi_size);
3431 const tmp = registerAlias(tmp_reg, src_abi_size);3403 const tmp = registerAlias(tmp_reg, src_abi_size);
3432 const imm = if (src_abi_size > 4)3404 const imm = if (src_abi_size > 4)
3433 try self.register_manager.allocReg(null, gp)3405 try self.register_manager.allocReg(null, gp)
3434 else3406 else
3435 undefined;3407 undefined;
34363408
3437 const mask = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - src_abi_size * 8);3409 const mask = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - src_abi_size * 8);
3438 const imm_0000_1111 = Immediate.u(mask / 0b0001_0001);3410 const imm_0000_1111 = Immediate.u(mask / 0b0001_0001);
3439 const imm_00_11 = Immediate.u(mask / 0b01_01);3411 const imm_00_11 = Immediate.u(mask / 0b01_01);
3440 const imm_0_1 = Immediate.u(mask / 0b1_1);3412 const imm_0_1 = Immediate.u(mask / 0b1_1);
34413413
3442 // dst = temp1 = bswap(operand)3414 // dst = temp1 = bswap(operand)
3443 try self.asmRegisterRegister(.mov, tmp, dst);3415 try self.asmRegisterRegister(.mov, tmp, dst);
3444 // tmp = temp13416 // tmp = temp1
3445 try self.asmRegisterImmediate(.shr, dst, Immediate.u(4));3417 try self.asmRegisterImmediate(.shr, dst, Immediate.u(4));
3446 // dst = temp1 >> 43418 // dst = temp1 >> 4
3447 if (src_abi_size > 4) {3419 if (src_abi_size > 4) {
3448 try self.asmRegisterImmediate(.mov, imm, imm_0000_1111);3420 try self.asmRegisterImmediate(.mov, imm, imm_0000_1111);
3449 try self.asmRegisterRegister(.@"and", tmp, imm);3421 try self.asmRegisterRegister(.@"and", tmp, imm);
3450 try self.asmRegisterRegister(.@"and", dst, imm);3422 try self.asmRegisterRegister(.@"and", dst, imm);
3451 } else {3423 } else {
3452 try self.asmRegisterImmediate(.@"and", tmp, imm_0000_1111);3424 try self.asmRegisterImmediate(.@"and", tmp, imm_0000_1111);
3453 try self.asmRegisterImmediate(.@"and", dst, imm_0000_1111);3425 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)
3507 }3426 }
35083427 // tmp = temp1 & 0x0F...0F
3509 switch (self.regExtraBits(src_ty)) {3428 // dst = (temp1 >> 4) & 0x0F...0F
3510 0 => {},3429 try self.asmRegisterImmediate(.shl, tmp, Immediate.u(4));
3511 else => |extra| try self.genBinOpMir(3430 // tmp = (temp1 & 0x0F...0F) << 4
3512 if (src_ty.isSignedInt()) .sar else .shr,3431 try self.asmRegisterRegister(.@"or", dst, tmp);
3513 src_ty,3432 // dst = temp2 = ((temp1 >> 4) & 0x0F...0F) | ((temp1 & 0x0F...0F) << 4)
3514 dst_mcv,3433 try self.asmRegisterRegister(.mov, tmp, dst);
3515 .{ .immediate = extra },3434 // tmp = temp2
3516 ),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);
3517 }3444 }
3518 break :result dst_mcv;3445 // tmp = temp2 & 0x33...33
3519 };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 });
3522}3492}
35233493
3524fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {3494fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
3525 const un_op = self.air.instructions.items(.data)[inst].un_op;3495 const un_op = self.air.instructions.items(.data)[inst].un_op;
3526 const result: MCValue = if (self.liveness.isUnused(inst))3496 _ = un_op;
3527 .dead3497 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
3528 else3498 //return self.finishAir(inst, result, .{ un_op, .none, .none });
3529 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
3530 return self.finishAir(inst, result, .{ un_op, .none, .none });
3531}3499}
35323500
3533fn reuseOperand(3501fn reuseOperand(
...@@ -3559,10 +3527,7 @@ fn reuseOperand(...@@ -3559,10 +3527,7 @@ fn reuseOperand(
35593527
3560 // Prevent the operand deaths processing code from deallocating it.3528 // Prevent the operand deaths processing code from deallocating it.
3561 self.liveness.clearOperandDeath(inst, op_index);3529 self.liveness.clearOperandDeath(inst, op_index);
35623530 self.getResolvedInstValue(Air.refToIndex(operand).?).reuse(self);
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);
35663531
3567 return true;3532 return true;
3568}3533}
...@@ -3703,9 +3668,6 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -3703,9 +3668,6 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
3703 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);3668 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);
37043669
3705 const ptr = try self.resolveInst(ty_op.operand);3670 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
3709 const dst_mcv: MCValue = if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr))3671 const dst_mcv: MCValue = if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr))
3710 // The MCValue that holds the pointer can be re-used as the value.3672 // The MCValue that holds the pointer can be re-used as the value.
3711 ptr3673 ptr
...@@ -4002,10 +3964,6 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {...@@ -4002,10 +3964,6 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
4002}3964}
40033965
4004fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue {3966fn 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
4009 const mcv = try self.resolveInst(operand);3967 const mcv = try self.resolveInst(operand);
4010 const ptr_ty = self.air.typeOf(operand);3968 const ptr_ty = self.air.typeOf(operand);
4011 const container_ty = ptr_ty.childType();3969 const container_ty = ptr_ty.childType();
...@@ -4072,7 +4030,7 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32...@@ -4072,7 +4030,7 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32
4072fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {4030fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
4073 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;4031 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4074 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;4032 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: {
4076 const operand = extra.struct_operand;4034 const operand = extra.struct_operand;
4077 const index = extra.field_index;4035 const index = extra.field_index;
40784036
...@@ -4220,11 +4178,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -4220,11 +4178,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
42204178
4221fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {4179fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
4222 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4180 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4223 const result: MCValue = if (self.liveness.isUnused(inst))4181 _ = ty_op;
4224 .dead4182 return self.fail("TODO implement airFieldParentPtr for {}", .{self.target.cpu.arch});
4225 else4183 //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4226 return self.fail("TODO implement airFieldParentPtr for {}", .{self.target.cpu.arch});
4227 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4228}4184}
42294185
4230fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: Air.Inst.Ref) !MCValue {4186fn 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 {...@@ -5443,9 +5399,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
5443 const src_index = self.air.instructions.items(.data)[inst].arg.src_index;5399 const src_index = self.air.instructions.items(.data)[inst].arg.src_index;
5444 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index);5400 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index);
54455401
5446 const result: MCValue = result: {5402 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
5447 if (self.liveness.isUnused(inst)) break :result .dead;
5448
5449 const dst_mcv: MCValue = switch (mcv) {5403 const dst_mcv: MCValue = switch (mcv) {
5450 .register => |reg| blk: {5404 .register => |reg| blk: {
5451 self.register_manager.getRegAssumeFree(reg.to64(), inst);5405 self.register_manager.getRegAssumeFree(reg.to64(), inst);
...@@ -5536,23 +5490,17 @@ fn airBreakpoint(self: *Self) !void {...@@ -5536,23 +5490,17 @@ fn airBreakpoint(self: *Self) !void {
5536}5490}
55375491
5538fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {5492fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
5539 const result = if (self.liveness.isUnused(inst)) .dead else result: {5493 const dst_mcv = try self.allocRegOrMem(inst, true);
5540 const dst_mcv = try self.allocRegOrMem(inst, true);5494 try self.setRegOrMem(Type.usize, dst_mcv, .{
5541 try self.setRegOrMem(Type.usize, dst_mcv, .{5495 .stack_offset = -@as(i32, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)),
5542 .stack_offset = -@as(i32, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)),5496 });
5543 });5497 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
5544 break :result dst_mcv;
5545 };
5546 return self.finishAir(inst, result, .{ .none, .none, .none });
5547}5498}
55485499
5549fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {5500fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
5550 const result = if (self.liveness.isUnused(inst)) .dead else result: {5501 const dst_mcv = try self.allocRegOrMem(inst, true);
5551 const dst_mcv = try self.allocRegOrMem(inst, true);5502 try self.setRegOrMem(Type.usize, dst_mcv, .{ .register = .rbp });
5552 try self.setRegOrMem(Type.usize, dst_mcv, .{ .register = .rbp });5503 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
5553 break :result dst_mcv;
5554 };
5555 return self.finishAir(inst, result, .{ .none, .none, .none });
5556}5504}
55575505
5558fn airFence(self: *Self, inst: Air.Inst.Index) !void {5506fn airFence(self: *Self, inst: Air.Inst.Index) !void {
...@@ -5749,7 +5697,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -5749,7 +5697,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
5749 }5697 }
57505698
5751 const result: MCValue = result: {5699 const result: MCValue = result: {
5752 if (self.liveness.isUnused(inst)) break :result .dead;5700 if (self.liveness.isUnused(inst)) break :result .unreach;
57535701
5754 switch (info.return_value) {5702 switch (info.return_value) {
5755 .register => {5703 .register => {
...@@ -5771,12 +5719,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -5771,12 +5719,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
5771 std.mem.copy(Air.Inst.Ref, buf[1..], args);5719 std.mem.copy(Air.Inst.Ref, buf[1..], args);
5772 return self.finishAir(inst, result, buf);5720 return self.finishAir(inst, result, buf);
5773 }5721 }
5774 var bt = try self.iterateBigTomb(inst, 1 + args.len);5722 var bt = self.liveness.iterateBigTomb(inst);
5775 bt.feed(callee);5723 self.feed(&bt, callee);
5776 for (args) |arg| {5724 for (args) |arg| self.feed(&bt, arg);
5777 bt.feed(arg);5725 return self.finishAirResult(inst, result);
5778 }
5779 return bt.finishAir(result);
5780}5726}
57815727
5782fn airRet(self: *Self, inst: Air.Inst.Index) !void {5728fn airRet(self: *Self, inst: Air.Inst.Index) !void {
...@@ -5799,7 +5745,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {...@@ -5799,7 +5745,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
5799 // which is available if the jump is 127 bytes or less forward.5745 // which is available if the jump is 127 bytes or less forward.
5800 const jmp_reloc = try self.asmJmpReloc(undefined);5746 const jmp_reloc = try self.asmJmpReloc(undefined);
5801 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);5747 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 });
5803}5749}
58045750
5805fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {5751fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
...@@ -5829,65 +5775,63 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -5829,65 +5775,63 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
5829 // which is available if the jump is 127 bytes or less forward.5775 // which is available if the jump is 127 bytes or less forward.
5830 const jmp_reloc = try self.asmJmpReloc(undefined);5776 const jmp_reloc = try self.asmJmpReloc(undefined);
5831 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);5777 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 });
5833}5779}
58345780
5835fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {5781fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
5836 const bin_op = self.air.instructions.items(.data)[inst].bin_op;5782 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5837 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {5783 const ty = self.air.typeOf(bin_op.lhs);
5838 const ty = self.air.typeOf(bin_op.lhs);5784 const ty_abi_size = ty.abiSize(self.target.*);
5839 const ty_abi_size = ty.abiSize(self.target.*);5785 const can_reuse = ty_abi_size <= 8;
5840 const can_reuse = ty_abi_size <= 8;
58415786
5842 try self.spillEflagsIfOccupied();5787 try self.spillEflagsIfOccupied();
5843 self.eflags_inst = inst;5788 self.eflags_inst = inst;
58445789
5845 const lhs_mcv = try self.resolveInst(bin_op.lhs);5790 const lhs_mcv = try self.resolveInst(bin_op.lhs);
5846 const lhs_lock = switch (lhs_mcv) {5791 const lhs_lock = switch (lhs_mcv) {
5847 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),5792 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5848 else => null,5793 else => null,
5849 };5794 };
5850 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);5795 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
58515796
5852 const rhs_mcv = try self.resolveInst(bin_op.rhs);5797 const rhs_mcv = try self.resolveInst(bin_op.rhs);
5853 const rhs_lock = switch (rhs_mcv) {5798 const rhs_lock = switch (rhs_mcv) {
5854 .register => |reg| self.register_manager.lockReg(reg),5799 .register => |reg| self.register_manager.lockReg(reg),
5855 else => null,5800 else => null,
5856 };5801 };
5857 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);5802 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);
58795803
5880 const src_mcv = if (flipped) lhs_mcv else rhs_mcv;5804 const dst_mem_ok = !ty.isRuntimeFloat();
5881 try self.genBinOpMir(switch (ty.tag()) {5805 var flipped = false;
5882 else => .cmp,5806 const dst_mcv: MCValue = if (can_reuse and !lhs_mcv.isImmediate() and
5883 .f32 => .ucomiss,5807 (dst_mem_ok or lhs_mcv.isRegister()) and self.liveness.operandDies(inst, 0))
5884 .f64 => .ucomisd,5808 lhs_mcv
5885 }, ty, dst_mcv, src_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;5825 const src_mcv = if (flipped) lhs_mcv else rhs_mcv;
5888 break :result .{5826 try self.genBinOpMir(switch (ty.tag()) {
5889 .eflags = Condition.fromCompareOperator(signedness, if (flipped) op.reverse() else op),5827 else => .cmp,
5890 };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),
5891 };5835 };
5892 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });5836 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
5893}5837}
...@@ -5899,56 +5843,55 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {...@@ -5899,56 +5843,55 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
58995843
5900fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {5844fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
5901 const un_op = self.air.instructions.items(.data)[inst].un_op;5845 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();5847 const addr_reg = try self.register_manager.allocReg(null, gp);
5936 self.eflags_inst = inst;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);5851 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
5939 const op_abi_size = @intCast(u32, op_ty.abiSize(self.target.*));5852 const atom_index = try elf_file.getOrCreateAtomForLazySymbol(
5940 const op_mcv = try self.resolveInst(un_op);5853 .{ .kind = .const_data, .ty = Type.anyerror },
5941 const dst_reg = switch (op_mcv) {5854 4, // dword alignment
5942 .register => |reg| reg,5855 );
5943 else => try self.copyToTmpRegister(op_ty, op_mcv),5856 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
5944 };5857 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
5945 try self.asmRegisterMemory(5858 .base = .ds,
5946 .cmp,5859 .disp = @intCast(i32, got_addr),
5947 registerAlias(dst_reg, op_abi_size),5860 }));
5948 Memory.sib(Memory.PtrSize.fromSize(op_abi_size), .{ .base = addr_reg }),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
5949 );5865 );
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),
5951 };5888 };
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 };
5952 return self.finishAir(inst, result, .{ un_op, .none, .none });5895 return self.finishAir(inst, result, .{ un_op, .none, .none });
5953}5896}
59545897
...@@ -5957,9 +5900,8 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {...@@ -5957,9 +5900,8 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {
5957 const extra = self.air.extraData(Air.Try, pl_op.payload);5900 const extra = self.air.extraData(Air.Try, pl_op.payload);
5958 const body = self.air.extra[extra.end..][0..extra.data.body_len];5901 const body = self.air.extra[extra.end..][0..extra.data.body_len];
5959 const err_union_ty = self.air.typeOf(pl_op.operand);5902 const err_union_ty = self.air.typeOf(pl_op.operand);
5960 const err_union = try self.resolveInst(pl_op.operand);5903 const result = try self.genTry(inst, pl_op.operand, body, err_union_ty, false);
5961 const result = try self.genTry(inst, err_union, body, err_union_ty, false);5904 return self.finishAir(inst, result, .{ .none, .none, .none });
5962 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
5963}5905}
59645906
5965fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {5907fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
...@@ -5967,15 +5909,14 @@ fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -5967,15 +5909,14 @@ fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
5967 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);5909 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
5968 const body = self.air.extra[extra.end..][0..extra.data.body_len];5910 const body = self.air.extra[extra.end..][0..extra.data.body_len];
5969 const err_union_ty = self.air.typeOf(extra.data.ptr).childType();5911 const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
5970 const err_union_ptr = try self.resolveInst(extra.data.ptr);5912 const result = try self.genTry(inst, extra.data.ptr, body, err_union_ty, true);
5971 const result = try self.genTry(inst, err_union_ptr, body, err_union_ty, true);5913 return self.finishAir(inst, result, .{ .none, .none, .none });
5972 return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none });
5973}5914}
59745915
5975fn genTry(5916fn genTry(
5976 self: *Self,5917 self: *Self,
5977 inst: Air.Inst.Index,5918 inst: Air.Inst.Index,
5978 err_union: MCValue,5919 err_union: Air.Inst.Ref,
5979 body: []const Air.Inst.Index,5920 body: []const Air.Inst.Index,
5980 err_union_ty: Type,5921 err_union_ty: Type,
5981 operand_is_ptr: bool,5922 operand_is_ptr: bool,
...@@ -5983,14 +5924,37 @@ fn genTry(...@@ -5983,14 +5924,37 @@ fn genTry(
5983 if (operand_is_ptr) {5924 if (operand_is_ptr) {
5984 return self.fail("TODO genTry for pointers", .{});5925 return self.fail("TODO genTry for pointers", .{});
5985 }5926 }
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
5987 const reloc = try self.genCondBrMir(Type.anyerror, is_err_mcv);5932 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);
5988 try self.genBody(body);5942 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
5989 try self.performReloc(reloc);5950 try self.performReloc(reloc);
5951
5952 for (liveness_cond_br.then_deaths) |operand| self.processDeath(operand);
5953
5990 const result = if (self.liveness.isUnused(inst))5954 const result = if (self.liveness.isUnused(inst))
5991 .dead5955 .unreach
5992 else5956 else
5993 try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union);5957 try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union_mcv);
5994 return result;5958 return result;
5995}5959}
59965960
...@@ -6013,12 +5977,12 @@ fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {...@@ -6013,12 +5977,12 @@ fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
6013 const function = self.air.values[ty_pl.payload].castTag(.function).?.data;5977 const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
6014 // TODO emit debug info for function change5978 // TODO emit debug info for function change
6015 _ = function;5979 _ = function;
6016 return self.finishAir(inst, .dead, .{ .none, .none, .none });5980 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
6017}5981}
60185982
6019fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {5983fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
6020 // TODO emit debug info lexical block5984 // TODO emit debug info lexical block
6021 return self.finishAir(inst, .dead, .{ .none, .none, .none });5985 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
6022}5986}
60235987
6024fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {5988fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
...@@ -6034,7 +5998,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {...@@ -6034,7 +5998,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
6034 const tag = self.air.instructions.items(.tag)[inst];5998 const tag = self.air.instructions.items(.tag)[inst];
6035 try self.genVarDbgInfo(tag, ty, mcv, name);5999 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 });
6038}6002}
60396003
6040fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {6004fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
...@@ -6071,7 +6035,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -6071,7 +6035,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
6071 const extra = self.air.extraData(Air.CondBr, pl_op.payload);6035 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
6072 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];6036 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
6073 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];6037 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
6076 const reloc = try self.genCondBrMir(cond_ty, cond);6040 const reloc = try self.genCondBrMir(cond_ty, cond);
60776041
...@@ -6082,60 +6046,37 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -6082,60 +6046,37 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
6082 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);6046 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
6083 }6047 }
60846048
6085 // Capture the state of register and stack allocation state so that we can revert to it.6049 const outer_state = try self.saveState();
6086 const saved_state = self.captureState();
6087
6088 {6050 {
6089 try self.branch_stack.append(.{});6051 self.scope_generation += 1;
6090 errdefer _ = self.branch_stack.pop();6052 const inner_state = try self.saveState();
60916053
6092 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);6054 for (liveness_cond_br.then_deaths) |operand| self.processDeath(operand);
6093 for (liveness_condbr.then_deaths) |operand| {
6094 self.processDeath(operand);
6095 }
6096 try self.genBody(then_body);6055 try self.genBody(then_body);
6097 }6056 try self.restoreState(inner_state, &.{}, .{
60986057 .emit_instructions = false,
6099 // Revert to the previous register and stack allocation state.6058 .update_tracking = true,
61006059 .resurrect = true,
6101 var then_branch = self.branch_stack.pop();6060 .close_scope = true,
6102 defer then_branch.deinit(self.gpa);6061 });
6103
6104 self.revertState(saved_state);
6105
6106 try self.performReloc(reloc);
61076062
6108 {6063 try self.performReloc(reloc);
6109 try self.branch_stack.append(.{});
6110 errdefer _ = self.branch_stack.pop();
61116064
6112 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);6065 for (liveness_cond_br.else_deaths) |operand| self.processDeath(operand);
6113 for (liveness_condbr.else_deaths) |operand| {
6114 self.processDeath(operand);
6115 }
6116 try self.genBody(else_body);6066 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 });
6117 }6073 }
61186074 try self.restoreState(outer_state, &.{}, .{
6119 var else_branch = self.branch_stack.pop();6075 .emit_instructions = false,
6120 defer else_branch.deinit(self.gpa);6076 .update_tracking = false,
61216077 .resurrect = false,
6122 // At this point, each branch will possibly have conflicting values for where6078 .close_scope = true,
6123 // each instruction is stored. They agree, however, on which instructions are alive/dead.6079 });
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);
61396080
6140 // We already took care of pl_op.operand earlier, so we're going6081 // We already took care of pl_op.operand earlier, so we're going
6141 // to pass .none here6082 // to pass .none here
...@@ -6309,67 +6250,53 @@ fn isNonErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCVa...@@ -6309,67 +6250,53 @@ fn isNonErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCVa
63096250
6310fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {6251fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
6311 const un_op = self.air.instructions.items(.data)[inst].un_op;6252 const un_op = self.air.instructions.items(.data)[inst].un_op;
6312 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {6253 const operand = try self.resolveInst(un_op);
6313 const operand = try self.resolveInst(un_op);6254 const ty = self.air.typeOf(un_op);
6314 const ty = self.air.typeOf(un_op);6255 const result = try self.isNull(inst, ty, operand);
6315 break :result try self.isNull(inst, ty, operand);
6316 };
6317 return self.finishAir(inst, result, .{ un_op, .none, .none });6256 return self.finishAir(inst, result, .{ un_op, .none, .none });
6318}6257}
63196258
6320fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {6259fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
6321 const un_op = self.air.instructions.items(.data)[inst].un_op;6260 const un_op = self.air.instructions.items(.data)[inst].un_op;
6322 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {6261 const operand = try self.resolveInst(un_op);
6323 const operand = try self.resolveInst(un_op);6262 const ty = self.air.typeOf(un_op);
6324 const ty = self.air.typeOf(un_op);6263 const result = try self.isNullPtr(inst, ty, operand);
6325 break :result try self.isNullPtr(inst, ty, operand);
6326 };
6327 return self.finishAir(inst, result, .{ un_op, .none, .none });6264 return self.finishAir(inst, result, .{ un_op, .none, .none });
6328}6265}
63296266
6330fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {6267fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
6331 const un_op = self.air.instructions.items(.data)[inst].un_op;6268 const un_op = self.air.instructions.items(.data)[inst].un_op;
6332 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {6269 const operand = try self.resolveInst(un_op);
6333 const operand = try self.resolveInst(un_op);6270 const ty = self.air.typeOf(un_op);
6334 const ty = self.air.typeOf(un_op);6271 const result = switch (try self.isNull(inst, ty, operand)) {
6335 break :result switch (try self.isNull(inst, ty, operand)) {6272 .eflags => |cc| .{ .eflags = cc.negate() },
6336 .eflags => |cc| .{ .eflags = cc.negate() },6273 else => unreachable,
6337 else => unreachable,
6338 };
6339 };6274 };
6340 return self.finishAir(inst, result, .{ un_op, .none, .none });6275 return self.finishAir(inst, result, .{ un_op, .none, .none });
6341}6276}
63426277
6343fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {6278fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
6344 const un_op = self.air.instructions.items(.data)[inst].un_op;6279 const un_op = self.air.instructions.items(.data)[inst].un_op;
6345 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {6280 const operand = try self.resolveInst(un_op);
6346 const operand = try self.resolveInst(un_op);6281 const ty = self.air.typeOf(un_op);
6347 const ty = self.air.typeOf(un_op);6282 const result = switch (try self.isNullPtr(inst, ty, operand)) {
6348 break :result switch (try self.isNullPtr(inst, ty, operand)) {6283 .eflags => |cc| .{ .eflags = cc.negate() },
6349 .eflags => |cc| .{ .eflags = cc.negate() },6284 else => unreachable,
6350 else => unreachable,
6351 };
6352 };6285 };
6353 return self.finishAir(inst, result, .{ un_op, .none, .none });6286 return self.finishAir(inst, result, .{ un_op, .none, .none });
6354}6287}
63556288
6356fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {6289fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
6357 const un_op = self.air.instructions.items(.data)[inst].un_op;6290 const un_op = self.air.instructions.items(.data)[inst].un_op;
6358 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {6291 const operand = try self.resolveInst(un_op);
6359 const operand = try self.resolveInst(un_op);6292 const ty = self.air.typeOf(un_op);
6360 const ty = self.air.typeOf(un_op);6293 const result = try self.isErr(inst, ty, operand);
6361 break :result try self.isErr(inst, ty, operand);
6362 };
6363 return self.finishAir(inst, result, .{ un_op, .none, .none });6294 return self.finishAir(inst, result, .{ un_op, .none, .none });
6364}6295}
63656296
6366fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {6297fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
6367 const un_op = self.air.instructions.items(.data)[inst].un_op;6298 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
6373 const operand_ptr = try self.resolveInst(un_op);6300 const operand_ptr = try self.resolveInst(un_op);
6374 const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {6301 const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {
6375 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),6302 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
...@@ -6395,21 +6322,15 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -6395,21 +6322,15 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
63956322
6396fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {6323fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
6397 const un_op = self.air.instructions.items(.data)[inst].un_op;6324 const un_op = self.air.instructions.items(.data)[inst].un_op;
6398 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {6325 const operand = try self.resolveInst(un_op);
6399 const operand = try self.resolveInst(un_op);6326 const ty = self.air.typeOf(un_op);
6400 const ty = self.air.typeOf(un_op);6327 const result = try self.isNonErr(inst, ty, operand);
6401 break :result try self.isNonErr(inst, ty, operand);
6402 };
6403 return self.finishAir(inst, result, .{ un_op, .none, .none });6328 return self.finishAir(inst, result, .{ un_op, .none, .none });
6404}6329}
64056330
6406fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {6331fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
6407 const un_op = self.air.instructions.items(.data)[inst].un_op;6332 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
6413 const operand_ptr = try self.resolveInst(un_op);6334 const operand_ptr = try self.resolveInst(un_op);
6414 const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {6335 const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {
6415 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),6336 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
...@@ -6439,103 +6360,61 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {...@@ -6439,103 +6360,61 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
6439 const loop = self.air.extraData(Air.Block, ty_pl.payload);6360 const loop = self.air.extraData(Air.Block, ty_pl.payload);
6440 const body = self.air.extra[loop.end..][0..loop.data.body_len];6361 const body = self.air.extra[loop.end..][0..loop.data.body_len];
6441 const jmp_target = @intCast(u32, self.mir_instructions.len);6362 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();6364 self.scope_generation += 1;
6452 defer branch.deinit(self.gpa);6365 const state = try self.saveState();
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);
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 });
6465 _ = try self.asmJmpReloc(jmp_target);6374 _ = 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
6472 return self.finishAirBookkeeping();6376 return self.finishAirBookkeeping();
6473}6377}
64746378
6475fn airBlock(self: *Self, inst: Air.Inst.Index) !void {6379fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
6476 // A block is a setup to be able to jump to the end.6380 // A block is a setup to be able to jump to the end.
6477 const branch_depth = @intCast(u32, self.branch_stack.items.len);6381 self.inst_tracking.putAssumeCapacityNoClobber(inst, InstTracking.init(.unreach));
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();
65006382
6501 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;6383 self.scope_generation += 1;
6502 const extra = self.air.extraData(Air.Block, ty_pl.payload);6384 try self.blocks.putNoClobber(self.gpa, inst, .{ .state = self.initRetroactiveState() });
6503 const body = self.air.extra[extra.end..][0..extra.data.body_len];6385 const liveness = self.liveness.getBlock(inst);
6504 try self.genBody(body);
6505 }
65066386
6507 const block_data = self.blocks.getPtr(inst).?;6387 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
6508 const target_branch = self.branch_stack.pop();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});6392 var block_data = self.blocks.fetchRemove(inst).?;
6511 log.debug("Upper branches:", .{});6393 defer block_data.value.deinit(self.gpa);
6512 for (self.branch_stack.items) |bs| {6394 if (block_data.value.relocs.items.len > 0) {
6513 log.debug("{}", .{bs.fmtDebug()});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);
6514 }6402 }
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);6404 const tracking = self.inst_tracking.getPtr(inst).?;
65216405 if (self.liveness.isUnused(inst)) tracking.die(self);
6522 const result = if (unused) .dead else self.getResolvedInstValue(inst).?.*;6406 self.getValue(tracking.short, inst);
6523 self.getValue(result, inst);
6524 self.finishAirBookkeeping();6407 self.finishAirBookkeeping();
6525}6408}
65266409
6527fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {6410fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
6528 const pl_op = self.air.instructions.items(.data)[inst].pl_op;6411 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
6529 const condition = try self.resolveInst(pl_op.operand);6412 const condition = try self.resolveInst(pl_op.operand);
6530 const condition_ty = self.air.typeOf(pl_op.operand);6413 const condition_ty = self.air.typeOf(pl_op.operand);
6531 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);6414 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
6532 var extra_index: usize = switch_br.end;6415 var extra_index: usize = switch_br.end;
6533 var case_i: u32 = 0;6416 var case_i: u32 = 0;
6534 const liveness = try self.liveness.getSwitchBr(6417 const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.data.cases_len + 1);
6535 self.gpa,
6536 inst,
6537 switch_br.data.cases_len + 1,
6538 );
6539 defer self.gpa.free(liveness.deaths);6418 defer self.gpa.free(liveness.deaths);
65406419
6541 // If the condition dies here in this switch instruction, process6420 // If the condition dies here in this switch instruction, process
...@@ -6545,186 +6424,69 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -6545,186 +6424,69 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
6545 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);6424 if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst);
6546 }6425 }
65476426
6548 log.debug("airSwitch: %{d}", .{inst});6427 const outer_state = try self.saveState();
6549 log.debug("Upper branches:", .{});6428 {
6550 for (self.branch_stack.items) |bs| {6429 self.scope_generation += 1;
6551 log.debug("{}", .{bs.fmtDebug()});6430 const inner_state = try self.saveState();
6552 }6431
65536432 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
6554 var prev_branch: ?Branch = null;6433 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
6555 defer if (prev_branch) |*branch| branch.deinit(self.gpa);6434 const items = @ptrCast(
65566435 []const Air.Inst.Ref,
6557 // Capture the state of register and stack allocation state so that we can revert to it.6436 self.air.extra[case.end..][0..case.data.items_len],
6558 const saved_state = self.captureState();6437 );
65596438 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
6560 const cases_len = switch_br.data.cases_len + @boolToInt(switch_br.data.else_body_len > 0);6439 extra_index = case.end + items.len + case_body.len;
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 }
65796440
6580 {6441 var relocs = try self.gpa.alloc(u32, items.len);
6581 if (cases_len > 1) try self.branch_stack.append(.{});6442 defer self.gpa.free(relocs);
6582 errdefer _ = if (cases_len > 1) self.branch_stack.pop();
65836443
6584 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);6444 for (items, relocs) |item, *reloc| {
6585 for (liveness.deaths[case_i]) |operand| {6445 try self.spillEflagsIfOccupied();
6586 self.processDeath(operand);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);
6587 }6449 }
65886450
6589 try self.genBody(case_body);6451 for (liveness.deaths[case_i]) |operand| self.processDeath(operand);
6590 }
65916452
6592 // Consolidate returned MCValues between prongs like we do in airCondBr.6453 try self.genBody(case_body);
6593 if (cases_len > 1) {6454 try self.restoreState(inner_state, &.{}, .{
6594 var case_branch = self.branch_stack.pop();6455 .emit_instructions = false,
6595 errdefer case_branch.deinit(self.gpa);6456 .update_tracking = true,
6457 .resurrect = true,
6458 .close_scope = true,
6459 });
65966460
6597 log.debug("Case-{d} branch: {}", .{ case_i, case_branch.fmtDebug() });6461 for (relocs) |reloc| try self.performReloc(reloc);
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;
6604 }6462 }
66056463
6606 for (relocs) |reloc| try self.performReloc(reloc);6464 if (switch_br.data.else_body_len > 0) {
6607 }6465 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
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();
66186466
6619 const else_deaths = liveness.deaths.len - 1;6467 const else_deaths = liveness.deaths.len - 1;
6620 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);6468 for (liveness.deaths[else_deaths]) |operand| self.processDeath(operand);
6621 for (liveness.deaths[else_deaths]) |operand| {
6622 self.processDeath(operand);
6623 }
66246469
6625 try self.genBody(else_body);6470 try self.genBody(else_body);
6626 }6471 try self.restoreState(inner_state, &.{}, .{
66276472 .emit_instructions = false,
6628 // Consolidate returned MCValues between a prong and the else branch like we do in airCondBr.6473 .update_tracking = true,
6629 if (cases_len > 1) {6474 .resurrect = true,
6630 var else_branch = self.branch_stack.pop();6475 .close_scope = true,
6631 errdefer else_branch.deinit(self.gpa);6476 });
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;
6639 }6477 }
6640 }6478 }
6479 try self.restoreState(outer_state, &.{}, .{
6480 .emit_instructions = false,
6481 .update_tracking = false,
6482 .resurrect = false,
6483 .close_scope = true,
6484 });
66416485
6642 // We already took care of pl_op.operand earlier, so we're going to pass .none here6486 // We already took care of pl_op.operand earlier, so we're going to pass .none here
6643 return self.finishAir(inst, .unreach, .{ .none, .none, .none });6487 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
6644}6488}
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
6728fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {6490fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
6729 const next_inst = @intCast(u32, self.mir_instructions.len);6491 const next_inst = @intCast(u32, self.mir_instructions.len);
6730 switch (self.mir_instructions.items(.tag)[reloc]) {6492 switch (self.mir_instructions.items(.tag)[reloc]) {
...@@ -6740,76 +6502,53 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {...@@ -6740,76 +6502,53 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
67406502
6741fn airBr(self: *Self, inst: Air.Inst.Index) !void {6503fn airBr(self: *Self, inst: Air.Inst.Index) !void {
6742 const br = self.air.instructions.items(.data)[inst].br;6504 const br = self.air.instructions.items(.data)[inst].br;
6743 const block = br.block_inst;6505 const src_mcv = try self.resolveInst(br.operand);
67446506
6745 // The first break instruction encounters `.none` here and chooses a6507 const block_ty = self.air.typeOfIndex(br.block_inst);
6746 // machine code value for the block result, populating this field.6508 const block_unused =
6747 // Following break instructions encounter that value and use it for6509 !block_ty.hasRuntimeBitsIgnoreComptime() or self.liveness.isUnused(br.block_inst);
6748 // the location to store their block results.6510 const block_tracking = self.inst_tracking.getPtr(br.block_inst).?;
6749 if (self.getResolvedInstValue(block)) |dst_mcv| {6511 const block_data = self.blocks.getPtr(br.block_inst).?;
6750 const src_mcv = try self.resolveInst(br.operand);6512
6751 switch (dst_mcv.*) {6513 if (block_data.relocs.items.len == 0) {
6752 .none => {6514 block_tracking.* = InstTracking.init(result: {
6753 const result = result: {6515 if (block_unused) break :result .none;
6754 if (self.reuseOperand(inst, br.operand, 0, src_mcv)) break :result src_mcv;6516 if (self.reuseOperand(inst, br.operand, 0, src_mcv)) {
67556517 // Fix instruction tracking
6756 const new_mcv = try self.allocRegOrMem(block, true);6518 switch (src_mcv) {
6757 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, src_mcv);6519 .register => |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
6758 break :result new_mcv;6520 self.register_manager.registers[index] = br.block_inst;
6759 };6521 },
6760 dst_mcv.* = result;6522 else => {},
6761 self.freeValue(result);6523 }
6762 },6524 break :result src_mcv;
6763 else => try self.setRegOrMem(self.air.typeOfIndex(block), dst_mcv.*, src_mcv),6525 }
6764 }6526
6765 }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.
6768 if (self.liveness.operandDies(inst, 0)) {6534 if (self.liveness.operandDies(inst, 0)) {
6769 if (Air.refToIndex(br.operand)) |op_inst| self.processDeath(op_inst);6535 if (Air.refToIndex(br.operand)) |op_inst| self.processDeath(op_inst);
6770 }6536 }
67716537
6772 const block_data = self.blocks.getPtr(block).?;6538 if (block_data.relocs.items.len == 0) {
6773 {6539 try self.saveRetroactiveState(&block_data.state);
6774 var branch = Branch{};6540 block_tracking.die(self);
6775 errdefer branch.deinit(self.gpa);6541 } else try self.restoreState(block_data.state, &.{}, .{
67766542 .emit_instructions = true,
6777 var branch_i = self.branch_stack.items.len - 1;6543 .update_tracking = false,
6778 while (branch_i >= block_data.branch_depth) : (branch_i -= 1) {6544 .resurrect = false,
6779 const table = &self.branch_stack.items[branch_i].inst_table;6545 .close_scope = false,
6780 try branch.inst_table.ensureUnusedCapacity(self.gpa, table.count());6546 });
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 }
68076547
6808 // Emit a jump with a relocation. It will be patched up after the block ends.6548 // Emit a jump with a relocation. It will be patched up after the block ends.
6809 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
6810 // Leave the jump offset undefined6549 // Leave the jump offset undefined
6811 const jmp_reloc = try self.asmJmpReloc(undefined);6550 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
6814 self.finishAirBookkeeping();6553 self.finishAirBookkeeping();
6815}6554}
...@@ -6817,7 +6556,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -6817,7 +6556,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {
6817fn airAsm(self: *Self, inst: Air.Inst.Index) !void {6556fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
6818 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;6557 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
6819 const extra = self.air.extraData(Air.Asm, ty_pl.payload);6558 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
6820 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
6821 const clobbers_len = @truncate(u31, extra.data.flags);6559 const clobbers_len = @truncate(u31, extra.data.flags);
6822 var extra_i: usize = extra.end;6560 var extra_i: usize = extra.end;
6823 const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);6561 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 {...@@ -6826,216 +6564,214 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
6826 extra_i += inputs.len;6564 extra_i += inputs.len;
68276565
6828 var result: MCValue = .none;6566 var result: MCValue = .none;
6829 if (!is_volatile and self.liveness.isUnused(inst)) result = .dead else {6567 var args = std.StringArrayHashMap(MCValue).init(self.gpa);
6830 var args = std.StringArrayHashMap(MCValue).init(self.gpa);6568 try args.ensureTotalCapacity(outputs.len + inputs.len + clobbers_len);
6831 try args.ensureTotalCapacity(outputs.len + inputs.len + clobbers_len);6569 defer {
6832 defer {6570 for (args.values()) |arg| switch (arg) {
6833 for (args.values()) |arg| switch (arg) {6571 .register => |reg| self.register_manager.unlockReg(.{ .register = reg }),
6834 .register => |reg| self.register_manager.unlockReg(.{ .register = reg }),6572 else => {},
6835 else => {},6573 };
6836 };6574 args.deinit();
6837 args.deinit();6575 }
6838 }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) {6581 for (outputs) |output| {
6841 return self.fail("TODO implement codegen for asm with more than 1 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 => {},
6842 }6605 }
6606 if (output == .none) result = mcv;
6607 }
68436608
6844 for (outputs) |output| {6609 for (inputs) |input| {
6845 if (output != .none) {6610 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
6846 return self.fail("TODO implement codegen for non-expr asm", .{});6611 const constraint = std.mem.sliceTo(input_bytes, 0);
6847 }6612 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
6848 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);6613 // This equation accounts for the fact that even if we have exactly 4 bytes
6849 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);6614 // for the string, we still use the next u32 for the null terminator.
6850 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);6615 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
6851 // This equation accounts for the fact that even if we have exactly 4 bytes6616
6852 // for the string, we still use the next u32 for the null terminator.6617 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
6853 extra_i += (constraint.len + name.len + (2 + 3)) / 4;6618 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
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;
6870 }6619 }
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| {6629 {
6873 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);6630 var clobber_i: u32 = 0;
6874 const constraint = std.mem.sliceTo(input_bytes, 0);6631 while (clobber_i < clobbers_len) : (clobber_i += 1) {
6875 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);6632 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
6876 // This equation accounts for the fact that even if we have exactly 4 bytes6633 // This equation accounts for the fact that even if we have exactly 4 bytes
6877 // for the string, we still use the next u32 for the null terminator.6634 // for the string, we still use the next u32 for the null terminator.
6878 extra_i += (constraint.len + name.len + (2 + 3)) / 4;6635 extra_i += clobber.len / 4 + 1;
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});
68866636
6887 const arg_mcv = try self.resolveInst(input);6637 // TODO honor these
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 }
6902 }6638 }
6639 }
69036640
6904 const asm_source = mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];6641 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;");6642 var line_it = mem.tokenize(u8, asm_source, "\n\r;");
6906 while (line_it.next()) |line| {6643 while (line_it.next()) |line| {
6907 var mnem_it = mem.tokenize(u8, line, " \t");6644 var mnem_it = mem.tokenize(u8, line, " \t");
6908 const mnem_str = mnem_it.next() orelse continue;6645 const mnem_str = mnem_it.next() orelse continue;
6909 if (mem.startsWith(u8, mnem_str, "#")) continue;6646 if (mem.startsWith(u8, mnem_str, "#")) continue;
69106647
6911 const mnem_size: ?Memory.PtrSize = if (mem.endsWith(u8, mnem_str, "b"))6648 const mnem_size: ?Memory.PtrSize = if (mem.endsWith(u8, mnem_str, "b"))
6912 .byte6649 .byte
6913 else if (mem.endsWith(u8, mnem_str, "w"))6650 else if (mem.endsWith(u8, mnem_str, "w"))
6914 .word6651 .word
6915 else if (mem.endsWith(u8, mnem_str, "l"))6652 else if (mem.endsWith(u8, mnem_str, "l"))
6916 .dword6653 .dword
6917 else if (mem.endsWith(u8, mnem_str, "q"))6654 else if (mem.endsWith(u8, mnem_str, "q"))
6918 .qword6655 .qword
6919 else6656 else
6920 null;6657 null;
6921 const mnem = std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse6658 const mnem = std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse
6922 (if (mnem_size) |_|6659 (if (mnem_size) |_|
6923 std.meta.stringToEnum(Mir.Inst.Tag, mnem_str[0 .. mnem_str.len - 1])6660 std.meta.stringToEnum(Mir.Inst.Tag, mnem_str[0 .. mnem_str.len - 1])
6924 else6661 else
6925 null) orelse return self.fail("Invalid mnemonic: '{s}'", .{mnem_str});6662 null) orelse return self.fail("Invalid mnemonic: '{s}'", .{mnem_str});
69266663
6927 var op_it = mem.tokenize(u8, mnem_it.rest(), ",");6664 var op_it = mem.tokenize(u8, mnem_it.rest(), ",");
6928 var ops = [1]encoder.Instruction.Operand{.none} ** 4;6665 var ops = [1]encoder.Instruction.Operand{.none} ** 4;
6929 for (&ops) |*op| {6666 for (&ops) |*op| {
6930 const op_str = mem.trim(u8, op_it.next() orelse break, " \t");6667 const op_str = mem.trim(u8, op_it.next() orelse break, " \t");
6931 if (mem.startsWith(u8, op_str, "#")) break;6668 if (mem.startsWith(u8, op_str, "#")) break;
6932 if (mem.startsWith(u8, op_str, "%%")) {6669 if (mem.startsWith(u8, op_str, "%%")) {
6933 const colon = mem.indexOfScalarPos(u8, op_str, "%%".len + 2, ':');6670 const colon = mem.indexOfScalarPos(u8, op_str, "%%".len + 2, ':');
6934 const reg = parseRegName(op_str["%%".len .. colon orelse op_str.len]) orelse6671 const reg = parseRegName(op_str["%%".len .. colon orelse op_str.len]) orelse
6935 return self.fail("Invalid register: '{s}'", .{op_str});6672 return self.fail("Invalid register: '{s}'", .{op_str});
6936 if (colon) |colon_pos| {6673 if (colon) |colon_pos| {
6937 const disp = std.fmt.parseInt(i32, op_str[colon_pos + 1 ..], 0) catch6674 const disp = std.fmt.parseInt(i32, op_str[colon_pos + 1 ..], 0) catch
6938 return self.fail("Invalid displacement: '{s}'", .{op_str});6675 return self.fail("Invalid displacement: '{s}'", .{op_str});
6939 op.* = .{ .mem = Memory.sib(6676 op.* = .{ .mem = Memory.sib(
6940 mnem_size orelse return self.fail("Unknown size: '{s}'", .{op_str}),6677 mnem_size orelse return self.fail("Unknown size: '{s}'", .{op_str}),
6941 .{ .base = reg, .disp = disp },6678 .{ .base = reg, .disp = disp },
6942 ) };6679 ) };
6943 } else {6680 } else {
6944 if (mnem_size) |size| if (reg.bitSize() != size.bitSize())6681 if (mnem_size) |size| if (reg.bitSize() != size.bitSize())
6945 return self.fail("Invalid register size: '{s}'", .{op_str});6682 return self.fail("Invalid register size: '{s}'", .{op_str});
6946 op.* = .{ .reg = reg };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});
6947 }6698 }
6948 } else if (mem.startsWith(u8, op_str, "%[") and mem.endsWith(u8, op_str, "]")) {6699 op.* = .{ .imm = Immediate.s(s) };
6949 switch (args.get(op_str["%[".len .. op_str.len - "]".len]) orelse6700 } else |_| if (std.fmt.parseInt(u64, op_str["$".len..], 0)) |u| {
6950 return self.fail("No matching constraint: '{s}'", .{op_str})) {6701 if (mnem_size) |size| {
6951 .register => |reg| op.* = .{ .reg = reg },6702 const max = @as(u64, math.maxInt(u64)) >>
6952 else => return self.fail("Invalid constraint: '{s}'", .{op_str}),6703 @intCast(u6, 64 - size.bitSize());
6704 if (u > max)
6705 return self.fail("Invalid immediate size: '{s}'", .{op_str});
6953 }6706 }
6954 } else if (mem.startsWith(u8, op_str, "$")) {6707 op.* = .{ .imm = Immediate.u(u) };
6955 if (std.fmt.parseInt(i32, op_str["$".len..], 0)) |s| {6708 } else |_| return self.fail("Invalid immediate: '{s}'", .{op_str});
6956 if (mnem_size) |size| {6709 } else return self.fail("Invalid operand: '{s}'", .{op_str});
6957 const max = @as(u64, math.maxInt(u64)) >>6710 } else if (op_it.next()) |op_str| return self.fail("Extra operand: '{s}'", .{op_str});
6958 @intCast(u6, 64 - (size.bitSize() - 1));6711
6959 if ((if (s < 0) ~s else s) > max)6712 (switch (ops[0]) {
6960 return self.fail("Invalid immediate size: '{s}'", .{op_str});6713 .none => self.asmOpOnly(mnem),
6961 }6714 .reg => |reg0| switch (ops[1]) {
6962 op.* = .{ .imm = Immediate.s(s) };6715 .none => self.asmRegister(mnem, reg0),
6963 } else |_| if (std.fmt.parseInt(u64, op_str["$".len..], 0)) |u| {6716 .reg => |reg1| switch (ops[2]) {
6964 if (mnem_size) |size| {6717 .none => self.asmRegisterRegister(mnem, reg1, reg0),
6965 const max = @as(u64, math.maxInt(u64)) >>6718 .reg => |reg2| switch (ops[3]) {
6966 @intCast(u6, 64 - size.bitSize());6719 .none => self.asmRegisterRegisterRegister(mnem, reg2, reg1, reg0),
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 },
6989 else => error.InvalidInstruction,6720 else => error.InvalidInstruction,
6990 },6721 },
6991 .mem => |mem1| switch (ops[2]) {6722 .mem => |mem2| switch (ops[3]) {
6992 .none => self.asmMemoryRegister(mnem, mem1, reg0),6723 .none => self.asmMemoryRegisterRegister(mnem, mem2, reg1, reg0),
6993 else => error.InvalidInstruction,6724 else => error.InvalidInstruction,
6994 },6725 },
6995 else => error.InvalidInstruction,6726 else => error.InvalidInstruction,
6996 },6727 },
6997 .mem => |mem0| switch (ops[1]) {6728 .mem => |mem1| switch (ops[2]) {
6998 .none => self.asmMemory(mnem, mem0),6729 .none => self.asmMemoryRegister(mnem, mem1, reg0),
6999 .reg => |reg1| switch (ops[2]) {
7000 .none => self.asmRegisterMemory(mnem, reg1, mem0),
7001 else => error.InvalidInstruction,
7002 },
7003 else => error.InvalidInstruction,6730 else => error.InvalidInstruction,
7004 },6731 },
7005 .imm => |imm0| switch (ops[1]) {6732 else => error.InvalidInstruction,
7006 .none => self.asmImmediate(mnem, imm0),6733 },
7007 .reg => |reg1| switch (ops[2]) {6734 .mem => |mem0| switch (ops[1]) {
7008 .none => self.asmRegisterImmediate(mnem, reg1, imm0),6735 .none => self.asmMemory(mnem, mem0),
7009 .reg => |reg2| switch (ops[3]) {6736 .reg => |reg1| switch (ops[2]) {
7010 .none => self.asmRegisterRegisterImmediate(mnem, reg2, reg1, imm0),6737 .none => self.asmRegisterMemory(mnem, reg1, mem0),
7011 else => error.InvalidInstruction,6738 else => error.InvalidInstruction,
7012 },6739 },
7013 .mem => |mem2| switch (ops[3]) {6740 else => error.InvalidInstruction,
7014 .none => self.asmMemoryRegisterImmediate(mnem, mem2, reg1, imm0),6741 },
7015 else => error.InvalidInstruction,6742 .imm => |imm0| switch (ops[1]) {
7016 },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),
7017 else => error.InvalidInstruction,6748 else => error.InvalidInstruction,
7018 },6749 },
7019 .mem => |mem1| switch (ops[2]) {6750 .mem => |mem2| switch (ops[3]) {
7020 .none => self.asmMemoryImmediate(mnem, mem1, imm0),6751 .none => self.asmMemoryRegisterImmediate(mnem, mem2, reg1, imm0),
7021 else => error.InvalidInstruction,6752 else => error.InvalidInstruction,
7022 },6753 },
7023 else => error.InvalidInstruction,6754 else => error.InvalidInstruction,
7024 },6755 },
7025 }) catch |err| switch (err) {6756 .mem => |mem1| switch (ops[2]) {
7026 error.InvalidInstruction => return self.fail(6757 .none => self.asmMemoryImmediate(mnem, mem1, imm0),
7027 "Invalid instruction: '{s} {s} {s} {s} {s}'",6758 else => error.InvalidInstruction,
7028 .{6759 },
7029 @tagName(mnem),6760 else => error.InvalidInstruction,
7030 @tagName(ops[0]),6761 },
7031 @tagName(ops[1]),6762 }) catch |err| switch (err) {
7032 @tagName(ops[2]),6763 error.InvalidInstruction => return self.fail(
7033 @tagName(ops[3]),6764 "Invalid instruction: '{s} {s} {s} {s} {s}'",
7034 },6765 .{
7035 ),6766 @tagName(mnem),
7036 else => |e| return e,6767 @tagName(ops[0]),
7037 };6768 @tagName(ops[1]),
7038 }6769 @tagName(ops[2]),
6770 @tagName(ops[3]),
6771 },
6772 ),
6773 else => |e| return e,
6774 };
7039 }6775 }
70406776
7041 simple: {6777 simple: {
...@@ -7052,25 +6788,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -7052,25 +6788,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
7052 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);6788 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
7053 return self.finishAir(inst, result, buf);6789 return self.finishAir(inst, result, buf);
7054 }6790 }
7055 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);6791 var bt = self.liveness.iterateBigTomb(inst);
7056 for (outputs) |output| {6792 for (outputs) |output| if (output != .none) self.feed(&bt, output);
7057 if (output == .none) continue;6793 for (inputs) |input| self.feed(&bt, input);
70586794 return self.finishAirResult(inst, result);
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 };
7074}6795}
70756796
7076/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.6797/// 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...@@ -7952,7 +7673,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
79527673
7953fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {7674fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
7954 const un_op = self.air.instructions.items(.data)[inst].un_op;7675 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: {
7956 const src_mcv = try self.resolveInst(un_op);7677 const src_mcv = try self.resolveInst(un_op);
7957 if (self.reuseOperand(inst, un_op, 0, src_mcv)) break :result src_mcv;7678 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 {...@@ -7966,7 +7687,7 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
79667687
7967fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {7688fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
7968 const ty_op = self.air.instructions.items(.data)[inst].ty_op;7689 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: {
7970 const operand = try self.resolveInst(ty_op.operand);7691 const operand = try self.resolveInst(ty_op.operand);
7971 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand;7692 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 {...@@ -7991,28 +7712,24 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
7991 const ptr = try self.resolveInst(ty_op.operand);7712 const ptr = try self.resolveInst(ty_op.operand);
7992 const array_ty = ptr_ty.childType();7713 const array_ty = ptr_ty.childType();
7993 const array_len = array_ty.arrayLen();7714 const array_len = array_ty.arrayLen();
7994 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {7715
7995 const stack_offset = @intCast(i32, try self.allocMem(inst, 16, 16));7716 const stack_offset = @intCast(i32, try self.allocMem(inst, 16, 16));
7996 try self.genSetStack(ptr_ty, stack_offset, ptr, .{});7717 try self.genSetStack(ptr_ty, stack_offset, ptr, .{});
7997 try self.genSetStack(Type.u64, stack_offset - 8, .{ .immediate = array_len }, .{});7718 try self.genSetStack(Type.u64, stack_offset - 8, .{ .immediate = array_len }, .{});
7998 break :blk .{ .stack_offset = stack_offset };7719
7999 };7720 const result = MCValue{ .stack_offset = stack_offset };
8000 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });7721 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
8001}7722}
80027723
8003fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void {7724fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void {
8004 const ty_op = self.air.instructions.items(.data)[inst].ty_op;7725 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8005 const result: MCValue = if (self.liveness.isUnused(inst))7726 _ = ty_op;
8006 .dead7727 return self.fail("TODO implement airIntToFloat for {}", .{self.target.cpu.arch});
8007 else7728 //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
8008 return self.fail("TODO implement airIntToFloat for {}", .{self.target.cpu.arch});
8009 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
8010}7729}
80117730
8012fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {7731fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
8013 const ty_op = self.air.instructions.items(.data)[inst].ty_op;7732 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
8017 const src_ty = self.air.typeOf(ty_op.operand);7734 const src_ty = self.air.typeOf(ty_op.operand);
8018 const dst_ty = self.air.typeOfIndex(inst);7735 const dst_ty = self.air.typeOfIndex(inst);
...@@ -8114,7 +7831,7 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {...@@ -8114,7 +7831,7 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
8114 }7831 }
81157832
8116 const result: MCValue = result: {7833 const result: MCValue = result: {
8117 if (self.liveness.isUnused(inst)) break :result .dead;7834 if (self.liveness.isUnused(inst)) break :result .unreach;
81187835
8119 if (val_abi_size <= 8) {7836 if (val_abi_size <= 8) {
8120 self.eflags_inst = inst;7837 self.eflags_inst = inst;
...@@ -8212,7 +7929,7 @@ fn atomicOp(...@@ -8212,7 +7929,7 @@ fn atomicOp(
8212 .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),7929 .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),
8213 } } });7930 } } });
82147931
8215 return if (unused) .none else dst_mcv;7932 return if (unused) .unreach else dst_mcv;
8216 },7933 },
8217 .loop => _ = if (val_abi_size <= 8) {7934 .loop => _ = if (val_abi_size <= 8) {
8218 const tmp_reg = try self.register_manager.allocReg(null, gp);7935 const tmp_reg = try self.register_manager.allocReg(null, gp);
...@@ -8285,7 +8002,7 @@ fn atomicOp(...@@ -8285,7 +8002,7 @@ fn atomicOp(
8285 .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),8002 .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)),
8286 } } });8003 } } });
8287 _ = try self.asmJccReloc(loop, .ne);8004 _ = try self.asmJccReloc(loop, .ne);
8288 return if (unused) .none else .{ .register = .rax };8005 return if (unused) .unreach else .{ .register = .rax };
8289 } else {8006 } else {
8290 try self.asmRegisterMemory(.mov, .rax, Memory.sib(.qword, .{8007 try self.asmRegisterMemory(.mov, .rax, Memory.sib(.qword, .{
8291 .base = ptr_mem.sib.base,8008 .base = ptr_mem.sib.base,
...@@ -8354,7 +8071,7 @@ fn atomicOp(...@@ -8354,7 +8071,7 @@ fn atomicOp(
8354 } });8071 } });
8355 _ = try self.asmJccReloc(loop, .ne);8072 _ = try self.asmJccReloc(loop, .ne);
83568073
8357 if (unused) return .none;8074 if (unused) return .unreach;
8358 const dst_mcv = try self.allocTempRegOrMem(val_ty, false);8075 const dst_mcv = try self.allocTempRegOrMem(val_ty, false);
8359 try self.asmMemoryRegister(8076 try self.asmMemoryRegister(
8360 .mov,8077 .mov,
...@@ -8396,27 +8113,22 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {...@@ -8396,27 +8113,22 @@ fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
8396fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {8113fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
8397 const atomic_load = self.air.instructions.items(.data)[inst].atomic_load;8114 const atomic_load = self.air.instructions.items(.data)[inst].atomic_load;
83988115
8399 const result: MCValue = result: {8116 const ptr_ty = self.air.typeOf(atomic_load.ptr);
8400 if (self.liveness.isUnused(inst)) break :result .dead;8117 const ptr_mcv = try self.resolveInst(atomic_load.ptr);
84018118 const ptr_lock = switch (ptr_mcv) {
8402 const ptr_ty = self.air.typeOf(atomic_load.ptr);8119 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
8403 const ptr_mcv = try self.resolveInst(atomic_load.ptr);8120 else => null,
8404 const ptr_lock = switch (ptr_mcv) {8121 };
8405 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),8122 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
8406 else => null,
8407 };
8408 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
84098123
8410 const dst_mcv =8124 const dst_mcv =
8411 if (self.reuseOperand(inst, atomic_load.ptr, 0, ptr_mcv))8125 if (self.reuseOperand(inst, atomic_load.ptr, 0, ptr_mcv))
8412 ptr_mcv8126 ptr_mcv
8413 else8127 else
8414 try self.allocRegOrMem(inst, true);8128 try self.allocRegOrMem(inst, true);
84158129
8416 try self.load(dst_mcv, ptr_mcv, ptr_ty);8130 try self.load(dst_mcv, ptr_mcv, ptr_ty);
8417 break :result dst_mcv;8131 return self.finishAir(inst, dst_mcv, .{ atomic_load.ptr, .none, .none });
8418 };
8419 return self.finishAir(inst, result, .{ atomic_load.ptr, .none, .none });
8420}8132}
84218133
8422fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {8134fn 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 {...@@ -8459,7 +8171,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
84598171
8460 try self.genInlineMemset(dst_ptr, src_val, len, .{});8172 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 });
8463}8175}
84648176
8465fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {8177fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
...@@ -8489,128 +8201,129 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {...@@ -8489,128 +8201,129 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
84898201
8490 try self.genInlineMemcpy(dst_ptr, src_ptr, len, .{});8202 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 });
8493}8205}
84948206
8495fn airTagName(self: *Self, inst: Air.Inst.Index) !void {8207fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
8496 const un_op = self.air.instructions.items(.data)[inst].un_op;8208 const un_op = self.air.instructions.items(.data)[inst].un_op;
8497 const operand = try self.resolveInst(un_op);8209 const operand = try self.resolveInst(un_op);
8498 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {8210 _ = operand;
8499 _ = operand;8211 return self.fail("TODO implement airTagName for x86_64", .{});
8500 return self.fail("TODO implement airTagName for x86_64", .{});8212 //return self.finishAir(inst, result, .{ un_op, .none, .none });
8501 };
8502 return self.finishAir(inst, result, .{ un_op, .none, .none });
8503}8213}
85048214
8505fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {8215fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
8506 const un_op = self.air.instructions.items(.data)[inst].un_op;8216 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);8218 const err_ty = self.air.typeOf(un_op);
8551 const end_lock = self.register_manager.lockRegAssumeUnused(end_reg);8219 const err_mcv = try self.resolveInst(un_op);
8552 defer self.register_manager.unlockReg(end_lock);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, .{8228 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
8557 .base = addr_reg.to64(),8229 const atom_index = try elf_file.getOrCreateAtomForLazySymbol(
8558 .scale_index = .{ .scale = 4, .index = err_reg.to64() },8230 .{ .kind = .const_data, .ty = Type.anyerror },
8559 .disp = 4,8231 4, // dword alignment
8560 }));8232 );
8561 try self.asmRegisterMemory(.mov, end_reg.to32(), Memory.sib(.dword, .{8233 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
8562 .base = addr_reg.to64(),8234 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
8563 .scale_index = .{ .scale = 4, .index = err_reg.to64() },8235 .base = .ds,
8564 .disp = 8,8236 .disp = @intCast(i32, got_addr),
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,
8575 }));8237 }));
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);8256 const start_reg = try self.register_manager.allocReg(null, gp);
8578 try self.asmMemoryRegister(.mov, Memory.sib(.qword, .{8257 const start_lock = self.register_manager.lockRegAssumeUnused(start_reg);
8579 .base = .rbp,8258 defer self.register_manager.unlockReg(start_lock);
8580 .disp = 0 - dst_mcv.stack_offset,8259
8581 }), start_reg.to64());8260 const end_reg = try self.register_manager.allocReg(null, gp);
8582 try self.asmMemoryRegister(.mov, Memory.sib(.qword, .{8261 const end_lock = self.register_manager.lockRegAssumeUnused(end_reg);
8583 .base = .rbp,8262 defer self.register_manager.unlockReg(end_lock);
8584 .disp = 8 - dst_mcv.stack_offset,8263
8585 }), end_reg.to64());8264 try self.truncateRegister(err_ty, err_reg.to32());
8586 break :result dst_mcv;8265
8587 };8266 try self.asmRegisterMemory(.mov, start_reg.to32(), Memory.sib(.dword, .{
8588 return self.finishAir(inst, result, .{ un_op, .none, .none });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 });
8589}8298}
85908299
8591fn airSplat(self: *Self, inst: Air.Inst.Index) !void {8300fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
8592 const ty_op = self.air.instructions.items(.data)[inst].ty_op;8301 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", .{});8302 _ = ty_op;
8594 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });8303 return self.fail("TODO implement airSplat for x86_64", .{});
8304 //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
8595}8305}
85968306
8597fn airSelect(self: *Self, inst: Air.Inst.Index) !void {8307fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
8598 const pl_op = self.air.instructions.items(.data)[inst].pl_op;8308 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
8599 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;8309 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", .{});8310 _ = extra;
8601 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });8311 return self.fail("TODO implement airSelect for x86_64", .{});
8312 //return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });
8602}8313}
86038314
8604fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {8315fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
8605 const ty_op = self.air.instructions.items(.data)[inst].ty_op;8316 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", .{});8317 _ = ty_op;
8607 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });8318 return self.fail("TODO implement airShuffle for x86_64", .{});
8319 //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
8608}8320}
86098321
8610fn airReduce(self: *Self, inst: Air.Inst.Index) !void {8322fn airReduce(self: *Self, inst: Air.Inst.Index) !void {
8611 const reduce = self.air.instructions.items(.data)[inst].reduce;8323 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", .{});8324 _ = reduce;
8613 return self.finishAir(inst, result, .{ reduce.operand, .none, .none });8325 return self.fail("TODO implement airReduce for x86_64", .{});
8326 //return self.finishAir(inst, result, .{ reduce.operand, .none, .none });
8614}8327}
86158328
8616fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {8329fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
...@@ -8620,8 +8333,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {...@@ -8620,8 +8333,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
8620 const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);8333 const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
8621 const abi_size = @intCast(u32, result_ty.abiSize(self.target.*));8334 const abi_size = @intCast(u32, result_ty.abiSize(self.target.*));
8622 const abi_align = result_ty.abiAlignment(self.target.*);8335 const abi_align = result_ty.abiAlignment(self.target.*);
8623 const result: MCValue = res: {8336 const result: MCValue = result: {
8624 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
8625 switch (result_ty.zigTypeTag()) {8337 switch (result_ty.zigTypeTag()) {
8626 .Struct => {8338 .Struct => {
8627 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));8339 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 {...@@ -8712,7 +8424,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
8712 };8424 };
8713 try self.genSetStack(elem_ty, stack_offset - elem_off, mat_elem_mcv, .{});8425 try self.genSetStack(elem_ty, stack_offset - elem_off, mat_elem_mcv, .{});
8714 }8426 }
8715 break :res .{ .stack_offset = stack_offset };8427 break :result .{ .stack_offset = stack_offset };
8716 },8428 },
8717 .Array => {8429 .Array => {
8718 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));8430 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 {...@@ -8728,7 +8440,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
8728 const elem_off = @intCast(i32, elem_size * elem_i);8440 const elem_off = @intCast(i32, elem_size * elem_i);
8729 try self.genSetStack(elem_ty, stack_offset - elem_off, mat_elem_mcv, .{});8441 try self.genSetStack(elem_ty, stack_offset - elem_off, mat_elem_mcv, .{});
8730 }8442 }
8731 break :res MCValue{ .stack_offset = stack_offset };8443 break :result MCValue{ .stack_offset = stack_offset };
8732 },8444 },
8733 .Vector => return self.fail("TODO implement aggregate_init for vectors", .{}),8445 .Vector => return self.fail("TODO implement aggregate_init for vectors", .{}),
8734 else => unreachable,8446 else => unreachable,
...@@ -8740,82 +8452,70 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {...@@ -8740,82 +8452,70 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
8740 std.mem.copy(Air.Inst.Ref, &buf, elements);8452 std.mem.copy(Air.Inst.Ref, &buf, elements);
8741 return self.finishAir(inst, result, buf);8453 return self.finishAir(inst, result, buf);
8742 }8454 }
8743 var bt = try self.iterateBigTomb(inst, elements.len);8455 var bt = self.liveness.iterateBigTomb(inst);
8744 for (elements) |elem| {8456 for (elements) |elem| self.feed(&bt, elem);
8745 bt.feed(elem);8457 return self.finishAirResult(inst, result);
8746 }
8747 return bt.finishAir(result);
8748}8458}
87498459
8750fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {8460fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
8751 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;8461 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
8752 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;8462 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
8753 const result: MCValue = res: {8463 _ = extra;
8754 if (self.liveness.isUnused(inst)) break :res MCValue.dead;8464 return self.fail("TODO implement airAggregateInit for x86_64", .{});
8755 return self.fail("TODO implement airAggregateInit for x86_64", .{});8465 //return self.finishAir(inst, result, .{ extra.init, .none, .none });
8756 };
8757 return self.finishAir(inst, result, .{ extra.init, .none, .none });
8758}8466}
87598467
8760fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {8468fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
8761 const prefetch = self.air.instructions.items(.data)[inst].prefetch;8469 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 });
8763}8471}
87648472
8765fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {8473fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
8766 const pl_op = self.air.instructions.items(.data)[inst].pl_op;8474 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
8767 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;8475 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
8768 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {8476 _ = extra;
8769 return self.fail("TODO implement airMulAdd for x86_64", .{});8477 return self.fail("TODO implement airMulAdd for x86_64", .{});
8770 };8478 //return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
8771 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
8772}8479}
87738480
8774fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {8481fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue {
8775 // First section of indexes correspond to a set number of constant values.8482 const ty = self.air.typeOf(ref);
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 }
87848483
8785 // If the type has no codegen bits, no need to store it.8484 // If the type has no codegen bits, no need to store it.
8786 const inst_ty = self.air.typeOf(inst);8485 if (!ty.hasRuntimeBitsIgnoreComptime() and !ty.isError()) return .none;
8787 if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError())8486
8788 return .none;8487 if (Air.refToIndex(ref)) |inst| {
87898488 const mcv = switch (self.air.instructions.items(.tag)[inst]) {
8790 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);8489 .constant => tracking: {
8791 switch (self.air.instructions.items(.tag)[inst_index]) {8490 const gop = try self.const_tracking.getOrPut(self.gpa, inst);
8792 .constant => {8491 if (!gop.found_existing) gop.value_ptr.* = InstTracking.init(try self.genTypedValue(.{
8793 // Constants have static lifetimes, so they are always memoized in the outer most table.8492 .ty = ty,
8794 const branch = &self.branch_stack.items[0];8493 .val = self.air.value(ref).?,
8795 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);8494 }));
8796 if (!gop.found_existing) {8495 break :tracking gop.value_ptr;
8797 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;8496 },
8798 gop.value_ptr.* = try self.genTypedValue(.{8497 .const_ty => unreachable,
8799 .ty = inst_ty,8498 else => self.inst_tracking.getPtr(inst).?,
8800 .val = self.air.values[ty_pl.payload],8499 }.short;
8801 });8500 switch (mcv) {
8802 }8501 .none, .unreach, .dead => unreachable,
8803 return gop.value_ptr.*;8502 else => return mcv,
8804 },8503 }
8805 .const_ty => unreachable,
8806 else => return self.getResolvedInstValue(inst_index).?.*,
8807 }8504 }
8505
8506 return self.genTypedValue(.{ .ty = ty, .val = self.air.value(ref).? });
8808}8507}
88098508
8810fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?*MCValue {8509fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) *InstTracking {
8811 // Treat each stack item as a "layer" on top of the previous one.8510 const tracking = switch (self.air.instructions.items(.tag)[inst]) {
8812 var i: usize = self.branch_stack.items.len;8511 .constant => &self.const_tracking,
8813 while (true) {8512 .const_ty => unreachable,
8814 i -= 1;8513 else => &self.inst_tracking,
8815 if (self.branch_stack.items[i].inst_table.getPtr(inst)) |mcv| {8514 }.getPtr(inst).?;
8816 return if (mcv.* != .dead) mcv else null;8515 return switch (tracking.short) {
8817 }8516 .none, .unreach, .dead => unreachable,
8818 }8517 else => tracking,
8518 };
8819}8519}
88208520
8821/// If the MCValue is an immediate, and it does not fit within this type,8521/// 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;...@@ -78,7 +78,6 @@ const LoopDepth = u16;
78const Local = struct {78const Local = struct {
79 cty_idx: CType.Index,79 cty_idx: CType.Index,
80 alignas: CType.AlignAs,80 alignas: CType.AlignAs,
81 is_in_clone: bool,
8281
83 pub fn getType(local: Local) LocalType {82 pub fn getType(local: Local) LocalType {
84 return .{ .cty_idx = local.cty_idx, .alignas = local.alignas };83 return .{ .cty_idx = local.cty_idx, .alignas = local.alignas };
...@@ -275,16 +274,13 @@ pub const Function = struct {...@@ -275,16 +274,13 @@ pub const Function = struct {
275 /// All the locals, to be emitted at the top of the function.274 /// All the locals, to be emitted at the top of the function.
276 locals: std.ArrayListUnmanaged(Local) = .{},275 locals: std.ArrayListUnmanaged(Local) = .{},
277 /// Which locals are available for reuse, based on Type.276 /// 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.
280 free_locals_map: LocalsMap = .{},277 free_locals_map: LocalsMap = .{},
281 is_in_clone: bool = false,
282 /// Locals which will not be freed by Liveness. This is used after a278 /// Locals which will not be freed by Liveness. This is used after a
283 /// Function body is lowered in order to make `free_locals_map` have279 /// Function body is lowered in order to make `free_locals_map` have
284 /// 100% of the locals within so that it can be used to render the block280 /// 100% of the locals within so that it can be used to render the block
285 /// of variable declarations at the top of a function, sorted descending281 /// of variable declarations at the top of a function, sorted descending
286 /// by type alignment.282 /// 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.
288 allocs: std.AutoArrayHashMapUnmanaged(LocalIndex, bool) = .{},284 allocs: std.AutoArrayHashMapUnmanaged(LocalIndex, bool) = .{},
289 /// Needed for memory used by the keys of free_locals_map entries.285 /// Needed for memory used by the keys of free_locals_map entries.
290 arena: std.heap.ArenaAllocator,286 arena: std.heap.ArenaAllocator,
...@@ -302,7 +298,7 @@ pub const Function = struct {...@@ -302,7 +298,7 @@ pub const Function = struct {
302 const alignment = 0;298 const alignment = 0;
303 const decl_c_value = try f.allocLocalValue(ty, alignment);299 const decl_c_value = try f.allocLocalValue(ty, alignment);
304 const gpa = f.object.dg.gpa;300 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);
306 try writer.writeAll("static ");302 try writer.writeAll("static ");
307 try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, Const, alignment, .complete);303 try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, Const, alignment, .complete);
308 try writer.writeAll(" = ");304 try writer.writeAll(" = ");
...@@ -323,14 +319,15 @@ pub const Function = struct {...@@ -323,14 +319,15 @@ pub const Function = struct {
323 };319 };
324 }320 }
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.
327 fn allocLocalValue(f: *Function, ty: Type, alignment: u32) !CValue {325 fn allocLocalValue(f: *Function, ty: Type, alignment: u32) !CValue {
328 const gpa = f.object.dg.gpa;326 const gpa = f.object.dg.gpa;
329 const target = f.object.dg.module.getTarget();327 const target = f.object.dg.module.getTarget();
330 try f.locals.append(gpa, .{328 try f.locals.append(gpa, .{
331 .cty_idx = try f.typeToIndex(ty, .complete),329 .cty_idx = try f.typeToIndex(ty, .complete),
332 .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)),330 .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)),
333 .is_in_clone = f.is_in_clone,
334 });331 });
335 return .{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) };332 return .{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) };
336 }333 }
...@@ -341,7 +338,8 @@ pub const Function = struct {...@@ -341,7 +338,8 @@ pub const Function = struct {
341 return result;338 return result;
342 }339 }
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`).
345 fn allocAlignedLocal(f: *Function, ty: Type, _: CQualifiers, alignment: u32) !CValue {343 fn allocAlignedLocal(f: *Function, ty: Type, _: CQualifiers, alignment: u32) !CValue {
346 const target = f.object.dg.module.getTarget();344 const target = f.object.dg.module.getTarget();
347 if (f.free_locals_map.getPtr(.{345 if (f.free_locals_map.getPtr(.{
...@@ -2586,7 +2584,7 @@ pub fn genFunc(f: *Function) !void {...@@ -2586,7 +2584,7 @@ pub fn genFunc(f: *Function) !void {
2586 f.free_locals_map.clearRetainingCapacity();2584 f.free_locals_map.clearRetainingCapacity();
25872585
2588 const main_body = f.air.getMainBody();2586 const main_body = f.air.getMainBody();
2589 try genBody(f, main_body);2587 try genBodyResolveState(f, undefined, &.{}, main_body, false);
25902588
2591 try o.indent_writer.insertNewline();2589 try o.indent_writer.insertNewline();
25922590
...@@ -2597,8 +2595,8 @@ pub fn genFunc(f: *Function) !void {...@@ -2597,8 +2595,8 @@ pub fn genFunc(f: *Function) !void {
2597 // alignment, descending.2595 // alignment, descending.
2598 const free_locals = &f.free_locals_map;2596 const free_locals = &f.free_locals_map;
2599 assert(f.value_map.count() == 0); // there must not be any unfreed locals2597 assert(f.value_map.count() == 0); // there must not be any unfreed locals
2600 for (f.allocs.keys(), f.allocs.values()) |local_index, value| {2598 for (f.allocs.keys(), f.allocs.values()) |local_index, should_emit| {
2601 if (value) continue; // static2599 if (!should_emit) continue;
2602 const local = f.locals.items[local_index];2600 const local = f.locals.items[local_index];
2603 log.debug("inserting local {d} into free_locals", .{local_index});2601 log.debug("inserting local {d} into free_locals", .{local_index});
2604 const gop = try free_locals.getOrPut(gpa, local.getType());2602 const gop = try free_locals.getOrPut(gpa, local.getType());
...@@ -2715,6 +2713,10 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {...@@ -2715,6 +2713,10 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
2715 }2713 }
2716}2714}
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`.
2718fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {2720fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
2719 const writer = f.object.writer();2721 const writer = f.object.writer();
2720 if (body.len == 0) {2722 if (body.len == 0) {
...@@ -2728,10 +2730,69 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -2728,10 +2730,69 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
2728 }2730 }
2729}2731}
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
2731fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {2788fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
2732 const air_tags = f.air.instructions.items(.tag);2789 const air_tags = f.air.instructions.items(.tag);
27332790
2734 for (body) |inst| {2791 for (body) |inst| {
2792 if (f.liveness.isUnused(inst) and !f.air.mustLower(inst)) {
2793 continue;
2794 }
2795
2735 const result_value = switch (air_tags[inst]) {2796 const result_value = switch (air_tags[inst]) {
2736 // zig fmt: off2797 // zig fmt: off
2737 .constant => unreachable, // excluded from function bodies2798 .constant => unreachable, // excluded from function bodies
...@@ -3009,11 +3070,6 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,...@@ -3009,11 +3070,6 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
3009fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: []const u8) !CValue {3070fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: []const u8) !CValue {
3010 const ty_op = f.air.instructions.items(.data)[inst].ty_op;3071 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
3017 const inst_ty = f.air.typeOfIndex(inst);3073 const inst_ty = f.air.typeOfIndex(inst);
3018 const operand = try f.resolveInst(ty_op.operand);3074 const operand = try f.resolveInst(ty_op.operand);
3019 try reap(f, inst, &.{ty_op.operand});3075 try reap(f, inst, &.{ty_op.operand});
...@@ -3032,10 +3088,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [...@@ -3032,10 +3088,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [
3032fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {3088fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3033 const inst_ty = f.air.typeOfIndex(inst);3089 const inst_ty = f.air.typeOfIndex(inst);
3034 const bin_op = f.air.instructions.items(.data)[inst].bin_op;3090 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
3035 const ptr_ty = f.air.typeOf(bin_op.lhs);3091 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) {
3036 if ((!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) or
3037 !inst_ty.hasRuntimeBitsIgnoreComptime())
3038 {
3039 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3092 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3040 return .none;3093 return .none;
3041 }3094 }
...@@ -3074,11 +3127,6 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3074,11 +3127,6 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3074 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;3127 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
3075 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;3128 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
3082 const inst_ty = f.air.typeOfIndex(inst);3130 const inst_ty = f.air.typeOfIndex(inst);
3083 const ptr_ty = f.air.typeOf(bin_op.lhs);3131 const ptr_ty = f.air.typeOf(bin_op.lhs);
3084 const child_ty = ptr_ty.childType();3132 const child_ty = ptr_ty.childType();
...@@ -3116,10 +3164,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3116,10 +3164,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3116fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {3164fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3117 const inst_ty = f.air.typeOfIndex(inst);3165 const inst_ty = f.air.typeOfIndex(inst);
3118 const bin_op = f.air.instructions.items(.data)[inst].bin_op;3166 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
3119 const slice_ty = f.air.typeOf(bin_op.lhs);3167 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) {
3120 if ((!slice_ty.isVolatilePtr() and f.liveness.isUnused(inst)) or
3121 !inst_ty.hasRuntimeBitsIgnoreComptime())
3122 {
3123 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3168 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3124 return .none;3169 return .none;
3125 }3170 }
...@@ -3158,11 +3203,6 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3158,11 +3203,6 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3158 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;3203 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
3159 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;3204 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
3166 const slice_ty = f.air.typeOf(bin_op.lhs);3206 const slice_ty = f.air.typeOf(bin_op.lhs);
3167 const child_ty = slice_ty.elemType2();3207 const child_ty = slice_ty.elemType2();
3168 const slice = try f.resolveInst(bin_op.lhs);3208 const slice = try f.resolveInst(bin_op.lhs);
...@@ -3188,7 +3228,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3188,7 +3228,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3188fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {3228fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3189 const bin_op = f.air.instructions.items(.data)[inst].bin_op;3229 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
3190 const inst_ty = f.air.typeOfIndex(inst);3230 const inst_ty = f.air.typeOfIndex(inst);
3191 if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBitsIgnoreComptime()) {3231 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) {
3192 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3232 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3193 return .none;3233 return .none;
3194 }3234 }
...@@ -3224,40 +3264,34 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3224,40 +3264,34 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3224}3264}
32253265
3226fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {3266fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
3227 if (f.liveness.isUnused(inst)) return .none;
3228
3229 const inst_ty = f.air.typeOfIndex(inst);3267 const inst_ty = f.air.typeOfIndex(inst);
3230 const elem_type = inst_ty.elemType();3268 const elem_type = inst_ty.elemType();
3231 if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) return .{ .undef = inst_ty };3269 if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) return .{ .undef = inst_ty };
32323270
3233 const target = f.object.dg.module.getTarget();3271 const target = f.object.dg.module.getTarget();
3234 const local = try f.allocAlignedLocal(3272 const local = try f.allocLocalValue(
3235 elem_type,3273 elem_type,
3236 CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }),
3237 inst_ty.ptrAlignment(target),3274 inst_ty.ptrAlignment(target),
3238 );3275 );
3239 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });3276 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
3240 const gpa = f.object.dg.module.gpa;3277 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);
3242 return .{ .local_ref = local.new_local };3279 return .{ .local_ref = local.new_local };
3243}3280}
32443281
3245fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {3282fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3246 if (f.liveness.isUnused(inst)) return .none;
3247
3248 const inst_ty = f.air.typeOfIndex(inst);3283 const inst_ty = f.air.typeOfIndex(inst);
3249 const elem_ty = inst_ty.elemType();3284 const elem_ty = inst_ty.elemType();
3250 if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return .{ .undef = inst_ty };3285 if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return .{ .undef = inst_ty };
32513286
3252 const target = f.object.dg.module.getTarget();3287 const target = f.object.dg.module.getTarget();
3253 const local = try f.allocAlignedLocal(3288 const local = try f.allocLocalValue(
3254 elem_ty,3289 elem_ty,
3255 CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }),
3256 inst_ty.ptrAlignment(target),3290 inst_ty.ptrAlignment(target),
3257 );3291 );
3258 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });3292 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
3259 const gpa = f.object.dg.module.gpa;3293 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);
3261 return .{ .local_ref = local.new_local };3295 return .{ .local_ref = local.new_local };
3262}3296}
32633297
...@@ -3293,9 +3327,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3293,9 +3327,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
3293 const ptr_info = ptr_scalar_ty.ptrInfo().data;3327 const ptr_info = ptr_scalar_ty.ptrInfo().data;
3294 const src_ty = ptr_info.pointee_type;3328 const src_ty = ptr_info.pointee_type;
32953329
3296 if (!src_ty.hasRuntimeBitsIgnoreComptime() or3330 if (!src_ty.hasRuntimeBitsIgnoreComptime()) {
3297 (!ptr_info.@"volatile" and f.liveness.isUnused(inst)))
3298 {
3299 try reap(f, inst, &.{ty_op.operand});3331 try reap(f, inst, &.{ty_op.operand});
3300 return .none;3332 return .none;
3301 }3333 }
...@@ -3442,11 +3474,6 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {...@@ -3442,11 +3474,6 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
3442fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {3474fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
3443 const ty_op = f.air.instructions.items(.data)[inst].ty_op;3475 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
3450 const operand = try f.resolveInst(ty_op.operand);3477 const operand = try f.resolveInst(ty_op.operand);
3451 try reap(f, inst, &.{ty_op.operand});3478 try reap(f, inst, &.{ty_op.operand});
34523479
...@@ -3470,10 +3497,6 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3470,10 +3497,6 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
34703497
3471fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {3498fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
3472 const ty_op = f.air.instructions.items(.data)[inst].ty_op;3499 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
3478 const operand = try f.resolveInst(ty_op.operand);3501 const operand = try f.resolveInst(ty_op.operand);
3479 try reap(f, inst, &.{ty_op.operand});3502 try reap(f, inst, &.{ty_op.operand});
...@@ -3569,10 +3592,6 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3569,10 +3592,6 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
35693592
3570fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {3593fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
3571 const un_op = f.air.instructions.items(.data)[inst].un_op;3594 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 }
3576 const operand = try f.resolveInst(un_op);3595 const operand = try f.resolveInst(un_op);
3577 try reap(f, inst, &.{un_op});3596 try reap(f, inst, &.{un_op});
3578 const writer = f.object.writer();3597 const writer = f.object.writer();
...@@ -3746,11 +3765,6 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:...@@ -3746,11 +3765,6 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
3746 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;3765 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
3747 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;3766 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
3754 const lhs = try f.resolveInst(bin_op.lhs);3768 const lhs = try f.resolveInst(bin_op.lhs);
3755 const rhs = try f.resolveInst(bin_op.rhs);3769 const rhs = try f.resolveInst(bin_op.rhs);
3756 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3770 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
...@@ -3790,11 +3804,6 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3790,11 +3804,6 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
3790 const scalar_ty = operand_ty.scalarType();3804 const scalar_ty = operand_ty.scalarType();
3791 if (scalar_ty.tag() != .bool) return try airUnBuiltinCall(f, inst, "not", .bits);3805 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
3798 const op = try f.resolveInst(ty_op.operand);3807 const op = try f.resolveInst(ty_op.operand);
3799 try reap(f, inst, &.{ty_op.operand});3808 try reap(f, inst, &.{ty_op.operand});
38003809
...@@ -3829,11 +3838,6 @@ fn airBinOp(...@@ -3829,11 +3838,6 @@ fn airBinOp(
3829 if ((scalar_ty.isInt() and scalar_ty.bitSize(target) > 64) or scalar_ty.isRuntimeFloat())3838 if ((scalar_ty.isInt() and scalar_ty.bitSize(target) > 64) or scalar_ty.isRuntimeFloat())
3830 return try airBinBuiltinCall(f, inst, operation, info);3839 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
3837 const lhs = try f.resolveInst(bin_op.lhs);3841 const lhs = try f.resolveInst(bin_op.lhs);
3838 const rhs = try f.resolveInst(bin_op.rhs);3842 const rhs = try f.resolveInst(bin_op.rhs);
3839 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3843 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
...@@ -3865,11 +3869,6 @@ fn airCmpOp(...@@ -3865,11 +3869,6 @@ fn airCmpOp(
3865 data: anytype,3869 data: anytype,
3866 operator: std.math.CompareOperator,3870 operator: std.math.CompareOperator,
3867) !CValue {3871) !CValue {
3868 if (f.liveness.isUnused(inst)) {
3869 try reap(f, inst, &.{ data.lhs, data.rhs });
3870 return .none;
3871 }
3872
3873 const operand_ty = f.air.typeOf(data.lhs);3872 const operand_ty = f.air.typeOf(data.lhs);
3874 const scalar_ty = operand_ty.scalarType();3873 const scalar_ty = operand_ty.scalarType();
38753874
...@@ -3918,11 +3917,6 @@ fn airEquality(...@@ -3918,11 +3917,6 @@ fn airEquality(
3918) !CValue {3917) !CValue {
3919 const bin_op = f.air.instructions.items(.data)[inst].bin_op;3918 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
3926 const operand_ty = f.air.typeOf(bin_op.lhs);3920 const operand_ty = f.air.typeOf(bin_op.lhs);
3927 const target = f.object.dg.module.getTarget();3921 const target = f.object.dg.module.getTarget();
3928 const operand_bits = operand_ty.bitSize(target);3922 const operand_bits = operand_ty.bitSize(target);
...@@ -3987,11 +3981,6 @@ fn airEquality(...@@ -3987,11 +3981,6 @@ fn airEquality(
3987fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {3981fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {
3988 const un_op = f.air.instructions.items(.data)[inst].un_op;3982 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
3995 const inst_ty = f.air.typeOfIndex(inst);3984 const inst_ty = f.air.typeOfIndex(inst);
3996 const operand = try f.resolveInst(un_op);3985 const operand = try f.resolveInst(un_op);
3997 try reap(f, inst, &.{un_op});3986 try reap(f, inst, &.{un_op});
...@@ -4008,10 +3997,6 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4008,10 +3997,6 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {
4008fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {3997fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
4009 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;3998 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
4010 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;3999 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
4016 const lhs = try f.resolveInst(bin_op.lhs);4001 const lhs = try f.resolveInst(bin_op.lhs);
4017 const rhs = try f.resolveInst(bin_op.rhs);4002 const rhs = try f.resolveInst(bin_op.rhs);
...@@ -4059,11 +4044,6 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {...@@ -4059,11 +4044,6 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
4059fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !CValue {4044fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !CValue {
4060 const bin_op = f.air.instructions.items(.data)[inst].bin_op;4045 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
4067 const inst_ty = f.air.typeOfIndex(inst);4047 const inst_ty = f.air.typeOfIndex(inst);
4068 const inst_scalar_ty = inst_ty.scalarType();4048 const inst_scalar_ty = inst_ty.scalarType();
40694049
...@@ -4107,11 +4087,6 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4107,11 +4087,6 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
4107 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;4087 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
4108 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;4088 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
4115 const ptr = try f.resolveInst(bin_op.lhs);4090 const ptr = try f.resolveInst(bin_op.lhs);
4116 const len = try f.resolveInst(bin_op.rhs);4091 const len = try f.resolveInst(bin_op.rhs);
4117 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });4092 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
...@@ -4316,6 +4291,7 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4316,6 +4291,7 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
4316 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;4291 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
4317 const extra = f.air.extraData(Air.Block, ty_pl.payload);4292 const extra = f.air.extraData(Air.Block, ty_pl.payload);
4318 const body = f.air.extra[extra.end..][0..extra.data.body_len];4293 const body = f.air.extra[extra.end..][0..extra.data.body_len];
4294 const liveness_block = f.liveness.getBlock(inst);
43194295
4320 const block_id: usize = f.next_block_index;4296 const block_id: usize = f.next_block_index;
4321 f.next_block_index += 1;4297 f.next_block_index += 1;
...@@ -4332,7 +4308,15 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4332,7 +4308,15 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
4332 .result = result,4308 .result = result,
4333 });4309 });
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
4336 try f.object.indent_writer.insertNewline();4320 try f.object.indent_writer.insertNewline();
4337 // label might be unused, add a dummy goto4321 // label might be unused, add a dummy goto
4338 // label must be followed by an expression, add an empty one.4322 // label must be followed by an expression, add an empty one.
...@@ -4366,6 +4350,7 @@ fn lowerTry(...@@ -4366,6 +4350,7 @@ fn lowerTry(
4366) !CValue {4350) !CValue {
4367 const err_union = try f.resolveInst(operand);4351 const err_union = try f.resolveInst(operand);
4368 const result_ty = f.air.typeOfIndex(inst);4352 const result_ty = f.air.typeOfIndex(inst);
4353 const liveness_condbr = f.liveness.getCondBr(inst);
4369 const writer = f.object.writer();4354 const writer = f.object.writer();
4370 const payload_ty = err_union_ty.errorUnionPayload();4355 const payload_ty = err_union_ty.errorUnionPayload();
4371 const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime();4356 const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime();
...@@ -4389,10 +4374,15 @@ fn lowerTry(...@@ -4389,10 +4374,15 @@ fn lowerTry(
4389 }4374 }
4390 try writer.writeByte(')');4375 try writer.writeByte(')');
43914376
4392 try genBody(f, body);4377 try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false);
4393 try f.object.indent_writer.insertNewline();4378 try f.object.indent_writer.insertNewline();
4394 }4379 }
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
4396 if (!payload_has_bits) {4386 if (!payload_has_bits) {
4397 if (!operand_is_ptr) {4387 if (!operand_is_ptr) {
4398 return .none;4388 return .none;
...@@ -4466,10 +4456,6 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4466,10 +4456,6 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
4466fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {4456fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
4467 const ty_op = f.air.instructions.items(.data)[inst].ty_op;4457 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
4468 const dest_ty = f.air.typeOfIndex(inst);4458 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
4474 const operand = try f.resolveInst(ty_op.operand);4460 const operand = try f.resolveInst(ty_op.operand);
4475 try reap(f, inst, &.{ty_op.operand});4461 try reap(f, inst, &.{ty_op.operand});
...@@ -4593,7 +4579,6 @@ fn airBreakpoint(writer: anytype) !CValue {...@@ -4593,7 +4579,6 @@ fn airBreakpoint(writer: anytype) !CValue {
4593}4579}
45944580
4595fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {4581fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
4596 if (f.liveness.isUnused(inst)) return .none;
4597 const writer = f.object.writer();4582 const writer = f.object.writer();
4598 const local = try f.allocLocal(inst, Type.usize);4583 const local = try f.allocLocal(inst, Type.usize);
4599 try f.writeCValue(writer, local, .Other);4584 try f.writeCValue(writer, local, .Other);
...@@ -4604,7 +4589,6 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4604,7 +4589,6 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
4604}4589}
46054590
4606fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {4591fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
4607 if (f.liveness.isUnused(inst)) return .none;
4608 const writer = f.object.writer();4592 const writer = f.object.writer();
4609 const local = try f.allocLocal(inst, Type.usize);4593 const local = try f.allocLocal(inst, Type.usize);
4610 try f.writeCValue(writer, local, .Other);4594 try f.writeCValue(writer, local, .Other);
...@@ -4637,17 +4621,12 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4637,17 +4621,12 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue {
4637 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;4621 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
4638 const loop = f.air.extraData(Air.Block, ty_pl.payload);4622 const loop = f.air.extraData(Air.Block, ty_pl.payload);
4639 const body = f.air.extra[loop.end..][0..loop.data.body_len];4623 const body = f.air.extra[loop.end..][0..loop.data.body_len];
4640 const liveness_loop = f.liveness.getLoop(inst);
4641 const writer = f.object.writer();4624 const writer = f.object.writer();
46424625
4643 try writer.writeAll("for (;;) ");4626 try writer.writeAll("for (;;) ");
4644 try genBody(f, body);4627 try genBody(f, body); // no need to restore state, we're noreturn
4645 try writer.writeByte('\n');4628 try writer.writeByte('\n');
46464629
4647 for (liveness_loop.deaths) |operand| {
4648 try die(f, inst, Air.indexToRef(operand));
4649 }
4650
4651 return .none;4630 return .none;
4652}4631}
46534632
...@@ -4661,61 +4640,24 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4661,61 +4640,24 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue {
4661 const liveness_condbr = f.liveness.getCondBr(inst);4640 const liveness_condbr = f.liveness.getCondBr(inst);
4662 const writer = f.object.writer();4641 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
4687 try writer.writeAll("if (");4643 try writer.writeAll("if (");
4688 try f.writeCValue(writer, cond, .Other);4644 try f.writeCValue(writer, cond, .Other);
4689 try writer.writeAll(") ");4645 try writer.writeAll(") ");
4690 try genBody(f, then_body);
46914646
4692 // TODO: If body ends in goto, elide the else block?4647 try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false);
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 }
46994648
4700 f.value_map.deinit();4649 // We don't need to use `genBodyResolveState` for the else block, because this instruction is
4701 f.value_map = cloned_map.move();4650 // noreturn so must terminate a body, therefore we don't need to leave `value_map` or
4702 const free_locals = &f.free_locals_map;4651 // `free_locals_map` well defined (our parent is responsible for doing that).
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);
47114652
4712 if (needs_else) {4653 for (liveness_condbr.else_deaths) |death| {
4713 try genBody(f, else_body);4654 try die(f, inst, Air.indexToRef(death));
4714 } else {
4715 try genBodyInner(f, else_body);
4716 }4655 }
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
4720 return .none;4662 return .none;
4721}4663}
...@@ -4746,9 +4688,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4746,9 +4688,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
4746 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.data.cases_len + 1);4688 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.data.cases_len + 1);
4747 defer gpa.free(liveness.deaths);4689 defer gpa.free(liveness.deaths);
47484690
4749 // On the final iteration we do not clone the map. This ensures that4691 // On the final iteration we do not need to fix any state. This is because, like in the `else`
4750 // lowering proceeds after the switch_br taking into account the4692 // branch of a `cond_br`, our parent has to do it for this entire body anyway.
4751 // mutations to the liveness information.
4752 const last_case_i = switch_br.data.cases_len - @boolToInt(switch_br.data.else_body_len == 0);4693 const last_case_i = switch_br.data.cases_len - @boolToInt(switch_br.data.else_body_len == 0);
47534694
4754 var extra_index: usize = switch_br.end;4695 var extra_index: usize = switch_br.end;
...@@ -4772,56 +4713,23 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4772,56 +4713,23 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
4772 try writer.writeByte(' ');4713 try writer.writeByte(' ');
47734714
4774 if (case_i != last_case_i) {4715 if (case_i != last_case_i) {
4775 const old_value_map = f.value_map;4716 try genBodyResolveState(f, inst, liveness.deaths[case_i], case_body, false);
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);
4809 } else {4717 } else {
4810 for (liveness.deaths[case_i]) |operand| {4718 for (liveness.deaths[case_i]) |death| {
4811 try die(f, inst, Air.indexToRef(operand));4719 try die(f, inst, Air.indexToRef(death));
4812 }4720 }
4813 try genBody(f, case_body);4721 try genBody(f, case_body);
4814 }4722 }
48154723
4816 // The case body must be noreturn so we don't need to insert a break.4724 // The case body must be noreturn so we don't need to insert a break.
4817
4818 }4725 }
48194726
4820 const else_body = f.air.extra[extra_index..][0..switch_br.data.else_body_len];4727 const else_body = f.air.extra[extra_index..][0..switch_br.data.else_body_len];
4821 try f.object.indent_writer.insertNewline();4728 try f.object.indent_writer.insertNewline();
4822 if (else_body.len > 0) {4729 if (else_body.len > 0) {
4823 for (liveness.deaths[liveness.deaths.len - 1]) |operand| {4730 // Note that this must be the last case (i.e. the `last_case_i` case was not hit above)
4824 try die(f, inst, Air.indexToRef(operand));4731 for (liveness.deaths[liveness.deaths.len - 1]) |death| {
4732 try die(f, inst, Air.indexToRef(death));
4825 }4733 }
4826 try writer.writeAll("default: ");4734 try writer.writeAll("default: ");
4827 try genBody(f, else_body);4735 try genBody(f, else_body);
...@@ -4848,6 +4756,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4848,6 +4756,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
4848 const extra = f.air.extraData(Air.Asm, ty_pl.payload);4756 const extra = f.air.extraData(Air.Asm, ty_pl.payload);
4849 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;4757 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
4850 const clobbers_len = @truncate(u31, extra.data.flags);4758 const clobbers_len = @truncate(u31, extra.data.flags);
4759 const gpa = f.object.dg.gpa;
4851 var extra_i: usize = extra.end;4760 var extra_i: usize = extra.end;
4852 const outputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]);4761 const outputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]);
4853 extra_i += outputs.len;4762 extra_i += outputs.len;
...@@ -4855,8 +4764,6 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4855,8 +4764,6 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
4855 extra_i += inputs.len;4764 extra_i += inputs.len;
48564765
4857 const result = result: {4766 const result = result: {
4858 if (!is_volatile and f.liveness.isUnused(inst)) break :result .none;
4859
4860 const writer = f.object.writer();4767 const writer = f.object.writer();
4861 const inst_ty = f.air.typeOfIndex(inst);4768 const inst_ty = f.air.typeOfIndex(inst);
4862 const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: {4769 const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: {
...@@ -4892,6 +4799,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4892,6 +4799,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
4892 try writer.writeAll("register ");4799 try writer.writeAll("register ");
4893 const alignment = 0;4800 const alignment = 0;
4894 const local_value = try f.allocLocalValue(output_ty, alignment);4801 const local_value = try f.allocLocalValue(output_ty, alignment);
4802 try f.allocs.put(gpa, local_value.new_local, false);
4895 try f.object.dg.renderTypeAndName(writer, output_ty, local_value, .{}, alignment, .complete);4803 try f.object.dg.renderTypeAndName(writer, output_ty, local_value, .{}, alignment, .complete);
4896 try writer.writeAll(" __asm(\"");4804 try writer.writeAll(" __asm(\"");
4897 try writer.writeAll(constraint["={".len .. constraint.len - "}".len]);4805 try writer.writeAll(constraint["={".len .. constraint.len - "}".len]);
...@@ -4924,6 +4832,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4924,6 +4832,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
4924 if (is_reg) try writer.writeAll("register ");4832 if (is_reg) try writer.writeAll("register ");
4925 const alignment = 0;4833 const alignment = 0;
4926 const local_value = try f.allocLocalValue(input_ty, alignment);4834 const local_value = try f.allocLocalValue(input_ty, alignment);
4835 try f.allocs.put(gpa, local_value.new_local, false);
4927 try f.object.dg.renderTypeAndName(writer, input_ty, local_value, Const, alignment, .complete);4836 try f.object.dg.renderTypeAndName(writer, input_ty, local_value, Const, alignment, .complete);
4928 if (is_reg) {4837 if (is_reg) {
4929 try writer.writeAll(" __asm(\"");4838 try writer.writeAll(" __asm(\"");
...@@ -5106,11 +5015,6 @@ fn airIsNull(...@@ -5106,11 +5015,6 @@ fn airIsNull(
5106) !CValue {5015) !CValue {
5107 const un_op = f.air.instructions.items(.data)[inst].un_op;5016 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
5114 const writer = f.object.writer();5018 const writer = f.object.writer();
5115 const operand = try f.resolveInst(un_op);5019 const operand = try f.resolveInst(un_op);
5116 try reap(f, inst, &.{un_op});5020 try reap(f, inst, &.{un_op});
...@@ -5156,11 +5060,6 @@ fn airIsNull(...@@ -5156,11 +5060,6 @@ fn airIsNull(
5156fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {5060fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
5157 const ty_op = f.air.instructions.items(.data)[inst].ty_op;5061 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
5164 const operand = try f.resolveInst(ty_op.operand);5063 const operand = try f.resolveInst(ty_op.operand);
5165 try reap(f, inst, &.{ty_op.operand});5064 try reap(f, inst, &.{ty_op.operand});
5166 const opt_ty = f.air.typeOf(ty_op.operand);5065 const opt_ty = f.air.typeOf(ty_op.operand);
...@@ -5208,11 +5107,6 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5208,11 +5107,6 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
5208fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue {5107fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5209 const ty_op = f.air.instructions.items(.data)[inst].ty_op;5108 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
5216 const writer = f.object.writer();5110 const writer = f.object.writer();
5217 const operand = try f.resolveInst(ty_op.operand);5111 const operand = try f.resolveInst(ty_op.operand);
5218 try reap(f, inst, &.{ty_op.operand});5112 try reap(f, inst, &.{ty_op.operand});
...@@ -5342,11 +5236,6 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5342,11 +5236,6 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5342 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;5236 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
5343 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;5237 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
5350 const container_ptr_val = try f.resolveInst(extra.struct_operand);5239 const container_ptr_val = try f.resolveInst(extra.struct_operand);
5351 try reap(f, inst, &.{extra.struct_operand});5240 try reap(f, inst, &.{extra.struct_operand});
5352 const container_ptr_ty = f.air.typeOf(extra.struct_operand);5241 const container_ptr_ty = f.air.typeOf(extra.struct_operand);
...@@ -5356,11 +5245,6 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5356,11 +5245,6 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5356fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue {5245fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue {
5357 const ty_op = f.air.instructions.items(.data)[inst].ty_op;5246 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
5364 const container_ptr_val = try f.resolveInst(ty_op.operand);5248 const container_ptr_val = try f.resolveInst(ty_op.operand);
5365 try reap(f, inst, &.{ty_op.operand});5249 try reap(f, inst, &.{ty_op.operand});
5366 const container_ptr_ty = f.air.typeOf(ty_op.operand);5250 const container_ptr_ty = f.air.typeOf(ty_op.operand);
...@@ -5371,11 +5255,6 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5371,11 +5255,6 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5371 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;5255 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
5372 const extra = f.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;5256 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
5379 const target = f.object.dg.module.getTarget();5258 const target = f.object.dg.module.getTarget();
5380 const container_ptr_ty = f.air.typeOfIndex(inst);5259 const container_ptr_ty = f.air.typeOfIndex(inst);
5381 const container_ty = container_ptr_ty.childType();5260 const container_ty = container_ptr_ty.childType();
...@@ -5494,11 +5373,6 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5494,11 +5373,6 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
5494 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;5373 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
5495 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;5374 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
5502 const inst_ty = f.air.typeOfIndex(inst);5376 const inst_ty = f.air.typeOfIndex(inst);
5503 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) {5377 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) {
5504 try reap(f, inst, &.{extra.struct_operand});5378 try reap(f, inst, &.{extra.struct_operand});
...@@ -5644,11 +5518,6 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5644,11 +5518,6 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
5644fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {5518fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
5645 const ty_op = f.air.instructions.items(.data)[inst].ty_op;5519 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
5652 const inst_ty = f.air.typeOfIndex(inst);5521 const inst_ty = f.air.typeOfIndex(inst);
5653 const operand = try f.resolveInst(ty_op.operand);5522 const operand = try f.resolveInst(ty_op.operand);
5654 const operand_ty = f.air.typeOf(ty_op.operand);5523 const operand_ty = f.air.typeOf(ty_op.operand);
...@@ -5681,11 +5550,6 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5681,11 +5550,6 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
5681fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {5550fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
5682 const ty_op = f.air.instructions.items(.data)[inst].ty_op;5551 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
5689 const inst_ty = f.air.typeOfIndex(inst);5553 const inst_ty = f.air.typeOfIndex(inst);
5690 const operand = try f.resolveInst(ty_op.operand);5554 const operand = try f.resolveInst(ty_op.operand);
5691 try reap(f, inst, &.{ty_op.operand});5555 try reap(f, inst, &.{ty_op.operand});
...@@ -5723,11 +5587,6 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu...@@ -5723,11 +5587,6 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
5723fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {5587fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
5724 const ty_op = f.air.instructions.items(.data)[inst].ty_op;5588 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
5731 const inst_ty = f.air.typeOfIndex(inst);5590 const inst_ty = f.air.typeOfIndex(inst);
5732 const payload = try f.resolveInst(ty_op.operand);5591 const payload = try f.resolveInst(ty_op.operand);
5733 try reap(f, inst, &.{ty_op.operand});5592 try reap(f, inst, &.{ty_op.operand});
...@@ -5769,10 +5628,6 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5769,10 +5628,6 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
57695628
5770fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {5629fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
5771 const ty_op = f.air.instructions.items(.data)[inst].ty_op;5630 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
5777 const writer = f.object.writer();5632 const writer = f.object.writer();
5778 const operand = try f.resolveInst(ty_op.operand);5633 const operand = try f.resolveInst(ty_op.operand);
...@@ -5836,7 +5691,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5836,7 +5691,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
5836}5691}
58375692
5838fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {5693fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
5839 if (f.liveness.isUnused(inst)) return .none;5694 _ = inst;
5840 return f.fail("TODO: C backend: implement airErrReturnTrace", .{});5695 return f.fail("TODO: C backend: implement airErrReturnTrace", .{});
5841}5696}
58425697
...@@ -5852,10 +5707,6 @@ fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5852,10 +5707,6 @@ fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue {
58525707
5853fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {5708fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
5854 const ty_op = f.air.instructions.items(.data)[inst].ty_op;5709 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
5860 const inst_ty = f.air.typeOfIndex(inst);5711 const inst_ty = f.air.typeOfIndex(inst);
5861 const payload_ty = inst_ty.errorUnionPayload();5712 const payload_ty = inst_ty.errorUnionPayload();
...@@ -5890,11 +5741,6 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5890,11 +5741,6 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
5890fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue {5741fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue {
5891 const un_op = f.air.instructions.items(.data)[inst].un_op;5742 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
5898 const writer = f.object.writer();5744 const writer = f.object.writer();
5899 const operand = try f.resolveInst(un_op);5745 const operand = try f.resolveInst(un_op);
5900 try reap(f, inst, &.{un_op});5746 try reap(f, inst, &.{un_op});
...@@ -5928,11 +5774,6 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const...@@ -5928,11 +5774,6 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const
5928fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {5774fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
5929 const ty_op = f.air.instructions.items(.data)[inst].ty_op;5775 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
5936 const operand = try f.resolveInst(ty_op.operand);5777 const operand = try f.resolveInst(ty_op.operand);
5937 try reap(f, inst, &.{ty_op.operand});5778 try reap(f, inst, &.{ty_op.operand});
5938 const inst_ty = f.air.typeOfIndex(inst);5779 const inst_ty = f.air.typeOfIndex(inst);
...@@ -5966,11 +5807,6 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5966,11 +5807,6 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
5966fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {5807fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
5967 const ty_op = f.air.instructions.items(.data)[inst].ty_op;5808 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
5974 const inst_ty = f.air.typeOfIndex(inst);5810 const inst_ty = f.air.typeOfIndex(inst);
5975 const operand = try f.resolveInst(ty_op.operand);5811 const operand = try f.resolveInst(ty_op.operand);
5976 try reap(f, inst, &.{ty_op.operand});5812 try reap(f, inst, &.{ty_op.operand});
...@@ -6014,11 +5850,6 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6014,11 +5850,6 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
6014fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {5850fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
6015 const un_op = f.air.instructions.items(.data)[inst].un_op;5851 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
6022 const operand = try f.resolveInst(un_op);5853 const operand = try f.resolveInst(un_op);
6023 try reap(f, inst, &.{un_op});5854 try reap(f, inst, &.{un_op});
6024 const inst_ty = f.air.typeOfIndex(inst);5855 const inst_ty = f.air.typeOfIndex(inst);
...@@ -6042,11 +5873,6 @@ fn airUnBuiltinCall(...@@ -6042,11 +5873,6 @@ fn airUnBuiltinCall(
6042) !CValue {5873) !CValue {
6043 const ty_op = f.air.instructions.items(.data)[inst].ty_op;5874 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
6050 const operand = try f.resolveInst(ty_op.operand);5876 const operand = try f.resolveInst(ty_op.operand);
6051 try reap(f, inst, &.{ty_op.operand});5877 try reap(f, inst, &.{ty_op.operand});
6052 const inst_ty = f.air.typeOfIndex(inst);5878 const inst_ty = f.air.typeOfIndex(inst);
...@@ -6090,11 +5916,6 @@ fn airBinBuiltinCall(...@@ -6090,11 +5916,6 @@ fn airBinBuiltinCall(
6090) !CValue {5916) !CValue {
6091 const bin_op = f.air.instructions.items(.data)[inst].bin_op;5917 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
6098 const operand_ty = f.air.typeOf(bin_op.lhs);5919 const operand_ty = f.air.typeOf(bin_op.lhs);
6099 const operand_cty = try f.typeToCType(operand_ty, .complete);5920 const operand_cty = try f.typeToCType(operand_ty, .complete);
6100 const is_big = operand_cty.tag() == .array;5921 const is_big = operand_cty.tag() == .array;
...@@ -6147,11 +5968,6 @@ fn airCmpBuiltinCall(...@@ -6147,11 +5968,6 @@ fn airCmpBuiltinCall(
6147 operation: enum { cmp, operator },5968 operation: enum { cmp, operator },
6148 info: BuiltinInfo,5969 info: BuiltinInfo,
6149) !CValue {5970) !CValue {
6150 if (f.liveness.isUnused(inst)) {
6151 try reap(f, inst, &.{ data.lhs, data.rhs });
6152 return .none;
6153 }
6154
6155 const lhs = try f.resolveInst(data.lhs);5971 const lhs = try f.resolveInst(data.lhs);
6156 const rhs = try f.resolveInst(data.rhs);5972 const rhs = try f.resolveInst(data.rhs);
6157 try reap(f, inst, &.{ data.lhs, data.rhs });5973 try reap(f, inst, &.{ data.lhs, data.rhs });
...@@ -6322,9 +6138,6 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6322,9 +6138,6 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
6322 const ptr = try f.resolveInst(atomic_load.ptr);6138 const ptr = try f.resolveInst(atomic_load.ptr);
6323 try reap(f, inst, &.{atomic_load.ptr});6139 try reap(f, inst, &.{atomic_load.ptr});
6324 const ptr_ty = f.air.typeOf(atomic_load.ptr);6140 const ptr_ty = f.air.typeOf(atomic_load.ptr);
6325 if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) {
6326 return .none;
6327 }
63286141
6329 const inst_ty = f.air.typeOfIndex(inst);6142 const inst_ty = f.air.typeOfIndex(inst);
6330 const writer = f.object.writer();6143 const writer = f.object.writer();
...@@ -6468,11 +6281,6 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6468,11 +6281,6 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
6468fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {6281fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
6469 const ty_op = f.air.instructions.items(.data)[inst].ty_op;6282 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
6476 const operand = try f.resolveInst(ty_op.operand);6284 const operand = try f.resolveInst(ty_op.operand);
6477 try reap(f, inst, &.{ty_op.operand});6285 try reap(f, inst, &.{ty_op.operand});
64786286
...@@ -6496,11 +6304,6 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6496,11 +6304,6 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
6496fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {6304fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
6497 const un_op = f.air.instructions.items(.data)[inst].un_op;6305 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
6504 const inst_ty = f.air.typeOfIndex(inst);6307 const inst_ty = f.air.typeOfIndex(inst);
6505 const enum_ty = f.air.typeOf(un_op);6308 const enum_ty = f.air.typeOf(un_op);
6506 const operand = try f.resolveInst(un_op);6309 const operand = try f.resolveInst(un_op);
...@@ -6521,11 +6324,6 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6521,11 +6324,6 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
6521fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {6324fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
6522 const un_op = f.air.instructions.items(.data)[inst].un_op;6325 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
6529 const writer = f.object.writer();6327 const writer = f.object.writer();
6530 const inst_ty = f.air.typeOfIndex(inst);6328 const inst_ty = f.air.typeOfIndex(inst);
6531 const operand = try f.resolveInst(un_op);6329 const operand = try f.resolveInst(un_op);
...@@ -6542,11 +6340,6 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6542,11 +6340,6 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
6542fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {6340fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
6543 const ty_op = f.air.instructions.items(.data)[inst].ty_op;6341 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
6550 const operand = try f.resolveInst(ty_op.operand);6343 const operand = try f.resolveInst(ty_op.operand);
6551 try reap(f, inst, &.{ty_op.operand});6344 try reap(f, inst, &.{ty_op.operand});
65526345
...@@ -6578,11 +6371,6 @@ fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6578,11 +6371,6 @@ fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {
6578 const pl_op = f.air.instructions.items(.data)[inst].pl_op;6371 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
6579 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;6372 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
6586 const pred = try f.resolveInst(pl_op.operand);6374 const pred = try f.resolveInst(pl_op.operand);
6587 const lhs = try f.resolveInst(extra.lhs);6375 const lhs = try f.resolveInst(extra.lhs);
6588 const rhs = try f.resolveInst(extra.rhs);6376 const rhs = try f.resolveInst(extra.rhs);
...@@ -6614,11 +6402,6 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6614,11 +6402,6 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {
6614 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;6402 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
6615 const extra = f.air.extraData(Air.Shuffle, ty_pl.payload).data;6403 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
6622 const mask = f.air.values[extra.mask];6405 const mask = f.air.values[extra.mask];
6623 const lhs = try f.resolveInst(extra.a);6406 const lhs = try f.resolveInst(extra.a);
6624 const rhs = try f.resolveInst(extra.b);6407 const rhs = try f.resolveInst(extra.b);
...@@ -6660,11 +6443,6 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6660,11 +6443,6 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {
6660fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {6443fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
6661 const reduce = f.air.instructions.items(.data)[inst].reduce;6444 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
6668 const target = f.object.dg.module.getTarget();6446 const target = f.object.dg.module.getTarget();
6669 const scalar_ty = f.air.typeOfIndex(inst);6447 const scalar_ty = f.air.typeOfIndex(inst);
6670 const operand = try f.resolveInst(reduce.operand);6448 const operand = try f.resolveInst(reduce.operand);
...@@ -6836,8 +6614,6 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6836,8 +6614,6 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
6836 }6614 }
6837 }6615 }
68386616
6839 if (f.liveness.isUnused(inst)) return .none;
6840
6841 const target = f.object.dg.module.getTarget();6617 const target = f.object.dg.module.getTarget();
68426618
6843 const writer = f.object.writer();6619 const writer = f.object.writer();
...@@ -7004,11 +6780,6 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7004,11 +6780,6 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
7004 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;6780 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
7005 const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data;6781 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
7012 const union_ty = f.air.typeOfIndex(inst);6783 const union_ty = f.air.typeOfIndex(inst);
7013 const target = f.object.dg.module.getTarget();6784 const target = f.object.dg.module.getTarget();
7014 const union_obj = union_ty.cast(Type.Payload.Union).?.data;6785 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
...@@ -7075,8 +6846,6 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7075,8 +6846,6 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
7075}6846}
70766847
7077fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {6848fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
7078 if (f.liveness.isUnused(inst)) return .none;
7079
7080 const pl_op = f.air.instructions.items(.data)[inst].pl_op;6849 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
70816850
7082 const writer = f.object.writer();6851 const writer = f.object.writer();
...@@ -7109,10 +6878,6 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7109,10 +6878,6 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
71096878
7110fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {6879fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {
7111 const un_op = f.air.instructions.items(.data)[inst].un_op;6880 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
7117 const operand = try f.resolveInst(un_op);6882 const operand = try f.resolveInst(un_op);
7118 try reap(f, inst, &.{un_op});6883 try reap(f, inst, &.{un_op});
...@@ -7138,10 +6903,6 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7138,10 +6903,6 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {
71386903
7139fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue {6904fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue {
7140 const un_op = f.air.instructions.items(.data)[inst].un_op;6905 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
7146 const operand = try f.resolveInst(un_op);6907 const operand = try f.resolveInst(un_op);
7147 try reap(f, inst, &.{un_op});6908 try reap(f, inst, &.{un_op});
...@@ -7169,10 +6930,6 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal...@@ -7169,10 +6930,6 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal
71696930
7170fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue {6931fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue {
7171 const bin_op = f.air.instructions.items(.data)[inst].bin_op;6932 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
7177 const lhs = try f.resolveInst(bin_op.lhs);6934 const lhs = try f.resolveInst(bin_op.lhs);
7178 const rhs = try f.resolveInst(bin_op.rhs);6935 const rhs = try f.resolveInst(bin_op.rhs);
...@@ -7205,10 +6962,6 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa...@@ -7205,10 +6962,6 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa
7205fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {6962fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
7206 const pl_op = f.air.instructions.items(.data)[inst].pl_op;6963 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
7207 const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data;6964 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
7213 const mulend1 = try f.resolveInst(bin_op.lhs);6966 const mulend1 = try f.resolveInst(bin_op.lhs);
7214 const mulend2 = try f.resolveInst(bin_op.rhs);6967 const mulend2 = try f.resolveInst(bin_op.rhs);
...@@ -7241,8 +6994,6 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7241,8 +6994,6 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
7241}6994}
72426995
7243fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {6996fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {
7244 if (f.liveness.isUnused(inst)) return .none;
7245
7246 const inst_ty = f.air.typeOfIndex(inst);6997 const inst_ty = f.air.typeOfIndex(inst);
7247 const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete);6998 const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete);
7248 const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len;6999 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 {...@@ -7261,10 +7012,6 @@ fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {
72617012
7262fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue {7013fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue {
7263 const ty_op = f.air.instructions.items(.data)[inst].ty_op;7014 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
7269 const inst_ty = f.air.typeOfIndex(inst);7016 const inst_ty = f.air.typeOfIndex(inst);
7270 const va_list = try f.resolveInst(ty_op.operand);7017 const va_list = try f.resolveInst(ty_op.operand);
...@@ -7296,10 +7043,6 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7296,10 +7043,6 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue {
72967043
7297fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue {7044fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue {
7298 const ty_op = f.air.instructions.items(.data)[inst].ty_op;7045 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
7304 const inst_ty = f.air.typeOfIndex(inst);7047 const inst_ty = f.air.typeOfIndex(inst);
7305 const va_list = try f.resolveInst(ty_op.operand);7048 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...@@ -7863,7 +7606,6 @@ fn freeLocal(f: *Function, inst: Air.Inst.Index, local_index: LocalIndex, ref_in
7863 const gpa = f.object.dg.gpa;7606 const gpa = f.object.dg.gpa;
7864 const local = &f.locals.items[local_index];7607 const local = &f.locals.items[local_index];
7865 log.debug("%{d}: freeing t{d} (operand %{d})", .{ inst, local_index, ref_inst });7608 log.debug("%{d}: freeing t{d} (operand %{d})", .{ inst, local_index, ref_inst });
7866 if (f.is_in_clone != local.is_in_clone) return;
7867 const gop = try f.free_locals_map.getOrPut(gpa, local.getType());7609 const gop = try f.free_locals_map.getOrPut(gpa, local.getType());
7868 if (!gop.found_existing) gop.value_ptr.* = .{};7610 if (!gop.found_existing) gop.value_ptr.* = .{};
7869 if (std.debug.runtime_safety) {7611 if (std.debug.runtime_safety) {
...@@ -7921,35 +7663,3 @@ fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void {...@@ -7921,35 +7663,3 @@ fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void {
7921 }7663 }
7922 map.deinit(gpa);7664 map.deinit(gpa);
7923}7665}
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 {...@@ -4523,6 +4523,10 @@ pub const FuncGen = struct {
4523 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) Error!void {4523 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) Error!void {
4524 const air_tags = self.air.instructions.items(.tag);4524 const air_tags = self.air.instructions.items(.tag);
4525 for (body, 0..) |inst, i| {4525 for (body, 0..) |inst, i| {
4526 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
4527 continue;
4528 }
4529
4526 const opt_value: ?*llvm.Value = switch (air_tags[inst]) {4530 const opt_value: ?*llvm.Value = switch (air_tags[inst]) {
4527 // zig fmt: off4531 // zig fmt: off
4528 .add => try self.airAdd(inst, false),4532 .add => try self.airAdd(inst, false),
...@@ -5166,8 +5170,6 @@ pub const FuncGen = struct {...@@ -5166,8 +5170,6 @@ pub const FuncGen = struct {
5166 }5170 }
51675171
5168 fn airCVaArg(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {5172 fn airCVaArg(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5169 if (self.liveness.isUnused(inst)) return null;
5170
5171 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5173 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5172 const list = try self.resolveInst(ty_op.operand);5174 const list = try self.resolveInst(ty_op.operand);
5173 const arg_ty = self.air.getRefType(ty_op.ty);5175 const arg_ty = self.air.getRefType(ty_op.ty);
...@@ -5177,8 +5179,6 @@ pub const FuncGen = struct {...@@ -5177,8 +5179,6 @@ pub const FuncGen = struct {
5177 }5179 }
51785180
5179 fn airCVaCopy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {5181 fn airCVaCopy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5180 if (self.liveness.isUnused(inst)) return null;
5181
5182 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5182 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5183 const src_list = try self.resolveInst(ty_op.operand);5183 const src_list = try self.resolveInst(ty_op.operand);
5184 const va_list_ty = self.air.getRefType(ty_op.ty);5184 const va_list_ty = self.air.getRefType(ty_op.ty);
...@@ -5226,8 +5226,6 @@ pub const FuncGen = struct {...@@ -5226,8 +5226,6 @@ pub const FuncGen = struct {
5226 }5226 }
52275227
5228 fn airCVaStart(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {5228 fn airCVaStart(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5229 if (self.liveness.isUnused(inst)) return null;
5230
5231 const va_list_ty = self.air.typeOfIndex(inst);5229 const va_list_ty = self.air.typeOfIndex(inst);
5232 const llvm_va_list_ty = try self.dg.lowerType(va_list_ty);5230 const llvm_va_list_ty = try self.dg.lowerType(va_list_ty);
52335231
...@@ -5254,7 +5252,6 @@ pub const FuncGen = struct {...@@ -5254,7 +5252,6 @@ pub const FuncGen = struct {
5254 }5252 }
52555253
5256 fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator, want_fast_math: bool) !?*llvm.Value {5254 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;
5258 self.builder.setFastMath(want_fast_math);5255 self.builder.setFastMath(want_fast_math);
52595256
5260 const bin_op = self.air.instructions.items(.data)[inst].bin_op;5257 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -5266,7 +5263,6 @@ pub const FuncGen = struct {...@@ -5266,7 +5263,6 @@ pub const FuncGen = struct {
5266 }5263 }
52675264
5268 fn airCmpVector(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {5265 fn airCmpVector(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
5269 if (self.liveness.isUnused(inst)) return null;
5270 self.builder.setFastMath(want_fast_math);5266 self.builder.setFastMath(want_fast_math);
52715267
5272 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;5268 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
...@@ -5281,8 +5277,6 @@ pub const FuncGen = struct {...@@ -5281,8 +5277,6 @@ pub const FuncGen = struct {
5281 }5277 }
52825278
5283 fn airCmpLtErrorsLen(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {5279 fn airCmpLtErrorsLen(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5284 if (self.liveness.isUnused(inst)) return null;
5285
5286 const un_op = self.air.instructions.items(.data)[inst].un_op;5280 const un_op = self.air.instructions.items(.data)[inst].un_op;
5287 const operand = try self.resolveInst(un_op);5281 const operand = try self.resolveInst(un_op);
5288 const llvm_fn = try self.getCmpLtErrorsLenFunction();5282 const llvm_fn = try self.getCmpLtErrorsLenFunction();
...@@ -5650,9 +5644,6 @@ pub const FuncGen = struct {...@@ -5650,9 +5644,6 @@ pub const FuncGen = struct {
5650 }5644 }
56515645
5652 fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {5646 fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5653 if (self.liveness.isUnused(inst))
5654 return null;
5655
5656 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5647 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5657 const operand_ty = self.air.typeOf(ty_op.operand);5648 const operand_ty = self.air.typeOf(ty_op.operand);
5658 const array_ty = operand_ty.childType();5649 const array_ty = operand_ty.childType();
...@@ -5674,9 +5665,6 @@ pub const FuncGen = struct {...@@ -5674,9 +5665,6 @@ pub const FuncGen = struct {
5674 }5665 }
56755666
5676 fn airIntToFloat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {5667 fn airIntToFloat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5677 if (self.liveness.isUnused(inst))
5678 return null;
5679
5680 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5668 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
56815669
5682 const operand = try self.resolveInst(ty_op.operand);5670 const operand = try self.resolveInst(ty_op.operand);
...@@ -5733,9 +5721,6 @@ pub const FuncGen = struct {...@@ -5733,9 +5721,6 @@ pub const FuncGen = struct {
5733 }5721 }
57345722
5735 fn airFloatToInt(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {5723 fn airFloatToInt(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
5736 if (self.liveness.isUnused(inst))
5737 return null;
5738
5739 self.builder.setFastMath(want_fast_math);5724 self.builder.setFastMath(want_fast_math);
57405725
5741 const target = self.dg.module.getTarget();5726 const target = self.dg.module.getTarget();
...@@ -5792,16 +5777,12 @@ pub const FuncGen = struct {...@@ -5792,16 +5777,12 @@ pub const FuncGen = struct {
5792 }5777 }
57935778
5794 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {5779 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {
5795 if (self.liveness.isUnused(inst)) return null;
5796
5797 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5780 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5798 const operand = try self.resolveInst(ty_op.operand);5781 const operand = try self.resolveInst(ty_op.operand);
5799 return self.builder.buildExtractValue(operand, index, "");5782 return self.builder.buildExtractValue(operand, index, "");
5800 }5783 }
58015784
5802 fn airPtrSliceFieldPtr(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {5785 fn airPtrSliceFieldPtr(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {
5803 if (self.liveness.isUnused(inst)) return null;
5804
5805 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5786 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5806 const slice_ptr = try self.resolveInst(ty_op.operand);5787 const slice_ptr = try self.resolveInst(ty_op.operand);
5807 const slice_ptr_ty = self.air.typeOf(ty_op.operand);5788 const slice_ptr_ty = self.air.typeOf(ty_op.operand);
...@@ -5814,8 +5795,6 @@ pub const FuncGen = struct {...@@ -5814,8 +5795,6 @@ pub const FuncGen = struct {
5814 const inst = body_tail[0];5795 const inst = body_tail[0];
5815 const bin_op = self.air.instructions.items(.data)[inst].bin_op;5796 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5816 const slice_ty = self.air.typeOf(bin_op.lhs);5797 const slice_ty = self.air.typeOf(bin_op.lhs);
5817 if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
5818
5819 const slice = try self.resolveInst(bin_op.lhs);5798 const slice = try self.resolveInst(bin_op.lhs);
5820 const index = try self.resolveInst(bin_op.rhs);5799 const index = try self.resolveInst(bin_op.rhs);
5821 const elem_ty = slice_ty.childType();5800 const elem_ty = slice_ty.childType();
...@@ -5835,7 +5814,6 @@ pub const FuncGen = struct {...@@ -5835,7 +5814,6 @@ pub const FuncGen = struct {
5835 }5814 }
58365815
5837 fn airSliceElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {5816 fn airSliceElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5838 if (self.liveness.isUnused(inst)) return null;
5839 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;5817 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5840 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;5818 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
5841 const slice_ty = self.air.typeOf(bin_op.lhs);5819 const slice_ty = self.air.typeOf(bin_op.lhs);
...@@ -5850,7 +5828,6 @@ pub const FuncGen = struct {...@@ -5850,7 +5828,6 @@ pub const FuncGen = struct {
58505828
5851 fn airArrayElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {5829 fn airArrayElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
5852 const inst = body_tail[0];5830 const inst = body_tail[0];
5853 if (self.liveness.isUnused(inst)) return null;
58545831
5855 const bin_op = self.air.instructions.items(.data)[inst].bin_op;5832 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5856 const array_ty = self.air.typeOf(bin_op.lhs);5833 const array_ty = self.air.typeOf(bin_op.lhs);
...@@ -5881,8 +5858,6 @@ pub const FuncGen = struct {...@@ -5881,8 +5858,6 @@ pub const FuncGen = struct {
5881 const inst = body_tail[0];5858 const inst = body_tail[0];
5882 const bin_op = self.air.instructions.items(.data)[inst].bin_op;5859 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5883 const ptr_ty = self.air.typeOf(bin_op.lhs);5860 const ptr_ty = self.air.typeOf(bin_op.lhs);
5884 if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
5885
5886 const elem_ty = ptr_ty.childType();5861 const elem_ty = ptr_ty.childType();
5887 const llvm_elem_ty = try self.dg.lowerPtrElemTy(elem_ty);5862 const llvm_elem_ty = try self.dg.lowerPtrElemTy(elem_ty);
5888 const base_ptr = try self.resolveInst(bin_op.lhs);5863 const base_ptr = try self.resolveInst(bin_op.lhs);
...@@ -5908,8 +5883,6 @@ pub const FuncGen = struct {...@@ -5908,8 +5883,6 @@ pub const FuncGen = struct {
5908 }5883 }
59095884
5910 fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {5885 fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5911 if (self.liveness.isUnused(inst)) return null;
5912
5913 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;5886 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5914 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;5887 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
5915 const ptr_ty = self.air.typeOf(bin_op.lhs);5888 const ptr_ty = self.air.typeOf(bin_op.lhs);
...@@ -5934,9 +5907,6 @@ pub const FuncGen = struct {...@@ -5934,9 +5907,6 @@ pub const FuncGen = struct {
5934 }5907 }
59355908
5936 fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {5909 fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5937 if (self.liveness.isUnused(inst))
5938 return null;
5939
5940 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;5910 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5941 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;5911 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
5942 const struct_ptr = try self.resolveInst(struct_field.struct_operand);5912 const struct_ptr = try self.resolveInst(struct_field.struct_operand);
...@@ -5949,8 +5919,6 @@ pub const FuncGen = struct {...@@ -5949,8 +5919,6 @@ pub const FuncGen = struct {
5949 inst: Air.Inst.Index,5919 inst: Air.Inst.Index,
5950 field_index: u32,5920 field_index: u32,
5951 ) !?*llvm.Value {5921 ) !?*llvm.Value {
5952 if (self.liveness.isUnused(inst)) return null;
5953
5954 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5922 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5955 const struct_ptr = try self.resolveInst(ty_op.operand);5923 const struct_ptr = try self.resolveInst(ty_op.operand);
5956 const struct_ptr_ty = self.air.typeOf(ty_op.operand);5924 const struct_ptr_ty = self.air.typeOf(ty_op.operand);
...@@ -5959,8 +5927,6 @@ pub const FuncGen = struct {...@@ -5959,8 +5927,6 @@ pub const FuncGen = struct {
59595927
5960 fn airStructFieldVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {5928 fn airStructFieldVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
5961 const inst = body_tail[0];5929 const inst = body_tail[0];
5962 if (self.liveness.isUnused(inst)) return null;
5963
5964 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;5930 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5965 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;5931 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
5966 const struct_ty = self.air.typeOf(struct_field.struct_operand);5932 const struct_ty = self.air.typeOf(struct_field.struct_operand);
...@@ -6060,8 +6026,6 @@ pub const FuncGen = struct {...@@ -6060,8 +6026,6 @@ pub const FuncGen = struct {
6060 }6026 }
60616027
6062 fn airFieldParentPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {6028 fn airFieldParentPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6063 if (self.liveness.isUnused(inst)) return null;
6064
6065 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;6029 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
6066 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;6030 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
60676031
...@@ -6083,9 +6047,6 @@ pub const FuncGen = struct {...@@ -6083,9 +6047,6 @@ pub const FuncGen = struct {
6083 }6047 }
60846048
6085 fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {6049 fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6086 if (self.liveness.isUnused(inst))
6087 return null;
6088
6089 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6050 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6090 const operand = try self.resolveInst(ty_op.operand);6051 const operand = try self.resolveInst(ty_op.operand);
60916052
...@@ -6263,8 +6224,6 @@ pub const FuncGen = struct {...@@ -6263,8 +6224,6 @@ pub const FuncGen = struct {
6263 const clobbers_len = @truncate(u31, extra.data.flags);6224 const clobbers_len = @truncate(u31, extra.data.flags);
6264 var extra_i: usize = extra.end;6225 var extra_i: usize = extra.end;
62656226
6266 if (!is_volatile and self.liveness.isUnused(inst)) return null;
6267
6268 const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);6227 const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
6269 extra_i += outputs.len;6228 extra_i += outputs.len;
6270 const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);6229 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 {...@@ -6610,8 +6569,6 @@ pub const FuncGen = struct {
6610 operand_is_ptr: bool,6569 operand_is_ptr: bool,
6611 pred: llvm.IntPredicate,6570 pred: llvm.IntPredicate,
6612 ) !?*llvm.Value {6571 ) !?*llvm.Value {
6613 if (self.liveness.isUnused(inst)) return null;
6614
6615 const un_op = self.air.instructions.items(.data)[inst].un_op;6572 const un_op = self.air.instructions.items(.data)[inst].un_op;
6616 const operand = try self.resolveInst(un_op);6573 const operand = try self.resolveInst(un_op);
6617 const operand_ty = self.air.typeOf(un_op);6574 const operand_ty = self.air.typeOf(un_op);
...@@ -6659,8 +6616,6 @@ pub const FuncGen = struct {...@@ -6659,8 +6616,6 @@ pub const FuncGen = struct {
6659 op: llvm.IntPredicate,6616 op: llvm.IntPredicate,
6660 operand_is_ptr: bool,6617 operand_is_ptr: bool,
6661 ) !?*llvm.Value {6618 ) !?*llvm.Value {
6662 if (self.liveness.isUnused(inst)) return null;
6663
6664 const un_op = self.air.instructions.items(.data)[inst].un_op;6619 const un_op = self.air.instructions.items(.data)[inst].un_op;
6665 const operand = try self.resolveInst(un_op);6620 const operand = try self.resolveInst(un_op);
6666 const operand_ty = self.air.typeOf(un_op);6621 const operand_ty = self.air.typeOf(un_op);
...@@ -6701,8 +6656,6 @@ pub const FuncGen = struct {...@@ -6701,8 +6656,6 @@ pub const FuncGen = struct {
6701 }6656 }
67026657
6703 fn airOptionalPayloadPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {6658 fn airOptionalPayloadPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6704 if (self.liveness.isUnused(inst)) return null;
6705
6706 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6659 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6707 const operand = try self.resolveInst(ty_op.operand);6660 const operand = try self.resolveInst(ty_op.operand);
6708 const optional_ty = self.air.typeOf(ty_op.operand).childType();6661 const optional_ty = self.air.typeOf(ty_op.operand).childType();
...@@ -6756,8 +6709,6 @@ pub const FuncGen = struct {...@@ -6756,8 +6709,6 @@ pub const FuncGen = struct {
67566709
6757 fn airOptionalPayload(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {6710 fn airOptionalPayload(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
6758 const inst = body_tail[0];6711 const inst = body_tail[0];
6759 if (self.liveness.isUnused(inst)) return null;
6760
6761 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6712 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6762 const operand = try self.resolveInst(ty_op.operand);6713 const operand = try self.resolveInst(ty_op.operand);
6763 const optional_ty = self.air.typeOf(ty_op.operand);6714 const optional_ty = self.air.typeOf(ty_op.operand);
...@@ -6780,8 +6731,6 @@ pub const FuncGen = struct {...@@ -6780,8 +6731,6 @@ pub const FuncGen = struct {
6780 operand_is_ptr: bool,6731 operand_is_ptr: bool,
6781 ) !?*llvm.Value {6732 ) !?*llvm.Value {
6782 const inst = body_tail[0];6733 const inst = body_tail[0];
6783 if (self.liveness.isUnused(inst)) return null;
6784
6785 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6734 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6786 const operand = try self.resolveInst(ty_op.operand);6735 const operand = try self.resolveInst(ty_op.operand);
6787 const operand_ty = self.air.typeOf(ty_op.operand);6736 const operand_ty = self.air.typeOf(ty_op.operand);
...@@ -6817,9 +6766,6 @@ pub const FuncGen = struct {...@@ -6817,9 +6766,6 @@ pub const FuncGen = struct {
6817 inst: Air.Inst.Index,6766 inst: Air.Inst.Index,
6818 operand_is_ptr: bool,6767 operand_is_ptr: bool,
6819 ) !?*llvm.Value {6768 ) !?*llvm.Value {
6820 if (self.liveness.isUnused(inst))
6821 return null;
6822
6823 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6769 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6824 const operand = try self.resolveInst(ty_op.operand);6770 const operand = try self.resolveInst(ty_op.operand);
6825 const operand_ty = self.air.typeOf(ty_op.operand);6771 const operand_ty = self.air.typeOf(ty_op.operand);
...@@ -6893,8 +6839,6 @@ pub const FuncGen = struct {...@@ -6893,8 +6839,6 @@ pub const FuncGen = struct {
6893 }6839 }
68946840
6895 fn airSaveErrReturnTraceIndex(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {6841 fn airSaveErrReturnTraceIndex(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6896 if (self.liveness.isUnused(inst)) return null;
6897
6898 const target = self.dg.module.getTarget();6842 const target = self.dg.module.getTarget();
68996843
6900 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;6844 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
...@@ -6911,8 +6855,6 @@ pub const FuncGen = struct {...@@ -6911,8 +6855,6 @@ pub const FuncGen = struct {
6911 }6855 }
69126856
6913 fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {6857 fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6914 if (self.liveness.isUnused(inst)) return null;
6915
6916 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6858 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6917 const payload_ty = self.air.typeOf(ty_op.operand);6859 const payload_ty = self.air.typeOf(ty_op.operand);
6918 const non_null_bit = self.context.intType(8).constInt(1, .False);6860 const non_null_bit = self.context.intType(8).constInt(1, .False);
...@@ -6943,8 +6885,6 @@ pub const FuncGen = struct {...@@ -6943,8 +6885,6 @@ pub const FuncGen = struct {
6943 }6885 }
69446886
6945 fn airWrapErrUnionPayload(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {6887 fn airWrapErrUnionPayload(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6946 if (self.liveness.isUnused(inst)) return null;
6947
6948 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6888 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6949 const err_un_ty = self.air.typeOfIndex(inst);6889 const err_un_ty = self.air.typeOfIndex(inst);
6950 const operand = try self.resolveInst(ty_op.operand);6890 const operand = try self.resolveInst(ty_op.operand);
...@@ -6978,8 +6918,6 @@ pub const FuncGen = struct {...@@ -6978,8 +6918,6 @@ pub const FuncGen = struct {
6978 }6918 }
69796919
6980 fn airWrapErrUnionErr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {6920 fn airWrapErrUnionErr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6981 if (self.liveness.isUnused(inst)) return null;
6982
6983 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6921 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6984 const err_un_ty = self.air.typeOfIndex(inst);6922 const err_un_ty = self.air.typeOfIndex(inst);
6985 const payload_ty = err_un_ty.errorUnionPayload();6923 const payload_ty = err_un_ty.errorUnionPayload();
...@@ -7015,8 +6953,6 @@ pub const FuncGen = struct {...@@ -7015,8 +6953,6 @@ pub const FuncGen = struct {
7015 }6953 }
70166954
7017 fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {6955 fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7018 if (self.liveness.isUnused(inst)) return null;
7019
7020 const pl_op = self.air.instructions.items(.data)[inst].pl_op;6956 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
7021 const index = pl_op.payload;6957 const index = pl_op.payload;
7022 const llvm_u32 = self.context.intType(32);6958 const llvm_u32 = self.context.intType(32);
...@@ -7061,8 +6997,6 @@ pub const FuncGen = struct {...@@ -7061,8 +6997,6 @@ pub const FuncGen = struct {
7061 }6997 }
70626998
7063 fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {6999 fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7064 if (self.liveness.isUnused(inst)) return null;
7065
7066 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7000 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
7067 const lhs = try self.resolveInst(bin_op.lhs);7001 const lhs = try self.resolveInst(bin_op.lhs);
7068 const rhs = try self.resolveInst(bin_op.rhs);7002 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -7074,8 +7008,6 @@ pub const FuncGen = struct {...@@ -7074,8 +7008,6 @@ pub const FuncGen = struct {
7074 }7008 }
70757009
7076 fn airMax(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7010 fn airMax(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7077 if (self.liveness.isUnused(inst)) return null;
7078
7079 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7011 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
7080 const lhs = try self.resolveInst(bin_op.lhs);7012 const lhs = try self.resolveInst(bin_op.lhs);
7081 const rhs = try self.resolveInst(bin_op.rhs);7013 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -7087,8 +7019,6 @@ pub const FuncGen = struct {...@@ -7087,8 +7019,6 @@ pub const FuncGen = struct {
7087 }7019 }
70887020
7089 fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7021 fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7090 if (self.liveness.isUnused(inst)) return null;
7091
7092 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;7022 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
7093 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;7023 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
7094 const ptr = try self.resolveInst(bin_op.lhs);7024 const ptr = try self.resolveInst(bin_op.lhs);
...@@ -7103,7 +7033,6 @@ pub const FuncGen = struct {...@@ -7103,7 +7033,6 @@ pub const FuncGen = struct {
7103 }7033 }
71047034
7105 fn airAdd(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7035 fn airAdd(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7106 if (self.liveness.isUnused(inst)) return null;
7107 self.builder.setFastMath(want_fast_math);7036 self.builder.setFastMath(want_fast_math);
71087037
7109 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7038 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7118,7 +7047,6 @@ pub const FuncGen = struct {...@@ -7118,7 +7047,6 @@ pub const FuncGen = struct {
7118 }7047 }
71197048
7120 fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7049 fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7121 if (self.liveness.isUnused(inst)) return null;
7122 self.builder.setFastMath(want_fast_math);7050 self.builder.setFastMath(want_fast_math);
71237051
7124 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7052 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7129,8 +7057,6 @@ pub const FuncGen = struct {...@@ -7129,8 +7057,6 @@ pub const FuncGen = struct {
7129 }7057 }
71307058
7131 fn airAddSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7059 fn airAddSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7132 if (self.liveness.isUnused(inst)) return null;
7133
7134 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7060 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
7135 const lhs = try self.resolveInst(bin_op.lhs);7061 const lhs = try self.resolveInst(bin_op.lhs);
7136 const rhs = try self.resolveInst(bin_op.rhs);7062 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -7144,7 +7070,6 @@ pub const FuncGen = struct {...@@ -7144,7 +7070,6 @@ pub const FuncGen = struct {
7144 }7070 }
71457071
7146 fn airSub(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7072 fn airSub(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7147 if (self.liveness.isUnused(inst)) return null;
7148 self.builder.setFastMath(want_fast_math);7073 self.builder.setFastMath(want_fast_math);
71497074
7150 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7075 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7159,7 +7084,6 @@ pub const FuncGen = struct {...@@ -7159,7 +7084,6 @@ pub const FuncGen = struct {
7159 }7084 }
71607085
7161 fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7086 fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7162 if (self.liveness.isUnused(inst)) return null;
7163 self.builder.setFastMath(want_fast_math);7087 self.builder.setFastMath(want_fast_math);
71647088
7165 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7089 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7170,8 +7094,6 @@ pub const FuncGen = struct {...@@ -7170,8 +7094,6 @@ pub const FuncGen = struct {
7170 }7094 }
71717095
7172 fn airSubSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7096 fn airSubSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7173 if (self.liveness.isUnused(inst)) return null;
7174
7175 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7097 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
7176 const lhs = try self.resolveInst(bin_op.lhs);7098 const lhs = try self.resolveInst(bin_op.lhs);
7177 const rhs = try self.resolveInst(bin_op.rhs);7099 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -7184,7 +7106,6 @@ pub const FuncGen = struct {...@@ -7184,7 +7106,6 @@ pub const FuncGen = struct {
7184 }7106 }
71857107
7186 fn airMul(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7108 fn airMul(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7187 if (self.liveness.isUnused(inst)) return null;
7188 self.builder.setFastMath(want_fast_math);7109 self.builder.setFastMath(want_fast_math);
71897110
7190 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7111 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7199,7 +7120,6 @@ pub const FuncGen = struct {...@@ -7199,7 +7120,6 @@ pub const FuncGen = struct {
7199 }7120 }
72007121
7201 fn airMulWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7122 fn airMulWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7202 if (self.liveness.isUnused(inst)) return null;
7203 self.builder.setFastMath(want_fast_math);7123 self.builder.setFastMath(want_fast_math);
72047124
7205 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7125 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7210,8 +7130,6 @@ pub const FuncGen = struct {...@@ -7210,8 +7130,6 @@ pub const FuncGen = struct {
7210 }7130 }
72117131
7212 fn airMulSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7132 fn airMulSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7213 if (self.liveness.isUnused(inst)) return null;
7214
7215 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7133 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
7216 const lhs = try self.resolveInst(bin_op.lhs);7134 const lhs = try self.resolveInst(bin_op.lhs);
7217 const rhs = try self.resolveInst(bin_op.rhs);7135 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -7224,7 +7142,6 @@ pub const FuncGen = struct {...@@ -7224,7 +7142,6 @@ pub const FuncGen = struct {
7224 }7142 }
72257143
7226 fn airDivFloat(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7144 fn airDivFloat(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7227 if (self.liveness.isUnused(inst)) return null;
7228 self.builder.setFastMath(want_fast_math);7145 self.builder.setFastMath(want_fast_math);
72297146
7230 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7147 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7236,7 +7153,6 @@ pub const FuncGen = struct {...@@ -7236,7 +7153,6 @@ pub const FuncGen = struct {
7236 }7153 }
72377154
7238 fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7155 fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7239 if (self.liveness.isUnused(inst)) return null;
7240 self.builder.setFastMath(want_fast_math);7156 self.builder.setFastMath(want_fast_math);
72417157
7242 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7158 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7254,7 +7170,6 @@ pub const FuncGen = struct {...@@ -7254,7 +7170,6 @@ pub const FuncGen = struct {
7254 }7170 }
72557171
7256 fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7172 fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7257 if (self.liveness.isUnused(inst)) return null;
7258 self.builder.setFastMath(want_fast_math);7173 self.builder.setFastMath(want_fast_math);
72597174
7260 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7175 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7287,7 +7202,6 @@ pub const FuncGen = struct {...@@ -7287,7 +7202,6 @@ pub const FuncGen = struct {
7287 }7202 }
72887203
7289 fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7204 fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7290 if (self.liveness.isUnused(inst)) return null;
7291 self.builder.setFastMath(want_fast_math);7205 self.builder.setFastMath(want_fast_math);
72927206
7293 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7207 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7302,7 +7216,6 @@ pub const FuncGen = struct {...@@ -7302,7 +7216,6 @@ pub const FuncGen = struct {
7302 }7216 }
73037217
7304 fn airRem(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7218 fn airRem(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7305 if (self.liveness.isUnused(inst)) return null;
7306 self.builder.setFastMath(want_fast_math);7219 self.builder.setFastMath(want_fast_math);
73077220
7308 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7221 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7317,7 +7230,6 @@ pub const FuncGen = struct {...@@ -7317,7 +7230,6 @@ pub const FuncGen = struct {
7317 }7230 }
73187231
7319 fn airMod(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {7232 fn airMod(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
7320 if (self.liveness.isUnused(inst)) return null;
7321 self.builder.setFastMath(want_fast_math);7233 self.builder.setFastMath(want_fast_math);
73227234
7323 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7235 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -7347,8 +7259,6 @@ pub const FuncGen = struct {...@@ -7347,8 +7259,6 @@ pub const FuncGen = struct {
7347 }7259 }
73487260
7349 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7261 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7350 if (self.liveness.isUnused(inst)) return null;
7351
7352 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;7262 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
7353 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;7263 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
7354 const base_ptr = try self.resolveInst(bin_op.lhs);7264 const base_ptr = try self.resolveInst(bin_op.lhs);
...@@ -7368,8 +7278,6 @@ pub const FuncGen = struct {...@@ -7368,8 +7278,6 @@ pub const FuncGen = struct {
7368 }7278 }
73697279
7370 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7280 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7371 if (self.liveness.isUnused(inst)) return null;
7372
7373 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;7281 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
7374 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;7282 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
7375 const base_ptr = try self.resolveInst(bin_op.lhs);7283 const base_ptr = try self.resolveInst(bin_op.lhs);
...@@ -7395,9 +7303,6 @@ pub const FuncGen = struct {...@@ -7395,9 +7303,6 @@ pub const FuncGen = struct {
7395 signed_intrinsic: []const u8,7303 signed_intrinsic: []const u8,
7396 unsigned_intrinsic: []const u8,7304 unsigned_intrinsic: []const u8,
7397 ) !?*llvm.Value {7305 ) !?*llvm.Value {
7398 if (self.liveness.isUnused(inst))
7399 return null;
7400
7401 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;7306 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
7402 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;7307 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
74037308
...@@ -7686,8 +7591,6 @@ pub const FuncGen = struct {...@@ -7686,8 +7591,6 @@ pub const FuncGen = struct {
7686 }7591 }
76877592
7688 fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7593 fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7689 if (self.liveness.isUnused(inst)) return null;
7690
7691 const pl_op = self.air.instructions.items(.data)[inst].pl_op;7594 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
7692 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;7595 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
76937596
...@@ -7700,9 +7603,6 @@ pub const FuncGen = struct {...@@ -7700,9 +7603,6 @@ pub const FuncGen = struct {
7700 }7603 }
77017604
7702 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7605 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7703 if (self.liveness.isUnused(inst))
7704 return null;
7705
7706 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;7606 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
7707 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;7607 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
77087608
...@@ -7759,8 +7659,6 @@ pub const FuncGen = struct {...@@ -7759,8 +7659,6 @@ pub const FuncGen = struct {
7759 }7659 }
77607660
7761 fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7661 fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7762 if (self.liveness.isUnused(inst))
7763 return null;
7764 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7662 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
7765 const lhs = try self.resolveInst(bin_op.lhs);7663 const lhs = try self.resolveInst(bin_op.lhs);
7766 const rhs = try self.resolveInst(bin_op.rhs);7664 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -7768,8 +7666,6 @@ pub const FuncGen = struct {...@@ -7768,8 +7666,6 @@ pub const FuncGen = struct {
7768 }7666 }
77697667
7770 fn airOr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7668 fn airOr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7771 if (self.liveness.isUnused(inst))
7772 return null;
7773 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7669 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
7774 const lhs = try self.resolveInst(bin_op.lhs);7670 const lhs = try self.resolveInst(bin_op.lhs);
7775 const rhs = try self.resolveInst(bin_op.rhs);7671 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -7777,8 +7673,6 @@ pub const FuncGen = struct {...@@ -7777,8 +7673,6 @@ pub const FuncGen = struct {
7777 }7673 }
77787674
7779 fn airXor(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7675 fn airXor(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7780 if (self.liveness.isUnused(inst))
7781 return null;
7782 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7676 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
7783 const lhs = try self.resolveInst(bin_op.lhs);7677 const lhs = try self.resolveInst(bin_op.lhs);
7784 const rhs = try self.resolveInst(bin_op.rhs);7678 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -7786,8 +7680,6 @@ pub const FuncGen = struct {...@@ -7786,8 +7680,6 @@ pub const FuncGen = struct {
7786 }7680 }
77877681
7788 fn airShlExact(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7682 fn airShlExact(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7789 if (self.liveness.isUnused(inst)) return null;
7790
7791 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7683 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
77927684
7793 const lhs = try self.resolveInst(bin_op.lhs);7685 const lhs = try self.resolveInst(bin_op.lhs);
...@@ -7809,8 +7701,6 @@ pub const FuncGen = struct {...@@ -7809,8 +7701,6 @@ pub const FuncGen = struct {
7809 }7701 }
78107702
7811 fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7703 fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7812 if (self.liveness.isUnused(inst)) return null;
7813
7814 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7704 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
78157705
7816 const lhs = try self.resolveInst(bin_op.lhs);7706 const lhs = try self.resolveInst(bin_op.lhs);
...@@ -7831,8 +7721,6 @@ pub const FuncGen = struct {...@@ -7831,8 +7721,6 @@ pub const FuncGen = struct {
7831 }7721 }
78327722
7833 fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7723 fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7834 if (self.liveness.isUnused(inst)) return null;
7835
7836 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7724 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
78377725
7838 const lhs = try self.resolveInst(bin_op.lhs);7726 const lhs = try self.resolveInst(bin_op.lhs);
...@@ -7876,8 +7764,6 @@ pub const FuncGen = struct {...@@ -7876,8 +7764,6 @@ pub const FuncGen = struct {
7876 }7764 }
78777765
7878 fn airShr(self: *FuncGen, inst: Air.Inst.Index, is_exact: bool) !?*llvm.Value {7766 fn airShr(self: *FuncGen, inst: Air.Inst.Index, is_exact: bool) !?*llvm.Value {
7879 if (self.liveness.isUnused(inst)) return null;
7880
7881 const bin_op = self.air.instructions.items(.data)[inst].bin_op;7767 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
78827768
7883 const lhs = try self.resolveInst(bin_op.lhs);7769 const lhs = try self.resolveInst(bin_op.lhs);
...@@ -7912,9 +7798,6 @@ pub const FuncGen = struct {...@@ -7912,9 +7798,6 @@ pub const FuncGen = struct {
7912 }7798 }
79137799
7914 fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7800 fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7915 if (self.liveness.isUnused(inst))
7916 return null;
7917
7918 const target = self.dg.module.getTarget();7801 const target = self.dg.module.getTarget();
7919 const ty_op = self.air.instructions.items(.data)[inst].ty_op;7802 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
7920 const dest_ty = self.air.typeOfIndex(inst);7803 const dest_ty = self.air.typeOfIndex(inst);
...@@ -7937,8 +7820,6 @@ pub const FuncGen = struct {...@@ -7937,8 +7820,6 @@ pub const FuncGen = struct {
7937 }7820 }
79387821
7939 fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7822 fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7940 if (self.liveness.isUnused(inst)) return null;
7941
7942 const ty_op = self.air.instructions.items(.data)[inst].ty_op;7823 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
7943 const operand = try self.resolveInst(ty_op.operand);7824 const operand = try self.resolveInst(ty_op.operand);
7944 const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));7825 const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));
...@@ -7946,9 +7827,6 @@ pub const FuncGen = struct {...@@ -7946,9 +7827,6 @@ pub const FuncGen = struct {
7946 }7827 }
79477828
7948 fn airFptrunc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7829 fn airFptrunc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7949 if (self.liveness.isUnused(inst))
7950 return null;
7951
7952 const ty_op = self.air.instructions.items(.data)[inst].ty_op;7830 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
7953 const operand = try self.resolveInst(ty_op.operand);7831 const operand = try self.resolveInst(ty_op.operand);
7954 const operand_ty = self.air.typeOf(ty_op.operand);7832 const operand_ty = self.air.typeOf(ty_op.operand);
...@@ -7978,9 +7856,6 @@ pub const FuncGen = struct {...@@ -7978,9 +7856,6 @@ pub const FuncGen = struct {
7978 }7856 }
79797857
7980 fn airFpext(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7858 fn airFpext(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7981 if (self.liveness.isUnused(inst))
7982 return null;
7983
7984 const ty_op = self.air.instructions.items(.data)[inst].ty_op;7859 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
7985 const operand = try self.resolveInst(ty_op.operand);7860 const operand = try self.resolveInst(ty_op.operand);
7986 const operand_ty = self.air.typeOf(ty_op.operand);7861 const operand_ty = self.air.typeOf(ty_op.operand);
...@@ -8010,9 +7885,6 @@ pub const FuncGen = struct {...@@ -8010,9 +7885,6 @@ pub const FuncGen = struct {
8010 }7885 }
80117886
8012 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7887 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8013 if (self.liveness.isUnused(inst))
8014 return null;
8015
8016 const un_op = self.air.instructions.items(.data)[inst].un_op;7888 const un_op = self.air.instructions.items(.data)[inst].un_op;
8017 const operand = try self.resolveInst(un_op);7889 const operand = try self.resolveInst(un_op);
8018 const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));7890 const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));
...@@ -8020,8 +7892,6 @@ pub const FuncGen = struct {...@@ -8020,8 +7892,6 @@ pub const FuncGen = struct {
8020 }7892 }
80217893
8022 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7894 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8023 if (self.liveness.isUnused(inst)) return null;
8024
8025 const ty_op = self.air.instructions.items(.data)[inst].ty_op;7895 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8026 const operand_ty = self.air.typeOf(ty_op.operand);7896 const operand_ty = self.air.typeOf(ty_op.operand);
8027 const inst_ty = self.air.typeOfIndex(inst);7897 const inst_ty = self.air.typeOfIndex(inst);
...@@ -8137,9 +8007,6 @@ pub const FuncGen = struct {...@@ -8137,9 +8007,6 @@ pub const FuncGen = struct {
8137 }8007 }
81388008
8139 fn airBoolToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8009 fn airBoolToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8140 if (self.liveness.isUnused(inst))
8141 return null;
8142
8143 const un_op = self.air.instructions.items(.data)[inst].un_op;8010 const un_op = self.air.instructions.items(.data)[inst].un_op;
8144 const operand = try self.resolveInst(un_op);8011 const operand = try self.resolveInst(un_op);
8145 return operand;8012 return operand;
...@@ -8189,7 +8056,6 @@ pub const FuncGen = struct {...@@ -8189,7 +8056,6 @@ pub const FuncGen = struct {
8189 }8056 }
81908057
8191 fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8058 fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8192 if (self.liveness.isUnused(inst)) return null;
8193 const ptr_ty = self.air.typeOfIndex(inst);8059 const ptr_ty = self.air.typeOfIndex(inst);
8194 const pointee_type = ptr_ty.childType();8060 const pointee_type = ptr_ty.childType();
8195 if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime()) return self.dg.lowerPtrToVoid(ptr_ty);8061 if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime()) return self.dg.lowerPtrToVoid(ptr_ty);
...@@ -8201,7 +8067,6 @@ pub const FuncGen = struct {...@@ -8201,7 +8067,6 @@ pub const FuncGen = struct {
8201 }8067 }
82028068
8203 fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8069 fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8204 if (self.liveness.isUnused(inst)) return null;
8205 const ptr_ty = self.air.typeOfIndex(inst);8070 const ptr_ty = self.air.typeOfIndex(inst);
8206 const ret_ty = ptr_ty.childType();8071 const ret_ty = ptr_ty.childType();
8207 if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return self.dg.lowerPtrToVoid(ptr_ty);8072 if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return self.dg.lowerPtrToVoid(ptr_ty);
...@@ -8289,8 +8154,6 @@ pub const FuncGen = struct {...@@ -8289,8 +8154,6 @@ pub const FuncGen = struct {
8289 const ptr = try fg.resolveInst(ty_op.operand);8154 const ptr = try fg.resolveInst(ty_op.operand);
82908155
8291 elide: {8156 elide: {
8292 if (ptr_info.@"volatile") break :elide;
8293 if (fg.liveness.isUnused(inst)) return null;
8294 if (!isByRef(ptr_info.pointee_type)) break :elide;8157 if (!isByRef(ptr_info.pointee_type)) break :elide;
8295 if (!canElideLoad(fg, body_tail)) break :elide;8158 if (!canElideLoad(fg, body_tail)) break :elide;
8296 return ptr;8159 return ptr;
...@@ -8314,8 +8177,7 @@ pub const FuncGen = struct {...@@ -8314,8 +8177,7 @@ pub const FuncGen = struct {
8314 }8177 }
83158178
8316 fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8179 fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8317 if (self.liveness.isUnused(inst)) return null;8180 _ = inst;
8318
8319 const llvm_usize = try self.dg.lowerType(Type.usize);8181 const llvm_usize = try self.dg.lowerType(Type.usize);
8320 const target = self.dg.module.getTarget();8182 const target = self.dg.module.getTarget();
8321 if (!target_util.supportsReturnAddress(target)) {8183 if (!target_util.supportsReturnAddress(target)) {
...@@ -8331,8 +8193,7 @@ pub const FuncGen = struct {...@@ -8331,8 +8193,7 @@ pub const FuncGen = struct {
8331 }8193 }
83328194
8333 fn airFrameAddress(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8195 fn airFrameAddress(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8334 if (self.liveness.isUnused(inst)) return null;8196 _ = inst;
8335
8336 const llvm_i32 = self.context.intType(32);8197 const llvm_i32 = self.context.intType(32);
8337 const llvm_fn_name = "llvm.frameaddress.p0";8198 const llvm_fn_name = "llvm.frameaddress.p0";
8338 const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {8199 const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
...@@ -8462,8 +8323,6 @@ pub const FuncGen = struct {...@@ -8462,8 +8323,6 @@ pub const FuncGen = struct {
8462 const ptr = try self.resolveInst(atomic_load.ptr);8323 const ptr = try self.resolveInst(atomic_load.ptr);
8463 const ptr_ty = self.air.typeOf(atomic_load.ptr);8324 const ptr_ty = self.air.typeOf(atomic_load.ptr);
8464 const ptr_info = ptr_ty.ptrInfo().data;8325 const ptr_info = ptr_ty.ptrInfo().data;
8465 if (!ptr_info.@"volatile" and self.liveness.isUnused(inst))
8466 return null;
8467 const elem_ty = ptr_info.pointee_type;8326 const elem_ty = ptr_info.pointee_type;
8468 if (!elem_ty.hasRuntimeBitsIgnoreComptime())8327 if (!elem_ty.hasRuntimeBitsIgnoreComptime())
8469 return null;8328 return null;
...@@ -8577,8 +8436,6 @@ pub const FuncGen = struct {...@@ -8577,8 +8436,6 @@ pub const FuncGen = struct {
8577 }8436 }
85788437
8579 fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8438 fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8580 if (self.liveness.isUnused(inst)) return null;
8581
8582 const ty_op = self.air.instructions.items(.data)[inst].ty_op;8439 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8583 const un_ty = self.air.typeOf(ty_op.operand);8440 const un_ty = self.air.typeOf(ty_op.operand);
8584 const target = self.dg.module.getTarget();8441 const target = self.dg.module.getTarget();
...@@ -8603,8 +8460,6 @@ pub const FuncGen = struct {...@@ -8603,8 +8460,6 @@ pub const FuncGen = struct {
8603 }8460 }
86048461
8605 fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: FloatOp) !?*llvm.Value {8462 fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: FloatOp) !?*llvm.Value {
8606 if (self.liveness.isUnused(inst)) return null;
8607
8608 const un_op = self.air.instructions.items(.data)[inst].un_op;8463 const un_op = self.air.instructions.items(.data)[inst].un_op;
8609 const operand = try self.resolveInst(un_op);8464 const operand = try self.resolveInst(un_op);
8610 const operand_ty = self.air.typeOf(un_op);8465 const operand_ty = self.air.typeOf(un_op);
...@@ -8613,7 +8468,6 @@ pub const FuncGen = struct {...@@ -8613,7 +8468,6 @@ pub const FuncGen = struct {
8613 }8468 }
86148469
8615 fn airNeg(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {8470 fn airNeg(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
8616 if (self.liveness.isUnused(inst)) return null;
8617 self.builder.setFastMath(want_fast_math);8471 self.builder.setFastMath(want_fast_math);
86188472
8619 const un_op = self.air.instructions.items(.data)[inst].un_op;8473 const un_op = self.air.instructions.items(.data)[inst].un_op;
...@@ -8624,8 +8478,6 @@ pub const FuncGen = struct {...@@ -8624,8 +8478,6 @@ pub const FuncGen = struct {
8624 }8478 }
86258479
8626 fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {8480 fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {
8627 if (self.liveness.isUnused(inst)) return null;
8628
8629 const ty_op = self.air.instructions.items(.data)[inst].ty_op;8481 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8630 const operand_ty = self.air.typeOf(ty_op.operand);8482 const operand_ty = self.air.typeOf(ty_op.operand);
8631 const operand = try self.resolveInst(ty_op.operand);8483 const operand = try self.resolveInst(ty_op.operand);
...@@ -8652,8 +8504,6 @@ pub const FuncGen = struct {...@@ -8652,8 +8504,6 @@ pub const FuncGen = struct {
8652 }8504 }
86538505
8654 fn airBitOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {8506 fn airBitOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {
8655 if (self.liveness.isUnused(inst)) return null;
8656
8657 const ty_op = self.air.instructions.items(.data)[inst].ty_op;8507 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8658 const operand_ty = self.air.typeOf(ty_op.operand);8508 const operand_ty = self.air.typeOf(ty_op.operand);
8659 const operand = try self.resolveInst(ty_op.operand);8509 const operand = try self.resolveInst(ty_op.operand);
...@@ -8679,8 +8529,6 @@ pub const FuncGen = struct {...@@ -8679,8 +8529,6 @@ pub const FuncGen = struct {
8679 }8529 }
86808530
8681 fn airByteSwap(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {8531 fn airByteSwap(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {
8682 if (self.liveness.isUnused(inst)) return null;
8683
8684 const target = self.dg.module.getTarget();8532 const target = self.dg.module.getTarget();
8685 const ty_op = self.air.instructions.items(.data)[inst].ty_op;8533 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8686 const operand_ty = self.air.typeOf(ty_op.operand);8534 const operand_ty = self.air.typeOf(ty_op.operand);
...@@ -8734,8 +8582,6 @@ pub const FuncGen = struct {...@@ -8734,8 +8582,6 @@ pub const FuncGen = struct {
8734 }8582 }
87358583
8736 fn airErrorSetHasValue(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8584 fn airErrorSetHasValue(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8737 if (self.liveness.isUnused(inst)) return null;
8738
8739 const ty_op = self.air.instructions.items(.data)[inst].ty_op;8585 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8740 const operand = try self.resolveInst(ty_op.operand);8586 const operand = try self.resolveInst(ty_op.operand);
8741 const error_set_ty = self.air.getRefType(ty_op.ty);8587 const error_set_ty = self.air.getRefType(ty_op.ty);
...@@ -8781,8 +8627,6 @@ pub const FuncGen = struct {...@@ -8781,8 +8627,6 @@ pub const FuncGen = struct {
8781 }8627 }
87828628
8783 fn airIsNamedEnumValue(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8629 fn airIsNamedEnumValue(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8784 if (self.liveness.isUnused(inst)) return null;
8785
8786 const un_op = self.air.instructions.items(.data)[inst].un_op;8630 const un_op = self.air.instructions.items(.data)[inst].un_op;
8787 const operand = try self.resolveInst(un_op);8631 const operand = try self.resolveInst(un_op);
8788 const enum_ty = self.air.typeOf(un_op);8632 const enum_ty = self.air.typeOf(un_op);
...@@ -8862,8 +8706,6 @@ pub const FuncGen = struct {...@@ -8862,8 +8706,6 @@ pub const FuncGen = struct {
8862 }8706 }
88638707
8864 fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8708 fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8865 if (self.liveness.isUnused(inst)) return null;
8866
8867 const un_op = self.air.instructions.items(.data)[inst].un_op;8709 const un_op = self.air.instructions.items(.data)[inst].un_op;
8868 const operand = try self.resolveInst(un_op);8710 const operand = try self.resolveInst(un_op);
8869 const enum_ty = self.air.typeOf(un_op);8711 const enum_ty = self.air.typeOf(un_op);
...@@ -8995,8 +8837,6 @@ pub const FuncGen = struct {...@@ -8995,8 +8837,6 @@ pub const FuncGen = struct {
8995 }8837 }
89968838
8997 fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8839 fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
8998 if (self.liveness.isUnused(inst)) return null;
8999
9000 const un_op = self.air.instructions.items(.data)[inst].un_op;8840 const un_op = self.air.instructions.items(.data)[inst].un_op;
9001 const operand = try self.resolveInst(un_op);8841 const operand = try self.resolveInst(un_op);
9002 const slice_ty = self.air.typeOfIndex(inst);8842 const slice_ty = self.air.typeOfIndex(inst);
...@@ -9011,8 +8851,6 @@ pub const FuncGen = struct {...@@ -9011,8 +8851,6 @@ pub const FuncGen = struct {
9011 }8851 }
90128852
9013 fn airSplat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8853 fn airSplat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9014 if (self.liveness.isUnused(inst)) return null;
9015
9016 const ty_op = self.air.instructions.items(.data)[inst].ty_op;8854 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
9017 const scalar = try self.resolveInst(ty_op.operand);8855 const scalar = try self.resolveInst(ty_op.operand);
9018 const vector_ty = self.air.typeOfIndex(inst);8856 const vector_ty = self.air.typeOfIndex(inst);
...@@ -9021,8 +8859,6 @@ pub const FuncGen = struct {...@@ -9021,8 +8859,6 @@ pub const FuncGen = struct {
9021 }8859 }
90228860
9023 fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8861 fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9024 if (self.liveness.isUnused(inst)) return null;
9025
9026 const pl_op = self.air.instructions.items(.data)[inst].pl_op;8862 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
9027 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;8863 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
9028 const pred = try self.resolveInst(pl_op.operand);8864 const pred = try self.resolveInst(pl_op.operand);
...@@ -9033,8 +8869,6 @@ pub const FuncGen = struct {...@@ -9033,8 +8869,6 @@ pub const FuncGen = struct {
9033 }8869 }
90348870
9035 fn airShuffle(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8871 fn airShuffle(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9036 if (self.liveness.isUnused(inst)) return null;
9037
9038 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;8872 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
9039 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;8873 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
9040 const a = try self.resolveInst(extra.a);8874 const a = try self.resolveInst(extra.a);
...@@ -9134,7 +8968,6 @@ pub const FuncGen = struct {...@@ -9134,7 +8968,6 @@ pub const FuncGen = struct {
9134 }8968 }
91358969
9136 fn airReduce(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {8970 fn airReduce(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
9137 if (self.liveness.isUnused(inst)) return null;
9138 self.builder.setFastMath(want_fast_math);8971 self.builder.setFastMath(want_fast_math);
9139 const target = self.dg.module.getTarget();8972 const target = self.dg.module.getTarget();
91408973
...@@ -9221,8 +9054,6 @@ pub const FuncGen = struct {...@@ -9221,8 +9054,6 @@ pub const FuncGen = struct {
9221 }9054 }
92229055
9223 fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {9056 fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9224 if (self.liveness.isUnused(inst)) return null;
9225
9226 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;9057 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
9227 const result_ty = self.air.typeOfIndex(inst);9058 const result_ty = self.air.typeOfIndex(inst);
9228 const len = @intCast(usize, result_ty.arrayLen());9059 const len = @intCast(usize, result_ty.arrayLen());
...@@ -9360,8 +9191,6 @@ pub const FuncGen = struct {...@@ -9360,8 +9191,6 @@ pub const FuncGen = struct {
9360 }9191 }
93619192
9362 fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {9193 fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9363 if (self.liveness.isUnused(inst)) return null;
9364
9365 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;9194 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
9366 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;9195 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
9367 const union_ty = self.air.typeOfIndex(inst);9196 const union_ty = self.air.typeOfIndex(inst);
...@@ -9566,8 +9395,6 @@ pub const FuncGen = struct {...@@ -9566,8 +9395,6 @@ pub const FuncGen = struct {
9566 }9395 }
95679396
9568 fn airAddrSpaceCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {9397 fn airAddrSpaceCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9569 if (self.liveness.isUnused(inst)) return null;
9570
9571 const ty_op = self.air.instructions.items(.data)[inst].ty_op;9398 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
9572 const inst_ty = self.air.typeOfIndex(inst);9399 const inst_ty = self.air.typeOfIndex(inst);
9573 const operand = try self.resolveInst(ty_op.operand);9400 const operand = try self.resolveInst(ty_op.operand);
...@@ -9592,8 +9419,6 @@ pub const FuncGen = struct {...@@ -9592,8 +9419,6 @@ pub const FuncGen = struct {
9592 }9419 }
95939420
9594 fn airWorkItemId(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {9421 fn airWorkItemId(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9595 if (self.liveness.isUnused(inst)) return null;
9596
9597 const target = self.dg.module.getTarget();9422 const target = self.dg.module.getTarget();
9598 assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures9423 assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures
95999424
...@@ -9603,8 +9428,6 @@ pub const FuncGen = struct {...@@ -9603,8 +9428,6 @@ pub const FuncGen = struct {
9603 }9428 }
96049429
9605 fn airWorkGroupSize(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {9430 fn airWorkGroupSize(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9606 if (self.liveness.isUnused(inst)) return null;
9607
9608 const target = self.dg.module.getTarget();9431 const target = self.dg.module.getTarget();
9609 assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures9432 assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures
96109433
...@@ -9634,8 +9457,6 @@ pub const FuncGen = struct {...@@ -9634,8 +9457,6 @@ pub const FuncGen = struct {
9634 }9457 }
96359458
9636 fn airWorkGroupId(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {9459 fn airWorkGroupId(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
9637 if (self.liveness.isUnused(inst)) return null;
9638
9639 const target = self.dg.module.getTarget();9460 const target = self.dg.module.getTarget();
9640 assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures9461 assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures
96419462
...@@ -9756,8 +9577,6 @@ pub const FuncGen = struct {...@@ -9756,8 +9577,6 @@ pub const FuncGen = struct {
9756 struct_ptr_ty: Type,9577 struct_ptr_ty: Type,
9757 field_index: u32,9578 field_index: u32,
9758 ) !?*llvm.Value {9579 ) !?*llvm.Value {
9759 if (self.liveness.isUnused(inst)) return null;
9760
9761 const target = self.dg.object.target;9580 const target = self.dg.object.target;
9762 const struct_ty = struct_ptr_ty.childType();9581 const struct_ty = struct_ptr_ty.childType();
9763 switch (struct_ty.zigTypeTag()) {9582 switch (struct_ty.zigTypeTag()) {
src/codegen/spirv.zig+5
...@@ -1507,6 +1507,11 @@ pub const DeclGen = struct {...@@ -1507,6 +1507,11 @@ pub const DeclGen = struct {
1507 }1507 }
15081508
1509 fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void {1509 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
1510 const air_tags = self.air.instructions.items(.tag);1515 const air_tags = self.air.instructions.items(.tag);
1511 const maybe_result_id: ?IdRef = switch (air_tags[inst]) {1516 const maybe_result_id: ?IdRef = switch (air_tags[inst]) {
1512 // zig fmt: off1517 // zig fmt: off
src/print_air.zig+74-29
...@@ -8,16 +8,16 @@ const Type = @import("type.zig").Type;...@@ -8,16 +8,16 @@ const Type = @import("type.zig").Type;
8const Air = @import("Air.zig");8const Air = @import("Air.zig");
9const Liveness = @import("Liveness.zig");9const 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 {
12 const instruction_bytes = air.instructions.len *12 const instruction_bytes = air.instructions.len *
13 // Here we don't use @sizeOf(Air.Inst.Data) because it would include13 // Here we don't use @sizeOf(Air.Inst.Data) because it would include
14 // the debug safety tag but we want to measure release size.14 // the debug safety tag but we want to measure release size.
15 (@sizeOf(Air.Inst.Tag) + 8);15 (@sizeOf(Air.Inst.Tag) + 8);
16 const extra_bytes = air.extra.len * @sizeOf(u32);16 const extra_bytes = air.extra.len * @sizeOf(u32);
17 const values_bytes = air.values.len * @sizeOf(Value);17 const values_bytes = air.values.len * @sizeOf(Value);
18 const tomb_bytes = liveness.tomb_bits.len * @sizeOf(usize);18 const tomb_bytes = if (liveness) |l| l.tomb_bits.len * @sizeOf(usize) else 0;
19 const liveness_extra_bytes = liveness.extra.len * @sizeOf(u32);19 const liveness_extra_bytes = if (liveness) |l| l.extra.len * @sizeOf(u32) else 0;
20 const liveness_special_bytes = liveness.special.count() * 8;20 const liveness_special_bytes = if (liveness) |l| l.special.count() * 8 else 0;
21 const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes +21 const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes +
22 values_bytes + @sizeOf(Liveness) + liveness_extra_bytes +22 values_bytes + @sizeOf(Liveness) + liveness_extra_bytes +
23 liveness_special_bytes + tomb_bytes;23 liveness_special_bytes + tomb_bytes;
...@@ -38,8 +38,8 @@ pub fn write(stream: anytype, module: *Module, air: Air, liveness: Liveness) voi...@@ -38,8 +38,8 @@ pub fn write(stream: anytype, module: *Module, air: Air, liveness: Liveness) voi
38 air.extra.len, fmtIntSizeBin(extra_bytes),38 air.extra.len, fmtIntSizeBin(extra_bytes),
39 air.values.len, fmtIntSizeBin(values_bytes),39 air.values.len, fmtIntSizeBin(values_bytes),
40 fmtIntSizeBin(tomb_bytes),40 fmtIntSizeBin(tomb_bytes),
41 liveness.extra.len, fmtIntSizeBin(liveness_extra_bytes),41 if (liveness) |l| l.extra.len else 0, fmtIntSizeBin(liveness_extra_bytes),
42 liveness.special.count(), fmtIntSizeBin(liveness_special_bytes),42 if (liveness) |l| l.special.count() else 0, fmtIntSizeBin(liveness_special_bytes),
43 }) catch return;43 }) catch return;
44 // zig fmt: on44 // zig fmt: on
4545
...@@ -61,7 +61,7 @@ pub fn writeInst(...@@ -61,7 +61,7 @@ pub fn writeInst(
61 inst: Air.Inst.Index,61 inst: Air.Inst.Index,
62 module: *Module,62 module: *Module,
63 air: Air,63 air: Air,
64 liveness: Liveness,64 liveness: ?Liveness,
65) void {65) void {
66 var writer: Writer = .{66 var writer: Writer = .{
67 .module = module,67 .module = module,
...@@ -74,11 +74,11 @@ pub fn writeInst(...@@ -74,11 +74,11 @@ pub fn writeInst(
74 writer.writeInst(stream, inst) catch return;74 writer.writeInst(stream, inst) catch return;
75}75}
7676
77pub fn dump(module: *Module, air: Air, liveness: Liveness) void {77pub fn dump(module: *Module, air: Air, liveness: ?Liveness) void {
78 write(std.io.getStdErr().writer(), module, air, liveness);78 write(std.io.getStdErr().writer(), module, air, liveness);
79}79}
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 {
82 writeInst(std.io.getStdErr().writer(), inst, module, air, liveness);82 writeInst(std.io.getStdErr().writer(), inst, module, air, liveness);
83}83}
8484
...@@ -86,7 +86,7 @@ const Writer = struct {...@@ -86,7 +86,7 @@ const Writer = struct {
86 module: *Module,86 module: *Module,
87 gpa: Allocator,87 gpa: Allocator,
88 air: Air,88 air: Air,
89 liveness: Liveness,89 liveness: ?Liveness,
90 indent: usize,90 indent: usize,
91 skip_body: bool,91 skip_body: bool,
9292
...@@ -109,7 +109,7 @@ const Writer = struct {...@@ -109,7 +109,7 @@ const Writer = struct {
109 try s.writeByteNTimes(' ', w.indent);109 try s.writeByteNTimes(' ', w.indent);
110 try s.print("%{d}{c}= {s}(", .{110 try s.print("%{d}{c}= {s}(", .{
111 inst,111 inst,
112 @as(u8, if (w.liveness.isUnused(inst)) '!' else ' '),112 @as(u8, if (if (w.liveness) |liveness| liveness.isUnused(inst) else false) '!' else ' '),
113 @tagName(tag),113 @tagName(tag),
114 });114 });
115 switch (tag) {115 switch (tag) {
...@@ -389,6 +389,10 @@ const Writer = struct {...@@ -389,6 +389,10 @@ const Writer = struct {
389 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;389 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
390 const extra = w.air.extraData(Air.Block, ty_pl.payload);390 const extra = w.air.extraData(Air.Block, ty_pl.payload);
391 const body = w.air.extra[extra.end..][0..extra.data.body_len];391 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
393 try w.writeType(s, w.air.getRefType(ty_pl.ty));397 try w.writeType(s, w.air.getRefType(ty_pl.ty));
394 if (w.skip_body) return s.writeAll(", ...");398 if (w.skip_body) return s.writeAll(", ...");
...@@ -399,13 +403,16 @@ const Writer = struct {...@@ -399,13 +403,16 @@ const Writer = struct {
399 w.indent = old_indent;403 w.indent = old_indent;
400 try s.writeByteNTimes(' ', w.indent);404 try s.writeByteNTimes(' ', w.indent);
401 try s.writeAll("}");405 try s.writeAll("}");
406
407 for (liveness_block.deaths) |operand| {
408 try s.print(" %{d}!", .{operand});
409 }
402 }410 }
403411
404 fn writeLoop(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {412 fn writeLoop(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
405 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;413 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
406 const extra = w.air.extraData(Air.Block, ty_pl.payload);414 const extra = w.air.extraData(Air.Block, ty_pl.payload);
407 const body = w.air.extra[extra.end..][0..extra.data.body_len];415 const body = w.air.extra[extra.end..][0..extra.data.body_len];
408 const liveness_loop = w.liveness.getLoop(inst);
409416
410 try w.writeType(s, w.air.getRefType(ty_pl.ty));417 try w.writeType(s, w.air.getRefType(ty_pl.ty));
411 if (w.skip_body) return s.writeAll(", ...");418 if (w.skip_body) return s.writeAll(", ...");
...@@ -413,14 +420,6 @@ const Writer = struct {...@@ -413,14 +420,6 @@ const Writer = struct {
413 const old_indent = w.indent;420 const old_indent = w.indent;
414 w.indent += 2;421 w.indent += 2;
415 try w.writeBody(s, body);422 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 }
424 w.indent = old_indent;423 w.indent = old_indent;
425 try s.writeByteNTimes(' ', w.indent);424 try s.writeByteNTimes(' ', w.indent);
426 try s.writeAll("}");425 try s.writeAll("}");
...@@ -746,22 +745,44 @@ const Writer = struct {...@@ -746,22 +745,44 @@ const Writer = struct {
746 const pl_op = w.air.instructions.items(.data)[inst].pl_op;745 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
747 const extra = w.air.extraData(Air.Try, pl_op.payload);746 const extra = w.air.extraData(Air.Try, pl_op.payload);
748 const body = w.air.extra[extra.end..][0..extra.data.body_len];747 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
750 try w.writeOperand(s, inst, 0, pl_op.operand);753 try w.writeOperand(s, inst, 0, pl_op.operand);
751 if (w.skip_body) return s.writeAll(", ...");754 if (w.skip_body) return s.writeAll(", ...");
752 try s.writeAll(", {\n");755 try s.writeAll(", {\n");
753 const old_indent = w.indent;756 const old_indent = w.indent;
754 w.indent += 2;757 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 }
755 try w.writeBody(s, body);767 try w.writeBody(s, body);
768
756 w.indent = old_indent;769 w.indent = old_indent;
757 try s.writeByteNTimes(' ', w.indent);770 try s.writeByteNTimes(' ', w.indent);
758 try s.writeAll("}");771 try s.writeAll("}");
772
773 for (liveness_condbr.then_deaths) |operand| {
774 try s.print(" %{d}!", .{operand});
775 }
759 }776 }
760777
761 fn writeTryPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {778 fn writeTryPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
762 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;779 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
763 const extra = w.air.extraData(Air.TryPtr, ty_pl.payload);780 const extra = w.air.extraData(Air.TryPtr, ty_pl.payload);
764 const body = w.air.extra[extra.end..][0..extra.data.body_len];781 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
766 try w.writeOperand(s, inst, 0, extra.data.ptr);787 try w.writeOperand(s, inst, 0, extra.data.ptr);
767788
...@@ -771,10 +792,24 @@ const Writer = struct {...@@ -771,10 +792,24 @@ const Writer = struct {
771 try s.writeAll(", {\n");792 try s.writeAll(", {\n");
772 const old_indent = w.indent;793 const old_indent = w.indent;
773 w.indent += 2;794 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 }
774 try w.writeBody(s, body);804 try w.writeBody(s, body);
805
775 w.indent = old_indent;806 w.indent = old_indent;
776 try s.writeByteNTimes(' ', w.indent);807 try s.writeByteNTimes(' ', w.indent);
777 try s.writeAll("}");808 try s.writeAll("}");
809
810 for (liveness_condbr.then_deaths) |operand| {
811 try s.print(" %{d}!", .{operand});
812 }
778 }813 }
779814
780 fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {815 fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
...@@ -782,7 +817,10 @@ const Writer = struct {...@@ -782,7 +817,10 @@ const Writer = struct {
782 const extra = w.air.extraData(Air.CondBr, pl_op.payload);817 const extra = w.air.extraData(Air.CondBr, pl_op.payload);
783 const then_body = w.air.extra[extra.end..][0..extra.data.then_body_len];818 const then_body = w.air.extra[extra.end..][0..extra.data.then_body_len];
784 const else_body = w.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];819 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
787 try w.writeOperand(s, inst, 0, pl_op.operand);825 try w.writeOperand(s, inst, 0, pl_op.operand);
788 if (w.skip_body) return s.writeAll(", ...");826 if (w.skip_body) return s.writeAll(", ...");
...@@ -822,8 +860,15 @@ const Writer = struct {...@@ -822,8 +860,15 @@ const Writer = struct {
822 fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {860 fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
823 const pl_op = w.air.instructions.items(.data)[inst].pl_op;861 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
824 const switch_br = w.air.extraData(Air.SwitchBr, pl_op.payload);862 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) catch863 const liveness = if (w.liveness) |liveness|
826 @panic("out of memory");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 };
827 defer w.gpa.free(liveness.deaths);872 defer w.gpa.free(liveness.deaths);
828 var extra_index: usize = switch_br.end;873 var extra_index: usize = switch_br.end;
829 var case_i: u32 = 0;874 var case_i: u32 = 0;
...@@ -913,13 +958,13 @@ const Writer = struct {...@@ -913,13 +958,13 @@ const Writer = struct {
913 operand: Air.Inst.Ref,958 operand: Air.Inst.Ref,
914 ) @TypeOf(s).Error!void {959 ) @TypeOf(s).Error!void {
915 const small_tomb_bits = Liveness.bpi - 1;960 const small_tomb_bits = Liveness.bpi - 1;
916 const dies = if (op_index < small_tomb_bits)961 const dies = if (w.liveness) |liveness| blk: {
917 w.liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index))962 if (op_index < small_tomb_bits)
918 else blk: {963 break :blk liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index));
919 var extra_index = w.liveness.special.get(inst).?;964 var extra_index = liveness.special.get(inst).?;
920 var tomb_op_index: usize = small_tomb_bits;965 var tomb_op_index: usize = small_tomb_bits;
921 while (true) {966 while (true) {
922 const bits = w.liveness.extra[extra_index];967 const bits = liveness.extra[extra_index];
923 if (op_index < tomb_op_index + 31) {968 if (op_index < tomb_op_index + 31) {
924 break :blk @truncate(u1, bits >> @intCast(u5, op_index - tomb_op_index)) != 0;969 break :blk @truncate(u1, bits >> @intCast(u5, op_index - tomb_op_index)) != 0;
925 }970 }
...@@ -927,7 +972,7 @@ const Writer = struct {...@@ -927,7 +972,7 @@ const Writer = struct {
927 extra_index += 1;972 extra_index += 1;
928 tomb_op_index += 31;973 tomb_op_index += 31;
929 }974 }
930 };975 } else false;
931 return w.writeInstRef(s, operand, dies);976 return w.writeInstRef(s, operand, dies);
932 }977 }
933978
src/register_manager.zig+4
...@@ -95,6 +95,10 @@ pub fn RegisterManager(...@@ -95,6 +95,10 @@ pub fn RegisterManager(
95 return indexOfReg(tracked_registers, reg);95 return indexOfReg(tracked_registers, reg);
96 }96 }
9797
98 pub fn regAtTrackedIndex(index: RegisterBitSet.ShiftInt) Register {
99 return tracked_registers[index];
100 }
101
98 /// Returns true when this register is not tracked102 /// Returns true when this register is not tracked
99 pub fn isRegFree(self: Self, reg: Register) bool {103 pub fn isRegFree(self: Self, reg: Register) bool {
100 const index = indexOfRegIntoTracked(reg) orelse return true;104 const index = indexOfRegIntoTracked(reg) orelse return true;
test/behavior/for.zig-1
...@@ -274,7 +274,6 @@ test "two counters" {...@@ -274,7 +274,6 @@ test "two counters" {
274test "1-based counter and ptr to array" {274test "1-based counter and ptr to array" {
275 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO275 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
276 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO276 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
277 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
278277
279 var ok: usize = 0;278 var ok: usize = 0;
280279
test/behavior/var_args.zig+26
...@@ -215,3 +215,29 @@ test "copy VaList" {...@@ -215,3 +215,29 @@ test "copy VaList" {
215 try std.testing.expectEqual(@as(c_int, 3), S.add(1, @as(c_int, 1)));215 try std.testing.expectEqual(@as(c_int, 3), S.add(1, @as(c_int, 1)));
216 try std.testing.expectEqual(@as(c_int, 9), S.add(2, @as(c_int, 1), @as(c_int, 2)));216 try std.testing.expectEqual(@as(c_int, 9), S.add(2, @as(c_int, 1), @as(c_int, 2)));
217}217}
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 {...@@ -5,5 +5,5 @@ pub fn main() void {
5}5}
66
7// run7// run
8// target=x86_64-linux,x86_64-macos,aarch64-linux,aarch64-macos8// target=x86_64-linux,x86_64-macos
9//9//
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 {...@@ -33,6 +33,8 @@ fn assert(ok: bool) void {
33 if (!ok) unreachable;33 if (!ok) unreachable;
34}34}
3535
36// TODO: enable this for native backend
37
36// run38// run
39// backend=llvm
37// target=aarch64-linux,aarch64-macos40// 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 {...@@ -20,5 +20,5 @@ fn assert(ok: bool) void {
20}20}
2121
22// run22// run
23// target=x86_64-linux,x86_64-macos,arm-linux,wasm32-wasi23// target=x86_64-linux,x86_64-macos,wasm32-wasi
24//24//
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: {...@@ -94,14 +94,15 @@ const test_targets = blk: {
94 .use_llvm = false,94 .use_llvm = false,
95 .use_lld = false,95 .use_lld = false,
96 },96 },
97 .{97 // Doesn't support new liveness
98 .target = .{98 //.{
99 .cpu_arch = .aarch64,99 // .target = .{
100 .os_tag = .linux,100 // .cpu_arch = .aarch64,
101 },101 // .os_tag = .linux,
102 .use_llvm = false,102 // },
103 .use_lld = false,103 // .use_llvm = false,
104 },104 // .use_lld = false,
105 //},
105 .{106 .{
106 .target = .{107 .target = .{
107 .cpu_arch = .wasm32,108 .cpu_arch = .wasm32,
...@@ -128,15 +129,16 @@ const test_targets = blk: {...@@ -128,15 +129,16 @@ const test_targets = blk: {
128 // .use_llvm = false,129 // .use_llvm = false,
129 // .use_lld = false,130 // .use_lld = false,
130 //},131 //},
131 .{132 // Doesn't support new liveness
132 .target = .{133 //.{
133 .cpu_arch = .aarch64,134 // .target = .{
134 .os_tag = .macos,135 // .cpu_arch = .aarch64,
135 .abi = .none,136 // .os_tag = .macos,
136 },137 // .abi = .none,
137 .use_llvm = false,138 // },
138 .use_lld = false,139 // .use_llvm = false,
139 },140 // .use_lld = false,
141 //},
140 .{142 .{
141 .target = .{143 .target = .{
142 .cpu_arch = .x86_64,144 .cpu_arch = .x86_64,