diff options
| author | Luuk de Gram <luuk@degram.dev> | 2022-05-12 21:53:57 +0200 |
|---|---|---|
| committer | Luuk de Gram <luuk@degram.dev> | 2022-05-18 07:43:33 +0200 |
| commit | 03a3ea2c1577208311d85482faadac6361442971 (patch) | |
| tree | 4263a470f5091f3f64fa45ec4e2db1ff1402b77a /src/arch/wasm/CodeGen.zig | |
| parent | 167d3089ea11a8f0a45d6e083b1dacb107483b04 (diff) | |
| download | zig-03a3ea2c1577208311d85482faadac6361442971.tar.gz zig-03a3ea2c1577208311d85482faadac6361442971.zip | |
wasm: 128 bit intcast and binary operations
Also fixes some bugs in 128-bit binary comparisons where we checked
if the lsb were equal, rather than msb.
Diffstat (limited to 'src/arch/wasm/CodeGen.zig')
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 94 |
1 files changed, 55 insertions, 39 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c79a01f5e4..0b371a8cd2 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1958,25 +1958,31 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const operand_ty = self.air.typeOfIndex(inst); const ty = self.air.typeOf(bin_op.lhs); - if (isByRef(operand_ty, self.target)) { - return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty.fmtDebug()}); - } - return self.binOp(lhs, rhs, ty, op); } fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { - try self.emitWValue(lhs); - try self.emitWValue(rhs); + if (isByRef(ty, self.target)) { + if (ty.zigTypeTag() == .Int) { + return self.binOpBigInt(lhs, rhs, ty, op); + } else { + return self.fail( + "TODO: Implement binary operation for type: {}", + .{ty.fmt(self.bin_file.base.options.module.?)}, + ); + } + } const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target), .signedness = if (ty.isSignedInt()) .signed else .unsigned, }); + try self.emitWValue(lhs); + try self.emitWValue(rhs); + try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); // save the result in a temporary @@ -1985,6 +1991,37 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa return bin_local; } +fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { + if (ty.intInfo(self.target).bits != 128) { + return self.fail("TODO: Implement binary operation for big integer", .{}); + } + + if (op != .add and op != .sub) { + return self.fail("TODO: Implement binary operation for big integers", .{}); + } + + const result = try self.allocStack(ty); + const lhs_high_bit = try self.load(lhs, Type.u64, 0); + const lhs_low_bit = try self.load(lhs, Type.u64, 8); + const rhs_high_bit = try self.load(rhs, Type.u64, 0); + const rhs_low_bit = try self.load(rhs, Type.u64, 8); + + const low_op_res = try self.binOp(rhs_low_bit, lhs_low_bit, Type.u64, op); + const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); + + const lt = if (op == .add) blk: { + break :blk try self.cmp(high_op_res, rhs_high_bit, Type.u64, .lt); + } else if (op == .sub) blk: { + break :blk try self.cmp(rhs_high_bit, lhs_high_bit, Type.u64, .lt); + } else unreachable; + const tmp = try self.intcast(lt, Type.u32, Type.u64); + const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); + + try self.store(result, high_op_res, Type.u64, 0); + try self.store(result, tmp_op, Type.u64, 8); + return result; +} + fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); @@ -1993,10 +2030,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const ty = self.air.typeOf(bin_op.lhs); if (ty.zigTypeTag() == .Vector) { return self.fail("TODO: Implement wrapping arithmetic for vectors", .{}); - } else if (ty.abiSize(self.target) > 8 and op == .mul) { - return self.fail("TODO: Implement wrapping multiplication for bitsize > 64", .{}); - } else if (ty.abiSize(self.target) > 16) { - return self.fail("TODO: Implement wrapping arithmetic for bitsize > 128", .{}); } return self.wrapBinOp(lhs, rhs, ty, op); @@ -2007,30 +2040,9 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError var wasm_bits = toWasmBits(bit_size) orelse { return self.fail("TODO: Implement wrapping arithmetic for integers with bitsize: {d}\n", .{bit_size}); }; + if (wasm_bits == 128) { - if (op == .mul) return self.fail("TODO: Implement wrapping multiplication for 128bit integers", .{}); - if (bit_size != wasm_bits) return self.fail("TODO: Implement wrapping arithmetic for integers > 64 & < 128", .{}); - - const result = try self.allocStack(ty); - const lhs_high_bit = try self.load(lhs, Type.u64, 0); - const lhs_low_bit = try self.load(lhs, Type.u64, 8); - const rhs_high_bit = try self.load(rhs, Type.u64, 0); - const rhs_low_bit = try self.load(rhs, Type.u64, 8); - - const low_op_res = try self.binOp(rhs_low_bit, lhs_low_bit, Type.u64, op); - const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); - - const lt = if (op == .add) blk: { - break :blk try self.binOp(high_op_res, rhs_high_bit, Type.u64, .lt); - } else if (op == .sub) blk: { - break :blk try self.binOp(rhs_high_bit, lhs_high_bit, Type.u64, .lt); - } else unreachable; - const tmp = try self.intcast(lt, Type.u32, Type.u64); - const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); - - try self.store(result, high_op_res, Type.u64, 0); - try self.store(result, tmp_op, Type.u64, 8); - return result; + return self.binOp(lhs, rhs, ty, op); } const opcode: wasm.Opcode = buildOpcode(.{ @@ -2044,6 +2056,10 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); const bin_local = try self.allocLocal(ty); try self.addLabel(.local_set, bin_local.local); + if (wasm_bits == bit_size) { + return bin_local; + } + return self.wrapOperand(bin_local, ty); } @@ -2994,7 +3010,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); } return stack_ptr; - } else return self.fail("todo Wasm @intCast to 128bit integers", .{}); + } else return self.load(operand, wanted, 0); const result = try self.allocLocal(wanted); try self.addLabel(.local_set, result.local); @@ -3760,20 +3776,20 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or"); switch (op) { - .eq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .eq), - .neq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .neq), + .eq => return self.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq), + .neq => return self.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq), else => unreachable, } }, else => { const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64; - const low_bit_eql = try self.cmp(lhs_low_bit, rhs_low_bit, ty, .eq); + const high_bit_eql = try self.cmp(lhs_high_bit, rhs_high_bit, ty, .eq); const high_bit_cmp = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op); const low_bit_cmp = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op); try self.emitWValue(low_bit_cmp); try self.emitWValue(high_bit_cmp); - try self.emitWValue(low_bit_eql); + try self.emitWValue(high_bit_eql); try self.addTag(.select); }, } |
