| ... | ... | @@ -1086,29 +1086,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 1086 | 1086 | // have to be removed. this only happens if the dst if not a power-of-two size. |
| 1087 | 1087 | const dst_bit_size = dst_ty.bitSize(self.target.*); |
| 1088 | 1088 | if (!math.isPowerOfTwo(dst_bit_size) or dst_bit_size < 8) { |
| 1089 | | const max_reg_bit_width = Register.rax.size(); |
| 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 | | } |
| 1089 | try self.truncateRegister(dst_ty, reg); |
| 1112 | 1090 | } |
| 1113 | 1091 | |
| 1114 | 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 | 1392 | fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1415 | 1393 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1416 | 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); |
| 1417 | 1397 | |
| 1418 | | if (self.liveness.isUnused(inst)) { |
| 1419 | | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1420 | | } |
| 1398 | switch (ty.zigTypeTag()) { |
| 1399 | .Vector => return self.fail("TODO implement add_with_overflow for Vector type", .{}), |
| 1400 | .Int => { |
| 1401 | const int_info = ty.intInfo(self.target.*); |
| 1421 | 1402 | |
| 1422 | | const ty = self.air.typeOf(bin_op.lhs); |
| 1423 | | const signedness: std.builtin.Signedness = blk: { |
| 1424 | | if (ty.zigTypeTag() != .Int) { |
| 1425 | | return self.fail("TODO implement airAddWithOverflow for type {}", .{ty.fmtDebug()}); |
| 1426 | | } |
| 1427 | | break :blk ty.intInfo(self.target.*).signedness; |
| 1428 | | }; |
| 1403 | if (int_info.bits > 64) { |
| 1404 | return self.fail("TODO implement add_with_overflow for Ints larger than 64bits", .{}); |
| 1405 | } |
| 1429 | 1406 | |
| 1430 | | const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); |
| 1431 | | const result: MCValue = switch (signedness) { |
| 1432 | | .signed => .{ .register_overflow_signed = partial.register }, |
| 1433 | | .unsigned => .{ .register_overflow_unsigned = partial.register }, |
| 1407 | const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); |
| 1408 | const result: MCValue = switch (int_info.signedness) { |
| 1409 | .signed => .{ .register_overflow_signed = partial.register }, |
| 1410 | .unsigned => .{ .register_overflow_unsigned = partial.register }, |
| 1411 | }; |
| 1412 | break :result result; |
| 1413 | }, |
| 1414 | else => unreachable, |
| 1415 | } |
| 1434 | 1416 | }; |
| 1435 | 1417 | |
| 1436 | 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 | 1421 | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1440 | 1422 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1441 | 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); |
| 1442 | 1426 | |
| 1443 | | if (self.liveness.isUnused(inst)) { |
| 1444 | | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1445 | | } |
| 1427 | switch (ty.zigTypeTag()) { |
| 1428 | .Vector => return self.fail("TODO implement sub_with_overflow for Vector type", .{}), |
| 1429 | .Int => { |
| 1430 | const int_info = ty.intInfo(self.target.*); |
| 1446 | 1431 | |
| 1447 | | const ty = self.air.typeOf(bin_op.lhs); |
| 1448 | | const signedness: std.builtin.Signedness = blk: { |
| 1449 | | if (ty.zigTypeTag() != .Int) { |
| 1450 | | return self.fail("TODO implement airSubWithOverflow for type {}", .{ty.fmtDebug()}); |
| 1451 | | } |
| 1452 | | break :blk ty.intInfo(self.target.*).signedness; |
| 1453 | | }; |
| 1432 | if (int_info.bits > 64) { |
| 1433 | return self.fail("TODO implement sub_with_overflow for Ints larger than 64bits", .{}); |
| 1434 | } |
| 1454 | 1435 | |
| 1455 | | const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs); |
| 1456 | | const result: MCValue = switch (signedness) { |
| 1457 | | .signed => .{ .register_overflow_signed = partial.register }, |
| 1458 | | .unsigned => .{ .register_overflow_unsigned = partial.register }, |
| 1436 | const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs); |
| 1437 | const result: MCValue = switch (int_info.signedness) { |
| 1438 | .signed => .{ .register_overflow_signed = partial.register }, |
| 1439 | .unsigned => .{ .register_overflow_unsigned = partial.register }, |
| 1440 | }; |
| 1441 | break :result result; |
| 1442 | }, |
| 1443 | else => unreachable, |
| 1444 | } |
| 1459 | 1445 | }; |
| 1460 | 1446 | |
| 1461 | 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 | 1452 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1467 | 1453 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1468 | 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 | | }; |
| 1475 | 1455 | |
| 1476 | | // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. |
| 1477 | | try self.register_manager.getReg(.rax, inst); |
| 1478 | | try self.register_manager.getReg(.rdx, null); |
| 1479 | | self.register_manager.freezeRegs(&.{ .rax, .rdx }); |
| 1480 | | defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); |
| 1456 | switch (ty.zigTypeTag()) { |
| 1457 | .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), |
| 1458 | .Int => { |
| 1459 | try self.spillCompareFlagsIfOccupied(); |
| 1460 | self.compare_flags_inst = null; |
| 1481 | 1461 | |
| 1482 | | const lhs = try self.resolveInst(bin_op.lhs); |
| 1483 | | const rhs = try self.resolveInst(bin_op.rhs); |
| 1462 | const int_info = ty.intInfo(self.target.*); |
| 1484 | 1463 | |
| 1485 | | try self.genIntMulDivOpMir(switch (signedness) { |
| 1486 | | .signed => .imul, |
| 1487 | | .unsigned => .mul, |
| 1488 | | }, ty, signedness, lhs, rhs); |
| 1464 | if (int_info.bits > 64) { |
| 1465 | return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{}); |
| 1466 | } |
| 1489 | 1467 | |
| 1490 | | switch (signedness) { |
| 1491 | | .signed => break :result MCValue{ .register_overflow_signed = .rax }, |
| 1492 | | .unsigned => break :result MCValue{ .register_overflow_unsigned = .rax }, |
| 1468 | if (math.isPowerOfTwo(int_info.bits)) { |
| 1469 | // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. |
| 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 | }; |
| 1495 | 1600 | |
| ... | ... | @@ -1633,7 +1738,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa |
| 1633 | 1738 | }).encode(), |
| 1634 | 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 | 1742 | return MCValue{ .register = divisor }; |
| 1638 | 1743 | } |
| 1639 | 1744 | |
| ... | ... | @@ -2207,8 +2312,8 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { |
| 2207 | 2312 | } |
| 2208 | 2313 | // TODO we could allocate register here, but need to expect addr register and potentially |
| 2209 | 2314 | // offset register. |
| 2210 | | try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg.to64() }, .{ |
| 2211 | | .register = offset_reg.to64(), |
| 2315 | try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg }, .{ |
| 2316 | .register = offset_reg, |
| 2212 | 2317 | }); |
| 2213 | 2318 | return MCValue{ .register = addr_reg.to64() }; |
| 2214 | 2319 | } |
| ... | ... | @@ -2300,7 +2405,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2300 | 2405 | // TODO we could allocate register here, but need to expect addr register and potentially |
| 2301 | 2406 | // offset register. |
| 2302 | 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 | 2409 | try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty); |
| 2305 | 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 | 3268 | _ = try self.addInst(.{ |
| 3164 | 3269 | .tag = mir_tag, |
| 3165 | 3270 | .ops = (Mir.Ops{ |
| 3166 | | .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)), |
| 3167 | | .reg2 = src_reg, |
| 3271 | .reg1 = registerAlias(dst_reg, abi_size), |
| 3272 | .reg2 = registerAlias(src_reg, abi_size), |
| 3168 | 3273 | }).encode(), |
| 3169 | 3274 | .data = undefined, |
| 3170 | 3275 | }); |
| ... | ... | @@ -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. |
| 6627 | fn 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 | } |