authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-05 18:59:57+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-05 22:51:12+02:00
log0fc1e8b54f2022081a642c13eb74dfdda5aad867
tree4773a1e659907bd11ba5dc21a84ac03af35f601d
parentebfc2825abeb839acb01ebf787156865e95c6bde

x64: handle signed mul_with_overflow for non-pow-2 ints


2 files changed, 142 insertions(+), 43 deletions(-)

src/arch/x86_64/CodeGen.zig+141-42
...@@ -1086,29 +1086,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {...@@ -1086,29 +1086,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
1086 // have to be removed. this only happens if the dst if not a power-of-two size.1086 // have to be removed. this only happens if the dst if not a power-of-two size.
1087 const dst_bit_size = dst_ty.bitSize(self.target.*);1087 const dst_bit_size = dst_ty.bitSize(self.target.*);
1088 if (!math.isPowerOfTwo(dst_bit_size) or dst_bit_size < 8) {1088 if (!math.isPowerOfTwo(dst_bit_size) or dst_bit_size < 8) {
1089 const max_reg_bit_width = Register.rax.size();1089 try self.truncateRegister(dst_ty, reg);
1090 const shift = @intCast(u6, max_reg_bit_width - dst_ty.bitSize(self.target.*));
1091 const mask = (~@as(u64, 0)) >> shift;
1092 try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask });
1093
1094 if (src_ty.intInfo(self.target.*).signedness == .signed) {
1095 _ = try self.addInst(.{
1096 .tag = .sal,
1097 .ops = (Mir.Ops{
1098 .reg1 = reg,
1099 .flags = 0b10,
1100 }).encode(),
1101 .data = .{ .imm = shift },
1102 });
1103 _ = try self.addInst(.{
1104 .tag = .sar,
1105 .ops = (Mir.Ops{
1106 .reg1 = reg,
1107 .flags = 0b10,
1108 }).encode(),
1109 .data = .{ .imm = shift },
1110 });
1111 }
1112 }1090 }
11131091
1114 return self.finishAir(inst, .{ .register = reg }, .{ ty_op.operand, .none, .none });1092 return self.finishAir(inst, .{ .register = reg }, .{ ty_op.operand, .none, .none });
...@@ -1478,31 +1456,119 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1478,31 +1456,119 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1478 switch (ty.zigTypeTag()) {1456 switch (ty.zigTypeTag()) {
1479 .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}),1457 .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}),
1480 .Int => {1458 .Int => {
1459 try self.spillCompareFlagsIfOccupied();
1460 self.compare_flags_inst = null;
1461
1481 const int_info = ty.intInfo(self.target.*);1462 const int_info = ty.intInfo(self.target.*);
14821463
1483 if (int_info.bits > 64) {1464 if (int_info.bits > 64) {
1484 return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{});1465 return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{});
1485 }1466 }
14861467
1487 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.1468 if (math.isPowerOfTwo(int_info.bits)) {
1488 try self.register_manager.getReg(.rax, inst);1469 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
1489 try self.register_manager.getReg(.rdx, null);1470 try self.register_manager.getReg(.rax, inst);
1490 self.register_manager.freezeRegs(&.{ .rax, .rdx });1471 try self.register_manager.getReg(.rdx, null);
1491 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });1472 self.register_manager.freezeRegs(&.{ .rax, .rdx });
1473 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
1474
1475 const lhs = try self.resolveInst(bin_op.lhs);
1476 const rhs = try self.resolveInst(bin_op.rhs);
1477
1478 try self.genIntMulDivOpMir(switch (int_info.signedness) {
1479 .signed => .imul,
1480 .unsigned => .mul,
1481 }, ty, int_info.signedness, lhs, rhs);
1482
1483 const result: MCValue = switch (int_info.signedness) {
1484 .signed => .{ .register_overflow_signed = .rax },
1485 .unsigned => .{ .register_overflow_unsigned = .rax },
1486 };
1487 break :result result;
1488 }
14921489
1493 const lhs = try self.resolveInst(bin_op.lhs);1490 const lhs = try self.resolveInst(bin_op.lhs);
1494 const rhs = try self.resolveInst(bin_op.rhs);1491 const rhs = try self.resolveInst(bin_op.rhs);
14951492
1496 try self.genIntMulDivOpMir(switch (int_info.signedness) {1493 rhs.freezeIfRegister(&self.register_manager);
1497 .signed => .imul,1494 defer rhs.unfreezeIfRegister(&self.register_manager);
1498 .unsigned => .mul,
1499 }, ty, int_info.signedness, lhs, rhs);
15001495
1501 const result: MCValue = switch (int_info.signedness) {1496 // TODO check if we could reuse rhs instead, and swap the values out.
1502 .signed => .{ .register_overflow_signed = .rax },1497 const dst_mcv = blk: {
1503 .unsigned => .{ .register_overflow_unsigned = .rax },1498 if (self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
1499 if (lhs.isRegister()) break :blk lhs;
1500 }
1501 break :blk MCValue{ .register = try self.copyToTmpRegister(ty, lhs) };
1504 };1502 };
1505 break :result result;1503 dst_mcv.freezeIfRegister(&self.register_manager);
1504 defer dst_mcv.unfreezeIfRegister(&self.register_manager);
1505
1506 const rhs_mcv = blk: {
1507 if (rhs.isRegister() or rhs.isMemory()) break :blk rhs;
1508 break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) };
1509 };
1510 rhs_mcv.freezeIfRegister(&self.register_manager);
1511 defer rhs_mcv.unfreezeIfRegister(&self.register_manager);
1512
1513 const tuple_ty = self.air.typeOfIndex(inst);
1514 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));
1515 const tuple_align = tuple_ty.abiAlignment(self.target.*);
1516 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));
1517
1518 const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align));
1519 const extended_ty = switch (int_info.signedness) {
1520 .signed => Type.isize,
1521 .unsigned => ty,
1522 };
1523
1524 try self.genIntMulComplexOpMir(extended_ty, dst_mcv, rhs_mcv);
1525
1526 const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null });
1527 self.register_manager.freezeRegs(&temp_regs);
1528 defer self.register_manager.unfreezeRegs(&temp_regs);
1529
1530 const overflow_reg = temp_regs[0];
1531 const flags: u2 = switch (int_info.signedness) {
1532 .signed => 0b00,
1533 .unsigned => 0b10,
1534 };
1535 _ = try self.addInst(.{
1536 .tag = .cond_set_byte_overflow,
1537 .ops = (Mir.Ops{
1538 .reg1 = overflow_reg.to8(),
1539 .flags = flags,
1540 }).encode(),
1541 .data = undefined,
1542 });
1543
1544 const scratch_reg = temp_regs[1];
1545 try self.genSetReg(extended_ty, scratch_reg, dst_mcv);
1546 try self.truncateRegister(ty, scratch_reg);
1547 try self.genBinMathOpMir(.cmp, extended_ty, dst_mcv, .{ .register = scratch_reg });
1548
1549 const eq_reg = temp_regs[2];
1550 _ = try self.addInst(.{
1551 .tag = .cond_set_byte_eq_ne,
1552 .ops = (Mir.Ops{
1553 .reg1 = eq_reg.to8(),
1554 .flags = 0b00,
1555 }).encode(),
1556 .data = undefined,
1557 });
1558
1559 try self.genBinMathOpMir(
1560 .@"or",
1561 Type.u8,
1562 .{ .register = overflow_reg },
1563 .{ .register = eq_reg },
1564 );
1565
1566 try self.genSetStack(ty, stack_offset, .{ .register = scratch_reg }, .{});
1567 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{
1568 .register = overflow_reg.to8(),
1569 }, .{});
1570
1571 break :result MCValue{ .stack_offset = stack_offset };
1506 },1572 },
1507 else => unreachable,1573 else => unreachable,
1508 }1574 }
...@@ -1648,7 +1714,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa...@@ -1648,7 +1714,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa
1648 }).encode(),1714 }).encode(),
1649 .data = undefined,1715 .data = undefined,
1650 });1716 });
1651 try self.genBinMathOpMir(.add, Type.isize, .{ .register = divisor.to64() }, .{ .register = .rax });1717 try self.genBinMathOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax });
1652 return MCValue{ .register = divisor };1718 return MCValue{ .register = divisor };
1653}1719}
16541720
...@@ -2222,8 +2288,8 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {...@@ -2222,8 +2288,8 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
2222 }2288 }
2223 // TODO we could allocate register here, but need to expect addr register and potentially2289 // TODO we could allocate register here, but need to expect addr register and potentially
2224 // offset register.2290 // offset register.
2225 try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg.to64() }, .{2291 try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg }, .{
2226 .register = offset_reg.to64(),2292 .register = offset_reg,
2227 });2293 });
2228 return MCValue{ .register = addr_reg.to64() };2294 return MCValue{ .register = addr_reg.to64() };
2229}2295}
...@@ -2315,7 +2381,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2315,7 +2381,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
2315 // TODO we could allocate register here, but need to expect addr register and potentially2381 // TODO we could allocate register here, but need to expect addr register and potentially
2316 // offset register.2382 // offset register.
2317 const dst_mcv = try self.allocRegOrMem(inst, false);2383 const dst_mcv = try self.allocRegOrMem(inst, false);
2318 try self.genBinMathOpMir(.add, array_ty, .{ .register = addr_reg.to64() }, .{ .register = offset_reg.to64() });2384 try self.genBinMathOpMir(.add, Type.usize, .{ .register = addr_reg }, .{ .register = offset_reg });
2319 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty);2385 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty);
2320 break :result dst_mcv;2386 break :result dst_mcv;
2321 };2387 };
...@@ -3178,8 +3244,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC...@@ -3178,8 +3244,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
3178 _ = try self.addInst(.{3244 _ = try self.addInst(.{
3179 .tag = mir_tag,3245 .tag = mir_tag,
3180 .ops = (Mir.Ops{3246 .ops = (Mir.Ops{
3181 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),3247 .reg1 = registerAlias(dst_reg, abi_size),
3182 .reg2 = src_reg,3248 .reg2 = registerAlias(src_reg, abi_size),
3183 }).encode(),3249 }).encode(),
3184 .data = undefined,3250 .data = undefined,
3185 });3251 });
...@@ -6531,3 +6597,36 @@ fn shiftRegister(self: *Self, reg: Register, shift: u8) !void {...@@ -6531,3 +6597,36 @@ fn shiftRegister(self: *Self, reg: Register, shift: u8) !void {
6531 });6597 });
6532 }6598 }
6533}6599}
6600
6601/// Truncates the value in the register in place.
6602/// Clobbers any remaining bits.
6603fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {
6604 const int_info = ty.intInfo(self.target.*);
6605 const max_reg_bit_width = Register.rax.size();
6606 switch (int_info.signedness) {
6607 .signed => {
6608 const shift = @intCast(u6, max_reg_bit_width - int_info.bits);
6609 _ = try self.addInst(.{
6610 .tag = .sal,
6611 .ops = (Mir.Ops{
6612 .reg1 = reg.to64(),
6613 .flags = 0b10,
6614 }).encode(),
6615 .data = .{ .imm = shift },
6616 });
6617 _ = try self.addInst(.{
6618 .tag = .sar,
6619 .ops = (Mir.Ops{
6620 .reg1 = reg.to64(),
6621 .flags = 0b10,
6622 }).encode(),
6623 .data = .{ .imm = shift },
6624 });
6625 },
6626 .unsigned => {
6627 const shift = @intCast(u6, max_reg_bit_width - int_info.bits);
6628 const mask = (~@as(u64, 0)) >> shift;
6629 try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask });
6630 },
6631 }
6632}
src/arch/x86_64/Emit.zig+1-1
...@@ -1383,7 +1383,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -1383,7 +1383,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
1383 .sub => OpCode.oneByte(if (is_one_byte) 0x2a else 0x2b),1383 .sub => OpCode.oneByte(if (is_one_byte) 0x2a else 0x2b),
1384 .xor => OpCode.oneByte(if (is_one_byte) 0x32 else 0x33),1384 .xor => OpCode.oneByte(if (is_one_byte) 0x32 else 0x33),
1385 .@"and" => OpCode.oneByte(if (is_one_byte) 0x22 else 0x23),1385 .@"and" => OpCode.oneByte(if (is_one_byte) 0x22 else 0x23),
1386 .@"or" => OpCode.oneByte(if (is_one_byte) 0x0b else 0x0b),1386 .@"or" => OpCode.oneByte(if (is_one_byte) 0x0a else 0x0b),
1387 .sbb => OpCode.oneByte(if (is_one_byte) 0x1a else 0x1b),1387 .sbb => OpCode.oneByte(if (is_one_byte) 0x1a else 0x1b),
1388 .cmp => OpCode.oneByte(if (is_one_byte) 0x3a else 0x3b),1388 .cmp => OpCode.oneByte(if (is_one_byte) 0x3a else 0x3b),
1389 .mov => OpCode.oneByte(if (is_one_byte) 0x8a else 0x8b),1389 .mov => OpCode.oneByte(if (is_one_byte) 0x8a else 0x8b),