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...@@ -2000,7 +2000,7 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa
2000}2000}
20012001
2002fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {2002fn 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) {
2004 return self.fail("TODO: Implement binary operation for big integer", .{});2004 return self.fail("TODO: Implement binary operation for big integer", .{});
2005 }2005 }
20062006
...@@ -2050,7 +2050,8 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError...@@ -2050,7 +2050,8 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError
2050 };2050 };
20512051
2052 if (wasm_bits == 128) {2052 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);
2054 }2055 }
20552056
2056 const opcode: wasm.Opcode = buildOpcode(.{2057 const opcode: wasm.Opcode = buildOpcode(.{
...@@ -2064,28 +2065,46 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError...@@ -2064,28 +2065,46 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError
2064 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));2065 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
2065 const bin_local = try self.allocLocal(ty);2066 const bin_local = try self.allocLocal(ty);
2066 try self.addLabel(.local_set, bin_local.local);2067 try self.addLabel(.local_set, bin_local.local);
2067 if (wasm_bits == bit_size) {
2068 return bin_local;
2069 }
20702068
2071 return self.wrapOperand(bin_local, ty);2069 return self.wrapOperand(bin_local, ty);
2072}2070}
20732071
2074/// Wraps an operand based on a given type's bitsize.2072/// Wraps an operand based on a given type's bitsize.
2075/// Asserts `Type` is <= 64bits.2073/// Asserts `Type` is <= 128 bits.
2076fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue {2074fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
2077 assert(ty.abiSize(self.target) <= 8);2075 assert(ty.abiSize(self.target) <= 16);
2078 const result_local = try self.allocLocal(ty);2076 const result_local = try self.allocLocal(ty);
2079 const bitsize = ty.intInfo(self.target).bits;2077 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;
2081 try self.emitWValue(operand);2099 try self.emitWValue(operand);
2082 if (bitsize <= 32) {2100 if (bitsize <= 32) {
2083 try self.addImm32(@bitCast(i32, @intCast(u32, result)));2101 try self.addImm32(@bitCast(i32, @intCast(u32, result)));
2084 try self.addTag(.i32_and);2102 try self.addTag(.i32_and);
2085 } else {2103 } else if (bitsize <= 64) {
2086 try self.addImm64(result);2104 try self.addImm64(result);
2087 try self.addTag(.i64_and);2105 try self.addTag(.i64_and);
2088 }2106 } else unreachable;
2107
2089 try self.addLabel(.local_set, result_local.local);2108 try self.addLabel(.local_set, result_local.local);
2090 return result_local;2109 return result_local;
2091}2110}
...@@ -3231,13 +3250,20 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3231,13 +3250,20 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3231 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3250 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3232 const operand = try self.resolveInst(ty_op.operand);3251 const operand = try self.resolveInst(ty_op.operand);
3233 const wanted_ty = self.air.getRefType(ty_op.ty);3252 const wanted_ty = self.air.getRefType(ty_op.ty);
3234 const int_info = wanted_ty.intInfo(self.target);3253 const op_ty = self.air.typeOf(ty_op.operand);
3235 const wanted_bits = int_info.bits;
32363254
3237 _ = toWasmBits(wanted_bits) orelse {3255 const int_info = op_ty.intInfo(self.target);
3238 return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{wanted_bits});3256 if (toWasmBits(int_info.bits) == null) {
3239 };3257 return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits});
3240 return self.wrapOperand(operand, wanted_ty);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;
3241}3267}
32423268
3243fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3269fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -3927,6 +3953,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3927,6 +3953,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3927 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3953 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3928 const operand = try self.resolveInst(ty_op.operand);3954 const operand = try self.resolveInst(ty_op.operand);
3929 const op_ty = self.air.typeOf(ty_op.operand);3955 const op_ty = self.air.typeOf(ty_op.operand);
3956 const result_ty = self.air.typeOfIndex(inst);
39303957
3931 if (op_ty.zigTypeTag() == .Vector) {3958 if (op_ty.zigTypeTag() == .Vector) {
3932 return self.fail("TODO: Implement @popCount for vectors", .{});3959 return self.fail("TODO: Implement @popCount for vectors", .{});
...@@ -3938,32 +3965,32 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3938,32 +3965,32 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3938 return self.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits});3965 return self.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits});
3939 };3966 };
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
3960 switch (wasm_bits) {3968 switch (wasm_bits) {
3961 32 => try self.addTag(.i32_popcnt),3969 128 => {
3962 64 => try self.addTag(.i64_popcnt),3970 const msb = try self.load(operand, Type.u64, 0);
3963 else => unreachable,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 },
3964 }3991 }
39653992
3966 const result = try self.allocLocal(op_ty);3993 const result = try self.allocLocal(result_ty);
3967 try self.addLabel(.local_set, result.local);3994 try self.addLabel(.local_set, result.local);
3968 return result;3995 return result;
3969}3996}
...@@ -4366,6 +4393,7 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4366,6 +4393,7 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4366 try self.emitWValue(bin_op);4393 try self.emitWValue(bin_op);
4367 } else try self.emitWValue(operand);4394 } else try self.emitWValue(operand);
4368 try self.addTag(.i64_ctz);4395 try self.addTag(.i64_ctz);
4396 try self.addTag(.i32_wrap_i64);
4369 },4397 },
4370 128 => {4398 128 => {
4371 const msb = try self.load(operand, Type.u64, 0);4399 const msb = try self.load(operand, Type.u64, 0);
...@@ -4388,13 +4416,14 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -4388,13 +4416,14 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4388 }4416 }
4389 try self.emitWValue(neq);4417 try self.emitWValue(neq);
4390 try self.addTag(.select);4418 try self.addTag(.select);
4419 try self.addTag(.i32_wrap_i64);
4391 },4420 },
4392 else => unreachable,4421 else => unreachable,
4393 }4422 }
43944423
4395 const result = try self.allocLocal(ty);4424 const result = try self.allocLocal(result_ty);
4396 try self.addLabel(.local_set, result.local);4425 try self.addLabel(.local_set, result.local);
4397 return self.intcast(result, ty, result_ty);4426 return result;
4398}4427}
43994428
4400fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue {4429fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue {