authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-10 20:23:03+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-18 07:43:33+02:00
log59d3714b8d7008676902b51b891c4cc153447374
tree89bad9b0ae203a643cfe4625f026d0659c7377d0
parentc0ad0606df9a5bb657de70cd42229e20a91c51fc

wasm: 128bit integer cmp support

This implements support for all compare operations on a 128bit integer, for both signed and unsigned integers. The new implementation is almost more efficient as it requires no control-flow, unlike the old implementation which used a block with breaks.

1 files changed, 32 insertions(+), 23 deletions(-)

src/arch/wasm/CodeGen.zig+32-23
......@@ -2962,7 +2962,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W
29622962 try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
29632963 }
29642964 return stack_ptr;
2965 } else unreachable;
2965 } else return self.fail("todo Wasm @intCast to 128bit integers", .{});
29662966
29672967 const result = try self.allocLocal(wanted);
29682968 try self.addLabel(.local_set, result.local);
......@@ -3710,35 +3710,44 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
37103710}
37113711
37123712/// Compares big integers by checking both its high bits and low bits.
3713/// TODO: Lower this to compiler_rt call
3713/// TODO: Lower this to compiler_rt call when bitsize > 128
37143714fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
37153715 if (operand_ty.intInfo(self.target).bits > 128) {
37163716 return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits});
37173717 }
37183718
3719 const result = try self.allocLocal(Type.initTag(.i32));
3720 {
3721 try self.startBlock(.block, wasm.block_empty);
3722 const lhs_high_bit = try self.load(lhs, Type.u64, 0);
3723 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
3724 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
3725 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
3726 try self.emitWValue(lhs_high_bit);
3727 try self.emitWValue(rhs_high_bit);
3728 try self.addTag(.i64_ne);
3729 try self.addLabel(.br_if, 0);
3730 try self.emitWValue(lhs_low_bit);
3731 try self.emitWValue(rhs_low_bit);
3732 try self.addTag(.i64_ne);
3733 try self.addLabel(.br_if, 0);
3734 try self.addImm32(1);
3735 try self.addLabel(.local_set, result.local);
3736 try self.endBlock();
3719 const lhs_high_bit = try self.load(lhs, Type.u64, 0);
3720 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
3721 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
3722 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
3723
3724 try self.emitWValue(or_result);
3725 switch (op) {
3726 .eq, .neq => {
3727 const xor_high = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor);
3728 const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
3729 const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or");
3730
3731 switch (op) {
3732 .eq => try self.addTag(.i64_eqz),
3733 .neq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .neq),
3734 else => unreachable,
3735 }
3736 },
3737 else => {
3738 const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64;
3739 const low_bit_eql = try self.cmp(lhs_low_bit, rhs_low_bit, ty, .eq);
3740 const high_bit_cmp = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op);
3741 const low_bit_cmp = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op);
3742
3743 try self.emitWValue(low_bit_cmp);
3744 try self.emitWValue(high_bit_cmp);
3745 try self.emitWValue(low_bit_eql);
3746 try self.addTag(.select);
3747 },
37373748 }
37383749
3739 try self.emitWValue(result);
3740 try self.addImm32(0);
3741 try self.addTag(if (op == .eq) .i32_ne else .i32_eq);
3750 const result = try self.allocLocal(Type.initTag(.i32));
37423751 try self.addLabel(.local_set, result.local);
37433752 return result;
37443753}