authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-07-27 19:14:42+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-08-11 11:08:00+02:00
loga5e4fd7ef66bbd16ddad1a12d378eefcb740da1d
treeda07a631ac4ca59f6f7912e8867a7e3c8240ff55
parent3cd0cd12a08435fca5f5b2b6788ff519abfc6184
signaturelock-open Commit is signed but in an unrecognized format.

wasm: keep `load` values on the stack

We internally use a lot of `load`'s that used to put the result in a newly created local. For instance, when is considered byRef or when we need a specific field/element/bytes from a larger type. However, sometimes we want to directly use this value and then forget about it, which means storing it in a local first is wasted instructions as well as wasted locals that shouldn't be generated in the first place. With this change it's explicit and requires the usage of `toLocal`.

1 files changed, 116 insertions(+), 113 deletions(-)

src/arch/wasm/CodeGen.zig+116-113
......@@ -1789,8 +1789,8 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
17891789
17901790 const fn_info = self.decl.ty.fnInfo();
17911791 if (!firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
1792 const result = try self.load(operand, ret_ty, 0);
1793 try self.emitWValue(result);
1792 // leave on the stack
1793 _ = try self.load(operand, ret_ty, 0);
17941794 }
17951795
17961796 try self.restoreStackPointer();
......@@ -1943,20 +1943,26 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
19431943 .Pointer => {
19441944 if (ty.isSlice()) {
19451945 // store pointer first
1946 // lower it to the stack so we do not have to store rhs into a local first
1947 try self.emitWValue(lhs);
19461948 const ptr_local = try self.load(rhs, Type.usize, 0);
1947 try self.store(lhs, ptr_local, Type.usize, 0);
1949 try self.store(.{ .stack = {} }, ptr_local, Type.usize, 0 + lhs.offset());
19481950
19491951 // retrieve length from rhs, and store that alongside lhs as well
1952 try self.emitWValue(lhs);
19501953 const len_local = try self.load(rhs, Type.usize, self.ptrSize());
1951 try self.store(lhs, len_local, Type.usize, self.ptrSize());
1954 try self.store(.{ .stack = {} }, len_local, Type.usize, self.ptrSize() + lhs.offset());
19521955 return;
19531956 }
19541957 },
19551958 .Int => if (ty.intInfo(self.target).bits > 64) {
1959 try self.emitWValue(lhs);
19561960 const lsb = try self.load(rhs, Type.u64, 0);
1961 try self.store(.{ .stack = {} }, lsb, Type.u64, 0 + lhs.offset());
1962
1963 try self.emitWValue(lhs);
19571964 const msb = try self.load(rhs, Type.u64, 8);
1958 try self.store(lhs, lsb, Type.u64, 0);
1959 try self.store(lhs, msb, Type.u64, 8);
1965 try self.store(.{ .stack = {} }, msb, Type.u64, 8 + lhs.offset());
19601966 return;
19611967 },
19621968 else => {},
......@@ -1995,9 +2001,12 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
19952001 return new_local;
19962002 }
19972003
1998 return self.load(operand, ty, 0);
2004 const stack_loaded = try self.load(operand, ty, 0);
2005 return stack_loaded.toLocal(self, ty);
19992006}
20002007
2008/// Loads an operand from the linear memory section.
2009/// NOTE: Leaves the value on the stack.
20012010fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
20022011 // load local's value from memory by its stack position
20032012 try self.emitWValue(operand);
......@@ -2015,10 +2024,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
20152024 .{ .offset = offset + operand.offset(), .alignment = ty.abiAlignment(self.target) },
20162025 );
20172026
2018 // store the result in a local
2019 const result = try self.allocLocal(ty);
2020 try self.addLabel(.local_set, result.local);
2021 return result;
2027 return WValue{ .stack = {} };
20222028}
20232029
20242030fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -2124,18 +2130,15 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa
21242130 return WValue{ .stack = {} };
21252131}
21262132
2133/// Performs a binary operation for 16-bit floats.
2134/// NOTE: Leaves the result value on the stack
21272135fn binOpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue {
2128 const ext_lhs = try self.fpext(lhs, Type.f16, Type.f32);
2129 const ext_rhs = try self.fpext(rhs, Type.f16, Type.f32);
2130
21312136 const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = .f32, .signedness = .unsigned });
2132 try self.emitWValue(ext_lhs);
2133 try self.emitWValue(ext_rhs);
2137 _ = try self.fpext(lhs, Type.f16, Type.f32);
2138 _ = try self.fpext(rhs, Type.f16, Type.f32);
21342139 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
21352140
2136 // re-use temporary local
2137 try self.addLabel(.local_set, ext_lhs.local);
2138 return self.fptrunc(ext_lhs, Type.f32, Type.f16);
2141 return self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
21392142}
21402143
21412144fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
......@@ -2148,12 +2151,12 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr
21482151 }
21492152
21502153 const result = try self.allocStack(ty);
2151 const lhs_high_bit = try self.load(lhs, Type.u64, 0);
2154 const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
2155 const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
2156 const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
2157
21522158 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
2153 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
21542159 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
2155
2156 const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
21572160 const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);
21582161
21592162 const lt = if (op == .add) blk: {
......@@ -2204,14 +2207,14 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
22042207
22052208 if (wasm_bits == 128) {
22062209 assert(operand != .stack);
2207 const msb = try self.load(operand, Type.u64, 0);
22082210 const lsb = try self.load(operand, Type.u64, 8);
22092211
22102212 const result_ptr = try self.allocStack(ty);
2211 try self.store(result_ptr, lsb, Type.u64, 8);
2213 try self.emitWValue(result_ptr);
2214 try self.store(.{ .stack = {} }, lsb, Type.u64, 8 + result_ptr.offset());
22122215 const result = (@as(u64, 1) << @intCast(u6, 64 - (wasm_bits - bitsize))) - 1;
22132216 try self.emitWValue(result_ptr);
2214 try self.emitWValue(msb);
2217 _ = try self.load(operand, Type.u64, 0);
22152218 try self.addImm64(result);
22162219 try self.addTag(.i64_and);
22172220 try self.addMemArg(.i64_store, .{ .offset = result_ptr.offset(), .alignment = 8 });
......@@ -2692,10 +2695,9 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper
26922695 return WValue{ .stack = {} };
26932696}
26942697
2698/// Compares 16-bit floats
2699/// NOTE: The result value remains on top of the stack.
26952700fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperator) InnerError!WValue {
2696 const ext_lhs = try self.fpext(lhs, Type.f16, Type.f32);
2697 const ext_rhs = try self.fpext(rhs, Type.f16, Type.f32);
2698
26992701 const opcode: wasm.Opcode = buildOpcode(.{
27002702 .op = switch (op) {
27012703 .lt => .lt,
......@@ -2708,8 +2710,8 @@ fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperato
27082710 .valtype1 = .f32,
27092711 .signedness = .unsigned,
27102712 });
2711 try self.emitWValue(ext_lhs);
2712 try self.emitWValue(ext_rhs);
2713 _ = try self.fpext(lhs, Type.f16, Type.f32);
2714 _ = try self.fpext(rhs, Type.f16, Type.f32);
27132715 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
27142716
27152717 return WValue{ .stack = {} };
......@@ -2781,13 +2783,15 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27812783 },
27822784 128 => {
27832785 const result_ptr = try self.allocStack(operand_ty);
2786 try self.emitWValue(result_ptr);
27842787 const msb = try self.load(operand, Type.u64, 0);
2785 const lsb = try self.load(operand, Type.u64, 8);
2788 const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
2789 try self.store(.{ .stack = {} }, msb_xor, Type.u64, 0 + result_ptr.offset());
27862790
2787 const msb_xor = try (try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty);
2788 const lsb_xor = try (try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty);
2789 try self.store(result_ptr, msb_xor, Type.u64, 0);
2790 try self.store(result_ptr, lsb_xor, Type.u64, 8);
2791 try self.emitWValue(result_ptr);
2792 const lsb = try self.load(operand, Type.u64, 8);
2793 const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
2794 try self.store(result_ptr, lsb_xor, Type.u64, 8 + result_ptr.offset());
27912795 return result_ptr;
27922796 },
27932797 else => unreachable,
......@@ -2875,7 +2879,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28752879 }
28762880 }
28772881
2878 return self.load(operand, field_ty, offset);
2882 const field = try self.load(operand, field_ty, offset);
2883 return field.toLocal(self, field_ty);
28792884}
28802885
28812886fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3085,7 +3090,9 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool)
30853090 if (op_is_ptr or isByRef(payload_ty, self.target)) {
30863091 return self.buildPointerOffset(operand, pl_offset, .new);
30873092 }
3088 return self.load(operand, payload_ty, pl_offset);
3093
3094 const payload = try self.load(operand, payload_ty, pl_offset);
3095 return payload.toLocal(self, payload_ty);
30893096}
30903097
30913098fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue {
......@@ -3105,7 +3112,8 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In
31053112 return operand;
31063113 }
31073114
3108 return self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target)));
3115 const error_val = try self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target)));
3116 return error_val.toLocal(self, Type.anyerror);
31093117}
31103118
31113119fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3235,9 +3243,12 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en
32353243
32363244 const op_ty = self.air.typeOf(un_op);
32373245 const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty;
3238 return self.isNull(operand, optional_ty, opcode);
3246 const is_null = try self.isNull(operand, optional_ty, opcode);
3247 return is_null.toLocal(self, optional_ty);
32393248}
32403249
3250/// For a given type and operand, checks if it's considered `null`.
3251/// NOTE: Leaves the result on the stack
32413252fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {
32423253 try self.emitWValue(operand);
32433254 if (!optional_ty.optionalReprIsPayload()) {
......@@ -3254,9 +3265,7 @@ fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode)
32543265 try self.addImm32(0);
32553266 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
32563267
3257 const is_null_tmp = try self.allocLocal(Type.initTag(.i32));
3258 try self.addLabel(.local_set, is_null_tmp.local);
3259 return is_null_tmp;
3268 return WValue{ .stack = {} };
32603269}
32613270
32623271fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3274,7 +3283,8 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
32743283 return self.buildPointerOffset(operand, offset, .new);
32753284 }
32763285
3277 return self.load(operand, payload_ty, @intCast(u32, offset));
3286 const payload = try self.load(operand, payload_ty, @intCast(u32, offset));
3287 return payload.toLocal(self, payload_ty);
32783288}
32793289
32803290fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3377,7 +3387,8 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
33773387 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
33783388 const operand = try self.resolveInst(ty_op.operand);
33793389
3380 return self.load(operand, Type.usize, self.ptrSize());
3390 const len = try self.load(operand, Type.usize, self.ptrSize());
3391 return len.toLocal(self, Type.usize);
33813392}
33823393
33833394fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3391,8 +3402,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
33913402 const elem_size = elem_ty.abiSize(self.target);
33923403
33933404 // load pointer onto stack
3394 const slice_ptr = try self.load(slice, Type.usize, 0);
3395 try self.addLabel(.local_get, slice_ptr.local);
3405 _ = try self.load(slice, Type.usize, 0);
33963406
33973407 // calculate index into slice
33983408 try self.emitWValue(index);
......@@ -3406,7 +3416,9 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
34063416 if (isByRef(elem_ty, self.target)) {
34073417 return result;
34083418 }
3409 return self.load(result, elem_ty, 0);
3419
3420 const elem_val = try self.load(result, elem_ty, 0);
3421 return elem_val.toLocal(self, elem_ty);
34103422}
34113423
34123424fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3419,8 +3431,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
34193431 const slice = try self.resolveInst(bin_op.lhs);
34203432 const index = try self.resolveInst(bin_op.rhs);
34213433
3422 const slice_ptr = try self.load(slice, Type.usize, 0);
3423 try self.addLabel(.local_get, slice_ptr.local);
3434 _ = try self.load(slice, Type.usize, 0);
34243435
34253436 // calculate index into slice
34263437 try self.emitWValue(index);
......@@ -3428,7 +3439,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
34283439 try self.addTag(.i32_mul);
34293440 try self.addTag(.i32_add);
34303441
3431 const result = try self.allocLocal(Type.initTag(.i32));
3442 const result = try self.allocLocal(Type.i32);
34323443 try self.addLabel(.local_set, result.local);
34333444 return result;
34343445}
......@@ -3437,7 +3448,8 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
34373448 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
34383449 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
34393450 const operand = try self.resolveInst(ty_op.operand);
3440 return self.load(operand, Type.usize, 0);
3451 const ptr = try self.load(operand, Type.usize, 0);
3452 return ptr.toLocal(self, Type.usize);
34413453}
34423454
34433455fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3511,8 +3523,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
35113523
35123524 // load pointer onto the stack
35133525 if (ptr_ty.isSlice()) {
3514 const ptr_local = try self.load(ptr, Type.usize, 0);
3515 try self.addLabel(.local_get, ptr_local.local);
3526 _ = try self.load(ptr, Type.usize, 0);
35163527 } else {
35173528 try self.lowerToStack(ptr);
35183529 }
......@@ -3528,7 +3539,9 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
35283539 if (isByRef(elem_ty, self.target)) {
35293540 return result;
35303541 }
3531 return self.load(result, elem_ty, 0);
3542
3543 const elem_val = try self.load(result, elem_ty, 0);
3544 return elem_val.toLocal(self, elem_ty);
35323545}
35333546
35343547fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3544,8 +3557,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
35443557
35453558 // load pointer onto the stack
35463559 if (ptr_ty.isSlice()) {
3547 const ptr_local = try self.load(ptr, Type.usize, 0);
3548 try self.addLabel(.local_get, ptr_local.local);
3560 _ = try self.load(ptr, Type.usize, 0);
35493561 } else {
35503562 try self.lowerToStack(ptr);
35513563 }
......@@ -3556,7 +3568,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
35563568 try self.addTag(.i32_mul);
35573569 try self.addTag(.i32_add);
35583570
3559 const result = try self.allocLocal(Type.initTag(.i32));
3571 const result = try self.allocLocal(Type.i32);
35603572 try self.addLabel(.local_set, result.local);
35613573 return result;
35623574}
......@@ -3707,7 +3719,8 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
37073719 if (isByRef(elem_ty, self.target)) {
37083720 return result;
37093721 }
3710 return self.load(result, elem_ty, 0);
3722 const elem_val = try self.load(result, elem_ty, 0);
3723 return elem_val.toLocal(self, elem_ty);
37113724}
37123725
37133726fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3929,24 +3942,18 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
39293942 const payload_ty = operand_ty.optionalChild(&buf);
39303943 const offset = @intCast(u32, operand_ty.abiSize(self.target) - payload_ty.abiSize(self.target));
39313944
3932 const lhs_is_null = try self.isNull(lhs, operand_ty, .i32_eq);
3933 const rhs_is_null = try self.isNull(rhs, operand_ty, .i32_eq);
3934
39353945 // We store the final result in here that will be validated
39363946 // if the optional is truly equal.
39373947 const result = try self.allocLocal(Type.initTag(.i32));
39383948
39393949 try self.startBlock(.block, wasm.block_empty);
3940 try self.emitWValue(lhs_is_null);
3941 try self.emitWValue(rhs_is_null);
3950 _ = try self.isNull(lhs, operand_ty, .i32_eq);
3951 _ = try self.isNull(rhs, operand_ty, .i32_eq);
39423952 try self.addTag(.i32_ne); // inverse so we can exit early
39433953 try self.addLabel(.br_if, 0);
39443954
3945 const lhs_pl = try self.load(lhs, payload_ty, offset);
3946 const rhs_pl = try self.load(rhs, payload_ty, offset);
3947
3948 try self.emitWValue(lhs_pl);
3949 try self.emitWValue(rhs_pl);
3955 _ = try self.load(lhs, payload_ty, offset);
3956 _ = try self.load(rhs, payload_ty, offset);
39503957 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, self.target) });
39513958 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
39523959 try self.addLabel(.br_if, 0);
......@@ -3973,14 +3980,14 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma
39733980 return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits});
39743981 }
39753982
3976 const lhs_high_bit = try self.load(lhs, Type.u64, 0);
3977 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
3978 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
3979 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
3983 const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
3984 const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
39803985
39813986 switch (op) {
39823987 .eq, .neq => {
39833988 const xor_high = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor);
3989 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
3990 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
39843991 const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
39853992 const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or");
39863993
......@@ -3993,6 +4000,8 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma
39934000 else => {
39944001 const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64;
39954002 // leave those value on top of the stack for '.select'
4003 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
4004 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
39964005 _ = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op);
39974006 _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op);
39984007 _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, .eq);
......@@ -4040,7 +4049,8 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
40404049 const offset = if (layout.tag_align < layout.payload_align) blk: {
40414050 break :blk @intCast(u32, layout.payload_size);
40424051 } else @as(u32, 0);
4043 return self.load(operand, tag_ty, offset);
4052 const tag = try self.load(operand, tag_ty, offset);
4053 return tag.toLocal(self, tag_ty);
40444054}
40454055
40464056fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -4050,19 +4060,20 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
40504060 const dest_ty = self.air.typeOfIndex(inst);
40514061 const operand = try self.resolveInst(ty_op.operand);
40524062
4053 return self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty);
4063 const extended = try self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty);
4064 return extended.toLocal(self, dest_ty);
40544065}
40554066
4067/// Extends a float from a given `Type` to a larger wanted `Type`
4068/// NOTE: Leaves the result on the stack
40564069fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
40574070 const given_bits = given.floatBits(self.target);
40584071 const wanted_bits = wanted.floatBits(self.target);
40594072
40604073 if (wanted_bits == 64 and given_bits == 32) {
4061 const result = try self.allocLocal(wanted);
40624074 try self.emitWValue(operand);
40634075 try self.addTag(.f64_promote_f32);
4064 try self.addLabel(.local_set, result.local);
4065 return result;
4076 return WValue{ .stack = {} };
40664077 } else if (given_bits == 16) {
40674078 // call __extendhfsf2(f16) f32
40684079 const f32_result = try self.callIntrinsic(
......@@ -4076,11 +4087,9 @@ fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WVa
40764087 return f32_result;
40774088 }
40784089 if (wanted_bits == 64) {
4079 const result = try self.allocLocal(wanted);
40804090 try self.emitWValue(f32_result);
40814091 try self.addTag(.f64_promote_f32);
4082 try self.addLabel(.local_set, result.local);
4083 return result;
4092 return WValue{ .stack = {} };
40844093 }
40854094 return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits});
40864095 } else {
......@@ -4095,26 +4104,25 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
40954104 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
40964105 const dest_ty = self.air.typeOfIndex(inst);
40974106 const operand = try self.resolveInst(ty_op.operand);
4098 return self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty);
4107 const trunc = try self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty);
4108 return trunc.toLocal(self, dest_ty);
40994109}
41004110
4111/// Truncates a float from a given `Type` to its wanted `Type`
4112/// NOTE: The result value remains on the stack
41014113fn fptrunc(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
41024114 const given_bits = given.floatBits(self.target);
41034115 const wanted_bits = wanted.floatBits(self.target);
41044116
41054117 if (wanted_bits == 32 and given_bits == 64) {
4106 const result = try self.allocLocal(wanted);
41074118 try self.emitWValue(operand);
41084119 try self.addTag(.f32_demote_f64);
4109 try self.addLabel(.local_set, result.local);
4110 return result;
4120 return WValue{ .stack = {} };
41114121 } else if (wanted_bits == 16) {
41124122 const op: WValue = if (given_bits == 64) blk: {
4113 const tmp = try self.allocLocal(Type.f32);
41144123 try self.emitWValue(operand);
41154124 try self.addTag(.f32_demote_f64);
4116 try self.addLabel(.local_set, tmp.local);
4117 break :blk tmp;
4125 break :blk WValue{ .stack = {} };
41184126 } else operand;
41194127
41204128 // call __truncsfhf2(f32) f16
......@@ -4199,12 +4207,9 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
41994207
42004208 switch (wasm_bits) {
42014209 128 => {
4202 const msb = try self.load(operand, Type.u64, 0);
4203 const lsb = try self.load(operand, Type.u64, 8);
4204
4205 try self.emitWValue(msb);
4210 _ = try self.load(operand, Type.u64, 0);
42064211 try self.addTag(.i64_popcnt);
4207 try self.emitWValue(lsb);
4212 _ = try self.load(operand, Type.u64, 8);
42084213 try self.addTag(.i64_popcnt);
42094214 try self.addTag(.i64_add);
42104215 try self.addTag(.i32_wrap_i64);
......@@ -4351,10 +4356,10 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type,
43514356 return self.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits});
43524357 }
43534358
4354 const lhs_high_bit = try self.load(lhs, Type.u64, 0);
4355 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
4356 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
4357 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
4359 const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
4360 const lhs_low_bit = try (try self.load(lhs, Type.u64, 8)).toLocal(self, Type.u64);
4361 const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
4362 const rhs_low_bit = try (try self.load(rhs, Type.u64, 8)).toLocal(self, Type.u64);
43584363
43594364 const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64);
43604365 const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
......@@ -4563,9 +4568,9 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
45634568 const rhs = try self.resolveInst(bin_op.rhs);
45644569
45654570 if (ty.floatBits(self.target) == 16) {
4566 const addend_ext = try self.fpext(addend, ty, Type.f32);
4567 const lhs_ext = try self.fpext(lhs, ty, Type.f32);
45684571 const rhs_ext = try self.fpext(rhs, ty, Type.f32);
4572 const lhs_ext = try self.fpext(lhs, ty, Type.f32);
4573 const addend_ext = try self.fpext(addend, ty, Type.f32);
45694574 // call to compiler-rt `fn fmaf(f32, f32, f32) f32`
45704575 const result = try self.callIntrinsic(
45714576 "fmaf",
......@@ -4573,7 +4578,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
45734578 Type.f32,
45744579 &.{ rhs_ext, lhs_ext, addend_ext },
45754580 );
4576 return try self.fptrunc(result, Type.f32, ty);
4581 return try (try self.fptrunc(result, Type.f32, ty)).toLocal(self, ty);
45774582 }
45784583
45794584 const mul_result = try self.binOp(lhs, rhs, ty, .mul);
......@@ -4606,12 +4611,11 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
46064611 try self.addTag(.i32_wrap_i64);
46074612 },
46084613 128 => {
4609 const msb = try self.load(operand, Type.u64, 0);
4610 const lsb = try self.load(operand, Type.u64, 8);
4614 const lsb = try (try self.load(operand, Type.u64, 8)).toLocal(self, Type.u64);
46114615
46124616 try self.emitWValue(lsb);
46134617 try self.addTag(.i64_clz);
4614 try self.emitWValue(msb);
4618 _ = try self.load(operand, Type.u64, 0);
46154619 try self.addTag(.i64_clz);
46164620 try self.emitWValue(.{ .imm64 = 64 });
46174621 try self.addTag(.i64_add);
......@@ -4667,12 +4671,11 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
46674671 try self.addTag(.i32_wrap_i64);
46684672 },
46694673 128 => {
4670 const msb = try self.load(operand, Type.u64, 0);
4671 const lsb = try self.load(operand, Type.u64, 8);
4674 const msb = try (try self.load(operand, Type.u64, 0)).toLocal(self, Type.u64);
46724675
46734676 try self.emitWValue(msb);
46744677 try self.addTag(.i64_ctz);
4675 try self.emitWValue(lsb);
4678 _ = try self.load(operand, Type.u64, 8);
46764679 if (wasm_bits != int_info.bits) {
46774680 try self.addImm64(@as(u64, 1) << @intCast(u6, int_info.bits - 64));
46784681 try self.addTag(.i64_or);
......@@ -4810,7 +4813,8 @@ fn lowerTry(
48104813 if (isByRef(pl_ty, self.target)) {
48114814 return buildPointerOffset(self, err_union, pl_offset, .new);
48124815 }
4813 return self.load(err_union, pl_ty, pl_offset);
4816 const payload = try self.load(err_union, pl_ty, pl_offset);
4817 return payload.toLocal(self, pl_ty);
48144818}
48154819
48164820fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -4972,9 +4976,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
49724976 }
49734977
49744978 if (is_f16) {
4975 // we can re-use temporary local
4976 try self.addLabel(.local_set, lhs_operand.local);
4977 return self.fptrunc(lhs_operand, Type.f32, Type.f16);
4979 _ = try self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
49784980 }
49794981 }
49804982
......@@ -5066,9 +5068,7 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu
50665068 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
50675069
50685070 if (is_f16) {
5069 // re-use temporary to save locals
5070 try self.addLabel(.local_set, op_to_lower.local);
5071 return self.fptrunc(op_to_lower, Type.f32, Type.f16);
5071 _ = try self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
50725072 }
50735073
50745074 const result = try self.allocLocal(ty);
......@@ -5285,6 +5285,8 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
52855285/// Calls a compiler-rt intrinsic by creating an undefined symbol,
52865286/// then lowering the arguments and calling the symbol as a function call.
52875287/// This function call assumes the C-ABI.
5288/// Asserts arguments are not stack values when the return value is
5289/// passed as the first parameter.
52885290fn callIntrinsic(
52895291 self: *Self,
52905292 name: []const u8,
......@@ -5314,6 +5316,7 @@ fn callIntrinsic(
53145316
53155317 // Lower all arguments to the stack before we call our function
53165318 for (args) |arg, arg_i| {
5319 assert(!(want_sret_param and arg == .stack));
53175320 assert(param_types[arg_i].hasRuntimeBitsIgnoreComptime());
53185321 try self.lowerArg(.C, param_types[arg_i], arg);
53195322 }