authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-15 20:14:57+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-18 07:43:33+02:00
log10fe24c043c95180f658c3eb4d7fbbfd0388c14e
treedfc10ffd74a22dae5a517cb9b229b50493aa7f40
parentea073a6b767cc6597ff2b6ec3b5f14fde81dd6fc

wasm: Implement trunc/wrap for 128 bit integers

This also implments wrapping for arbitrary integer widths between 64 and 128. `@truncate` was fixed where the wasm types between operand and result differentiated. We solved this by first casting and then wrapping.

1 files changed, 70 insertions(+), 41 deletions(-)

src/arch/wasm/CodeGen.zig+70-41
......@@ -2000,7 +2000,7 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa
20002000}
20012001
20022002fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2003 if (ty.intInfo(self.target).bits != 128) {
2003 if (ty.intInfo(self.target).bits > 128) {
20042004 return self.fail("TODO: Implement binary operation for big integer", .{});
20052005 }
20062006
......@@ -2050,7 +2050,8 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError
20502050 };
20512051
20522052 if (wasm_bits == 128) {
2053 return self.binOp(lhs, rhs, ty, op);
2053 const bin_op = try self.binOpBigInt(lhs, rhs, ty, op);
2054 return self.wrapOperand(bin_op, ty);
20542055 }
20552056
20562057 const opcode: wasm.Opcode = buildOpcode(.{
......@@ -2064,28 +2065,46 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError
20642065 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
20652066 const bin_local = try self.allocLocal(ty);
20662067 try self.addLabel(.local_set, bin_local.local);
2067 if (wasm_bits == bit_size) {
2068 return bin_local;
2069 }
20702068
20712069 return self.wrapOperand(bin_local, ty);
20722070}
20732071
20742072/// Wraps an operand based on a given type's bitsize.
2075/// Asserts `Type` is <= 64bits.
2073/// Asserts `Type` is <= 128 bits.
20762074fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
2077 assert(ty.abiSize(self.target) <= 8);
2075 assert(ty.abiSize(self.target) <= 16);
20782076 const result_local = try self.allocLocal(ty);
20792077 const bitsize = ty.intInfo(self.target).bits;
2080 const result = @intCast(u64, (@as(u65, 1) << @intCast(u7, bitsize)) - 1);
2078 const wasm_bits = toWasmBits(bitsize) orelse {
2079 return self.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});
2080 };
2081 if (wasm_bits == bitsize) return operand;
2082
2083 if (wasm_bits == 128) {
2084 const msb = try self.load(operand, Type.u64, 0);
2085 const lsb = try self.load(operand, Type.u64, 8);
2086
2087 const result_ptr = try self.allocStack(ty);
2088 try self.store(result_ptr, lsb, Type.u64, 8);
2089 const result = (@as(u64, 1) << @intCast(u6, 64 - (wasm_bits - bitsize))) - 1;
2090 try self.emitWValue(result_ptr);
2091 try self.emitWValue(msb);
2092 try self.addImm64(result);
2093 try self.addTag(.i64_and);
2094 try self.addMemArg(.i64_store, .{ .offset = result_ptr.offset(), .alignment = 8 });
2095 return result_ptr;
2096 }
2097
2098 const result = (@as(u64, 1) << @intCast(u6, bitsize)) - 1;
20812099 try self.emitWValue(operand);
20822100 if (bitsize <= 32) {
20832101 try self.addImm32(@bitCast(i32, @intCast(u32, result)));
20842102 try self.addTag(.i32_and);
2085 } else {
2103 } else if (bitsize <= 64) {
20862104 try self.addImm64(result);
20872105 try self.addTag(.i64_and);
2088 }
2106 } else unreachable;
2107
20892108 try self.addLabel(.local_set, result_local.local);
20902109 return result_local;
20912110}
......@@ -3231,13 +3250,20 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
32313250 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
32323251 const operand = try self.resolveInst(ty_op.operand);
32333252 const wanted_ty = self.air.getRefType(ty_op.ty);
3234 const int_info = wanted_ty.intInfo(self.target);
3235 const wanted_bits = int_info.bits;
3253 const op_ty = self.air.typeOf(ty_op.operand);
32363254
3237 _ = toWasmBits(wanted_bits) orelse {
3238 return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{wanted_bits});
3239 };
3240 return self.wrapOperand(operand, wanted_ty);
3255 const int_info = op_ty.intInfo(self.target);
3256 if (toWasmBits(int_info.bits) == null) {
3257 return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits});
3258 }
3259
3260 const result = try self.intcast(operand, op_ty, wanted_ty);
3261 const wanted_bits = wanted_ty.intInfo(self.target).bits;
3262 const wasm_bits = toWasmBits(wanted_bits).?;
3263 if (wasm_bits != wanted_bits) {
3264 return self.wrapOperand(result, wanted_ty);
3265 }
3266 return result;
32413267}
32423268
32433269fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3927,6 +3953,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
39273953 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
39283954 const operand = try self.resolveInst(ty_op.operand);
39293955 const op_ty = self.air.typeOf(ty_op.operand);
3956 const result_ty = self.air.typeOfIndex(inst);
39303957
39313958 if (op_ty.zigTypeTag() == .Vector) {
39323959 return self.fail("TODO: Implement @popCount for vectors", .{});
......@@ -3938,32 +3965,32 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
39383965 return self.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits});
39393966 };
39403967
3941 try self.emitWValue(operand);
3942
3943 // for signed integers we first mask the signedness bit
3944 if (int_info.signedness == .signed and wasm_bits != bits) {
3945 switch (wasm_bits) {
3946 32 => {
3947 const mask = (@as(u32, 1) << @intCast(u5, bits)) - 1;
3948 try self.addImm32(@bitCast(i32, mask));
3949 try self.addTag(.i32_and);
3950 },
3951 64 => {
3952 const mask = (@as(u64, 1) << @intCast(u6, bits)) - 1;
3953 try self.addImm64(mask);
3954 try self.addTag(.i64_and);
3955 },
3956 else => unreachable,
3957 }
3958 }
3959
39603968 switch (wasm_bits) {
3961 32 => try self.addTag(.i32_popcnt),
3962 64 => try self.addTag(.i64_popcnt),
3963 else => unreachable,
3969 128 => {
3970 const msb = try self.load(operand, Type.u64, 0);
3971 const lsb = try self.load(operand, Type.u64, 8);
3972
3973 try self.emitWValue(msb);
3974 try self.addTag(.i64_popcnt);
3975 try self.emitWValue(lsb);
3976 try self.addTag(.i64_popcnt);
3977 try self.addTag(.i64_add);
3978 try self.addTag(.i32_wrap_i64);
3979 },
3980 else => {
3981 try self.emitWValue(operand);
3982 switch (wasm_bits) {
3983 32 => try self.addTag(.i32_popcnt),
3984 64 => {
3985 try self.addTag(.i64_popcnt);
3986 try self.addTag(.i32_wrap_i64);
3987 },
3988 else => unreachable,
3989 }
3990 },
39643991 }
39653992
3966 const result = try self.allocLocal(op_ty);
3993 const result = try self.allocLocal(result_ty);
39673994 try self.addLabel(.local_set, result.local);
39683995 return result;
39693996}
......@@ -4366,6 +4393,7 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
43664393 try self.emitWValue(bin_op);
43674394 } else try self.emitWValue(operand);
43684395 try self.addTag(.i64_ctz);
4396 try self.addTag(.i32_wrap_i64);
43694397 },
43704398 128 => {
43714399 const msb = try self.load(operand, Type.u64, 0);
......@@ -4388,13 +4416,14 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
43884416 }
43894417 try self.emitWValue(neq);
43904418 try self.addTag(.select);
4419 try self.addTag(.i32_wrap_i64);
43914420 },
43924421 else => unreachable,
43934422 }
43944423
4395 const result = try self.allocLocal(ty);
4424 const result = try self.allocLocal(result_ty);
43964425 try self.addLabel(.local_set, result.local);
4397 return self.intcast(result, ty, result_ty);
4426 return result;
43984427}
43994428
44004429fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue {