| ... | @@ -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 | } |
| 2001 | | 2001 | |
| 2002 | fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | 2002 | fn 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 | } |
| 2006 | | 2006 | |
| ... | @@ -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 | }; |
| 2051 | | 2051 | |
| 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 | } |
| 2055 | | 2056 | |
| 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 | } | | |
| 2070 | | 2068 | |
| 2071 | return self.wrapOperand(bin_local, ty); | 2069 | return self.wrapOperand(bin_local, ty); |
| 2072 | } | 2070 | } |
| 2073 | | 2071 | |
| 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. |
| 2076 | fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { | 2074 | fn 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; | | |
| 3236 | | 3254 | |
| 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 | } |
| 3242 | | 3268 | |
| 3243 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3269 | fn 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); |
| 3930 | | 3957 | |
| 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 | }; |
| 3940 | | 3967 | |
| 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 | } |
| 3965 | | 3992 | |
| 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 | } |
| 4394 | | 4423 | |
| 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 | } |
| 4399 | | 4428 | |
| 4400 | fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue { | 4429 | fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue { |