| ... | ... | @@ -29,6 +29,8 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 29 | 29 | const WValue = union(enum) { |
| 30 | 30 | /// May be referenced but is unused |
| 31 | 31 | none: void, |
| 32 | /// The value lives on top of the stack |
| 33 | stack: void, |
| 32 | 34 | /// Index of the local variable |
| 33 | 35 | local: u32, |
| 34 | 36 | /// An immediate 32bit value |
| ... | ... | @@ -55,7 +57,7 @@ const WValue = union(enum) { |
| 55 | 57 | /// In wasm function pointers are indexes into a function table, |
| 56 | 58 | /// rather than an address in the data section. |
| 57 | 59 | function_index: u32, |
| 58 | | /// Offset from the bottom of the stack, with the offset |
| 60 | /// Offset from the bottom of the virtual stack, with the offset |
| 59 | 61 | /// pointing to where the value lives. |
| 60 | 62 | stack_offset: u32, |
| 61 | 63 | |
| ... | ... | @@ -71,6 +73,21 @@ const WValue = union(enum) { |
| 71 | 73 | else => return 0, |
| 72 | 74 | } |
| 73 | 75 | } |
| 76 | |
| 77 | /// Promotes a `WValue` to a local when given value is on top of the stack. |
| 78 | /// When encountering a `local` or `stack_offset` this is essentially a no-op. |
| 79 | /// All other tags are illegal. |
| 80 | fn toLocal(self: WValue, gen: *Self, ty: Type) InnerError!WValue { |
| 81 | switch (self) { |
| 82 | .stack => { |
| 83 | const local = try gen.allocLocal(ty); |
| 84 | try gen.addLabel(.local_set, local.local); |
| 85 | return local; |
| 86 | }, |
| 87 | .local, .stack_offset => return self, |
| 88 | else => unreachable, |
| 89 | } |
| 90 | } |
| 74 | 91 | }; |
| 75 | 92 | |
| 76 | 93 | /// Wasm ops, but without input/output/signedness information |
| ... | ... | @@ -774,7 +791,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 { |
| 774 | 791 | /// Writes the bytecode depending on the given `WValue` in `val` |
| 775 | 792 | fn emitWValue(self: *Self, value: WValue) InnerError!void { |
| 776 | 793 | switch (value) { |
| 777 | | .none => {}, // no-op |
| 794 | .none, .stack => {}, // no-op |
| 778 | 795 | .local => |idx| try self.addLabel(.local_get, idx), |
| 779 | 796 | .imm32 => |val| try self.addImm32(@bitCast(i32, val)), |
| 780 | 797 | .imm64 => |val| try self.addImm64(val), |
| ... | ... | @@ -1892,6 +1909,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1892 | 1909 | } |
| 1893 | 1910 | |
| 1894 | 1911 | fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { |
| 1912 | assert(!(lhs != .stack and rhs == .stack)); |
| 1895 | 1913 | switch (ty.zigTypeTag()) { |
| 1896 | 1914 | .ErrorUnion => { |
| 1897 | 1915 | const pl_ty = ty.errorUnionPayload(); |
| ... | ... | @@ -2070,10 +2088,14 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 2070 | 2088 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2071 | 2089 | const ty = self.air.typeOf(bin_op.lhs); |
| 2072 | 2090 | |
| 2073 | | return self.binOp(lhs, rhs, ty, op); |
| 2091 | const stack_value = try self.binOp(lhs, rhs, ty, op); |
| 2092 | return stack_value.toLocal(self, ty); |
| 2074 | 2093 | } |
| 2075 | 2094 | |
| 2095 | /// Performs a binary operation on the given `WValue`'s |
| 2096 | /// NOTE: THis leaves the value on top of the stack. |
| 2076 | 2097 | fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 2098 | assert(!(lhs != .stack and rhs == .stack)); |
| 2077 | 2099 | if (isByRef(ty, self.target)) { |
| 2078 | 2100 | if (ty.zigTypeTag() == .Int) { |
| 2079 | 2101 | return self.binOpBigInt(lhs, rhs, ty, op); |
| ... | ... | @@ -2099,10 +2121,7 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa |
| 2099 | 2121 | |
| 2100 | 2122 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2101 | 2123 | |
| 2102 | | // save the result in a temporary |
| 2103 | | const bin_local = try self.allocLocal(ty); |
| 2104 | | try self.addLabel(.local_set, bin_local.local); |
| 2105 | | return bin_local; |
| 2124 | return WValue{ .stack = {} }; |
| 2106 | 2125 | } |
| 2107 | 2126 | |
| 2108 | 2127 | fn binOpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue { |
| ... | ... | @@ -2134,8 +2153,8 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr |
| 2134 | 2153 | const rhs_high_bit = try self.load(rhs, Type.u64, 0); |
| 2135 | 2154 | const rhs_low_bit = try self.load(rhs, Type.u64, 8); |
| 2136 | 2155 | |
| 2137 | | const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); |
| 2138 | | const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); |
| 2156 | const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64); |
| 2157 | const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); |
| 2139 | 2158 | |
| 2140 | 2159 | const lt = if (op == .add) blk: { |
| 2141 | 2160 | break :blk try self.cmp(high_op_res, rhs_high_bit, Type.u64, .lt); |
| ... | ... | @@ -2143,7 +2162,7 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr |
| 2143 | 2162 | break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); |
| 2144 | 2163 | } else unreachable; |
| 2145 | 2164 | const tmp = try self.intcast(lt, Type.u32, Type.u64); |
| 2146 | | const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); |
| 2165 | const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); |
| 2147 | 2166 | |
| 2148 | 2167 | try self.store(result, high_op_res, Type.u64, 0); |
| 2149 | 2168 | try self.store(result, tmp_op, Type.u64, 8); |
| ... | ... | @@ -2202,6 +2221,7 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { |
| 2202 | 2221 | if (wasm_bits == bitsize) return operand; |
| 2203 | 2222 | |
| 2204 | 2223 | if (wasm_bits == 128) { |
| 2224 | assert(operand != .stack); |
| 2205 | 2225 | const msb = try self.load(operand, Type.u64, 0); |
| 2206 | 2226 | const lsb = try self.load(operand, Type.u64, 8); |
| 2207 | 2227 | |
| ... | ... | @@ -2772,19 +2792,19 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2772 | 2792 | switch (wasm_bits) { |
| 2773 | 2793 | 32 => { |
| 2774 | 2794 | const bin_op = try self.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor); |
| 2775 | | return self.wrapOperand(bin_op, operand_ty); |
| 2795 | return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty); |
| 2776 | 2796 | }, |
| 2777 | 2797 | 64 => { |
| 2778 | 2798 | const bin_op = try self.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor); |
| 2779 | | return self.wrapOperand(bin_op, operand_ty); |
| 2799 | return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty); |
| 2780 | 2800 | }, |
| 2781 | 2801 | 128 => { |
| 2782 | 2802 | const result_ptr = try self.allocStack(operand_ty); |
| 2783 | 2803 | const msb = try self.load(operand, Type.u64, 0); |
| 2784 | 2804 | const lsb = try self.load(operand, Type.u64, 8); |
| 2785 | 2805 | |
| 2786 | | const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); |
| 2787 | | const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); |
| 2806 | const msb_xor = try (try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty); |
| 2807 | const lsb_xor = try (try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty); |
| 2788 | 2808 | try self.store(result_ptr, msb_xor, Type.u64, 0); |
| 2789 | 2809 | try self.store(result_ptr, lsb_xor, Type.u64, 8); |
| 2790 | 2810 | return result_ptr; |
| ... | ... | @@ -3215,7 +3235,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W |
| 3215 | 3235 | |
| 3216 | 3236 | // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value |
| 3217 | 3237 | if (wanted.isSignedInt()) { |
| 3218 | | const shr = try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr); |
| 3238 | const shr = try (try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr)).toLocal(self, Type.i64); |
| 3219 | 3239 | try self.store(stack_ptr, shr, Type.u64, 8); |
| 3220 | 3240 | } else { |
| 3221 | 3241 | // Ensure memory of lsb is zero'd |
| ... | ... | @@ -4320,7 +4340,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W |
| 4320 | 4340 | break :blk try self.signAbsValue(rhs_op, lhs_ty); |
| 4321 | 4341 | } else rhs_op; |
| 4322 | 4342 | |
| 4323 | | const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); |
| 4343 | const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty); |
| 4324 | 4344 | const result = if (wasm_bits != int_info.bits) blk: { |
| 4325 | 4345 | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4326 | 4346 | } else bin_op; |
| ... | ... | @@ -4330,7 +4350,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W |
| 4330 | 4350 | if (wasm_bits == int_info.bits) { |
| 4331 | 4351 | const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op); |
| 4332 | 4352 | const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt); |
| 4333 | | break :blk try self.binOp(cmp_zero, lt, Type.u32, .xor); // result of cmp_zero and lt is always 32bit |
| 4353 | break :blk try (try self.binOp(cmp_zero, lt, Type.u32, .xor)).toLocal(self, Type.u32); // result of cmp_zero and lt is always 32bit |
| 4334 | 4354 | } |
| 4335 | 4355 | const abs = try self.signAbsValue(bin_op, lhs_ty); |
| 4336 | 4356 | break :blk try self.cmp(abs, bin_op, lhs_ty, .neq); |
| ... | ... | @@ -4360,8 +4380,8 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, |
| 4360 | 4380 | const rhs_high_bit = try self.load(rhs, Type.u64, 0); |
| 4361 | 4381 | const rhs_low_bit = try self.load(rhs, Type.u64, 8); |
| 4362 | 4382 | |
| 4363 | | const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); |
| 4364 | | const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); |
| 4383 | const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64); |
| 4384 | const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); |
| 4365 | 4385 | |
| 4366 | 4386 | const lt = if (op == .add) blk: { |
| 4367 | 4387 | break :blk try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt); |
| ... | ... | @@ -4369,14 +4389,14 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, |
| 4369 | 4389 | break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); |
| 4370 | 4390 | } else unreachable; |
| 4371 | 4391 | const tmp = try self.intcast(lt, Type.u32, Type.u64); |
| 4372 | | const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); |
| 4392 | const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); |
| 4373 | 4393 | |
| 4374 | 4394 | const overflow_bit = if (is_signed) blk: { |
| 4375 | | const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor); |
| 4376 | 4395 | const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor); |
| 4377 | 4396 | const to_wrap = if (op == .add) wrap: { |
| 4378 | 4397 | break :wrap try self.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); |
| 4379 | 4398 | } else xor_low; |
| 4399 | const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor); |
| 4380 | 4400 | const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and"); |
| 4381 | 4401 | break :blk try self.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed |
| 4382 | 4402 | } else blk: { |
| ... | ... | @@ -4422,7 +4442,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4422 | 4442 | return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits}); |
| 4423 | 4443 | }; |
| 4424 | 4444 | |
| 4425 | | const shl = try self.binOp(lhs, rhs, lhs_ty, .shl); |
| 4445 | const shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty); |
| 4426 | 4446 | const result = if (wasm_bits != int_info.bits) blk: { |
| 4427 | 4447 | break :blk try self.wrapOperand(shl, lhs_ty); |
| 4428 | 4448 | } else shl; |
| ... | ... | @@ -4432,7 +4452,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4432 | 4452 | const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr); |
| 4433 | 4453 | break :blk try self.cmp(lhs, wrapped, lhs_ty, .neq); |
| 4434 | 4454 | } else blk: { |
| 4435 | | const shr = try self.binOp(result, rhs, lhs_ty, .shr); |
| 4455 | const shr = try (try self.binOp(result, rhs, lhs_ty, .shr)).toLocal(self, lhs_ty); |
| 4436 | 4456 | break :blk try self.cmp(lhs, shr, lhs_ty, .neq); |
| 4437 | 4457 | }; |
| 4438 | 4458 | |
| ... | ... | @@ -4478,7 +4498,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4478 | 4498 | const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; |
| 4479 | 4499 | const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty); |
| 4480 | 4500 | const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty); |
| 4481 | | const bin_op = try self.binOp(lhs_upcast, rhs_upcast, new_ty, .mul); |
| 4501 | const bin_op = try (try self.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(self, new_ty); |
| 4482 | 4502 | if (int_info.signedness == .unsigned) { |
| 4483 | 4503 | const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); |
| 4484 | 4504 | const wrap = try self.intcast(shr, new_ty, lhs_ty); |
| ... | ... | @@ -4488,7 +4508,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4488 | 4508 | break :blk try self.intcast(bin_op, new_ty, lhs_ty); |
| 4489 | 4509 | } else { |
| 4490 | 4510 | const down_cast = try self.intcast(bin_op, new_ty, lhs_ty); |
| 4491 | | const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr); |
| 4511 | const shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty); |
| 4492 | 4512 | |
| 4493 | 4513 | const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); |
| 4494 | 4514 | const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); |
| ... | ... | @@ -4500,14 +4520,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4500 | 4520 | } else if (int_info.signedness == .signed) blk: { |
| 4501 | 4521 | const lhs_abs = try self.signAbsValue(lhs, lhs_ty); |
| 4502 | 4522 | const rhs_abs = try self.signAbsValue(rhs, lhs_ty); |
| 4503 | | const bin_op = try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul); |
| 4523 | const bin_op = try (try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(self, lhs_ty); |
| 4504 | 4524 | const mul_abs = try self.signAbsValue(bin_op, lhs_ty); |
| 4505 | 4525 | const cmp_op = try self.cmp(mul_abs, bin_op, lhs_ty, .neq); |
| 4506 | 4526 | try self.emitWValue(cmp_op); |
| 4507 | 4527 | try self.addLabel(.local_set, overflow_bit.local); |
| 4508 | 4528 | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4509 | 4529 | } else blk: { |
| 4510 | | const bin_op = try self.binOp(lhs, rhs, lhs_ty, .mul); |
| 4530 | const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(self, lhs_ty); |
| 4511 | 4531 | const shift_imm = if (wasm_bits == 32) |
| 4512 | 4532 | WValue{ .imm32 = int_info.bits } |
| 4513 | 4533 | else |
| ... | ... | @@ -4587,7 +4607,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4587 | 4607 | } |
| 4588 | 4608 | |
| 4589 | 4609 | const mul_result = try self.binOp(lhs, rhs, ty, .mul); |
| 4590 | | return self.binOp(mul_result, addend, ty, .add); |
| 4610 | return (try self.binOp(mul_result, addend, ty, .add)).toLocal(self, ty); |
| 4591 | 4611 | } |
| 4592 | 4612 | |
| 4593 | 4613 | fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -4663,16 +4683,16 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4663 | 4683 | 32 => { |
| 4664 | 4684 | if (wasm_bits != int_info.bits) { |
| 4665 | 4685 | const val: u32 = @as(u32, 1) << @intCast(u5, int_info.bits); |
| 4666 | | const bin_op = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or"); |
| 4667 | | try self.emitWValue(bin_op); |
| 4686 | // leave value on the stack |
| 4687 | _ = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or"); |
| 4668 | 4688 | } else try self.emitWValue(operand); |
| 4669 | 4689 | try self.addTag(.i32_ctz); |
| 4670 | 4690 | }, |
| 4671 | 4691 | 64 => { |
| 4672 | 4692 | if (wasm_bits != int_info.bits) { |
| 4673 | 4693 | const val: u64 = @as(u64, 1) << @intCast(u6, int_info.bits); |
| 4674 | | const bin_op = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or"); |
| 4675 | | try self.emitWValue(bin_op); |
| 4694 | // leave value on the stack |
| 4695 | _ = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or"); |
| 4676 | 4696 | } else try self.emitWValue(operand); |
| 4677 | 4697 | try self.addTag(.i64_ctz); |
| 4678 | 4698 | try self.addTag(.i32_wrap_i64); |
| ... | ... | @@ -4847,45 +4867,45 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4847 | 4867 | switch (int_info.bits) { |
| 4848 | 4868 | 16 => { |
| 4849 | 4869 | const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); |
| 4850 | | const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and"); |
| 4851 | | const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr); |
| 4870 | const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and")).toLocal(self, ty); |
| 4871 | const shr_res = try (try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); |
| 4852 | 4872 | const res = if (int_info.signedness == .signed) blk: { |
| 4853 | 4873 | break :blk try self.wrapOperand(shr_res, Type.u8); |
| 4854 | 4874 | } else shr_res; |
| 4855 | | return self.binOp(lhs, res, ty, .@"or"); |
| 4875 | return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty); |
| 4856 | 4876 | }, |
| 4857 | 4877 | 24 => { |
| 4858 | 4878 | const msb = try self.wrapOperand(operand, Type.u16); |
| 4859 | 4879 | const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr); |
| 4860 | 4880 | |
| 4861 | 4881 | const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl); |
| 4862 | | const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and"); |
| 4863 | | const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr); |
| 4882 | const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and")).toLocal(self, Type.u16); |
| 4883 | const shr_res = try (try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); |
| 4864 | 4884 | |
| 4865 | 4885 | const res = if (int_info.signedness == .signed) blk: { |
| 4866 | 4886 | break :blk try self.wrapOperand(shr_res, Type.u8); |
| 4867 | 4887 | } else shr_res; |
| 4868 | 4888 | const lhs_tmp = try self.binOp(lhs, res, ty, .@"or"); |
| 4869 | | const lhs_result = try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr); |
| 4889 | const lhs_result = try (try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); |
| 4870 | 4890 | const rhs_wrap = try self.wrapOperand(msb, Type.u8); |
| 4871 | | const rhs_result = try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl); |
| 4891 | const rhs_result = try (try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl)).toLocal(self, ty); |
| 4872 | 4892 | |
| 4873 | 4893 | const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or"); |
| 4874 | | return self.binOp(tmp, lsb, ty, .@"or"); |
| 4894 | return (try self.binOp(tmp, lsb, ty, .@"or")).toLocal(self, ty); |
| 4875 | 4895 | }, |
| 4876 | 4896 | 32 => { |
| 4877 | 4897 | const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); |
| 4878 | | const lhs = try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and"); |
| 4898 | const lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty); |
| 4879 | 4899 | const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr); |
| 4880 | | const rhs = try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and"); |
| 4881 | | const tmp_or = try self.binOp(lhs, rhs, ty, .@"or"); |
| 4900 | const rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty); |
| 4901 | const tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty); |
| 4882 | 4902 | |
| 4883 | | const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); |
| 4884 | | const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr); |
| 4903 | const shr = try (try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr)).toLocal(self, ty); |
| 4885 | 4904 | const res = if (int_info.signedness == .signed) blk: { |
| 4886 | 4905 | break :blk try self.wrapOperand(shr, Type.u16); |
| 4887 | 4906 | } else shr; |
| 4888 | | return self.binOp(shl, res, ty, .@"or"); |
| 4907 | const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); |
| 4908 | return (try self.binOp(shl, res, ty, .@"or")).toLocal(self, ty); |
| 4889 | 4909 | }, |
| 4890 | 4910 | else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}), |
| 4891 | 4911 | } |
| ... | ... | @@ -4902,7 +4922,7 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4902 | 4922 | if (ty.isSignedInt()) { |
| 4903 | 4923 | return self.divSigned(lhs, rhs, ty); |
| 4904 | 4924 | } |
| 4905 | | return self.binOp(lhs, rhs, ty, .div); |
| 4925 | return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty); |
| 4906 | 4926 | } |
| 4907 | 4927 | |
| 4908 | 4928 | fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -4914,7 +4934,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4914 | 4934 | const rhs = try self.resolveInst(bin_op.rhs); |
| 4915 | 4935 | |
| 4916 | 4936 | if (ty.isUnsignedInt()) { |
| 4917 | | return self.binOp(lhs, rhs, ty, .div); |
| 4937 | return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty); |
| 4918 | 4938 | } else if (ty.isSignedInt()) { |
| 4919 | 4939 | const int_bits = ty.intInfo(self.target).bits; |
| 4920 | 4940 | const wasm_bits = toWasmBits(int_bits) orelse { |
| ... | ... | @@ -4927,9 +4947,6 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4927 | 4947 | break :blk try self.signAbsValue(rhs, ty); |
| 4928 | 4948 | } else rhs; |
| 4929 | 4949 | |
| 4930 | | const div_result = try self.binOp(lhs_res, rhs_res, ty, .div); |
| 4931 | | const rem_result = try self.binOp(lhs_res, rhs_res, ty, .rem); |
| 4932 | | |
| 4933 | 4950 | const zero = switch (wasm_bits) { |
| 4934 | 4951 | 32 => WValue{ .imm32 = 0 }, |
| 4935 | 4952 | 64 => WValue{ .imm64 = 0 }, |
| ... | ... | @@ -4938,7 +4955,10 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4938 | 4955 | const lhs_less_than_zero = try self.cmp(lhs_res, zero, ty, .lt); |
| 4939 | 4956 | const rhs_less_than_zero = try self.cmp(rhs_res, zero, ty, .lt); |
| 4940 | 4957 | |
| 4941 | | try self.emitWValue(div_result); |
| 4958 | const div_result = try self.allocLocal(ty); |
| 4959 | // leave on stack |
| 4960 | _ = try self.binOp(lhs_res, rhs_res, ty, .div); |
| 4961 | try self.addLabel(.local_tee, div_result.local); |
| 4942 | 4962 | try self.emitWValue(lhs_less_than_zero); |
| 4943 | 4963 | try self.emitWValue(rhs_less_than_zero); |
| 4944 | 4964 | switch (wasm_bits) { |
| ... | ... | @@ -4953,7 +4973,8 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4953 | 4973 | else => unreachable, |
| 4954 | 4974 | } |
| 4955 | 4975 | try self.emitWValue(div_result); |
| 4956 | | try self.emitWValue(rem_result); |
| 4976 | // leave value on the stack |
| 4977 | _ = try self.binOp(lhs_res, rhs_res, ty, .rem); |
| 4957 | 4978 | try self.addTag(.select); |
| 4958 | 4979 | } else { |
| 4959 | 4980 | const float_bits = ty.floatBits(self.target); |
| ... | ... | @@ -5110,7 +5131,7 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 5110 | 5131 | } |
| 5111 | 5132 | |
| 5112 | 5133 | const wasm_bits = toWasmBits(int_info.bits).?; |
| 5113 | | const bin_result = try self.binOp(lhs, rhs, ty, op); |
| 5134 | const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty); |
| 5114 | 5135 | if (wasm_bits != int_info.bits and op == .add) { |
| 5115 | 5136 | const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1); |
| 5116 | 5137 | const imm_val = switch (wasm_bits) { |
| ... | ... | @@ -5161,7 +5182,7 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op |
| 5161 | 5182 | else => unreachable, |
| 5162 | 5183 | }; |
| 5163 | 5184 | |
| 5164 | | const bin_result = try self.binOp(lhs, rhs, ty, op); |
| 5185 | const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty); |
| 5165 | 5186 | if (!is_wasm_bits) { |
| 5166 | 5187 | const cmp_result_lt = try self.cmp(bin_result, max_wvalue, ty, .lt); |
| 5167 | 5188 | try self.emitWValue(bin_result); |
| ... | ... | @@ -5185,14 +5206,14 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op |
| 5185 | 5206 | }; |
| 5186 | 5207 | const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt); |
| 5187 | 5208 | const cmp_zero_result = try self.cmp(rhs, zero, ty, if (op == .add) .lt else .gt); |
| 5188 | | const xor = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor. |
| 5189 | 5209 | const cmp_bin_zero_result = try self.cmp(bin_result, zero, ty, .lt); |
| 5190 | 5210 | try self.emitWValue(max_wvalue); |
| 5191 | 5211 | try self.emitWValue(min_wvalue); |
| 5192 | 5212 | try self.emitWValue(cmp_bin_zero_result); |
| 5193 | 5213 | try self.addTag(.select); |
| 5194 | 5214 | try self.emitWValue(bin_result); |
| 5195 | | try self.emitWValue(xor); |
| 5215 | // leave on stack |
| 5216 | _ = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor. |
| 5196 | 5217 | try self.addTag(.select); |
| 5197 | 5218 | try self.addLabel(.local_set, bin_result.local); // re-use local |
| 5198 | 5219 | return bin_result; |
| ... | ... | @@ -5216,8 +5237,8 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 5216 | 5237 | const result = try self.allocLocal(ty); |
| 5217 | 5238 | |
| 5218 | 5239 | if (wasm_bits == int_info.bits) { |
| 5219 | | const shl = try self.binOp(lhs, rhs, ty, .shl); |
| 5220 | | const shr = try self.binOp(shl, rhs, ty, .shr); |
| 5240 | const shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty); |
| 5241 | const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); |
| 5221 | 5242 | const cmp_result = try self.cmp(lhs, shr, ty, .neq); |
| 5222 | 5243 | |
| 5223 | 5244 | switch (wasm_bits) { |
| ... | ... | @@ -5258,9 +5279,9 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 5258 | 5279 | else => unreachable, |
| 5259 | 5280 | }; |
| 5260 | 5281 | |
| 5261 | | const shl_res = try self.binOp(lhs, shift_value, ty, .shl); |
| 5262 | | const shl = try self.binOp(shl_res, rhs, ty, .shl); |
| 5263 | | const shr = try self.binOp(shl, rhs, ty, .shr); |
| 5282 | const shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty); |
| 5283 | const shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty); |
| 5284 | const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); |
| 5264 | 5285 | const cmp_result = try self.cmp(shl_res, shr, ty, .neq); |
| 5265 | 5286 | |
| 5266 | 5287 | switch (wasm_bits) { |
| ... | ... | @@ -5294,7 +5315,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 5294 | 5315 | try self.emitWValue(cmp_result); |
| 5295 | 5316 | try self.addTag(.select); |
| 5296 | 5317 | try self.addLabel(.local_set, result.local); |
| 5297 | | const shift_result = try self.binOp(result, shift_value, ty, .shr); |
| 5318 | const shift_result = try (try self.binOp(result, shift_value, ty, .shr)).toLocal(self, ty); |
| 5298 | 5319 | if (is_signed) { |
| 5299 | 5320 | return self.wrapOperand(shift_result, ty); |
| 5300 | 5321 | } |