| ... | ... | @@ -2179,40 +2179,22 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 2179 | 2179 | return self.fail("TODO: Implement wrapping arithmetic for vectors", .{}); |
| 2180 | 2180 | } |
| 2181 | 2181 | |
| 2182 | | return self.wrapBinOp(lhs, rhs, ty, op); |
| 2182 | return (try self.wrapBinOp(lhs, rhs, ty, op)).toLocal(self, ty); |
| 2183 | 2183 | } |
| 2184 | 2184 | |
| 2185 | /// Performs a wrapping binary operation. |
| 2186 | /// Asserts rhs is not a stack value when lhs also isn't. |
| 2187 | /// NOTE: Leaves the result on the stack when its Type is <= 64 bits |
| 2185 | 2188 | fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 2186 | | const bit_size = ty.intInfo(self.target).bits; |
| 2187 | | var wasm_bits = toWasmBits(bit_size) orelse { |
| 2188 | | return self.fail("TODO: Implement wrapping arithmetic for integers with bitsize: {d}\n", .{bit_size}); |
| 2189 | | }; |
| 2190 | | |
| 2191 | | if (wasm_bits == 128) { |
| 2192 | | const bin_op = try self.binOpBigInt(lhs, rhs, ty, op); |
| 2193 | | return self.wrapOperand(bin_op, ty); |
| 2194 | | } |
| 2195 | | |
| 2196 | | const opcode: wasm.Opcode = buildOpcode(.{ |
| 2197 | | .op = op, |
| 2198 | | .valtype1 = typeToValtype(ty, self.target), |
| 2199 | | .signedness = if (ty.isSignedInt()) .signed else .unsigned, |
| 2200 | | }); |
| 2201 | | |
| 2202 | | try self.emitWValue(lhs); |
| 2203 | | try self.emitWValue(rhs); |
| 2204 | | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2205 | | const bin_local = try self.allocLocal(ty); |
| 2206 | | try self.addLabel(.local_set, bin_local.local); |
| 2207 | | |
| 2189 | const bin_local = try self.binOp(lhs, rhs, ty, op); |
| 2208 | 2190 | return self.wrapOperand(bin_local, ty); |
| 2209 | 2191 | } |
| 2210 | 2192 | |
| 2211 | 2193 | /// Wraps an operand based on a given type's bitsize. |
| 2212 | 2194 | /// Asserts `Type` is <= 128 bits. |
| 2195 | /// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack. |
| 2213 | 2196 | fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { |
| 2214 | 2197 | assert(ty.abiSize(self.target) <= 16); |
| 2215 | | const result_local = try self.allocLocal(ty); |
| 2216 | 2198 | const bitsize = ty.intInfo(self.target).bits; |
| 2217 | 2199 | const wasm_bits = toWasmBits(bitsize) orelse { |
| 2218 | 2200 | return self.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize}); |
| ... | ... | @@ -2246,8 +2228,7 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { |
| 2246 | 2228 | try self.addTag(.i64_and); |
| 2247 | 2229 | } else unreachable; |
| 2248 | 2230 | |
| 2249 | | try self.addLabel(.local_set, result_local.local); |
| 2250 | | return result_local; |
| 2231 | return WValue{ .stack = {} }; |
| 2251 | 2232 | } |
| 2252 | 2233 | |
| 2253 | 2234 | fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue { |
| ... | ... | @@ -3471,11 +3452,11 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3471 | 3452 | return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits}); |
| 3472 | 3453 | } |
| 3473 | 3454 | |
| 3474 | | const result = try self.intcast(operand, op_ty, wanted_ty); |
| 3455 | var result = try self.intcast(operand, op_ty, wanted_ty); |
| 3475 | 3456 | const wanted_bits = wanted_ty.intInfo(self.target).bits; |
| 3476 | 3457 | const wasm_bits = toWasmBits(wanted_bits).?; |
| 3477 | 3458 | if (wasm_bits != wanted_bits) { |
| 3478 | | return self.wrapOperand(result, wanted_ty); |
| 3459 | result = try self.wrapOperand(result, wanted_ty); |
| 3479 | 3460 | } |
| 3480 | 3461 | return result.toLocal(self, wanted_ty); |
| 3481 | 3462 | } |
| ... | ... | @@ -3749,11 +3730,8 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3749 | 3730 | .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned, |
| 3750 | 3731 | }); |
| 3751 | 3732 | try self.addTag(Mir.Inst.Tag.fromOpcode(op)); |
| 3752 | | |
| 3753 | | const result = try self.allocLocal(dest_ty); |
| 3754 | | try self.addLabel(.local_set, result.local); |
| 3755 | | |
| 3756 | | return self.wrapOperand(result, dest_ty); |
| 3733 | const wrapped = try self.wrapOperand(.{ .stack = {} }, dest_ty); |
| 3734 | return wrapped.toLocal(self, dest_ty); |
| 3757 | 3735 | } |
| 3758 | 3736 | |
| 3759 | 3737 | fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -4339,7 +4317,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W |
| 4339 | 4317 | |
| 4340 | 4318 | const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty); |
| 4341 | 4319 | const result = if (wasm_bits != int_info.bits) blk: { |
| 4342 | | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4320 | break :blk try (try self.wrapOperand(bin_op, lhs_ty)).toLocal(self, lhs_ty); |
| 4343 | 4321 | } else bin_op; |
| 4344 | 4322 | |
| 4345 | 4323 | const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt; |
| ... | ... | @@ -4438,13 +4416,15 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4438 | 4416 | |
| 4439 | 4417 | const shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty); |
| 4440 | 4418 | const result = if (wasm_bits != int_info.bits) blk: { |
| 4441 | | break :blk try self.wrapOperand(shl, lhs_ty); |
| 4419 | break :blk try (try self.wrapOperand(shl, lhs_ty)).toLocal(self, lhs_ty); |
| 4442 | 4420 | } else shl; |
| 4443 | 4421 | |
| 4444 | 4422 | const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: { |
| 4445 | 4423 | const abs = try self.signAbsValue(shl, lhs_ty); |
| 4424 | // emit lhs to stack to we can keep 'wrapped' on the stack also |
| 4425 | try self.emitWValue(lhs); |
| 4446 | 4426 | const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr); |
| 4447 | | break :blk try self.cmp(lhs, wrapped, lhs_ty, .neq); |
| 4427 | break :blk try self.cmp(.{ .stack = {} }, wrapped, lhs_ty, .neq); |
| 4448 | 4428 | } else blk: { |
| 4449 | 4429 | const shr = try (try self.binOp(result, rhs, lhs_ty, .shr)).toLocal(self, lhs_ty); |
| 4450 | 4430 | break :blk try self.cmp(lhs, shr, lhs_ty, .neq); |
| ... | ... | @@ -4499,7 +4479,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4499 | 4479 | const wrap = try self.intcast(shr, new_ty, lhs_ty); |
| 4500 | 4480 | _ = try self.cmp(wrap, zero, lhs_ty, .neq); |
| 4501 | 4481 | try self.addLabel(.local_set, overflow_bit.local); |
| 4502 | | break :blk try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty); |
| 4482 | break :blk try self.intcast(bin_op, new_ty, lhs_ty); |
| 4503 | 4483 | } else { |
| 4504 | 4484 | const down_cast = try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty); |
| 4505 | 4485 | const shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty); |
| ... | ... | @@ -4529,9 +4509,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4529 | 4509 | try self.addLabel(.local_set, overflow_bit.local); |
| 4530 | 4510 | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4531 | 4511 | }; |
| 4512 | const bin_op_local = try bin_op.toLocal(self, lhs_ty); |
| 4532 | 4513 | |
| 4533 | 4514 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |
| 4534 | | try self.store(result_ptr, bin_op, lhs_ty, 0); |
| 4515 | try self.store(result_ptr, bin_op_local, lhs_ty, 0); |
| 4535 | 4516 | const offset = @intCast(u32, lhs_ty.abiSize(self.target)); |
| 4536 | 4517 | try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); |
| 4537 | 4518 | |
| ... | ... | @@ -4854,29 +4835,29 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4854 | 4835 | switch (int_info.bits) { |
| 4855 | 4836 | 16 => { |
| 4856 | 4837 | const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); |
| 4857 | | const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and")).toLocal(self, ty); |
| 4858 | | const shr_res = try (try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); |
| 4838 | const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and"); |
| 4839 | const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr); |
| 4859 | 4840 | const res = if (int_info.signedness == .signed) blk: { |
| 4860 | 4841 | break :blk try self.wrapOperand(shr_res, Type.u8); |
| 4861 | 4842 | } else shr_res; |
| 4862 | 4843 | return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty); |
| 4863 | 4844 | }, |
| 4864 | 4845 | 24 => { |
| 4865 | | const msb = try self.wrapOperand(operand, Type.u16); |
| 4866 | | const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr); |
| 4846 | const msb = try (try self.wrapOperand(operand, Type.u16)).toLocal(self, Type.u16); |
| 4867 | 4847 | |
| 4868 | 4848 | const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl); |
| 4869 | | const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and")).toLocal(self, Type.u16); |
| 4870 | | const shr_res = try (try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); |
| 4849 | const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and"); |
| 4850 | const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr); |
| 4871 | 4851 | |
| 4872 | 4852 | const res = if (int_info.signedness == .signed) blk: { |
| 4873 | 4853 | break :blk try self.wrapOperand(shr_res, Type.u8); |
| 4874 | 4854 | } else shr_res; |
| 4875 | 4855 | const lhs_tmp = try self.binOp(lhs, res, ty, .@"or"); |
| 4876 | | const lhs_result = try (try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); |
| 4856 | const lhs_result = try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr); |
| 4877 | 4857 | const rhs_wrap = try self.wrapOperand(msb, Type.u8); |
| 4878 | | const rhs_result = try (try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl)).toLocal(self, ty); |
| 4858 | const rhs_result = try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl); |
| 4879 | 4859 | |
| 4860 | const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr); |
| 4880 | 4861 | const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or"); |
| 4881 | 4862 | return (try self.binOp(tmp, lsb, ty, .@"or")).toLocal(self, ty); |
| 4882 | 4863 | }, |
| ... | ... | @@ -4887,11 +4868,11 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4887 | 4868 | const rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty); |
| 4888 | 4869 | const tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty); |
| 4889 | 4870 | |
| 4890 | | const shr = try (try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr)).toLocal(self, ty); |
| 4871 | const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); |
| 4872 | const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr); |
| 4891 | 4873 | const res = if (int_info.signedness == .signed) blk: { |
| 4892 | 4874 | break :blk try self.wrapOperand(shr, Type.u16); |
| 4893 | 4875 | } else shr; |
| 4894 | | const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); |
| 4895 | 4876 | return (try self.binOp(shl, res, ty, .@"or")).toLocal(self, ty); |
| 4896 | 4877 | }, |
| 4897 | 4878 | else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}), |
| ... | ... | @@ -5178,7 +5159,7 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op |
| 5178 | 5159 | _ = try self.cmp(bin_result, min_wvalue, ty, .gt); |
| 5179 | 5160 | try self.addTag(.select); |
| 5180 | 5161 | try self.addLabel(.local_set, bin_result.local); // re-use local |
| 5181 | | return self.wrapOperand(bin_result, ty); |
| 5162 | return (try self.wrapOperand(bin_result, ty)).toLocal(self, ty); |
| 5182 | 5163 | } else { |
| 5183 | 5164 | const zero = switch (wasm_bits) { |
| 5184 | 5165 | 32 => WValue{ .imm32 = 0 }, |
| ... | ... | @@ -5289,11 +5270,11 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 5289 | 5270 | _ = try self.cmp(shl_res, shr, ty, .neq); |
| 5290 | 5271 | try self.addTag(.select); |
| 5291 | 5272 | try self.addLabel(.local_set, result.local); |
| 5292 | | const shift_result = try (try self.binOp(result, shift_value, ty, .shr)).toLocal(self, ty); |
| 5273 | var shift_result = try self.binOp(result, shift_value, ty, .shr); |
| 5293 | 5274 | if (is_signed) { |
| 5294 | | return self.wrapOperand(shift_result, ty); |
| 5275 | shift_result = try self.wrapOperand(shift_result, ty); |
| 5295 | 5276 | } |
| 5296 | | return shift_result; |
| 5277 | return try shift_result.toLocal(self, ty); |
| 5297 | 5278 | } |
| 5298 | 5279 | } |
| 5299 | 5280 | |