diff options
| author | Luuk de Gram <luuk@degram.dev> | 2022-06-10 21:53:43 +0200 |
|---|---|---|
| committer | Luuk de Gram <luuk@degram.dev> | 2022-06-11 19:38:00 +0200 |
| commit | 18afcc34c61a18ada7bda0fc50f48e929866ab82 (patch) | |
| tree | 3c2f959b0c6fadcc65ec586a276f1beec9af512c /src | |
| parent | 3011ef2d82fe29d47b9c6877b6a8bbea0938d7c4 (diff) | |
| download | zig-18afcc34c61a18ada7bda0fc50f48e929866ab82.tar.gz zig-18afcc34c61a18ada7bda0fc50f48e929866ab82.zip | |
wasm: implement `@divFloor` for signed integers
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 111 |
1 files changed, 89 insertions, 22 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 3e0612307e..41ca7d3a7b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2266,7 +2266,7 @@ fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo( const WantedT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); if (value >= 0) return @bitCast(WantedT, value); const max_value = @intCast(u64, (@as(u65, 1) << bits) - 1); - const flipped = (~-value) + 1; + const flipped = @intCast(T, (~-@as(i65, value)) + 1); const result = @bitCast(WantedT, flipped) & max_value; return @intCast(WantedT, result); } @@ -2294,7 +2294,10 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { val.toSignedInt(), @intCast(u6, int_info.bits), )) }, - 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) }, + 33...64 => return WValue{ .imm64 = toTwosComplement( + val.toSignedInt(), + @intCast(u7, int_info.bits), + ) }, else => unreachable, }, .unsigned => switch (int_info.bits) { @@ -4781,18 +4784,56 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const div_result = try self.binOp(lhs, rhs, ty, .div); if (ty.isUnsignedInt()) { - return div_result; + return self.binOp(lhs, rhs, ty, .div); } else if (ty.isSignedInt()) { - return self.fail("TODO: `@divFloor` for signed integers", .{}); - } + const int_bits = ty.intInfo(self.target).bits; + const wasm_bits = toWasmBits(int_bits) orelse { + return self.fail("TODO: `@divFloor` for signed integers larger than '{d}' bits", .{int_bits}); + }; + const lhs_res = if (wasm_bits != int_bits) blk: { + break :blk try self.signAbsValue(lhs, ty); + } else lhs; + const rhs_res = if (wasm_bits != int_bits) blk: { + break :blk try self.signAbsValue(rhs, ty); + } else rhs; + + const div_result = try self.binOp(lhs_res, rhs_res, ty, .div); + const rem_result = try self.binOp(lhs_res, rhs_res, ty, .rem); + + const zero = switch (wasm_bits) { + 32 => WValue{ .imm32 = 0 }, + 64 => WValue{ .imm64 = 0 }, + else => unreachable, + }; + const lhs_less_than_zero = try self.cmp(lhs_res, zero, ty, .lt); + const rhs_less_than_zero = try self.cmp(rhs_res, zero, ty, .lt); - try self.emitWValue(div_result); - switch (ty.floatBits(self.target)) { - 32 => try self.addTag(.f32_floor), - 64 => try self.addTag(.f64_floor), - else => |bit_size| return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{bit_size}), + try self.emitWValue(div_result); + try self.emitWValue(lhs_less_than_zero); + try self.emitWValue(rhs_less_than_zero); + switch (wasm_bits) { + 32 => { + try self.addTag(.i32_xor); + try self.addTag(.i32_sub); + }, + 64 => { + try self.addTag(.i64_xor); + try self.addTag(.i64_sub); + }, + else => unreachable, + } + try self.emitWValue(div_result); + try self.emitWValue(rem_result); + try self.addTag(.select); + } else { + const div_result = try self.binOp(lhs, rhs, ty, .div); + try self.emitWValue(div_result); + switch (ty.floatBits(self.target)) { + 32 => try self.addTag(.f32_floor), + 64 => try self.addTag(.f64_floor), + else => |bit_size| return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{bit_size}), + } } const result = try self.allocLocal(ty); @@ -4811,17 +4852,10 @@ fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue } if (wasm_bits != int_bits) { - const shift_val = switch (wasm_bits) { - 32 => WValue{ .imm32 = wasm_bits - int_bits }, - 64 => WValue{ .imm64 = wasm_bits - int_bits }, - else => unreachable, - }; - const shl_lhs = try self.binOp(lhs, shift_val, ty, .shl); - const shr_lhs = try self.binOp(shl_lhs, shift_val, ty, .shr); - const shl_rhs = try self.binOp(rhs, shift_val, ty, .shl); - const shr_rhs = try self.binOp(shl_rhs, shift_val, ty, .shr); - try self.emitWValue(shr_lhs); - try self.emitWValue(shr_rhs); + const lhs_abs = try self.signAbsValue(lhs, ty); + const rhs_abs = try self.signAbsValue(rhs, ty); + try self.emitWValue(lhs_abs); + try self.emitWValue(rhs_abs); } else { try self.emitWValue(lhs); try self.emitWValue(rhs); @@ -4832,3 +4866,36 @@ fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue try self.addLabel(.local_set, result.local); return result; } + +fn signAbsValue(self: *Self, operand: WValue, ty: Type) InnerError!WValue { + const int_bits = ty.intInfo(self.target).bits; + const wasm_bits = toWasmBits(int_bits) orelse { + return self.fail("TODO: signAbsValue for signed integers larger than '{d}' bits", .{int_bits}); + }; + + const shift_val = switch (wasm_bits) { + 32 => WValue{ .imm32 = wasm_bits - int_bits }, + 64 => WValue{ .imm64 = wasm_bits - int_bits }, + else => return self.fail("TODO: signAbsValue for i128", .{}), + }; + + try self.emitWValue(operand); + switch (wasm_bits) { + 32 => { + try self.emitWValue(shift_val); + try self.addTag(.i32_shl); + try self.emitWValue(shift_val); + try self.addTag(.i32_shr_s); + }, + 64 => { + try self.emitWValue(shift_val); + try self.addTag(.i64_shl); + try self.emitWValue(shift_val); + try self.addTag(.i64_shr_s); + }, + else => unreachable, + } + const result = try self.allocLocal(ty); + try self.addLabel(.local_set, result.local); + return result; +} |
