OpcodeDispatcher: optimize ADCX/ADOX

rewrite to use native adcs with flag fixups.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
This commit is contained in:
Alyssa Rosenzweig 2024-08-21 16:55:30 -04:00
parent b03c613fbf
commit 6951284924

View File

@ -1909,39 +1909,49 @@ void OpDispatchBuilder::PEXT(OpcodeArgs) {
void OpDispatchBuilder::ADXOp(OpcodeArgs) {
const auto OpSize = OpSizeFromSrc(Op);
// Calculate flags early.
CalculateDeferredFlags();
// Only 32/64-bit anyway so allow garbage, we use 32-bit ops.
auto* Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, {.AllowUpperGarbage = true});
auto* Before = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = true});
// Handles ADCX and ADOX
const bool IsADCX = Op->OP == 0x1F6;
auto Zero = _Constant(0);
auto* Flag = [&]() -> Ref {
if (IsADCX) {
return GetRFLAG(X86State::RFLAG_CF_RAW_LOC);
} else {
return GetRFLAG(X86State::RFLAG_OF_RAW_LOC);
}
}();
// Before we go trashing NZCV, save the current NZCV state.
Ref OldNZCV = GetNZCV();
auto* Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
auto* Before = LoadSource(GPRClass, Op, Op->Dest, Op->Flags);
auto ALUOp = _Add(OpSize, Src, Flag);
auto Result = _Add(OpSize, Before, ALUOp);
// We want to use arm64 adc. For ADOX, copy the overflow flag into CF. For
// ADCX, we just rectify the carry.
if (IsADCX) {
RectifyCarryInvert(false);
} else {
// If overflow, 0 - 0 sets carry. Else, forces carry to 0.
_CondSubNZCV(OpSize::i32Bit, Zero, Zero, {COND_FU}, 0x0 /* nzcv */);
}
// Do the actual add.
HandleNZCV_RMW();
auto Result = _AdcWithFlags(OpSize, Src, Before);
StoreResult(GPRClass, Op, Result, -1);
auto Zero = _Constant(0);
auto One = _Constant(1);
auto SelectOpLT = _Select(IR::COND_ULT, Result, Src, One, Zero);
auto SelectOpLE = _Select(IR::COND_ULE, Result, Src, One, Zero);
auto SelectFlag = _Select(IR::COND_EQ, Flag, One, SelectOpLE, SelectOpLT);
// Now restore all flags except the one we're updating.
if (CTX->HostFeatures.SupportsFlagM) {
// For ADOX, we need to copy the new carry into the overflow flag. If carry is clear (ULT with uninverted
// carry), 0 - 0 clears overflow. Else, force overflow on.
if (!IsADCX) {
_CondSubNZCV(OpSize::i32Bit, Zero, Zero, {COND_ULT}, 0x1 /* nzcV */);
}
if (IsADCX) {
SetCFDirect(SelectFlag);
_RmifNZCV(OldNZCV, 28, IsADCX ? 0xd /* NzcV */ : 0xe /* NZCv */);
} else {
SetRFLAG<X86State::RFLAG_OF_RAW_LOC>(SelectFlag);
// For either operation, insert the new flag into the old NZCV.
bool SavedCFInvert = CFInverted;
CFInverted = false;
Ref OutputCF = GetRFLAG(X86State::RFLAG_CF_RAW_LOC, IsADCX);
CFInverted = IsADCX ? true : SavedCFInvert;
Ref NewNZCV = _Bfi(OpSize::i32Bit, 1, IsADCX ? 29 : 28, OldNZCV, OutputCF);
SetNZCV(NewNZCV);
}
}