authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-08-22 16:36:47+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-10-16 15:54:16+02:00
logb9b20b14ea5886aa862927daa7164073aab56132
treee2b60819d8beb2e3fc7c6dcf6f7d65d1ff37b093
parent99c3578f697fe0b0151049f74208b86244b4d171
signaturelock-open Commit is signed but in an unrecognized format.

wasm: use liveness analysis for locals

This hooks reusal of locals into liveness analysis. Meaning that when an operand dies, and is a local, it will automatically be freed so it can be re-used when a new local is required. The result of this, is a lower allocation required for locals. Having less locals means smaller binary size, as well as faster compilation speed when loaded by the runtime.

1 files changed, 814 insertions(+), 626 deletions(-)

src/arch/wasm/CodeGen.zig+814-626
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
3const ArrayList = std.ArrayList;4const ArrayList = std.ArrayList;
4const assert = std.debug.assert;5const assert = std.debug.assert;
...@@ -91,11 +92,14 @@ const WValue = union(enum) {...@@ -91,11 +92,14 @@ const WValue = union(enum) {
9192
92 /// Marks a local as no longer being referenced and essentially allows93 /// Marks a local as no longer being referenced and essentially allows
93 /// us to re-use it somewhere else within the function.94 /// us to re-use it somewhere else within the function.
94 /// The valtype of the local is deducted by using the index of the given.95 /// The valtype of the local is deducted by using the index of the given `WValue`.
95 fn free(value: *WValue, gen: *Self) void {96 fn free(value: *WValue, gen: *Self) void {
96 if (value.* != .local) return;97 if (value.* != .local) return;
97 const local_value = value.local;98 const local_value = value.local;
98 const index = local_value - gen.args.len - @boolToInt(gen.return_value != .none);99 const reserved = gen.args.len + @boolToInt(gen.return_value != .none) + 2; // 2 for stack locals
100 if (local_value < reserved) return; // reserved locals may never be re-used.
101
102 const index = local_value - reserved;
99 const valtype = @intToEnum(wasm.Valtype, gen.locals.items[index]);103 const valtype = @intToEnum(wasm.Valtype, gen.locals.items[index]);
100 switch (valtype) {104 switch (valtype) {
101 .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead105 .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead
...@@ -650,6 +654,13 @@ free_locals_f32: std.ArrayListUnmanaged(u32) = .{},...@@ -650,6 +654,13 @@ free_locals_f32: std.ArrayListUnmanaged(u32) = .{},
650/// It is illegal to store a non-i32 valtype in this list.654/// It is illegal to store a non-i32 valtype in this list.
651free_locals_f64: std.ArrayListUnmanaged(u32) = .{},655free_locals_f64: std.ArrayListUnmanaged(u32) = .{},
652656
657/// When in debug mode, this tracks if no `finishAir` was missed.
658/// Forgetting to call `finishAir` will cause the result to not be
659/// stored in our `values` map and therefore cause bugs.
660air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init,
661
662const bookkeeping_init = if (builtin.mode == .Debug) @as(usize, 0) else {};
663
653const InnerError = error{664const InnerError = error{
654 OutOfMemory,665 OutOfMemory,
655 /// An error occurred when trying to lower AIR to MIR.666 /// An error occurred when trying to lower AIR to MIR.
...@@ -711,6 +722,65 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {...@@ -711,6 +722,65 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
711 return result;722 return result;
712}723}
713724
725fn finishAir(self: *Self, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) void {
726 assert(operands.len <= Liveness.bpi - 1);
727 var tomb_bits = self.liveness.getTombBits(inst);
728 for (operands) |operand| {
729 const dies = @truncate(u1, tomb_bits) != 0;
730 tomb_bits >>= 1;
731 if (!dies) continue;
732 processDeath(self, operand);
733 }
734
735 // results of `none` can never be referenced.
736 if (result != .none) {
737 assert(result != .stack); // it's illegal to store a stack value as we cannot track its position
738 self.values.putAssumeCapacityNoClobber(Air.indexToRef(inst), result);
739 }
740
741 if (builtin.mode == .Debug) {
742 self.air_bookkeeping += 1;
743 }
744}
745
746const BigTomb = struct {
747 gen: *Self,
748 inst: Air.Inst.Index,
749 lbt: Liveness.BigTomb,
750
751 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
752 _ = Air.refToIndex(op_ref) orelse return; // constants do not have to be freed regardless
753 const dies = bt.lbt.feed();
754 if (!dies) return;
755 processDeath(bt.gen, op_ref);
756 }
757
758 fn finishAir(bt: *BigTomb, result: WValue) void {
759 assert(result != .stack);
760 if (result != .none) {
761 bt.gen.values.putAssumeCapacityNoClobber(Air.indexToRef(bt.inst), result);
762 }
763
764 bt.gen.air_bookkeeping += 1;
765 }
766};
767
768fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
769 try self.values.ensureUnusedCapacity(self.gpa, @intCast(u32, operand_count + 1));
770 return BigTomb{
771 .gen = self,
772 .inst = inst,
773 .lbt = self.liveness.iterateBigTomb(inst),
774 };
775}
776
777fn processDeath(self: *Self, ref: Air.Inst.Ref) void {
778 const inst = Air.refToIndex(ref) orelse return;
779 if (self.air.instructions.items(.tag)[inst] == .constant) return;
780 var value = self.values.get(ref) orelse return;
781 value.free(self);
782}
783
714/// Appends a MIR instruction and returns its index within the list of instructions784/// Appends a MIR instruction and returns its index within the list of instructions
715fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!void {785fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!void {
716 try self.mir_instructions.append(self.gpa, inst);786 try self.mir_instructions.append(self.gpa, inst);
...@@ -1502,7 +1572,7 @@ fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum...@@ -1502,7 +1572,7 @@ fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum
1502 return result_ptr;1572 return result_ptr;
1503}1573}
15041574
1505fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {1575fn genInst(self: *Self, inst: Air.Inst.Index) InnerError!void {
1506 const air_tags = self.air.instructions.items(.tag);1576 const air_tags = self.air.instructions.items(.tag);
1507 return switch (air_tags[inst]) {1577 return switch (air_tags[inst]) {
1508 .constant => unreachable,1578 .constant => unreachable,
...@@ -1581,7 +1651,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1581,7 +1651,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1581 .dbg_inline_end,1651 .dbg_inline_end,
1582 .dbg_block_begin,1652 .dbg_block_begin,
1583 .dbg_block_end,1653 .dbg_block_end,
1584 => WValue.none,1654 => self.finishAir(inst, .none, &.{}),
15851655
1586 .dbg_var_ptr => self.airDbgVar(inst, true),1656 .dbg_var_ptr => self.airDbgVar(inst, true),
1587 .dbg_var_val => self.airDbgVar(inst, false),1657 .dbg_var_val => self.airDbgVar(inst, false),
...@@ -1730,15 +1800,24 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1730,15 +1800,24 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
17301800
1731fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {1801fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1732 for (body) |inst| {1802 for (body) |inst| {
1733 const result = try self.genInst(inst);1803 const old_bookkeeping_value = self.air_bookkeeping;
1734 if (result != .none) {1804 try self.values.ensureUnusedCapacity(self.gpa, Liveness.bpi);
1735 assert(result != .stack); // not allowed to store stack values as we cannot keep track of where they are on the stack1805 try self.genInst(inst);
1736 try self.values.putNoClobber(self.gpa, Air.indexToRef(inst), result);1806
1807 if (builtin.mode == .Debug and self.air_bookkeeping < old_bookkeeping_value + 1) {
1808 std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{}')", .{
1809 inst,
1810 self.air.instructions.items(.tag)[inst],
1811 });
1737 }1812 }
1813 // if (result != .none) {
1814 // assert(result != .stack); // not allowed to store stack values as we cannot keep track of where they are on the stack
1815 // try self.values.putNoClobber(self.gpa, Air.indexToRef(inst), result);
1816 // }
1738 }1817 }
1739}1818}
17401819
1741fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1820fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!void {
1742 const un_op = self.air.instructions.items(.data)[inst].un_op;1821 const un_op = self.air.instructions.items(.data)[inst].un_op;
1743 const operand = try self.resolveInst(un_op);1822 const operand = try self.resolveInst(un_op);
1744 const fn_info = self.decl.ty.fnInfo();1823 const fn_info = self.decl.ty.fnInfo();
...@@ -1776,25 +1855,30 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1776,25 +1855,30 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1776 }1855 }
1777 try self.restoreStackPointer();1856 try self.restoreStackPointer();
1778 try self.addTag(.@"return");1857 try self.addTag(.@"return");
1779 return WValue{ .none = {} };1858
1859 self.finishAir(inst, .none, &.{un_op});
1780}1860}
17811861
1782fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1862fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
1783 const child_type = self.air.typeOfIndex(inst).childType();1863 const child_type = self.air.typeOfIndex(inst).childType();
17841864
1785 if (!child_type.isFnOrHasRuntimeBitsIgnoreComptime()) {1865 var result = result: {
1786 return self.allocStack(Type.usize); // create pointer to void1866 if (!child_type.isFnOrHasRuntimeBitsIgnoreComptime()) {
1787 }1867 break :result try self.allocStack(Type.usize); // create pointer to void
1868 }
17881869
1789 const fn_info = self.decl.ty.fnInfo();1870 const fn_info = self.decl.ty.fnInfo();
1790 if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {1871 if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
1791 return self.return_value;1872 break :result self.return_value;
1792 }1873 }
17931874
1794 return self.allocStackPtr(inst);1875 break :result try self.allocStackPtr(inst);
1876 };
1877
1878 self.finishAir(inst, result, &.{});
1795}1879}
17961880
1797fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1881fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
1798 const un_op = self.air.instructions.items(.data)[inst].un_op;1882 const un_op = self.air.instructions.items(.data)[inst].un_op;
1799 const operand = try self.resolveInst(un_op);1883 const operand = try self.resolveInst(un_op);
1800 const ret_ty = self.air.typeOf(un_op).childType();1884 const ret_ty = self.air.typeOf(un_op).childType();
...@@ -1802,7 +1886,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1802,7 +1886,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1802 if (ret_ty.isError()) {1886 if (ret_ty.isError()) {
1803 try self.addImm32(0);1887 try self.addImm32(0);
1804 } else {1888 } else {
1805 return WValue.none;1889 return self.finishAir(inst, .none, &.{});
1806 }1890 }
1807 }1891 }
18081892
...@@ -1814,14 +1898,14 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1814,14 +1898,14 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
18141898
1815 try self.restoreStackPointer();1899 try self.restoreStackPointer();
1816 try self.addTag(.@"return");1900 try self.addTag(.@"return");
1817 return .none;1901 return self.finishAir(inst, .none, &.{});
1818}1902}
18191903
1820fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) InnerError!WValue {1904fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) InnerError!void {
1821 if (modifier == .always_tail) return self.fail("TODO implement tail calls for wasm", .{});1905 if (modifier == .always_tail) return self.fail("TODO implement tail calls for wasm", .{});
1822 const pl_op = self.air.instructions.items(.data)[inst].pl_op;1906 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1823 const extra = self.air.extraData(Air.Call, pl_op.payload);1907 const extra = self.air.extraData(Air.Call, pl_op.payload);
1824 const args = self.air.extra[extra.end..][0..extra.data.args_len];1908 const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
1825 const ty = self.air.typeOf(pl_op.operand);1909 const ty = self.air.typeOf(pl_op.operand);
18261910
1827 const fn_ty = switch (ty.zigTypeTag()) {1911 const fn_ty = switch (ty.zigTypeTag()) {
...@@ -1865,10 +1949,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -1865,10 +1949,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
1865 } else WValue{ .none = {} };1949 } else WValue{ .none = {} };
18661950
1867 for (args) |arg| {1951 for (args) |arg| {
1868 const arg_ref = @intToEnum(Air.Inst.Ref, arg);1952 const arg_val = try self.resolveInst(arg);
1869 const arg_val = try self.resolveInst(arg_ref);
18701953
1871 const arg_ty = self.air.typeOf(arg_ref);1954 const arg_ty = self.air.typeOf(arg);
1872 if (!arg_ty.hasRuntimeBitsIgnoreComptime()) continue;1955 if (!arg_ty.hasRuntimeBitsIgnoreComptime()) continue;
18731956
1874 try self.lowerArg(fn_ty.fnInfo().cc, arg_ty, arg_val);1957 try self.lowerArg(fn_ty.fnInfo().cc, arg_ty, arg_val);
...@@ -1890,33 +1973,41 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -1890,33 +1973,41 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
1890 try self.addLabel(.call_indirect, fn_type_index);1973 try self.addLabel(.call_indirect, fn_type_index);
1891 }1974 }
18921975
1893 if (self.liveness.isUnused(inst) or (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError())) {1976 const result_value = result_value: {
1894 return WValue.none;1977 if (self.liveness.isUnused(inst) or (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError())) {
1895 } else if (ret_ty.isNoReturn()) {1978 break :result_value WValue{ .none = {} };
1896 try self.addTag(.@"unreachable");1979 } else if (ret_ty.isNoReturn()) {
1897 return WValue.none;1980 try self.addTag(.@"unreachable");
1898 } else if (first_param_sret) {1981 break :result_value WValue{ .none = {} };
1899 return sret;1982 } else if (first_param_sret) {
1900 // TODO: Make this less fragile and optimize1983 break :result_value sret;
1901 } else if (fn_ty.fnInfo().cc == .C and ret_ty.zigTypeTag() == .Struct or ret_ty.zigTypeTag() == .Union) {1984 // TODO: Make this less fragile and optimize
1902 const result_local = try self.allocLocal(ret_ty);1985 } else if (fn_ty.fnInfo().cc == .C and ret_ty.zigTypeTag() == .Struct or ret_ty.zigTypeTag() == .Union) {
1903 try self.addLabel(.local_set, result_local.local);1986 const result_local = try self.allocLocal(ret_ty);
1904 const scalar_type = abi.scalarType(ret_ty, self.target);1987 try self.addLabel(.local_set, result_local.local);
1905 const result = try self.allocStack(scalar_type);1988 const scalar_type = abi.scalarType(ret_ty, self.target);
1906 try self.store(result, result_local, scalar_type, 0);1989 const result = try self.allocStack(scalar_type);
1907 return result;1990 try self.store(result, result_local, scalar_type, 0);
1908 } else {1991 break :result_value result;
1909 const result_local = try self.allocLocal(ret_ty);1992 } else {
1910 try self.addLabel(.local_set, result_local.local);1993 const result_local = try self.allocLocal(ret_ty);
1911 return result_local;1994 try self.addLabel(.local_set, result_local.local);
1912 }1995 break :result_value result_local;
1996 }
1997 };
1998
1999 var bt = try self.iterateBigTomb(inst, 1 + args.len);
2000 bt.feed(pl_op.operand);
2001 for (args) |arg| bt.feed(arg);
2002 return bt.finishAir(result_value);
1913}2003}
19142004
1915fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2005fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!void {
1916 return self.allocStackPtr(inst);2006 const value = try self.allocStackPtr(inst);
2007 self.finishAir(inst, value, &.{});
1917}2008}
19182009
1919fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2010fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!void {
1920 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2011 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
19212012
1922 const lhs = try self.resolveInst(bin_op.lhs);2013 const lhs = try self.resolveInst(bin_op.lhs);
...@@ -1924,7 +2015,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1924,7 +2015,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1924 const ty = self.air.typeOf(bin_op.lhs).childType();2015 const ty = self.air.typeOf(bin_op.lhs).childType();
19252016
1926 try self.store(lhs, rhs, ty, 0);2017 try self.store(lhs, rhs, ty, 0);
1927 return WValue{ .none = {} };2018 self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
1928}2019}
19292020
1930fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {2021fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
...@@ -2007,21 +2098,24 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -2007,21 +2098,24 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
2007 );2098 );
2008}2099}
20092100
2010fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2101fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
2011 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2102 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2012 const operand = try self.resolveInst(ty_op.operand);2103 const operand = try self.resolveInst(ty_op.operand);
2013 const ty = self.air.getRefType(ty_op.ty);2104 const ty = self.air.getRefType(ty_op.ty);
20142105
2015 if (!ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };2106 if (!ty.hasRuntimeBitsIgnoreComptime()) return self.finishAir(inst, .none, &.{ty_op.operand});
20162107
2017 if (isByRef(ty, self.target)) {2108 const result = result: {
2018 const new_local = try self.allocStack(ty);2109 if (isByRef(ty, self.target)) {
2019 try self.store(new_local, operand, ty, 0);2110 const new_local = try self.allocStack(ty);
2020 return new_local;2111 try self.store(new_local, operand, ty, 0);
2021 }2112 break :result new_local;
2113 }
20222114
2023 const stack_loaded = try self.load(operand, ty, 0);2115 const stack_loaded = try self.load(operand, ty, 0);
2024 return stack_loaded.toLocal(self, ty);2116 break :result try stack_loaded.toLocal(self, ty);
2117 };
2118 self.finishAir(inst, result, &.{ty_op.operand});
2025}2119}
20262120
2027/// Loads an operand from the linear memory section.2121/// Loads an operand from the linear memory section.
...@@ -2046,7 +2140,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {...@@ -2046,7 +2140,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
2046 return WValue{ .stack = {} };2140 return WValue{ .stack = {} };
2047}2141}
20482142
2049fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2143fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
2050 const arg_index = self.arg_index;2144 const arg_index = self.arg_index;
2051 const arg = self.args[arg_index];2145 const arg = self.args[arg_index];
2052 const cc = self.decl.ty.fnInfo().cc;2146 const cc = self.decl.ty.fnInfo().cc;
...@@ -2071,7 +2165,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2071,7 +2165,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2071 const result = try self.allocStack(arg_ty);2165 const result = try self.allocStack(arg_ty);
2072 try self.store(result, arg, Type.u64, 0);2166 try self.store(result, arg, Type.u64, 0);
2073 try self.store(result, self.args[arg_index + 1], Type.u64, 8);2167 try self.store(result, self.args[arg_index + 1], Type.u64, 8);
2074 return result;2168 return self.finishAir(inst, arg, &.{});
2075 }2169 }
2076 } else {2170 } else {
2077 self.arg_index += 1;2171 self.arg_index += 1;
...@@ -2102,19 +2196,19 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2102,19 +2196,19 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2102 },2196 },
2103 else => {},2197 else => {},
2104 }2198 }
2105 return arg;
2106}
21072199
2108fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {2200 self.finishAir(inst, arg, &.{});
2109 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };2201}
21102202
2203fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
2111 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2204 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2205 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
2112 const lhs = try self.resolveInst(bin_op.lhs);2206 const lhs = try self.resolveInst(bin_op.lhs);
2113 const rhs = try self.resolveInst(bin_op.rhs);2207 const rhs = try self.resolveInst(bin_op.rhs);
2114 const ty = self.air.typeOf(bin_op.lhs);2208 const ty = self.air.typeOf(bin_op.lhs);
21152209
2116 const stack_value = try self.binOp(lhs, rhs, ty, op);2210 const stack_value = try self.binOp(lhs, rhs, ty, op);
2117 return stack_value.toLocal(self, ty);2211 self.finishAir(inst, try stack_value.toLocal(self, ty), &.{ bin_op.lhs, bin_op.rhs });
2118}2212}
21192213
2120/// Performs a binary operation on the given `WValue`'s2214/// Performs a binary operation on the given `WValue`'s
...@@ -2195,17 +2289,20 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr...@@ -2195,17 +2289,20 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr
2195 return result;2289 return result;
2196}2290}
21972291
2198fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {2292fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
2199 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2293 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2294 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
2295
2200 const lhs = try self.resolveInst(bin_op.lhs);2296 const lhs = try self.resolveInst(bin_op.lhs);
2201 const rhs = try self.resolveInst(bin_op.rhs);2297 const rhs = try self.resolveInst(bin_op.rhs);
2202
2203 const ty = self.air.typeOf(bin_op.lhs);2298 const ty = self.air.typeOf(bin_op.lhs);
2299
2204 if (ty.zigTypeTag() == .Vector) {2300 if (ty.zigTypeTag() == .Vector) {
2205 return self.fail("TODO: Implement wrapping arithmetic for vectors", .{});2301 return self.fail("TODO: Implement wrapping arithmetic for vectors", .{});
2206 }2302 }
22072303
2208 return (try self.wrapBinOp(lhs, rhs, ty, op)).toLocal(self, ty);2304 const result = try (try self.wrapBinOp(lhs, rhs, ty, op)).toLocal(self, ty);
2305 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
2209}2306}
22102307
2211/// Performs a wrapping binary operation.2308/// Performs a wrapping binary operation.
...@@ -2582,7 +2679,7 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {...@@ -2582,7 +2679,7 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
2582 }2679 }
2583}2680}
25842681
2585fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2682fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!void {
2586 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2683 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2587 const block_ty = self.air.getRefType(ty_pl.ty);2684 const block_ty = self.air.getRefType(ty_pl.ty);
2588 const wasm_block_ty = genBlockType(block_ty, self.target);2685 const wasm_block_ty = genBlockType(block_ty, self.target);
...@@ -2592,7 +2689,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2592,7 +2689,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2592 // if wasm_block_ty is non-empty, we create a register to store the temporary value2689 // if wasm_block_ty is non-empty, we create a register to store the temporary value
2593 const block_result: WValue = if (wasm_block_ty != wasm.block_empty) blk: {2690 const block_result: WValue = if (wasm_block_ty != wasm.block_empty) blk: {
2594 const ty: Type = if (isByRef(block_ty, self.target)) Type.u32 else block_ty;2691 const ty: Type = if (isByRef(block_ty, self.target)) Type.u32 else block_ty;
2595 break :blk try self.allocLocal(ty);2692 break :blk try self.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten
2596 } else WValue.none;2693 } else WValue.none;
25972694
2598 try self.startBlock(.block, wasm.block_empty);2695 try self.startBlock(.block, wasm.block_empty);
...@@ -2605,7 +2702,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2605,7 +2702,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2605 try self.genBody(body);2702 try self.genBody(body);
2606 try self.endBlock();2703 try self.endBlock();
26072704
2608 return block_result;2705 self.finishAir(inst, block_result, &.{});
2609}2706}
26102707
2611/// appends a new wasm block to the code section and increases the `block_depth` by 12708/// appends a new wasm block to the code section and increases the `block_depth` by 1
...@@ -2623,7 +2720,7 @@ fn endBlock(self: *Self) !void {...@@ -2623,7 +2720,7 @@ fn endBlock(self: *Self) !void {
2623 self.block_depth -= 1;2720 self.block_depth -= 1;
2624}2721}
26252722
2626fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2723fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!void {
2627 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2724 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2628 const loop = self.air.extraData(Air.Block, ty_pl.payload);2725 const loop = self.air.extraData(Air.Block, ty_pl.payload);
2629 const body = self.air.extra[loop.end..][0..loop.data.body_len];2726 const body = self.air.extra[loop.end..][0..loop.data.body_len];
...@@ -2637,16 +2734,16 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2637,16 +2734,16 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2637 try self.addLabel(.br, 0);2734 try self.addLabel(.br, 0);
2638 try self.endBlock();2735 try self.endBlock();
26392736
2640 return .none;2737 self.finishAir(inst, .none, &.{});
2641}2738}
26422739
2643fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2740fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
2644 const pl_op = self.air.instructions.items(.data)[inst].pl_op;2741 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2645 const condition = try self.resolveInst(pl_op.operand);2742 const condition = try self.resolveInst(pl_op.operand);
2646 const extra = self.air.extraData(Air.CondBr, pl_op.payload);2743 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
2647 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];2744 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
2648 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];2745 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
2649 // TODO: Handle death instructions for then and else body2746 // const liveness_condbr = self.liveness.getCondBr(inst);
26502747
2651 // result type is always noreturn, so use `block_empty` as type.2748 // result type is always noreturn, so use `block_empty` as type.
2652 try self.startBlock(.block, wasm.block_empty);2749 try self.startBlock(.block, wasm.block_empty);
...@@ -2664,15 +2761,18 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2664,15 +2761,18 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2664 // Outer block that matches the condition2761 // Outer block that matches the condition
2665 try self.genBody(then_body);2762 try self.genBody(then_body);
26662763
2667 return .none;2764 self.finishAir(inst, .none, &.{});
2668}2765}
26692766
2670fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {2767fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {
2671 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2768 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2769 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
2770
2672 const lhs = try self.resolveInst(bin_op.lhs);2771 const lhs = try self.resolveInst(bin_op.lhs);
2673 const rhs = try self.resolveInst(bin_op.rhs);2772 const rhs = try self.resolveInst(bin_op.rhs);
2674 const operand_ty = self.air.typeOf(bin_op.lhs);2773 const operand_ty = self.air.typeOf(bin_op.lhs);
2675 return (try self.cmp(lhs, rhs, operand_ty, op)).toLocal(self, Type.u32); // comparison result is always 32 bits2774 const result = try (try self.cmp(lhs, rhs, operand_ty, op)).toLocal(self, Type.u32); // comparison result is always 32 bits
2775 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
2676}2776}
26772777
2678/// Compares two operands.2778/// Compares two operands.
...@@ -2746,14 +2846,12 @@ fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperato...@@ -2746,14 +2846,12 @@ fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperato
2746 return WValue{ .stack = {} };2846 return WValue{ .stack = {} };
2747}2847}
27482848
2749fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2849fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!void {
2750 _ = inst;2850 _ = inst;
2751 return self.fail("TODO implement airCmpVector for wasm", .{});2851 return self.fail("TODO implement airCmpVector for wasm", .{});
2752}2852}
27532853
2754fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2854fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
2755 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2756
2757 const un_op = self.air.instructions.items(.data)[inst].un_op;2855 const un_op = self.air.instructions.items(.data)[inst].un_op;
2758 const operand = try self.resolveInst(un_op);2856 const operand = try self.resolveInst(un_op);
27592857
...@@ -2761,7 +2859,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2761,7 +2859,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2761 return self.fail("TODO implement airCmpLtErrorsLen for wasm", .{});2859 return self.fail("TODO implement airCmpLtErrorsLen for wasm", .{});
2762}2860}
27632861
2764fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2862fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
2765 const br = self.air.instructions.items(.data)[inst].br;2863 const br = self.air.instructions.items(.data)[inst].br;
2766 const block = self.blocks.get(br.block_inst).?;2864 const block = self.blocks.get(br.block_inst).?;
27672865
...@@ -2780,76 +2878,82 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2780,76 +2878,82 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2780 const idx: u32 = self.block_depth - block.label;2878 const idx: u32 = self.block_depth - block.label;
2781 try self.addLabel(.br, idx);2879 try self.addLabel(.br, idx);
27822880
2783 return .none;2881 self.finishAir(inst, .none, &.{br.operand});
2784}2882}
27852883
2786fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2884fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!void {
2787 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2885 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2886 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
27882887
2789 const operand = try self.resolveInst(ty_op.operand);2888 const operand = try self.resolveInst(ty_op.operand);
2790 const operand_ty = self.air.typeOf(ty_op.operand);2889 const operand_ty = self.air.typeOf(ty_op.operand);
27912890
2792 if (operand_ty.zigTypeTag() == .Bool) {2891 const result = result: {
2793 try self.emitWValue(operand);2892 if (operand_ty.zigTypeTag() == .Bool) {
2794 try self.addTag(.i32_eqz);2893 try self.emitWValue(operand);
2795 const not_tmp = try self.allocLocal(operand_ty);2894 try self.addTag(.i32_eqz);
2796 try self.addLabel(.local_set, not_tmp.local);2895 const not_tmp = try self.allocLocal(operand_ty);
2797 return not_tmp;2896 try self.addLabel(.local_set, not_tmp.local);
2798 } else {2897 break :result not_tmp;
2799 const operand_bits = operand_ty.intInfo(self.target).bits;2898 } else {
2800 const wasm_bits = toWasmBits(operand_bits) orelse {2899 const operand_bits = operand_ty.intInfo(self.target).bits;
2801 return self.fail("TODO: Implement binary NOT for integer with bitsize '{d}'", .{operand_bits});2900 const wasm_bits = toWasmBits(operand_bits) orelse {
2802 };2901 return self.fail("TODO: Implement binary NOT for integer with bitsize '{d}'", .{operand_bits});
2902 };
28032903
2804 switch (wasm_bits) {2904 switch (wasm_bits) {
2805 32 => {2905 32 => {
2806 const bin_op = try self.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor);2906 const bin_op = try self.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor);
2807 return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty);2907 break :result try (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty);
2808 },2908 },
2809 64 => {2909 64 => {
2810 const bin_op = try self.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor);2910 const bin_op = try self.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor);
2811 return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty);2911 break :result try (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty);
2812 },2912 },
2813 128 => {2913 128 => {
2814 const result_ptr = try self.allocStack(operand_ty);2914 const result_ptr = try self.allocStack(operand_ty);
2815 try self.emitWValue(result_ptr);2915 try self.emitWValue(result_ptr);
2816 const msb = try self.load(operand, Type.u64, 0);2916 const msb = try self.load(operand, Type.u64, 0);
2817 const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);2917 const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
2818 try self.store(.{ .stack = {} }, msb_xor, Type.u64, 0 + result_ptr.offset());2918 try self.store(.{ .stack = {} }, msb_xor, Type.u64, 0 + result_ptr.offset());
28192919
2820 try self.emitWValue(result_ptr);2920 try self.emitWValue(result_ptr);
2821 const lsb = try self.load(operand, Type.u64, 8);2921 const lsb = try self.load(operand, Type.u64, 8);
2822 const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);2922 const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
2823 try self.store(result_ptr, lsb_xor, Type.u64, 8 + result_ptr.offset());2923 try self.store(result_ptr, lsb_xor, Type.u64, 8 + result_ptr.offset());
2824 return result_ptr;2924 break :result result_ptr;
2825 },2925 },
2826 else => unreachable,2926 else => unreachable,
2927 }
2827 }2928 }
2828 }2929 };
2930 self.finishAir(inst, result, &.{ty_op.operand});
2829}2931}
28302932
2831fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2933fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!void {
2832 _ = self;
2833 _ = inst;
2834 // unsupported by wasm itself. Can be implemented once we support DWARF2934 // unsupported by wasm itself. Can be implemented once we support DWARF
2835 // for wasm2935 // for wasm
2836 return .none;2936 self.finishAir(inst, .none, &.{});
2837}2937}
28382938
2839fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2939fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!void {
2840 _ = inst;
2841 try self.addTag(.@"unreachable");2940 try self.addTag(.@"unreachable");
2842 return .none;2941 self.finishAir(inst, .none, &.{});
2843}2942}
28442943
2845fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2944fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!void {
2846 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2945 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2847 return self.resolveInst(ty_op.operand);2946 const result = if (!self.liveness.isUnused(inst)) result: {
2947 break :result try self.resolveInst(ty_op.operand);
2948 } else WValue{ .none = {} };
2949 self.finishAir(inst, result, &.{});
2848}2950}
28492951
2850fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2952fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
2851 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2953 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2852 const extra = self.air.extraData(Air.StructField, ty_pl.payload);2954 const extra = self.air.extraData(Air.StructField, ty_pl.payload);
2955 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{extra.data.struct_operand});
2956
2853 const struct_ptr = try self.resolveInst(extra.data.struct_operand);2957 const struct_ptr = try self.resolveInst(extra.data.struct_operand);
2854 const struct_ty = self.air.typeOf(extra.data.struct_operand).childType();2958 const struct_ty = self.air.typeOf(extra.data.struct_operand).childType();
2855 const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) orelse {2959 const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) orelse {
...@@ -2858,11 +2962,13 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2858,11 +2962,13 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2858 struct_ty.structFieldType(extra.data.field_index).fmt(module),2962 struct_ty.structFieldType(extra.data.field_index).fmt(module),
2859 });2963 });
2860 };2964 };
2861 return self.structFieldPtr(struct_ptr, offset);2965 const result = try self.structFieldPtr(struct_ptr, offset);
2966 self.finishAir(inst, result, &.{extra.data.struct_operand});
2862}2967}
28632968
2864fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue {2969fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!void {
2865 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2970 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2971 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
2866 const struct_ptr = try self.resolveInst(ty_op.operand);2972 const struct_ptr = try self.resolveInst(ty_op.operand);
2867 const struct_ty = self.air.typeOf(ty_op.operand).childType();2973 const struct_ty = self.air.typeOf(ty_op.operand).childType();
2868 const field_ty = struct_ty.structFieldType(index);2974 const field_ty = struct_ty.structFieldType(index);
...@@ -2872,7 +2978,8 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr...@@ -2872,7 +2978,8 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr
2872 field_ty.fmt(module),2978 field_ty.fmt(module),
2873 });2979 });
2874 };2980 };
2875 return self.structFieldPtr(struct_ptr, offset);2981 const result = try self.structFieldPtr(struct_ptr, offset);
2982 self.finishAir(inst, result, &.{ty_op.operand});
2876}2983}
28772984
2878fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue {2985fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue {
...@@ -2884,35 +2991,39 @@ fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValu...@@ -2884,35 +2991,39 @@ fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValu
2884 }2991 }
2885}2992}
28862993
2887fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2994fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
2888 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2889
2890 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2995 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2891 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;2996 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
2997 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{struct_field.struct_operand});
2998
2892 const struct_ty = self.air.typeOf(struct_field.struct_operand);2999 const struct_ty = self.air.typeOf(struct_field.struct_operand);
2893 const operand = try self.resolveInst(struct_field.struct_operand);3000 const operand = try self.resolveInst(struct_field.struct_operand);
2894 const field_index = struct_field.field_index;3001 const field_index = struct_field.field_index;
2895 const field_ty = struct_ty.structFieldType(field_index);3002 const field_ty = struct_ty.structFieldType(field_index);
2896 if (!field_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };3003 if (!field_ty.hasRuntimeBitsIgnoreComptime()) return self.finishAir(inst, .none, &.{struct_field.struct_operand});
3004
2897 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) orelse {3005 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) orelse {
2898 const module = self.bin_file.base.options.module.?;3006 const module = self.bin_file.base.options.module.?;
2899 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});3007 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});
2900 };3008 };
29013009
2902 if (isByRef(field_ty, self.target)) {3010 const result = result: {
2903 switch (operand) {3011 if (isByRef(field_ty, self.target)) {
2904 .stack_offset => |stack_offset| {3012 switch (operand) {
2905 return WValue{ .stack_offset = stack_offset + offset };3013 .stack_offset => |stack_offset| {
2906 },3014 break :result WValue{ .stack_offset = stack_offset + offset };
2907 else => return self.buildPointerOffset(operand, offset, .new),3015 },
3016 else => break :result try self.buildPointerOffset(operand, offset, .new),
3017 }
2908 }3018 }
2909 }
29103019
2911 const field = try self.load(operand, field_ty, offset);3020 const field = try self.load(operand, field_ty, offset);
2912 return field.toLocal(self, field_ty);3021 break :result try field.toLocal(self, field_ty);
3022 };
3023 self.finishAir(inst, result, &.{struct_field.struct_operand});
2913}3024}
29143025
2915fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3026fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
2916 // result type is always 'noreturn'3027 // result type is always 'noreturn'
2917 const blocktype = wasm.block_empty;3028 const blocktype = wasm.block_empty;
2918 const pl_op = self.air.instructions.items(.data)[inst].pl_op;3029 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
...@@ -3071,133 +3182,149 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3071,133 +3182,149 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3071 try self.genBody(else_body);3182 try self.genBody(else_body);
3072 try self.endBlock();3183 try self.endBlock();
3073 }3184 }
3074 return .none;3185 self.finishAir(inst, .none, &.{});
3075}3186}
30763187
3077fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {3188fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void {
3078 const un_op = self.air.instructions.items(.data)[inst].un_op;3189 const un_op = self.air.instructions.items(.data)[inst].un_op;
3190 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{un_op});
3079 const operand = try self.resolveInst(un_op);3191 const operand = try self.resolveInst(un_op);
3080 const err_union_ty = self.air.typeOf(un_op);3192 const err_union_ty = self.air.typeOf(un_op);
3081 const pl_ty = err_union_ty.errorUnionPayload();3193 const pl_ty = err_union_ty.errorUnionPayload();
30823194
3083 if (err_union_ty.errorUnionSet().errorSetIsEmpty()) {3195 const result = result: {
3084 switch (opcode) {3196 if (err_union_ty.errorUnionSet().errorSetIsEmpty()) {
3085 .i32_ne => return WValue{ .imm32 = 0 },3197 switch (opcode) {
3086 .i32_eq => return WValue{ .imm32 = 1 },3198 .i32_ne => break :result WValue{ .imm32 = 0 },
3087 else => unreachable,3199 .i32_eq => break :result WValue{ .imm32 = 1 },
3200 else => unreachable,
3201 }
3088 }3202 }
3089 }
30903203
3091 try self.emitWValue(operand);3204 try self.emitWValue(operand);
3092 if (pl_ty.hasRuntimeBitsIgnoreComptime()) {3205 if (pl_ty.hasRuntimeBitsIgnoreComptime()) {
3093 try self.addMemArg(.i32_load16_u, .{3206 try self.addMemArg(.i32_load16_u, .{
3094 .offset = operand.offset() + @intCast(u32, errUnionErrorOffset(pl_ty, self.target)),3207 .offset = operand.offset() + @intCast(u32, errUnionErrorOffset(pl_ty, self.target)),
3095 .alignment = Type.anyerror.abiAlignment(self.target),3208 .alignment = Type.anyerror.abiAlignment(self.target),
3096 });3209 });
3097 }3210 }
30983211
3099 // Compare the error value with '0'3212 // Compare the error value with '0'
3100 try self.addImm32(0);3213 try self.addImm32(0);
3101 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));3214 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
31023215
3103 const is_err_tmp = try self.allocLocal(Type.i32);3216 const is_err_tmp = try self.allocLocal(Type.i32);
3104 try self.addLabel(.local_set, is_err_tmp.local);3217 try self.addLabel(.local_set, is_err_tmp.local);
3105 return is_err_tmp;3218 break :result is_err_tmp;
3219 };
3220 self.finishAir(inst, result, &.{un_op});
3106}3221}
31073222
3108fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue {3223fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
3109 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3110 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3224 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3225 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3226
3111 const operand = try self.resolveInst(ty_op.operand);3227 const operand = try self.resolveInst(ty_op.operand);
3112 const op_ty = self.air.typeOf(ty_op.operand);3228 const op_ty = self.air.typeOf(ty_op.operand);
3113 const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;3229 const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
3114 const payload_ty = err_ty.errorUnionPayload();3230 const payload_ty = err_ty.errorUnionPayload();
31153231
3116 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };3232 const result = result: {
3233 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result WValue{ .none = {} };
31173234
3118 const pl_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target));3235 const pl_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target));
3119 if (op_is_ptr or isByRef(payload_ty, self.target)) {3236 if (op_is_ptr or isByRef(payload_ty, self.target)) {
3120 return self.buildPointerOffset(operand, pl_offset, .new);3237 break :result try self.buildPointerOffset(operand, pl_offset, .new);
3121 }3238 }
31223239
3123 const payload = try self.load(operand, payload_ty, pl_offset);3240 const payload = try self.load(operand, payload_ty, pl_offset);
3124 return payload.toLocal(self, payload_ty);3241 break :result try payload.toLocal(self, payload_ty);
3242 };
3243 self.finishAir(inst, result, &.{ty_op.operand});
3125}3244}
31263245
3127fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue {3246fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
3128 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3129
3130 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3247 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3248 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3249
3131 const operand = try self.resolveInst(ty_op.operand);3250 const operand = try self.resolveInst(ty_op.operand);
3132 const op_ty = self.air.typeOf(ty_op.operand);3251 const op_ty = self.air.typeOf(ty_op.operand);
3133 const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;3252 const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
3134 const payload_ty = err_ty.errorUnionPayload();3253 const payload_ty = err_ty.errorUnionPayload();
31353254
3136 if (err_ty.errorUnionSet().errorSetIsEmpty()) {3255 const result = result: {
3137 return WValue{ .imm32 = 0 };3256 if (err_ty.errorUnionSet().errorSetIsEmpty()) {
3138 }3257 break :result WValue{ .imm32 = 0 };
3258 }
31393259
3140 if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime()) {3260 if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime()) {
3141 return operand;3261 break :result operand;
3142 }3262 }
31433263
3144 const error_val = try self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target)));3264 const error_val = try self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target)));
3145 return error_val.toLocal(self, Type.anyerror);3265 break :result try error_val.toLocal(self, Type.anyerror);
3266 };
3267 self.finishAir(inst, result, &.{ty_op.operand});
3146}3268}
31473269
3148fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3270fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3149 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3150
3151 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3271 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3272 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3273
3152 const operand = try self.resolveInst(ty_op.operand);3274 const operand = try self.resolveInst(ty_op.operand);
3153 const err_ty = self.air.typeOfIndex(inst);3275 const err_ty = self.air.typeOfIndex(inst);
31543276
3155 const pl_ty = self.air.typeOf(ty_op.operand);3277 const pl_ty = self.air.typeOf(ty_op.operand);
3156 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {3278 const result = result: {
3157 return operand;3279 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
3158 }3280 break :result operand;
31593281 }
3160 const err_union = try self.allocStack(err_ty);
3161 const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new);
3162 try self.store(payload_ptr, operand, pl_ty, 0);
31633282
3164 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.3283 const err_union = try self.allocStack(err_ty);
3165 try self.emitWValue(err_union);3284 const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new);
3166 try self.addImm32(0);3285 try self.store(payload_ptr, operand, pl_ty, 0);
3167 const err_val_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
3168 try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 });
31693286
3170 return err_union;3287 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
3288 try self.emitWValue(err_union);
3289 try self.addImm32(0);
3290 const err_val_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
3291 try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 });
3292 break :result err_union;
3293 };
3294 self.finishAir(inst, result, &.{ty_op.operand});
3171}3295}
31723296
3173fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3297fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3174 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3175
3176 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3298 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3299 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3300
3177 const operand = try self.resolveInst(ty_op.operand);3301 const operand = try self.resolveInst(ty_op.operand);
3178 const err_ty = self.air.getRefType(ty_op.ty);3302 const err_ty = self.air.getRefType(ty_op.ty);
3179 const pl_ty = err_ty.errorUnionPayload();3303 const pl_ty = err_ty.errorUnionPayload();
31803304
3181 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {3305 const result = result: {
3182 return operand;3306 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
3183 }3307 break :result operand;
3308 }
31843309
3185 const err_union = try self.allocStack(err_ty);3310 const err_union = try self.allocStack(err_ty);
3186 // store error value3311 // store error value
3187 try self.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(pl_ty, self.target)));3312 try self.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(pl_ty, self.target)));
31883313
3189 // write 'undefined' to the payload3314 // write 'undefined' to the payload
3190 const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new);3315 const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new);
3191 const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(self.target));3316 const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(self.target));
3192 try self.memset(payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaaaaaaaa });3317 try self.memset(payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaaaaaaaa });
31933318
3194 return err_union;3319 break :result err_union;
3320 };
3321 self.finishAir(inst, result, &.{ty_op.operand});
3195}3322}
31963323
3197fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3324fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!void {
3198 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3199
3200 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3325 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3326 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3327
3201 const ty = self.air.getRefType(ty_op.ty);3328 const ty = self.air.getRefType(ty_op.ty);
3202 const operand = try self.resolveInst(ty_op.operand);3329 const operand = try self.resolveInst(ty_op.operand);
3203 const operand_ty = self.air.typeOf(ty_op.operand);3330 const operand_ty = self.air.typeOf(ty_op.operand);
...@@ -3208,7 +3335,8 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3208,7 +3335,8 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3208 return self.fail("todo Wasm intcast for bitsize > 128", .{});3335 return self.fail("todo Wasm intcast for bitsize > 128", .{});
3209 }3336 }
32103337
3211 return (try self.intcast(operand, operand_ty, ty)).toLocal(self, ty);3338 const result = try (try self.intcast(operand, operand_ty, ty)).toLocal(self, ty);
3339 self.finishAir(inst, result, &.{ty_op.operand});
3212}3340}
32133341
3214/// Upcasts or downcasts an integer based on the given and wanted types,3342/// Upcasts or downcasts an integer based on the given and wanted types,
...@@ -3263,14 +3391,16 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W...@@ -3263,14 +3391,16 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W
3263 return WValue{ .stack = {} };3391 return WValue{ .stack = {} };
3264}3392}
32653393
3266fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!WValue {3394fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
3267 const un_op = self.air.instructions.items(.data)[inst].un_op;3395 const un_op = self.air.instructions.items(.data)[inst].un_op;
3396 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{un_op});
3268 const operand = try self.resolveInst(un_op);3397 const operand = try self.resolveInst(un_op);
32693398
3270 const op_ty = self.air.typeOf(un_op);3399 const op_ty = self.air.typeOf(un_op);
3271 const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty;3400 const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty;
3272 const is_null = try self.isNull(operand, optional_ty, opcode);3401 const is_null = try self.isNull(operand, optional_ty, opcode);
3273 return is_null.toLocal(self, optional_ty);3402 const result = try is_null.toLocal(self, optional_ty);
3403 self.finishAir(inst, result, &.{un_op});
3274}3404}
32753405
3276/// For a given type and operand, checks if it's considered `null`.3406/// For a given type and operand, checks if it's considered `null`.
...@@ -3294,43 +3424,50 @@ fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode)...@@ -3294,43 +3424,50 @@ fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode)
3294 return WValue{ .stack = {} };3424 return WValue{ .stack = {} };
3295}3425}
32963426
3297fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3427fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3298 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3299 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3428 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3300 const operand = try self.resolveInst(ty_op.operand);
3301 const opt_ty = self.air.typeOf(ty_op.operand);3429 const opt_ty = self.air.typeOf(ty_op.operand);
3302 const payload_ty = self.air.typeOfIndex(inst);3430 const payload_ty = self.air.typeOfIndex(inst);
3303 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };3431 if (self.liveness.isUnused(inst) or !payload_ty.hasRuntimeBitsIgnoreComptime()) {
3304 if (opt_ty.optionalReprIsPayload()) return operand;3432 return self.finishAir(inst, .none, &.{ty_op.operand});
3433 }
33053434
3306 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);3435 const result = result: {
3436 const operand = try self.resolveInst(ty_op.operand);
3437 if (opt_ty.optionalReprIsPayload()) break :result operand;
33073438
3308 if (isByRef(payload_ty, self.target)) {3439 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);
3309 return self.buildPointerOffset(operand, offset, .new);
3310 }
33113440
3312 const payload = try self.load(operand, payload_ty, @intCast(u32, offset));3441 if (isByRef(payload_ty, self.target)) {
3313 return payload.toLocal(self, payload_ty);3442 break :result try self.buildPointerOffset(operand, offset, .new);
3314}3443 }
33153444
3316fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3445 const payload = try self.load(operand, payload_ty, @intCast(u32, offset));
3317 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };3446 break :result try payload.toLocal(self, payload_ty);
3447 };
3448 self.finishAir(inst, result, &.{ty_op.operand});
3449}
33183450
3451fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3319 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3452 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3453 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3320 const operand = try self.resolveInst(ty_op.operand);3454 const operand = try self.resolveInst(ty_op.operand);
3321 const opt_ty = self.air.typeOf(ty_op.operand).childType();3455 const opt_ty = self.air.typeOf(ty_op.operand).childType();
33223456
3323 var buf: Type.Payload.ElemType = undefined;3457 const result = result: {
3324 const payload_ty = opt_ty.optionalChild(&buf);3458 var buf: Type.Payload.ElemType = undefined;
3325 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.optionalReprIsPayload()) {3459 const payload_ty = opt_ty.optionalChild(&buf);
3326 return operand;3460 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.optionalReprIsPayload()) {
3327 }3461 break :result operand;
3462 }
33283463
3329 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);3464 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);
3330 return self.buildPointerOffset(operand, offset, .new);3465 break :result try self.buildPointerOffset(operand, offset, .new);
3466 };
3467 self.finishAir(inst, result, &.{ty_op.operand});
3331}3468}
33323469
3333fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3470fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
3334 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3471 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3335 const operand = try self.resolveInst(ty_op.operand);3472 const operand = try self.resolveInst(ty_op.operand);
3336 const opt_ty = self.air.typeOf(ty_op.operand).childType();3473 const opt_ty = self.air.typeOf(ty_op.operand).childType();
...@@ -3341,7 +3478,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue...@@ -3341,7 +3478,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue
3341 }3478 }
33423479
3343 if (opt_ty.optionalReprIsPayload()) {3480 if (opt_ty.optionalReprIsPayload()) {
3344 return operand;3481 return self.finishAir(inst, operand, &.{ty_op.operand});
3345 }3482 }
33463483
3347 const offset = std.math.cast(u32, opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) orelse {3484 const offset = std.math.cast(u32, opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) orelse {
...@@ -3353,49 +3490,53 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue...@@ -3353,49 +3490,53 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue
3353 try self.addImm32(1);3490 try self.addImm32(1);
3354 try self.addMemArg(.i32_store8, .{ .offset = operand.offset(), .alignment = 1 });3491 try self.addMemArg(.i32_store8, .{ .offset = operand.offset(), .alignment = 1 });
33553492
3356 return self.buildPointerOffset(operand, offset, .new);3493 const result = try self.buildPointerOffset(operand, offset, .new);
3494 return self.finishAir(inst, result, &.{ty_op.operand});
3357}3495}
33583496
3359fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3497fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!void {
3360 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3361
3362 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3498 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3499 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3363 const payload_ty = self.air.typeOf(ty_op.operand);3500 const payload_ty = self.air.typeOf(ty_op.operand);
3364 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3365 const non_null_bit = try self.allocStack(Type.initTag(.u1));
3366 try self.emitWValue(non_null_bit);
3367 try self.addImm32(1);
3368 try self.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 });
3369 return non_null_bit;
3370 }
33713501
3372 const operand = try self.resolveInst(ty_op.operand);3502 const result = result: {
3373 const op_ty = self.air.typeOfIndex(inst);3503 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3374 if (op_ty.optionalReprIsPayload()) {3504 const non_null_bit = try self.allocStack(Type.initTag(.u1));
3375 return operand;3505 try self.emitWValue(non_null_bit);
3376 }3506 try self.addImm32(1);
3377 const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) orelse {3507 try self.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 });
3378 const module = self.bin_file.base.options.module.?;3508 break :result non_null_bit;
3379 return self.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(module)});3509 }
3380 };
33813510
3382 // Create optional type, set the non-null bit, and store the operand inside the optional type3511 const operand = try self.resolveInst(ty_op.operand);
3383 const result = try self.allocStack(op_ty);3512 const op_ty = self.air.typeOfIndex(inst);
3384 try self.emitWValue(result);3513 if (op_ty.optionalReprIsPayload()) {
3385 try self.addImm32(1);3514 break :result operand;
3386 try self.addMemArg(.i32_store8, .{ .offset = result.offset(), .alignment = 1 });3515 }
3516 const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) orelse {
3517 const module = self.bin_file.base.options.module.?;
3518 return self.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(module)});
3519 };
33873520
3388 const payload_ptr = try self.buildPointerOffset(result, offset, .new);3521 // Create optional type, set the non-null bit, and store the operand inside the optional type
3389 try self.store(payload_ptr, operand, payload_ty, 0);3522 const result_ptr = try self.allocStack(op_ty);
3523 try self.emitWValue(result_ptr);
3524 try self.addImm32(1);
3525 try self.addMemArg(.i32_store8, .{ .offset = result_ptr.offset(), .alignment = 1 });
33903526
3391 return result;3527 const payload_ptr = try self.buildPointerOffset(result_ptr, offset, .new);
3392}3528 try self.store(payload_ptr, operand, payload_ty, 0);
3529 break :result result_ptr;
3530 };
33933531
3394fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3532 self.finishAir(inst, result, &.{ty_op.operand});
3395 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };3533}
33963534
3535fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
3397 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;3536 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3398 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;3537 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3538 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
3539
3399 const lhs = try self.resolveInst(bin_op.lhs);3540 const lhs = try self.resolveInst(bin_op.lhs);
3400 const rhs = try self.resolveInst(bin_op.rhs);3541 const rhs = try self.resolveInst(bin_op.rhs);
3401 const slice_ty = self.air.typeOfIndex(inst);3542 const slice_ty = self.air.typeOfIndex(inst);
...@@ -3404,23 +3545,23 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3404,23 +3545,23 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3404 try self.store(slice, lhs, Type.usize, 0);3545 try self.store(slice, lhs, Type.usize, 0);
3405 try self.store(slice, rhs, Type.usize, self.ptrSize());3546 try self.store(slice, rhs, Type.usize, self.ptrSize());
34063547
3407 return slice;3548 self.finishAir(inst, slice, &.{ bin_op.lhs, bin_op.rhs });
3408}3549}
34093550
3410fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3551fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
3411 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3412
3413 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3552 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3414 const operand = try self.resolveInst(ty_op.operand);3553 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
34153554
3555 const operand = try self.resolveInst(ty_op.operand);
3416 const len = try self.load(operand, Type.usize, self.ptrSize());3556 const len = try self.load(operand, Type.usize, self.ptrSize());
3417 return len.toLocal(self, Type.usize);3557 const result = try len.toLocal(self, Type.usize);
3558 self.finishAir(inst, result, &.{ty_op.operand});
3418}3559}
34193560
3420fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3561fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3421 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3422
3423 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3562 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3563 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
3564
3424 const slice_ty = self.air.typeOf(bin_op.lhs);3565 const slice_ty = self.air.typeOf(bin_op.lhs);
3425 const slice = try self.resolveInst(bin_op.lhs);3566 const slice = try self.resolveInst(bin_op.lhs);
3426 const index = try self.resolveInst(bin_op.rhs);3567 const index = try self.resolveInst(bin_op.rhs);
...@@ -3436,21 +3577,22 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3436,21 +3577,22 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3436 try self.addTag(.i32_mul);3577 try self.addTag(.i32_mul);
3437 try self.addTag(.i32_add);3578 try self.addTag(.i32_add);
34383579
3439 const result = try self.allocLocal(elem_ty);3580 const result_ptr = try self.allocLocal(elem_ty);
3440 try self.addLabel(.local_set, result.local);3581 try self.addLabel(.local_set, result_ptr.local);
34413582
3442 if (isByRef(elem_ty, self.target)) {3583 const result = if (!isByRef(elem_ty, self.target)) result: {
3443 return result;3584 const elem_val = try self.load(result_ptr, elem_ty, 0);
3444 }3585 break :result try elem_val.toLocal(self, elem_ty);
3586 } else result_ptr;
34453587
3446 const elem_val = try self.load(result, elem_ty, 0);3588 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
3447 return elem_val.toLocal(self, elem_ty);
3448}3589}
34493590
3450fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3591fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3451 if (self.liveness.isUnused(inst)) return WValue.none;
3452 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;3592 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3453 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;3593 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3594 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
3595
3454 const elem_ty = self.air.getRefType(ty_pl.ty).childType();3596 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
3455 const elem_size = elem_ty.abiSize(self.target);3597 const elem_size = elem_ty.abiSize(self.target);
34563598
...@@ -3467,20 +3609,22 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3467,20 +3609,22 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
34673609
3468 const result = try self.allocLocal(Type.i32);3610 const result = try self.allocLocal(Type.i32);
3469 try self.addLabel(.local_set, result.local);3611 try self.addLabel(.local_set, result.local);
3470 return result;3612 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
3471}3613}
34723614
3473fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3615fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3474 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3475 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3616 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3617 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3476 const operand = try self.resolveInst(ty_op.operand);3618 const operand = try self.resolveInst(ty_op.operand);
3477 const ptr = try self.load(operand, Type.usize, 0);3619 const ptr = try self.load(operand, Type.usize, 0);
3478 return ptr.toLocal(self, Type.usize);3620 const result = try ptr.toLocal(self, Type.usize);
3621 self.finishAir(inst, result, &.{ty_op.operand});
3479}3622}
34803623
3481fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3624fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
3482 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3483 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3625 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3626 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3627
3484 const operand = try self.resolveInst(ty_op.operand);3628 const operand = try self.resolveInst(ty_op.operand);
3485 const wanted_ty = self.air.getRefType(ty_op.ty);3629 const wanted_ty = self.air.getRefType(ty_op.ty);
3486 const op_ty = self.air.typeOf(ty_op.operand);3630 const op_ty = self.air.typeOf(ty_op.operand);
...@@ -3496,16 +3640,24 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3496,16 +3640,24 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3496 if (wasm_bits != wanted_bits) {3640 if (wasm_bits != wanted_bits) {
3497 result = try self.wrapOperand(result, wanted_ty);3641 result = try self.wrapOperand(result, wanted_ty);
3498 }3642 }
3499 return result.toLocal(self, wanted_ty);3643
3644 self.finishAir(inst, try result.toLocal(self, wanted_ty), &.{ty_op.operand});
3500}3645}
35013646
3502fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3647fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!void {
3503 const un_op = self.air.instructions.items(.data)[inst].un_op;3648 const un_op = self.air.instructions.items(.data)[inst].un_op;
3504 return self.resolveInst(un_op);3649 const result = if (self.liveness.isUnused(inst))
3650 WValue{ .none = {} }
3651 else
3652 try self.resolveInst(un_op);
3653
3654 self.finishAir(inst, result, &.{un_op});
3505}3655}
35063656
3507fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3657fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
3508 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3658 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3659 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3660
3509 const operand = try self.resolveInst(ty_op.operand);3661 const operand = try self.resolveInst(ty_op.operand);
3510 const array_ty = self.air.typeOf(ty_op.operand).childType();3662 const array_ty = self.air.typeOf(ty_op.operand).childType();
3511 const slice_ty = self.air.getRefType(ty_op.ty);3663 const slice_ty = self.air.getRefType(ty_op.ty);
...@@ -3522,25 +3674,26 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3522,25 +3674,26 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3522 const len = WValue{ .imm32 = @intCast(u32, array_ty.arrayLen()) };3674 const len = WValue{ .imm32 = @intCast(u32, array_ty.arrayLen()) };
3523 try self.store(slice_local, len, Type.usize, self.ptrSize());3675 try self.store(slice_local, len, Type.usize, self.ptrSize());
35243676
3525 return slice_local;3677 self.finishAir(inst, slice_local, &.{ty_op.operand});
3526}3678}
35273679
3528fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3680fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!void {
3529 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3530 const un_op = self.air.instructions.items(.data)[inst].un_op;3681 const un_op = self.air.instructions.items(.data)[inst].un_op;
3682 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{un_op});
3531 const operand = try self.resolveInst(un_op);3683 const operand = try self.resolveInst(un_op);
35323684
3533 switch (operand) {3685 const result = switch (operand) {
3534 // for stack offset, return a pointer to this offset.3686 // for stack offset, return a pointer to this offset.
3535 .stack_offset => return self.buildPointerOffset(operand, 0, .new),3687 .stack_offset => try self.buildPointerOffset(operand, 0, .new),
3536 else => return operand,3688 else => operand,
3537 }3689 };
3690 self.finishAir(inst, result, &.{un_op});
3538}3691}
35393692
3540fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3693fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3541 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3542
3543 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3694 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3695 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
3696
3544 const ptr_ty = self.air.typeOf(bin_op.lhs);3697 const ptr_ty = self.air.typeOf(bin_op.lhs);
3545 const ptr = try self.resolveInst(bin_op.lhs);3698 const ptr = try self.resolveInst(bin_op.lhs);
3546 const index = try self.resolveInst(bin_op.rhs);3699 const index = try self.resolveInst(bin_op.rhs);
...@@ -3560,21 +3713,25 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3560,21 +3713,25 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3560 try self.addTag(.i32_mul);3713 try self.addTag(.i32_mul);
3561 try self.addTag(.i32_add);3714 try self.addTag(.i32_add);
35623715
3563 var result = try self.allocLocal(elem_ty);3716 const elem_result = val: {
3564 try self.addLabel(.local_set, result.local);3717 var result = try self.allocLocal(elem_ty);
3565 if (isByRef(elem_ty, self.target)) {3718 try self.addLabel(.local_set, result.local);
3566 return result;3719 if (isByRef(elem_ty, self.target)) {
3567 }3720 break :val result;
3568 defer result.free(self); // only free if it's not returned like above3721 }
3722 defer result.free(self); // only free if it's not returned like above
35693723
3570 const elem_val = try self.load(result, elem_ty, 0);3724 const elem_val = try self.load(result, elem_ty, 0);
3571 return elem_val.toLocal(self, elem_ty);3725 break :val try elem_val.toLocal(self, elem_ty);
3726 };
3727 self.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
3572}3728}
35733729
3574fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3730fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3575 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3576 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;3731 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3577 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;3732 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3733 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
3734
3578 const ptr_ty = self.air.typeOf(bin_op.lhs);3735 const ptr_ty = self.air.typeOf(bin_op.lhs);
3579 const elem_ty = self.air.getRefType(ty_pl.ty).childType();3736 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
3580 const elem_size = elem_ty.abiSize(self.target);3737 const elem_size = elem_ty.abiSize(self.target);
...@@ -3597,13 +3754,14 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3597,13 +3754,14 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
35973754
3598 const result = try self.allocLocal(Type.i32);3755 const result = try self.allocLocal(Type.i32);
3599 try self.addLabel(.local_set, result.local);3756 try self.addLabel(.local_set, result.local);
3600 return result;3757 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
3601}3758}
36023759
3603fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {3760fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
3604 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3605 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;3761 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3606 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;3762 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3763 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
3764
3607 const ptr = try self.resolveInst(bin_op.lhs);3765 const ptr = try self.resolveInst(bin_op.lhs);
3608 const offset = try self.resolveInst(bin_op.rhs);3766 const offset = try self.resolveInst(bin_op.rhs);
3609 const ptr_ty = self.air.typeOf(bin_op.lhs);3767 const ptr_ty = self.air.typeOf(bin_op.lhs);
...@@ -3624,10 +3782,10 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -3624,10 +3782,10 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
36243782
3625 const result = try self.allocLocal(Type.usize);3783 const result = try self.allocLocal(Type.usize);
3626 try self.addLabel(.local_set, result.local);3784 try self.addLabel(.local_set, result.local);
3627 return result;3785 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
3628}3786}
36293787
3630fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3788fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!void {
3631 const pl_op = self.air.instructions.items(.data)[inst].pl_op;3789 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3632 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;3790 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;
36333791
...@@ -3636,7 +3794,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3636,7 +3794,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3636 const len = try self.resolveInst(bin_op.rhs);3794 const len = try self.resolveInst(bin_op.rhs);
3637 try self.memset(ptr, len, value);3795 try self.memset(ptr, len, value);
36383796
3639 return WValue{ .none = {} };3797 self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
3640}3798}
36413799
3642/// Sets a region of memory at `ptr` to the value of `value`3800/// Sets a region of memory at `ptr` to the value of `value`
...@@ -3724,10 +3882,10 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void...@@ -3724,10 +3882,10 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void
3724 }3882 }
3725}3883}
37263884
3727fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3885fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3728 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3729
3730 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3886 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3887 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
3888
3731 const array_ty = self.air.typeOf(bin_op.lhs);3889 const array_ty = self.air.typeOf(bin_op.lhs);
3732 const array = try self.resolveInst(bin_op.lhs);3890 const array = try self.resolveInst(bin_op.lhs);
3733 const index = try self.resolveInst(bin_op.rhs);3891 const index = try self.resolveInst(bin_op.rhs);
...@@ -3740,22 +3898,26 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3740,22 +3898,26 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3740 try self.addTag(.i32_mul);3898 try self.addTag(.i32_mul);
3741 try self.addTag(.i32_add);3899 try self.addTag(.i32_add);
37423900
3743 var result = try self.allocLocal(Type.usize);3901 const elem_result = val: {
3744 try self.addLabel(.local_set, result.local);3902 var result = try self.allocLocal(Type.usize);
3903 try self.addLabel(.local_set, result.local);
37453904
3746 if (isByRef(elem_ty, self.target)) {3905 if (isByRef(elem_ty, self.target)) {
3747 return result;3906 break :val result;
3748 }3907 }
3749 defer result.free(self); // only free if no longer needed and not returned like above3908 defer result.free(self); // only free if no longer needed and not returned like above
37503909
3751 const elem_val = try self.load(result, elem_ty, 0);3910 const elem_val = try self.load(result, elem_ty, 0);
3752 return elem_val.toLocal(self, elem_ty);3911 break :val try elem_val.toLocal(self, elem_ty);
3753}3912 };
37543913
3755fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3914 self.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
3756 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };3915}
37573916
3917fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!void {
3758 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3918 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3919 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3920
3759 const operand = try self.resolveInst(ty_op.operand);3921 const operand = try self.resolveInst(ty_op.operand);
3760 const dest_ty = self.air.typeOfIndex(inst);3922 const dest_ty = self.air.typeOfIndex(inst);
3761 const op_ty = self.air.typeOf(ty_op.operand);3923 const op_ty = self.air.typeOf(ty_op.operand);
...@@ -3773,13 +3935,14 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3773,13 +3935,14 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3773 });3935 });
3774 try self.addTag(Mir.Inst.Tag.fromOpcode(op));3936 try self.addTag(Mir.Inst.Tag.fromOpcode(op));
3775 const wrapped = try self.wrapOperand(.{ .stack = {} }, dest_ty);3937 const wrapped = try self.wrapOperand(.{ .stack = {} }, dest_ty);
3776 return wrapped.toLocal(self, dest_ty);3938 const result = try wrapped.toLocal(self, dest_ty);
3939 self.finishAir(inst, result, &.{ty_op.operand});
3777}3940}
37783941
3779fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3942fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!void {
3780 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3781
3782 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3943 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3944 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3945
3783 const operand = try self.resolveInst(ty_op.operand);3946 const operand = try self.resolveInst(ty_op.operand);
3784 const dest_ty = self.air.typeOfIndex(inst);3947 const dest_ty = self.air.typeOfIndex(inst);
3785 const op_ty = self.air.typeOf(ty_op.operand);3948 const op_ty = self.air.typeOf(ty_op.operand);
...@@ -3799,12 +3962,10 @@ fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3799,12 +3962,10 @@ fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
37993962
3800 const result = try self.allocLocal(dest_ty);3963 const result = try self.allocLocal(dest_ty);
3801 try self.addLabel(.local_set, result.local);3964 try self.addLabel(.local_set, result.local);
3802 return result;3965 self.finishAir(inst, result, &.{ty_op.operand});
3803}3966}
38043967
3805fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3968fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!void {
3806 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3807
3808 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3969 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3809 const operand = try self.resolveInst(ty_op.operand);3970 const operand = try self.resolveInst(ty_op.operand);
38103971
...@@ -3812,9 +3973,7 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3812,9 +3973,7 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3812 return self.fail("TODO: Implement wasm airSplat", .{});3973 return self.fail("TODO: Implement wasm airSplat", .{});
3813}3974}
38143975
3815fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3976fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!void {
3816 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3817
3818 const pl_op = self.air.instructions.items(.data)[inst].pl_op;3977 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3819 const operand = try self.resolveInst(pl_op.operand);3978 const operand = try self.resolveInst(pl_op.operand);
38203979
...@@ -3822,9 +3981,7 @@ fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3822,9 +3981,7 @@ fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3822 return self.fail("TODO: Implement wasm airSelect", .{});3981 return self.fail("TODO: Implement wasm airSelect", .{});
3823}3982}
38243983
3825fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3984fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!void {
3826 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3827
3828 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3985 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3829 const operand = try self.resolveInst(ty_op.operand);3986 const operand = try self.resolveInst(ty_op.operand);
38303987
...@@ -3832,9 +3989,7 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3832,9 +3989,7 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3832 return self.fail("TODO: Implement wasm airShuffle", .{});3989 return self.fail("TODO: Implement wasm airShuffle", .{});
3833}3990}
38343991
3835fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3992fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!void {
3836 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3837
3838 const reduce = self.air.instructions.items(.data)[inst].reduce;3993 const reduce = self.air.instructions.items(.data)[inst].reduce;
3839 const operand = try self.resolveInst(reduce.operand);3994 const operand = try self.resolveInst(reduce.operand);
38403995
...@@ -3842,126 +3997,130 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3842,126 +3997,130 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3842 return self.fail("TODO: Implement wasm airReduce", .{});3997 return self.fail("TODO: Implement wasm airReduce", .{});
3843}3998}
38443999
3845fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4000fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
3846 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3847
3848 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;4001 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3849 const result_ty = self.air.typeOfIndex(inst);4002 const result_ty = self.air.typeOfIndex(inst);
3850 const len = @intCast(usize, result_ty.arrayLen());4003 const len = @intCast(usize, result_ty.arrayLen());
3851 const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);4004 const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
38524005
3853 switch (result_ty.zigTypeTag()) {4006 const result: WValue = result_value: {
3854 .Vector => return self.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),4007 if (self.liveness.isUnused(inst)) break :result_value WValue.none;
3855 .Array => {4008 switch (result_ty.zigTypeTag()) {
3856 const result = try self.allocStack(result_ty);4009 .Array => {
3857 const elem_ty = result_ty.childType();4010 const result = try self.allocStack(result_ty);
3858 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));4011 const elem_ty = result_ty.childType();
38594012 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
3860 // When the element type is by reference, we must copy the entire
3861 // value. It is therefore safer to move the offset pointer and store
3862 // each value individually, instead of using store offsets.
3863 if (isByRef(elem_ty, self.target)) {
3864 // copy stack pointer into a temporary local, which is
3865 // moved for each element to store each value in the right position.
3866 const offset = try self.buildPointerOffset(result, 0, .new);
3867 for (elements) |elem, elem_index| {
3868 const elem_val = try self.resolveInst(elem);
3869 try self.store(offset, elem_val, elem_ty, 0);
38704013
3871 if (elem_index < elements.len - 1) {4014 // When the element type is by reference, we must copy the entire
3872 _ = try self.buildPointerOffset(offset, elem_size, .modify);4015 // value. It is therefore safer to move the offset pointer and store
4016 // each value individually, instead of using store offsets.
4017 if (isByRef(elem_ty, self.target)) {
4018 // copy stack pointer into a temporary local, which is
4019 // moved for each element to store each value in the right position.
4020 const offset = try self.buildPointerOffset(result, 0, .new);
4021 for (elements) |elem, elem_index| {
4022 const elem_val = try self.resolveInst(elem);
4023 try self.store(offset, elem_val, elem_ty, 0);
4024
4025 if (elem_index < elements.len - 1) {
4026 _ = try self.buildPointerOffset(offset, elem_size, .modify);
4027 }
4028 }
4029 } else {
4030 var offset: u32 = 0;
4031 for (elements) |elem| {
4032 const elem_val = try self.resolveInst(elem);
4033 try self.store(result, elem_val, elem_ty, offset);
4034 offset += elem_size;
3873 }4035 }
3874 }4036 }
3875 } else {4037 break :result_value result;
3876 var offset: u32 = 0;4038 },
3877 for (elements) |elem| {4039 .Struct => {
3878 const elem_val = try self.resolveInst(elem);4040 const result = try self.allocStack(result_ty);
3879 try self.store(result, elem_val, elem_ty, offset);4041 const offset = try self.buildPointerOffset(result, 0, .new); // pointer to offset
3880 offset += elem_size;4042 for (elements) |elem, elem_index| {
3881 }4043 if (result_ty.structFieldValueComptime(elem_index) != null) continue;
3882 }
3883 return result;
3884 },
3885 .Struct => {
3886 const result = try self.allocStack(result_ty);
3887 const offset = try self.buildPointerOffset(result, 0, .new); // pointer to offset
3888 for (elements) |elem, elem_index| {
3889 if (result_ty.structFieldValueComptime(elem_index) != null) continue;
38904044
3891 const elem_ty = result_ty.structFieldType(elem_index);4045 const elem_ty = result_ty.structFieldType(elem_index);
3892 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));4046 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
3893 const value = try self.resolveInst(elem);4047 const value = try self.resolveInst(elem);
3894 try self.store(offset, value, elem_ty, 0);4048 try self.store(offset, value, elem_ty, 0);
38954049
3896 if (elem_index < elements.len - 1) {4050 if (elem_index < elements.len - 1) {
3897 _ = try self.buildPointerOffset(offset, elem_size, .modify);4051 _ = try self.buildPointerOffset(offset, elem_size, .modify);
4052 }
3898 }4053 }
3899 }
39004054
3901 return result;4055 break :result_value result;
3902 },4056 },
3903 else => unreachable,4057 .Vector => return self.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
3904 }4058 else => unreachable,
4059 }
4060 };
4061 self.finishAir(inst, result, &.{});
3905}4062}
39064063
3907fn airUnionInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4064fn airUnionInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
3908 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3909
3910 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;4065 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3911 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;4066 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
3912 const union_ty = self.air.typeOfIndex(inst);4067 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{extra.init});
3913 const layout = union_ty.unionGetLayout(self.target);4068
3914 if (layout.payload_size == 0) {4069 const result = result: {
3915 if (layout.tag_size == 0) {4070 const union_ty = self.air.typeOfIndex(inst);
3916 return WValue{ .none = {} };4071 const layout = union_ty.unionGetLayout(self.target);
4072 if (layout.payload_size == 0) {
4073 if (layout.tag_size == 0) {
4074 break :result WValue{ .none = {} };
4075 }
4076 assert(!isByRef(union_ty, self.target));
4077 break :result WValue{ .imm32 = extra.field_index };
3917 }4078 }
3918 assert(!isByRef(union_ty, self.target));4079 assert(isByRef(union_ty, self.target));
3919 return WValue{ .imm32 = extra.field_index };
3920 }
3921 assert(isByRef(union_ty, self.target));
39224080
3923 const result_ptr = try self.allocStack(union_ty);4081 const result_ptr = try self.allocStack(union_ty);
3924 const payload = try self.resolveInst(extra.init);4082 const payload = try self.resolveInst(extra.init);
3925 const union_obj = union_ty.cast(Type.Payload.Union).?.data;4083 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
3926 assert(union_obj.haveFieldTypes());4084 assert(union_obj.haveFieldTypes());
3927 const field = union_obj.fields.values()[extra.field_index];4085 const field = union_obj.fields.values()[extra.field_index];
39284086
3929 if (layout.tag_align >= layout.payload_align) {4087 if (layout.tag_align >= layout.payload_align) {
3930 const payload_ptr = try self.buildPointerOffset(result_ptr, layout.tag_size, .new);4088 const payload_ptr = try self.buildPointerOffset(result_ptr, layout.tag_size, .new);
3931 try self.store(payload_ptr, payload, field.ty, 0);4089 try self.store(payload_ptr, payload, field.ty, 0);
3932 } else {4090 } else {
3933 try self.store(result_ptr, payload, field.ty, 0);4091 try self.store(result_ptr, payload, field.ty, 0);
3934 }4092 }
4093 break :result result_ptr;
4094 };
39354095
3936 return result_ptr;4096 self.finishAir(inst, result, &.{extra.init});
3937}4097}
39384098
3939fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4099fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!void {
3940 const prefetch = self.air.instructions.items(.data)[inst].prefetch;4100 const prefetch = self.air.instructions.items(.data)[inst].prefetch;
3941 _ = prefetch;4101 self.finishAir(inst, .none, &.{prefetch.ptr});
3942 return WValue{ .none = {} };
3943}4102}
39444103
3945fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) !WValue {4104fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) InnerError!void {
3946 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3947
3948 const pl_op = self.air.instructions.items(.data)[inst].pl_op;4105 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4106 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{pl_op.operand});
39494107
3950 const result = try self.allocLocal(self.air.typeOfIndex(inst));4108 const result = try self.allocLocal(self.air.typeOfIndex(inst));
3951 try self.addLabel(.memory_size, pl_op.payload);4109 try self.addLabel(.memory_size, pl_op.payload);
3952 try self.addLabel(.local_set, result.local);4110 try self.addLabel(.local_set, result.local);
3953 return result;4111 self.finishAir(inst, result, &.{pl_op.operand});
3954}4112}
39554113
3956fn airWasmMemoryGrow(self: *Self, inst: Air.Inst.Index) !WValue {4114fn airWasmMemoryGrow(self: *Self, inst: Air.Inst.Index) !void {
3957 const pl_op = self.air.instructions.items(.data)[inst].pl_op;4115 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3958 const operand = try self.resolveInst(pl_op.operand);4116 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{pl_op.operand});
39594117
4118 const operand = try self.resolveInst(pl_op.operand);
3960 const result = try self.allocLocal(self.air.typeOfIndex(inst));4119 const result = try self.allocLocal(self.air.typeOfIndex(inst));
3961 try self.emitWValue(operand);4120 try self.emitWValue(operand);
3962 try self.addLabel(.memory_grow, pl_op.payload);4121 try self.addLabel(.memory_grow, pl_op.payload);
3963 try self.addLabel(.local_set, result.local);4122 try self.addLabel(.local_set, result.local);
3964 return result;4123 self.finishAir(inst, result, &.{pl_op.operand});
3965}4124}
39664125
3967fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {4126fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
...@@ -4042,17 +4201,18 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma...@@ -4042,17 +4201,18 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma
4042 return WValue{ .stack = {} };4201 return WValue{ .stack = {} };
4043}4202}
40444203
4045fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4204fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
4046 const bin_op = self.air.instructions.items(.data)[inst].bin_op;4205 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
4047 const un_ty = self.air.typeOf(bin_op.lhs).childType();4206 const un_ty = self.air.typeOf(bin_op.lhs).childType();
4048 const tag_ty = self.air.typeOf(bin_op.rhs);4207 const tag_ty = self.air.typeOf(bin_op.rhs);
4049 const layout = un_ty.unionGetLayout(self.target);4208 const layout = un_ty.unionGetLayout(self.target);
4050 if (layout.tag_size == 0) return WValue{ .none = {} };4209 if (layout.tag_size == 0) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4210
4051 const union_ptr = try self.resolveInst(bin_op.lhs);4211 const union_ptr = try self.resolveInst(bin_op.lhs);
4052 const new_tag = try self.resolveInst(bin_op.rhs);4212 const new_tag = try self.resolveInst(bin_op.rhs);
4053 if (layout.payload_size == 0) {4213 if (layout.payload_size == 0) {
4054 try self.store(union_ptr, new_tag, tag_ty, 0);4214 try self.store(union_ptr, new_tag, tag_ty, 0);
4055 return WValue{ .none = {} };4215 return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4056 }4216 }
40574217
4058 // when the tag alignment is smaller than the payload, the field will be stored4218 // when the tag alignment is smaller than the payload, the field will be stored
...@@ -4061,37 +4221,38 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4061,37 +4221,38 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4061 break :blk @intCast(u32, layout.payload_size);4221 break :blk @intCast(u32, layout.payload_size);
4062 } else @as(u32, 0);4222 } else @as(u32, 0);
4063 try self.store(union_ptr, new_tag, tag_ty, offset);4223 try self.store(union_ptr, new_tag, tag_ty, offset);
4064 return WValue{ .none = {} };4224 self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4065}4225}
40664226
4067fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4227fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
4068 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4069
4070 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4228 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4229 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
4230
4071 const un_ty = self.air.typeOf(ty_op.operand);4231 const un_ty = self.air.typeOf(ty_op.operand);
4072 const tag_ty = self.air.typeOfIndex(inst);4232 const tag_ty = self.air.typeOfIndex(inst);
4073 const layout = un_ty.unionGetLayout(self.target);4233 const layout = un_ty.unionGetLayout(self.target);
4074 if (layout.tag_size == 0) return WValue{ .none = {} };4234 if (layout.tag_size == 0) return self.finishAir(inst, .none, &.{ty_op.operand});
4075 const operand = try self.resolveInst(ty_op.operand);
40764235
4236 const operand = try self.resolveInst(ty_op.operand);
4077 // when the tag alignment is smaller than the payload, the field will be stored4237 // when the tag alignment is smaller than the payload, the field will be stored
4078 // after the payload.4238 // after the payload.
4079 const offset = if (layout.tag_align < layout.payload_align) blk: {4239 const offset = if (layout.tag_align < layout.payload_align) blk: {
4080 break :blk @intCast(u32, layout.payload_size);4240 break :blk @intCast(u32, layout.payload_size);
4081 } else @as(u32, 0);4241 } else @as(u32, 0);
4082 const tag = try self.load(operand, tag_ty, offset);4242 const tag = try self.load(operand, tag_ty, offset);
4083 return tag.toLocal(self, tag_ty);4243 const result = try tag.toLocal(self, tag_ty);
4244 self.finishAir(inst, result, &.{ty_op.operand});
4084}4245}
40854246
4086fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4247fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!void {
4087 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4088
4089 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4248 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4249 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
4250
4090 const dest_ty = self.air.typeOfIndex(inst);4251 const dest_ty = self.air.typeOfIndex(inst);
4091 const operand = try self.resolveInst(ty_op.operand);4252 const operand = try self.resolveInst(ty_op.operand);
4092
4093 const extended = try self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty);4253 const extended = try self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty);
4094 return extended.toLocal(self, dest_ty);4254 const result = try extended.toLocal(self, dest_ty);
4255 self.finishAir(inst, result, &.{ty_op.operand});
4095}4256}
40964257
4097/// Extends a float from a given `Type` to a larger wanted `Type`4258/// Extends a float from a given `Type` to a larger wanted `Type`
...@@ -4127,14 +4288,15 @@ fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WVa...@@ -4127,14 +4288,15 @@ fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WVa
4127 }4288 }
4128}4289}
41294290
4130fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4291fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
4131 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4132
4133 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4292 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4293 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
4294
4134 const dest_ty = self.air.typeOfIndex(inst);4295 const dest_ty = self.air.typeOfIndex(inst);
4135 const operand = try self.resolveInst(ty_op.operand);4296 const operand = try self.resolveInst(ty_op.operand);
4136 const trunc = try self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty);4297 const trunc = try self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty);
4137 return trunc.toLocal(self, dest_ty);4298 const result = try trunc.toLocal(self, dest_ty);
4299 self.finishAir(inst, result, &.{ty_op.operand});
4138}4300}
41394301
4140/// Truncates a float from a given `Type` to its wanted `Type`4302/// Truncates a float from a given `Type` to its wanted `Type`
...@@ -4162,8 +4324,10 @@ fn fptrunc(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W...@@ -4162,8 +4324,10 @@ fn fptrunc(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W
4162 }4324 }
4163}4325}
41644326
4165fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4327fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
4166 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4328 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4329 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
4330
4167 const err_set_ty = self.air.typeOf(ty_op.operand).childType();4331 const err_set_ty = self.air.typeOf(ty_op.operand).childType();
4168 const payload_ty = err_set_ty.errorUnionPayload();4332 const payload_ty = err_set_ty.errorUnionPayload();
4169 const operand = try self.resolveInst(ty_op.operand);4333 const operand = try self.resolveInst(ty_op.operand);
...@@ -4176,50 +4340,54 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue...@@ -4176,50 +4340,54 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue
4176 @intCast(u32, errUnionErrorOffset(payload_ty, self.target)),4340 @intCast(u32, errUnionErrorOffset(payload_ty, self.target)),
4177 );4341 );
41784342
4179 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };4343 const result = result: {
4344 if (self.liveness.isUnused(inst)) break :result WValue{ .none = {} };
41804345
4181 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {4346 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
4182 return operand;4347 break :result operand;
4183 }4348 }
41844349
4185 return self.buildPointerOffset(operand, @intCast(u32, errUnionPayloadOffset(payload_ty, self.target)), .new);4350 break :result try self.buildPointerOffset(operand, @intCast(u32, errUnionPayloadOffset(payload_ty, self.target)), .new);
4351 };
4352 self.finishAir(inst, result, &.{ty_op.operand});
4186}4353}
41874354
4188fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4355fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4189 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4190
4191 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;4356 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4192 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;4357 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
4193 const field_ptr = try self.resolveInst(extra.field_ptr);4358 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{extra.field_ptr});
41944359
4360 const field_ptr = try self.resolveInst(extra.field_ptr);
4195 const struct_ty = self.air.getRefType(ty_pl.ty).childType();4361 const struct_ty = self.air.getRefType(ty_pl.ty).childType();
4196 const field_offset = struct_ty.structFieldOffset(extra.field_index, self.target);4362 const field_offset = struct_ty.structFieldOffset(extra.field_index, self.target);
41974363
4198 if (field_offset == 0) {4364 const result = if (field_offset != 0) result: {
4199 return field_ptr;4365 const base = try self.buildPointerOffset(field_ptr, 0, .new);
4200 }4366 try self.addLabel(.local_get, base.local);
4367 try self.addImm32(@bitCast(i32, @intCast(u32, field_offset)));
4368 try self.addTag(.i32_sub);
4369 try self.addLabel(.local_set, base.local);
4370 break :result base;
4371 } else field_ptr;
42014372
4202 const base = try self.buildPointerOffset(field_ptr, 0, .new);4373 self.finishAir(inst, result, &.{extra.field_ptr});
4203 try self.addLabel(.local_get, base.local);
4204 try self.addImm32(@bitCast(i32, @intCast(u32, field_offset)));
4205 try self.addTag(.i32_sub);
4206 try self.addLabel(.local_set, base.local);
4207 return base;
4208}4374}
42094375
4210fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4376fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void {
4211 const pl_op = self.air.instructions.items(.data)[inst].pl_op;4377 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4212 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;4378 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;
4213 const dst = try self.resolveInst(pl_op.operand);4379 const dst = try self.resolveInst(pl_op.operand);
4214 const src = try self.resolveInst(bin_op.lhs);4380 const src = try self.resolveInst(bin_op.lhs);
4215 const len = try self.resolveInst(bin_op.rhs);4381 const len = try self.resolveInst(bin_op.rhs);
4216 try self.memcpy(dst, src, len);4382 try self.memcpy(dst, src, len);
4217 return WValue{ .none = {} };4383
4384 self.finishAir(inst, .none, &.{ pl_op.operand, bin_op.lhs, bin_op.rhs });
4218}4385}
42194386
4220fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4387fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!void {
4221 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4222 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4388 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4389 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
4390
4223 const operand = try self.resolveInst(ty_op.operand);4391 const operand = try self.resolveInst(ty_op.operand);
4224 const op_ty = self.air.typeOf(ty_op.operand);4392 const op_ty = self.air.typeOf(ty_op.operand);
4225 const result_ty = self.air.typeOfIndex(inst);4393 const result_ty = self.air.typeOfIndex(inst);
...@@ -4258,15 +4426,14 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4258,15 +4426,14 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
42584426
4259 const result = try self.allocLocal(result_ty);4427 const result = try self.allocLocal(result_ty);
4260 try self.addLabel(.local_set, result.local);4428 try self.addLabel(.local_set, result.local);
4261 return result;4429 self.finishAir(inst, result, &.{ty_op.operand});
4262}4430}
42634431
4264fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4432fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!void {
4265 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4266
4267 const un_op = self.air.instructions.items(.data)[inst].un_op;4433 const un_op = self.air.instructions.items(.data)[inst].un_op;
4268 const operand = try self.resolveInst(un_op);4434 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{un_op});
42694435
4436 const operand = try self.resolveInst(un_op);
4270 // First retrieve the symbol index to the error name table4437 // First retrieve the symbol index to the error name table
4271 // that will be used to emit a relocation for the pointer4438 // that will be used to emit a relocation for the pointer
4272 // to the error name table.4439 // to the error name table.
...@@ -4301,21 +4468,23 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4301,21 +4468,23 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
43014468
4302 const result_ptr = try self.allocLocal(Type.usize);4469 const result_ptr = try self.allocLocal(Type.usize);
4303 try self.addLabel(.local_set, result_ptr.local);4470 try self.addLabel(.local_set, result_ptr.local);
4304 return result_ptr;4471 self.finishAir(inst, result_ptr, &.{un_op});
4305}4472}
43064473
4307fn airPtrSliceFieldPtr(self: *Self, inst: Air.Inst.Index, offset: u32) InnerError!WValue {4474fn airPtrSliceFieldPtr(self: *Self, inst: Air.Inst.Index, offset: u32) InnerError!void {
4308 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4309
4310 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4475 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4476 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
4311 const slice_ptr = try self.resolveInst(ty_op.operand);4477 const slice_ptr = try self.resolveInst(ty_op.operand);
4312 return self.buildPointerOffset(slice_ptr, offset, .new);4478 const result = try self.buildPointerOffset(slice_ptr, offset, .new);
4479 self.finishAir(inst, result, &.{ty_op.operand});
4313}4480}
43144481
4315fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {4482fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
4316 assert(op == .add or op == .sub);4483 assert(op == .add or op == .sub);
4317 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;4484 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4318 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;4485 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
4486 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
4487
4319 const lhs_op = try self.resolveInst(extra.lhs);4488 const lhs_op = try self.resolveInst(extra.lhs);
4320 const rhs_op = try self.resolveInst(extra.rhs);4489 const rhs_op = try self.resolveInst(extra.rhs);
4321 const lhs_ty = self.air.typeOf(extra.lhs);4490 const lhs_ty = self.air.typeOf(extra.lhs);
...@@ -4331,7 +4500,8 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W...@@ -4331,7 +4500,8 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
4331 };4500 };
43324501
4333 if (wasm_bits == 128) {4502 if (wasm_bits == 128) {
4334 return self.airAddSubWithOverflowBigInt(lhs_op, rhs_op, lhs_ty, self.air.typeOfIndex(inst), op);4503 const result = try self.addSubWithOverflowBigInt(lhs_op, rhs_op, lhs_ty, self.air.typeOfIndex(inst), op);
4504 return self.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
4335 }4505 }
43364506
4337 const zero = switch (wasm_bits) {4507 const zero = switch (wasm_bits) {
...@@ -4349,6 +4519,15 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W...@@ -4349,6 +4519,15 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
4349 break :blk try (try self.signAbsValue(rhs_op, lhs_ty)).toLocal(self, lhs_ty);4519 break :blk try (try self.signAbsValue(rhs_op, lhs_ty)).toLocal(self, lhs_ty);
4350 } else rhs_op;4520 } else rhs_op;
43514521
4522 // in this case, we performed a signAbsValue which created a temporary local
4523 // so let's free this so it can be re-used instead.
4524 // In the other case we do not want to free it, because that would free the
4525 // resolved instructions which may be referenced by other instructions.
4526 defer if (wasm_bits != int_info.bits and is_signed) {
4527 lhs.free(self);
4528 rhs.free(self);
4529 };
4530
4352 var bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty);4531 var bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty);
4353 defer bin_op.free(self);4532 defer bin_op.free(self);
4354 var result = if (wasm_bits != int_info.bits) blk: {4533 var result = if (wasm_bits != int_info.bits) blk: {
...@@ -4377,19 +4556,10 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W...@@ -4377,19 +4556,10 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
4377 const offset = @intCast(u32, lhs_ty.abiSize(self.target));4556 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
4378 try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset);4557 try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset);
43794558
4380 // in this case, we performed a signAbsValue which created a temporary local4559 self.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
4381 // so let's free this so it can be re-used instead.
4382 // In the other case we do not want to free it, because that would free the
4383 // resolved instructions which may be referenced by other instructions.
4384 if (wasm_bits != int_info.bits and is_signed) {
4385 lhs.free(self);
4386 rhs.free(self);
4387 }
4388
4389 return result_ptr;
4390}4560}
43914561
4392fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, result_ty: Type, op: Op) InnerError!WValue {4562fn addSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, result_ty: Type, op: Op) InnerError!WValue {
4393 assert(op == .add or op == .sub);4563 assert(op == .add or op == .sub);
4394 const int_info = ty.intInfo(self.target);4564 const int_info = ty.intInfo(self.target);
4395 const is_signed = int_info.signedness == .signed;4565 const is_signed = int_info.signedness == .signed;
...@@ -4453,9 +4623,11 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type,...@@ -4453,9 +4623,11 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type,
4453 return result_ptr;4623 return result_ptr;
4454}4624}
44554625
4456fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4626fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
4457 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;4627 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4458 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;4628 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
4629 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
4630
4459 const lhs = try self.resolveInst(extra.lhs);4631 const lhs = try self.resolveInst(extra.lhs);
4460 const rhs = try self.resolveInst(extra.rhs);4632 const rhs = try self.resolveInst(extra.rhs);
4461 const lhs_ty = self.air.typeOf(extra.lhs);4633 const lhs_ty = self.air.typeOf(extra.lhs);
...@@ -4496,12 +4668,14 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4496,12 +4668,14 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4496 const offset = @intCast(u32, lhs_ty.abiSize(self.target));4668 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
4497 try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset);4669 try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset);
44984670
4499 return result_ptr;4671 self.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
4500}4672}
45014673
4502fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4674fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
4503 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;4675 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4504 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;4676 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
4677 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
4678
4505 const lhs = try self.resolveInst(extra.lhs);4679 const lhs = try self.resolveInst(extra.lhs);
4506 const rhs = try self.resolveInst(extra.rhs);4680 const rhs = try self.resolveInst(extra.rhs);
4507 const lhs_ty = self.air.typeOf(extra.lhs);4681 const lhs_ty = self.air.typeOf(extra.lhs);
...@@ -4581,12 +4755,13 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4581,12 +4755,13 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4581 const offset = @intCast(u32, lhs_ty.abiSize(self.target));4755 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
4582 try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset);4756 try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset);
45834757
4584 return result_ptr;4758 self.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
4585}4759}
45864760
4587fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerError!WValue {4761fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerError!void {
4588 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4589 const bin_op = self.air.instructions.items(.data)[inst].bin_op;4762 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
4763 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4764
4590 const ty = self.air.typeOfIndex(inst);4765 const ty = self.air.typeOfIndex(inst);
4591 if (ty.zigTypeTag() == .Vector) {4766 if (ty.zigTypeTag() == .Vector) {
4592 return self.fail("TODO: `@maximum` and `@minimum` for vectors", .{});4767 return self.fail("TODO: `@maximum` and `@minimum` for vectors", .{});
...@@ -4611,13 +4786,14 @@ fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerErro...@@ -4611,13 +4786,14 @@ fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerErro
4611 const result_ty = if (isByRef(ty, self.target)) Type.u32 else ty;4786 const result_ty = if (isByRef(ty, self.target)) Type.u32 else ty;
4612 const result = try self.allocLocal(result_ty);4787 const result = try self.allocLocal(result_ty);
4613 try self.addLabel(.local_set, result.local);4788 try self.addLabel(.local_set, result.local);
4614 return result;4789 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
4615}4790}
46164791
4617fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4792fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!void {
4618 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4619 const pl_op = self.air.instructions.items(.data)[inst].pl_op;4793 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4620 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;4794 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;
4795 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4796
4621 const ty = self.air.typeOfIndex(inst);4797 const ty = self.air.typeOfIndex(inst);
4622 if (ty.zigTypeTag() == .Vector) {4798 if (ty.zigTypeTag() == .Vector) {
4623 return self.fail("TODO: `@mulAdd` for vectors", .{});4799 return self.fail("TODO: `@mulAdd` for vectors", .{});
...@@ -4627,7 +4803,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4627,7 +4803,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4627 const lhs = try self.resolveInst(bin_op.lhs);4803 const lhs = try self.resolveInst(bin_op.lhs);
4628 const rhs = try self.resolveInst(bin_op.rhs);4804 const rhs = try self.resolveInst(bin_op.rhs);
46294805
4630 if (ty.floatBits(self.target) == 16) {4806 const result = if (ty.floatBits(self.target) == 16) fl_result: {
4631 const rhs_ext = try self.fpext(rhs, ty, Type.f32);4807 const rhs_ext = try self.fpext(rhs, ty, Type.f32);
4632 const lhs_ext = try self.fpext(lhs, ty, Type.f32);4808 const lhs_ext = try self.fpext(lhs, ty, Type.f32);
4633 const addend_ext = try self.fpext(addend, ty, Type.f32);4809 const addend_ext = try self.fpext(addend, ty, Type.f32);
...@@ -4638,16 +4814,19 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4638,16 +4814,19 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4638 Type.f32,4814 Type.f32,
4639 &.{ rhs_ext, lhs_ext, addend_ext },4815 &.{ rhs_ext, lhs_ext, addend_ext },
4640 );4816 );
4641 return try (try self.fptrunc(result, Type.f32, ty)).toLocal(self, ty);4817 break :fl_result try (try self.fptrunc(result, Type.f32, ty)).toLocal(self, ty);
4642 }4818 } else result: {
4819 const mul_result = try self.binOp(lhs, rhs, ty, .mul);
4820 break :result try (try self.binOp(mul_result, addend, ty, .add)).toLocal(self, ty);
4821 };
46434822
4644 const mul_result = try self.binOp(lhs, rhs, ty, .mul);4823 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
4645 return (try self.binOp(mul_result, addend, ty, .add)).toLocal(self, ty);
4646}4824}
46474825
4648fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4826fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!void {
4649 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4650 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4827 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4828 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
4829
4651 const ty = self.air.typeOf(ty_op.operand);4830 const ty = self.air.typeOf(ty_op.operand);
4652 const result_ty = self.air.typeOfIndex(inst);4831 const result_ty = self.air.typeOfIndex(inst);
4653 if (ty.zigTypeTag() == .Vector) {4832 if (ty.zigTypeTag() == .Vector) {
...@@ -4694,12 +4873,13 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4694,12 +4873,13 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
46944873
4695 const result = try self.allocLocal(result_ty);4874 const result = try self.allocLocal(result_ty);
4696 try self.addLabel(.local_set, result.local);4875 try self.addLabel(.local_set, result.local);
4697 return result;4876 self.finishAir(inst, result, &.{ty_op.operand});
4698}4877}
46994878
4700fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4879fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!void {
4701 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4702 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4880 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4881 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
4882
4703 const ty = self.air.typeOf(ty_op.operand);4883 const ty = self.air.typeOf(ty_op.operand);
4704 const result_ty = self.air.typeOfIndex(inst);4884 const result_ty = self.air.typeOfIndex(inst);
47054885
...@@ -4758,11 +4938,11 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4758,11 +4938,11 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
47584938
4759 const result = try self.allocLocal(result_ty);4939 const result = try self.allocLocal(result_ty);
4760 try self.addLabel(.local_set, result.local);4940 try self.addLabel(.local_set, result.local);
4761 return result;4941 self.finishAir(inst, result, &.{ty_op.operand});
4762}4942}
47634943
4764fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue {4944fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !void {
4765 if (self.debug_output != .dwarf) return WValue{ .none = {} };4945 if (self.debug_output != .dwarf) return self.finishAir(inst, .none, &.{});
47664946
4767 const pl_op = self.air.instructions.items(.data)[inst].pl_op;4947 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4768 const ty = self.air.typeOf(pl_op.operand);4948 const ty = self.air.typeOf(pl_op.operand);
...@@ -4799,11 +4979,11 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue {...@@ -4799,11 +4979,11 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue {
4799 try self.addDbgInfoTypeReloc(op_ty);4979 try self.addDbgInfoTypeReloc(op_ty);
4800 dbg_info.appendSliceAssumeCapacity(name);4980 dbg_info.appendSliceAssumeCapacity(name);
4801 dbg_info.appendAssumeCapacity(0);4981 dbg_info.appendAssumeCapacity(0);
4802 return WValue{ .none = {} };4982 self.finishAir(inst, .none, &.{});
4803}4983}
48044984
4805fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue {4985fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
4806 if (self.debug_output != .dwarf) return WValue{ .none = {} };4986 if (self.debug_output != .dwarf) return self.finishAir(inst, .none, &.{});
48074987
4808 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;4988 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
4809 try self.addInst(.{ .tag = .dbg_line, .data = .{4989 try self.addInst(.{ .tag = .dbg_line, .data = .{
...@@ -4812,25 +4992,27 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -4812,25 +4992,27 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue {
4812 .column = dbg_stmt.column,4992 .column = dbg_stmt.column,
4813 }),4993 }),
4814 } });4994 } });
4815 return WValue{ .none = {} };4995 self.finishAir(inst, .none, &.{});
4816}4996}
48174997
4818fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!WValue {4998fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!void {
4819 const pl_op = self.air.instructions.items(.data)[inst].pl_op;4999 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4820 const err_union = try self.resolveInst(pl_op.operand);5000 const err_union = try self.resolveInst(pl_op.operand);
4821 const extra = self.air.extraData(Air.Try, pl_op.payload);5001 const extra = self.air.extraData(Air.Try, pl_op.payload);
4822 const body = self.air.extra[extra.end..][0..extra.data.body_len];5002 const body = self.air.extra[extra.end..][0..extra.data.body_len];
4823 const err_union_ty = self.air.typeOf(pl_op.operand);5003 const err_union_ty = self.air.typeOf(pl_op.operand);
4824 return lowerTry(self, err_union, body, err_union_ty, false);5004 const result = try lowerTry(self, err_union, body, err_union_ty, false);
5005 self.finishAir(inst, result, &.{pl_op.operand});
4825}5006}
48265007
4827fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {5008fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4828 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;5009 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4829 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);5010 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
4830 const err_union_ptr = try self.resolveInst(extra.data.ptr);5011 const err_union_ptr = try self.resolveInst(extra.data.ptr);
4831 const body = self.air.extra[extra.end..][0..extra.data.body_len];5012 const body = self.air.extra[extra.end..][0..extra.data.body_len];
4832 const err_union_ty = self.air.typeOf(extra.data.ptr).childType();5013 const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
4833 return lowerTry(self, err_union_ptr, body, err_union_ty, true);5014 const result = try lowerTry(self, err_union_ptr, body, err_union_ty, true);
5015 self.finishAir(inst, result, &.{extra.data.ptr});
4834}5016}
48355017
4836fn lowerTry(5018fn lowerTry(
...@@ -4879,12 +5061,10 @@ fn lowerTry(...@@ -4879,12 +5061,10 @@ fn lowerTry(
4879 return payload.toLocal(self, pl_ty);5061 return payload.toLocal(self, pl_ty);
4880}5062}
48815063
4882fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {5064fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!void {
4883 if (self.liveness.isUnused(inst)) {
4884 return WValue{ .none = {} };
4885 }
4886
4887 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5065 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5066 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
5067
4888 const ty = self.air.typeOfIndex(inst);5068 const ty = self.air.typeOfIndex(inst);
4889 const operand = try self.resolveInst(ty_op.operand);5069 const operand = try self.resolveInst(ty_op.operand);
48905070
...@@ -4895,84 +5075,89 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4895,84 +5075,89 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
48955075
4896 // bytes are no-op5076 // bytes are no-op
4897 if (int_info.bits == 8) {5077 if (int_info.bits == 8) {
4898 return operand;5078 return self.finishAir(inst, operand, &.{ty_op.operand});
4899 }5079 }
49005080
4901 switch (int_info.bits) {5081 const result = result: {
4902 16 => {5082 switch (int_info.bits) {
4903 const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);5083 16 => {
4904 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and");5084 const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
4905 const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);5085 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and");
4906 const res = if (int_info.signedness == .signed) blk: {5086 const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
4907 break :blk try self.wrapOperand(shr_res, Type.u8);5087 const res = if (int_info.signedness == .signed) blk: {
4908 } else shr_res;5088 break :blk try self.wrapOperand(shr_res, Type.u8);
4909 return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty);5089 } else shr_res;
4910 },5090 break :result try (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty);
4911 24 => {5091 },
4912 var msb = try (try self.wrapOperand(operand, Type.u16)).toLocal(self, Type.u16);5092 24 => {
4913 defer msb.free(self);5093 var msb = try (try self.wrapOperand(operand, Type.u16)).toLocal(self, Type.u16);
49145094 defer msb.free(self);
4915 const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl);5095
4916 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and");5096 const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl);
4917 const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr);5097 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and");
49185098 const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr);
4919 const res = if (int_info.signedness == .signed) blk: {5099
4920 break :blk try self.wrapOperand(shr_res, Type.u8);5100 const res = if (int_info.signedness == .signed) blk: {
4921 } else shr_res;5101 break :blk try self.wrapOperand(shr_res, Type.u8);
4922 const lhs_tmp = try self.binOp(lhs, res, ty, .@"or");5102 } else shr_res;
4923 const lhs_result = try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr);5103 const lhs_tmp = try self.binOp(lhs, res, ty, .@"or");
4924 const rhs_wrap = try self.wrapOperand(msb, Type.u8);5104 const lhs_result = try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr);
4925 const rhs_result = try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl);5105 const rhs_wrap = try self.wrapOperand(msb, Type.u8);
49265106 const rhs_result = try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl);
4927 const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr);5107
4928 const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or");5108 const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr);
4929 return (try self.binOp(tmp, lsb, ty, .@"or")).toLocal(self, ty);5109 const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or");
4930 },5110 break :result try (try self.binOp(tmp, lsb, ty, .@"or")).toLocal(self, ty);
4931 32 => {5111 },
4932 const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);5112 32 => {
4933 var lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty);5113 const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
4934 defer lhs.free(self);5114 var lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty);
4935 const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);5115 defer lhs.free(self);
4936 var rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty);5116 const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
4937 defer rhs.free(self);5117 var rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty);
4938 var tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty);5118 defer rhs.free(self);
4939 defer tmp_or.free(self);5119 var tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty);
49405120 defer tmp_or.free(self);
4941 const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl);5121
4942 const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr);5122 const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl);
4943 const res = if (int_info.signedness == .signed) blk: {5123 const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr);
4944 break :blk try self.wrapOperand(shr, Type.u16);5124 const res = if (int_info.signedness == .signed) blk: {
4945 } else shr;5125 break :blk try self.wrapOperand(shr, Type.u16);
4946 return (try self.binOp(shl, res, ty, .@"or")).toLocal(self, ty);5126 } else shr;
4947 },5127 break :result try (try self.binOp(shl, res, ty, .@"or")).toLocal(self, ty);
4948 else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),5128 },
4949 }5129 else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),
5130 }
5131 };
5132 self.finishAir(inst, result, &.{ty_op.operand});
4950}5133}
49515134
4952fn airDiv(self: *Self, inst: Air.Inst.Index) InnerError!WValue {5135fn airDiv(self: *Self, inst: Air.Inst.Index) InnerError!void {
4953 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4954
4955 const bin_op = self.air.instructions.items(.data)[inst].bin_op;5136 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5137 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
5138
4956 const ty = self.air.typeOfIndex(inst);5139 const ty = self.air.typeOfIndex(inst);
4957 const lhs = try self.resolveInst(bin_op.lhs);5140 const lhs = try self.resolveInst(bin_op.lhs);
4958 const rhs = try self.resolveInst(bin_op.rhs);5141 const rhs = try self.resolveInst(bin_op.rhs);
49595142
4960 if (ty.isSignedInt()) {5143 const result = if (ty.isSignedInt())
4961 return self.divSigned(lhs, rhs, ty);5144 try self.divSigned(lhs, rhs, ty)
4962 }5145 else
4963 return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);5146 try (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);
5147 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
4964}5148}
49655149
4966fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {5150fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!void {
4967 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4968
4969 const bin_op = self.air.instructions.items(.data)[inst].bin_op;5151 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5152 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
5153
4970 const ty = self.air.typeOfIndex(inst);5154 const ty = self.air.typeOfIndex(inst);
4971 const lhs = try self.resolveInst(bin_op.lhs);5155 const lhs = try self.resolveInst(bin_op.lhs);
4972 const rhs = try self.resolveInst(bin_op.rhs);5156 const rhs = try self.resolveInst(bin_op.rhs);
49735157
4974 if (ty.isUnsignedInt()) {5158 if (ty.isUnsignedInt()) {
4975 return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);5159 const result = try (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);
5160 return self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
4976 } else if (ty.isSignedInt()) {5161 } else if (ty.isSignedInt()) {
4977 const int_bits = ty.intInfo(self.target).bits;5162 const int_bits = ty.intInfo(self.target).bits;
4978 const wasm_bits = toWasmBits(int_bits) orelse {5163 const wasm_bits = toWasmBits(int_bits) orelse {
...@@ -5048,7 +5233,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -5048,7 +5233,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
50485233
5049 const result = try self.allocLocal(ty);5234 const result = try self.allocLocal(ty);
5050 try self.addLabel(.local_set, result.local);5235 try self.addLabel(.local_set, result.local);
5051 return result;5236 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
5052}5237}
50535238
5054fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue {5239fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue {
...@@ -5110,10 +5295,10 @@ fn signAbsValue(self: *Self, operand: WValue, ty: Type) InnerError!WValue {...@@ -5110,10 +5295,10 @@ fn signAbsValue(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
5110 return WValue{ .stack = {} };5295 return WValue{ .stack = {} };
5111}5296}
51125297
5113fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {5298fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
5114 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
5115
5116 const un_op = self.air.instructions.items(.data)[inst].un_op;5299 const un_op = self.air.instructions.items(.data)[inst].un_op;
5300 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{un_op});
5301
5117 const ty = self.air.typeOfIndex(inst);5302 const ty = self.air.typeOfIndex(inst);
5118 const float_bits = ty.floatBits(self.target);5303 const float_bits = ty.floatBits(self.target);
5119 const is_f16 = float_bits == 16;5304 const is_f16 = float_bits == 16;
...@@ -5139,14 +5324,14 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu...@@ -5139,14 +5324,14 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu
51395324
5140 const result = try self.allocLocal(ty);5325 const result = try self.allocLocal(ty);
5141 try self.addLabel(.local_set, result.local);5326 try self.addLabel(.local_set, result.local);
5142 return result;5327 self.finishAir(inst, result, &.{un_op});
5143}5328}
51445329
5145fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {5330fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
5146 assert(op == .add or op == .sub);5331 assert(op == .add or op == .sub);
5147 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
5148
5149 const bin_op = self.air.instructions.items(.data)[inst].bin_op;5332 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5333 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
5334
5150 const ty = self.air.typeOfIndex(inst);5335 const ty = self.air.typeOfIndex(inst);
5151 const lhs = try self.resolveInst(bin_op.lhs);5336 const lhs = try self.resolveInst(bin_op.lhs);
5152 const rhs = try self.resolveInst(bin_op.rhs);5337 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -5159,7 +5344,8 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -5159,7 +5344,8 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
5159 }5344 }
51605345
5161 if (is_signed) {5346 if (is_signed) {
5162 return signedSat(self, lhs, rhs, ty, op);5347 const result = try signedSat(self, lhs, rhs, ty, op);
5348 return self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
5163 }5349 }
51645350
5165 const wasm_bits = toWasmBits(int_info.bits).?;5351 const wasm_bits = toWasmBits(int_info.bits).?;
...@@ -5189,7 +5375,7 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -5189,7 +5375,7 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
5189 try self.addTag(.select);5375 try self.addTag(.select);
5190 const result = try self.allocLocal(ty);5376 const result = try self.allocLocal(ty);
5191 try self.addLabel(.local_set, result.local);5377 try self.addLabel(.local_set, result.local);
5192 return result;5378 return self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
5193}5379}
51945380
5195fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue {5381fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue {
...@@ -5255,10 +5441,10 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op...@@ -5255,10 +5441,10 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op
5255 }5441 }
5256}5442}
52575443
5258fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {5444fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
5259 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
5260
5261 const bin_op = self.air.instructions.items(.data)[inst].bin_op;5445 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5446 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
5447
5262 const ty = self.air.typeOfIndex(inst);5448 const ty = self.air.typeOfIndex(inst);
5263 const int_info = ty.intInfo(self.target);5449 const int_info = ty.intInfo(self.target);
5264 const is_signed = int_info.signedness == .signed;5450 const is_signed = int_info.signedness == .signed;
...@@ -5271,7 +5457,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -5271,7 +5457,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
5271 const wasm_bits = toWasmBits(int_info.bits).?;5457 const wasm_bits = toWasmBits(int_info.bits).?;
5272 const result = try self.allocLocal(ty);5458 const result = try self.allocLocal(ty);
52735459
5274 if (wasm_bits == int_info.bits) {5460 if (wasm_bits == int_info.bits) outer_blk: {
5275 var shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty);5461 var shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty);
5276 defer shl.free(self);5462 defer shl.free(self);
5277 var shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);5463 var shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);
...@@ -5304,7 +5490,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -5304,7 +5490,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
5304 _ = try self.cmp(lhs, shr, ty, .neq);5490 _ = try self.cmp(lhs, shr, ty, .neq);
5305 try self.addTag(.select);5491 try self.addTag(.select);
5306 try self.addLabel(.local_set, result.local);5492 try self.addLabel(.local_set, result.local);
5307 return result;5493 break :outer_blk;
5308 } else {5494 } else {
5309 const shift_size = wasm_bits - int_info.bits;5495 const shift_size = wasm_bits - int_info.bits;
5310 const shift_value = switch (wasm_bits) {5496 const shift_value = switch (wasm_bits) {
...@@ -5353,8 +5539,10 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -5353,8 +5539,10 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
5353 if (is_signed) {5539 if (is_signed) {
5354 shift_result = try self.wrapOperand(shift_result, ty);5540 shift_result = try self.wrapOperand(shift_result, ty);
5355 }5541 }
5356 return shift_result.toLocal(self, ty);5542 try self.addLabel(.local_set, result.local);
5357 }5543 }
5544
5545 return self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
5358}5546}
53595547
5360/// Calls a compiler-rt intrinsic by creating an undefined symbol,5548/// Calls a compiler-rt intrinsic by creating an undefined symbol,