authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-06 07:28:44+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-05-06 07:28:44+02:00
logdf38dfa4d1c9028453f90c7e37dd6c06f829a995
tree1092812bea75587f79d933d1a6dd20a5d4731184
parent09d2b6c4e1b2edd1d011ef4f947cdd835313a2e5
parentc592f0ca21e8274681fc1dc3c4597ce96ab2096e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11591 from ziglang/x64-overflow


3 files changed, 221 insertions(+), 79 deletions(-)

src/arch/x86_64/CodeGen.zig+220-77
...@@ -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 });
...@@ -1414,23 +1392,27 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -1414,23 +1392,27 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
1414fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1392fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1415 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1393 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1416 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1394 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1395 const result = if (self.liveness.isUnused(inst)) .dead else result: {
1396 const ty = self.air.typeOf(bin_op.lhs);
14171397
1418 if (self.liveness.isUnused(inst)) {1398 switch (ty.zigTypeTag()) {
1419 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });1399 .Vector => return self.fail("TODO implement add_with_overflow for Vector type", .{}),
1420 }1400 .Int => {
1401 const int_info = ty.intInfo(self.target.*);
14211402
1422 const ty = self.air.typeOf(bin_op.lhs);1403 if (int_info.bits > 64) {
1423 const signedness: std.builtin.Signedness = blk: {1404 return self.fail("TODO implement add_with_overflow for Ints larger than 64bits", .{});
1424 if (ty.zigTypeTag() != .Int) {1405 }
1425 return self.fail("TODO implement airAddWithOverflow for type {}", .{ty.fmtDebug()});
1426 }
1427 break :blk ty.intInfo(self.target.*).signedness;
1428 };
14291406
1430 const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs);1407 const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs);
1431 const result: MCValue = switch (signedness) {1408 const result: MCValue = switch (int_info.signedness) {
1432 .signed => .{ .register_overflow_signed = partial.register },1409 .signed => .{ .register_overflow_signed = partial.register },
1433 .unsigned => .{ .register_overflow_unsigned = partial.register },1410 .unsigned => .{ .register_overflow_unsigned = partial.register },
1411 };
1412 break :result result;
1413 },
1414 else => unreachable,
1415 }
1434 };1416 };
14351417
1436 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1418 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
...@@ -1439,23 +1421,27 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1439,23 +1421,27 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1439fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1421fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1440 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1422 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1441 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1423 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1424 const result = if (self.liveness.isUnused(inst)) .dead else result: {
1425 const ty = self.air.typeOf(bin_op.lhs);
14421426
1443 if (self.liveness.isUnused(inst)) {1427 switch (ty.zigTypeTag()) {
1444 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });1428 .Vector => return self.fail("TODO implement sub_with_overflow for Vector type", .{}),
1445 }1429 .Int => {
1430 const int_info = ty.intInfo(self.target.*);
14461431
1447 const ty = self.air.typeOf(bin_op.lhs);1432 if (int_info.bits > 64) {
1448 const signedness: std.builtin.Signedness = blk: {1433 return self.fail("TODO implement sub_with_overflow for Ints larger than 64bits", .{});
1449 if (ty.zigTypeTag() != .Int) {1434 }
1450 return self.fail("TODO implement airSubWithOverflow for type {}", .{ty.fmtDebug()});
1451 }
1452 break :blk ty.intInfo(self.target.*).signedness;
1453 };
14541435
1455 const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs);1436 const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs);
1456 const result: MCValue = switch (signedness) {1437 const result: MCValue = switch (int_info.signedness) {
1457 .signed => .{ .register_overflow_signed = partial.register },1438 .signed => .{ .register_overflow_signed = partial.register },
1458 .unsigned => .{ .register_overflow_unsigned = partial.register },1439 .unsigned => .{ .register_overflow_unsigned = partial.register },
1440 };
1441 break :result result;
1442 },
1443 else => unreachable,
1444 }
1459 };1445 };
14601446
1461 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1447 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
...@@ -1466,30 +1452,149 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1466,30 +1452,149 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1466 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1452 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1467 const result = if (self.liveness.isUnused(inst)) .dead else result: {1453 const result = if (self.liveness.isUnused(inst)) .dead else result: {
1468 const ty = self.air.typeOf(bin_op.lhs);1454 const ty = self.air.typeOf(bin_op.lhs);
1469 const signedness: std.builtin.Signedness = blk: {
1470 if (ty.zigTypeTag() != .Int) {
1471 return self.fail("TODO implement airMulWithOverflow for type {}", .{ty.fmtDebug()});
1472 }
1473 break :blk ty.intInfo(self.target.*).signedness;
1474 };
14751455
1476 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.1456 switch (ty.zigTypeTag()) {
1477 try self.register_manager.getReg(.rax, inst);1457 .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}),
1478 try self.register_manager.getReg(.rdx, null);1458 .Int => {
1479 self.register_manager.freezeRegs(&.{ .rax, .rdx });1459 try self.spillCompareFlagsIfOccupied();
1480 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });1460 self.compare_flags_inst = null;
14811461
1482 const lhs = try self.resolveInst(bin_op.lhs);1462 const int_info = ty.intInfo(self.target.*);
1483 const rhs = try self.resolveInst(bin_op.rhs);
14841463
1485 try self.genIntMulDivOpMir(switch (signedness) {1464 if (int_info.bits > 64) {
1486 .signed => .imul,1465 return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{});
1487 .unsigned => .mul,1466 }
1488 }, ty, signedness, lhs, rhs);
14891467
1490 switch (signedness) {1468 if (math.isPowerOfTwo(int_info.bits)) {
1491 .signed => break :result MCValue{ .register_overflow_signed = .rax },1469 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
1492 .unsigned => break :result MCValue{ .register_overflow_unsigned = .rax },1470 try self.register_manager.getReg(.rax, inst);
1471 try self.register_manager.getReg(.rdx, null);
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 }
1489
1490 const dst_reg: Register = dst_reg: {
1491 switch (int_info.signedness) {
1492 .signed => {
1493 const lhs = try self.resolveInst(bin_op.lhs);
1494 const rhs = try self.resolveInst(bin_op.rhs);
1495
1496 rhs.freezeIfRegister(&self.register_manager);
1497 defer rhs.unfreezeIfRegister(&self.register_manager);
1498
1499 const dst_reg: Register = blk: {
1500 if (lhs.isRegister()) break :blk lhs.register;
1501 break :blk try self.copyToTmpRegister(ty, lhs);
1502 };
1503 self.register_manager.freezeRegs(&.{dst_reg});
1504
1505 const rhs_mcv = blk: {
1506 if (rhs.isRegister() or rhs.isMemory()) break :blk rhs;
1507 break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) };
1508 };
1509 rhs_mcv.freezeIfRegister(&self.register_manager);
1510 defer rhs_mcv.unfreezeIfRegister(&self.register_manager);
1511
1512 try self.genIntMulComplexOpMir(Type.isize, .{ .register = dst_reg }, rhs_mcv);
1513
1514 break :dst_reg dst_reg;
1515 },
1516 .unsigned => {
1517 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
1518 try self.register_manager.getReg(.rax, null);
1519 try self.register_manager.getReg(.rdx, null);
1520 self.register_manager.freezeRegs(&.{ .rax, .rdx });
1521 defer self.register_manager.unfreezeRegs(&.{.rdx});
1522
1523 const lhs = try self.resolveInst(bin_op.lhs);
1524 const rhs = try self.resolveInst(bin_op.rhs);
1525
1526 try self.genIntMulDivOpMir(.mul, ty, .unsigned, lhs, rhs);
1527
1528 break :dst_reg registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*)));
1529 },
1530 }
1531 };
1532 defer self.register_manager.unfreezeRegs(&.{dst_reg});
1533
1534 const tuple_ty = self.air.typeOfIndex(inst);
1535 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));
1536 const tuple_align = tuple_ty.abiAlignment(self.target.*);
1537 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));
1538
1539 const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align));
1540 const extended_ty = switch (int_info.signedness) {
1541 .signed => Type.isize,
1542 .unsigned => ty,
1543 };
1544
1545 const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null });
1546 self.register_manager.freezeRegs(&temp_regs);
1547 defer self.register_manager.unfreezeRegs(&temp_regs);
1548
1549 const overflow_reg = temp_regs[0];
1550 const flags: u2 = switch (int_info.signedness) {
1551 .signed => 0b00,
1552 .unsigned => 0b10,
1553 };
1554 _ = try self.addInst(.{
1555 .tag = .cond_set_byte_overflow,
1556 .ops = (Mir.Ops{
1557 .reg1 = overflow_reg.to8(),
1558 .flags = flags,
1559 }).encode(),
1560 .data = undefined,
1561 });
1562
1563 const scratch_reg = temp_regs[1];
1564 try self.genSetReg(extended_ty, scratch_reg, .{ .register = dst_reg });
1565 try self.truncateRegister(ty, scratch_reg);
1566 try self.genBinMathOpMir(
1567 .cmp,
1568 extended_ty,
1569 .{ .register = dst_reg },
1570 .{ .register = scratch_reg },
1571 );
1572
1573 const eq_reg = temp_regs[2];
1574 _ = try self.addInst(.{
1575 .tag = .cond_set_byte_eq_ne,
1576 .ops = (Mir.Ops{
1577 .reg1 = eq_reg.to8(),
1578 .flags = 0b00,
1579 }).encode(),
1580 .data = undefined,
1581 });
1582
1583 try self.genBinMathOpMir(
1584 .@"or",
1585 Type.u8,
1586 .{ .register = overflow_reg },
1587 .{ .register = eq_reg },
1588 );
1589
1590 try self.genSetStack(ty, stack_offset, .{ .register = scratch_reg }, .{});
1591 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{
1592 .register = overflow_reg.to8(),
1593 }, .{});
1594
1595 break :result MCValue{ .stack_offset = stack_offset };
1596 },
1597 else => unreachable,
1493 }1598 }
1494 };1599 };
14951600
...@@ -1633,7 +1738,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa...@@ -1633,7 +1738,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa
1633 }).encode(),1738 }).encode(),
1634 .data = undefined,1739 .data = undefined,
1635 });1740 });
1636 try self.genBinMathOpMir(.add, Type.isize, .{ .register = divisor.to64() }, .{ .register = .rax });1741 try self.genBinMathOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax });
1637 return MCValue{ .register = divisor };1742 return MCValue{ .register = divisor };
1638}1743}
16391744
...@@ -2207,8 +2312,8 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {...@@ -2207,8 +2312,8 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
2207 }2312 }
2208 // TODO we could allocate register here, but need to expect addr register and potentially2313 // TODO we could allocate register here, but need to expect addr register and potentially
2209 // offset register.2314 // offset register.
2210 try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg.to64() }, .{2315 try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg }, .{
2211 .register = offset_reg.to64(),2316 .register = offset_reg,
2212 });2317 });
2213 return MCValue{ .register = addr_reg.to64() };2318 return MCValue{ .register = addr_reg.to64() };
2214}2319}
...@@ -2300,7 +2405,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2300,7 +2405,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
2300 // TODO we could allocate register here, but need to expect addr register and potentially2405 // TODO we could allocate register here, but need to expect addr register and potentially
2301 // offset register.2406 // offset register.
2302 const dst_mcv = try self.allocRegOrMem(inst, false);2407 const dst_mcv = try self.allocRegOrMem(inst, false);
2303 try self.genBinMathOpMir(.add, array_ty, .{ .register = addr_reg.to64() }, .{ .register = offset_reg.to64() });2408 try self.genBinMathOpMir(.add, Type.usize, .{ .register = addr_reg }, .{ .register = offset_reg });
2304 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty);2409 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty);
2305 break :result dst_mcv;2410 break :result dst_mcv;
2306 };2411 };
...@@ -3163,8 +3268,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC...@@ -3163,8 +3268,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
3163 _ = try self.addInst(.{3268 _ = try self.addInst(.{
3164 .tag = mir_tag,3269 .tag = mir_tag,
3165 .ops = (Mir.Ops{3270 .ops = (Mir.Ops{
3166 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),3271 .reg1 = registerAlias(dst_reg, abi_size),
3167 .reg2 = src_reg,3272 .reg2 = registerAlias(src_reg, abi_size),
3168 }).encode(),3273 }).encode(),
3169 .data = undefined,3274 .data = undefined,
3170 });3275 });
...@@ -6516,3 +6621,41 @@ fn shiftRegister(self: *Self, reg: Register, shift: u8) !void {...@@ -6516,3 +6621,41 @@ fn shiftRegister(self: *Self, reg: Register, shift: u8) !void {
6516 });6621 });
6517 }6622 }
6518}6623}
6624
6625/// Truncates the value in the register in place.
6626/// Clobbers any remaining bits.
6627fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {
6628 const int_info = ty.intInfo(self.target.*);
6629 const max_reg_bit_width = Register.rax.size();
6630 switch (int_info.signedness) {
6631 .signed => {
6632 const shift = @intCast(u6, max_reg_bit_width - int_info.bits);
6633 _ = try self.addInst(.{
6634 .tag = .sal,
6635 .ops = (Mir.Ops{
6636 .reg1 = reg.to64(),
6637 .flags = 0b10,
6638 }).encode(),
6639 .data = .{ .imm = shift },
6640 });
6641 _ = try self.addInst(.{
6642 .tag = .sar,
6643 .ops = (Mir.Ops{
6644 .reg1 = reg.to64(),
6645 .flags = 0b10,
6646 }).encode(),
6647 .data = .{ .imm = shift },
6648 });
6649 },
6650 .unsigned => {
6651 const shift = @intCast(u6, max_reg_bit_width - int_info.bits);
6652 const mask = (~@as(u64, 0)) >> shift;
6653 if (int_info.bits <= 32) {
6654 try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask });
6655 } else {
6656 const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask });
6657 try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .register = tmp_reg });
6658 }
6659 },
6660 }
6661}
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),
test/behavior/math.zig-1
...@@ -686,7 +686,6 @@ test "basic @mulWithOverflow" {...@@ -686,7 +686,6 @@ test "basic @mulWithOverflow" {
686// TODO migrate to this for all backends once they handle more cases686// TODO migrate to this for all backends once they handle more cases
687test "extensive @mulWithOverflow" {687test "extensive @mulWithOverflow" {
688 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO688 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
689 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
690 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO689 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
691 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO690 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
692691