| ... | @@ -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 | } |
| 1113 | | 1091 | |
| 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 { |
| 1414 | fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1392 | fn 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); |
| 1417 | | 1397 | |
| 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.*); |
| 1421 | | 1402 | |
| 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 | }; | | |
| 1429 | | 1406 | |
| 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 | }; |
| 1435 | | 1417 | |
| 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 { |
| 1439 | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1421 | fn 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); |
| 1442 | | 1426 | |
| 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.*); |
| 1446 | | 1431 | |
| 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 | }; | | |
| 1454 | | 1435 | |
| 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 | }; |
| 1460 | | 1446 | |
| 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 | }; | | |
| 1475 | | 1455 | |
| 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; |
| 1481 | | 1461 | |
| 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); | | |
| 1484 | | 1463 | |
| 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); | | |
| 1489 | | 1467 | |
| 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 | }; |
| 1495 | | 1600 | |
| ... | @@ -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 | } |
| 1639 | | 1744 | |
| ... | @@ -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 potentially | 2313 | // 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 potentially | 2405 | // 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. |
| | 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 | } |