OpcodeDispatcher: rewrite DAA implementation

Based on https://www.righto.com/2023/01/

New implementation is branchless, which is theoretically easier to RA. It's also
massively simpler which is good for a demon opcode.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
This commit is contained in:
Alyssa Rosenzweig committed 2024-03-25 13:00:59 -04:00
1 parent 693d86dd67
commit 949717a95f
1 file changed
+18 -63
@@ -3119,77 +3119,32 @@ void OpDispatchBuilder::PopcountOp(OpcodeArgs) {
void OpDispatchBuilder::DAAOp(OpcodeArgs) {
CalculateDeferredFlags();
auto AL = LoadGPRRegister(X86State::REG_RAX, 1);
auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
auto AF = LoadAF();
auto AL = LoadGPRRegister(X86State::REG_RAX, 1);
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(_Constant(0));
CalculateDeferredFlags();
// AF |= ((AL & 0x0F) > 9);
AF = _Or(OpSize::i64Bit, AF,
_Select(FEXCore::IR::COND_UGT, _And(OpSize::i64Bit, AL, _Constant(0xF)), _Constant(9),
_Constant(1), _Constant(0)));
auto Cond = _Or(OpSize::i64Bit, AF, _Select(FEXCore::IR::COND_UGT, _And(OpSize::i64Bit, AL, _Constant(0xF)), _Constant(9), _Constant(1), _Constant(0)));
auto FalseBlock = CreateNewCodeBlockAfter(GetCurrentBlock());
auto TrueBlock = CreateNewCodeBlockAfter(FalseBlock);
auto EndBlock = CreateNewCodeBlockAfter(TrueBlock);
// CF |= (AL > 0x99);
CF = _Or(OpSize::i64Bit, CF, _Select(FEXCore::IR::COND_UGT, AL, _Constant(0x99), _Constant(1), _Constant(0)));
CalculateDeferredFlags();
CondJump(Cond, TrueBlock, FalseBlock);
SetCurrentCodeBlock(FalseBlock);
StartNewBlock();
{
SetAF(0);
Jump(EndBlock);
}
SetCurrentCodeBlock(TrueBlock);
StartNewBlock();
{
auto NewAL = _Add(OpSize::i64Bit, AL, _Constant(0x6));
StoreGPRRegister(X86State::REG_RAX, NewAL, 1);
CalculateDeferredFlags();
auto NewCF = GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
// XXX: I don't think this is correct. Needs Investigation.
// The `CF` variable is the original CF from the start of the operation
// The `NewCF` will be _Constant(0) stored aboved.
// So Or(CF, _Constant(0)) ill mean CF gets updated to the old value in the true case?
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(_Or(OpSize::i64Bit, CF, NewCF));
SetAF(1);
CalculateDeferredFlags();
Jump(EndBlock);
}
SetCurrentCodeBlock(EndBlock);
StartNewBlock();
// AL = AF ? (AL + 0x6) : AL;
AL = _Select(FEXCore::IR::COND_NEQ, AF, _Constant(0),
_Add(OpSize::i64Bit, AL, _Constant(0x6)), AL);
Cond = _Or(OpSize::i64Bit, CF, _Select(FEXCore::IR::COND_UGT, AL, _Constant(0x99), _Constant(1), _Constant(0)));
FalseBlock = CreateNewCodeBlockAfter(GetCurrentBlock());
TrueBlock = CreateNewCodeBlockAfter(FalseBlock);
EndBlock = CreateNewCodeBlockAfter(TrueBlock);
CondJump(Cond, TrueBlock, FalseBlock);
SetCurrentCodeBlock(FalseBlock);
StartNewBlock();
{
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(_Constant(0));
CalculateDeferredFlags();
Jump(EndBlock);
}
SetCurrentCodeBlock(TrueBlock);
StartNewBlock();
{
AL = LoadGPRRegister(X86State::REG_RAX, 1);
// AL = CF ? (AL + 0x60) : AL;
AL = _Select(FEXCore::IR::COND_NEQ, CF, _Constant(0),
_Add(OpSize::i64Bit, AL, _Constant(0x60)), AL);
auto NewAL = _Add(OpSize::i64Bit, AL, _Constant(0x60));
StoreGPRRegister(X86State::REG_RAX, NewAL, 1);
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(_Constant(1));
CalculateDeferredFlags();
Jump(EndBlock);
}
SetCurrentCodeBlock(EndBlock);
StartNewBlock();
// Update Flags
AL = LoadGPRRegister(X86State::REG_RAX, 1);
SetRFLAG<FEXCore::X86State::RFLAG_SF_RAW_LOC>(_Select(FEXCore::IR::COND_UGE, _And(OpSize::i64Bit, AL, _Constant(0x80)), _Constant(0), _Constant(1), _Constant(0)));
SetRFLAG<FEXCore::X86State::RFLAG_ZF_RAW_LOC>(_Select(FEXCore::IR::COND_EQ, _And(OpSize::i64Bit, AL, _Constant(0xFF)), _Constant(0), _Constant(1), _Constant(0)));
// SF, ZF, PF set according to result. CF set per above. OF undefined.
StoreGPRRegister(X86State::REG_RAX, AL, 1);
SetNZ_ZeroCV(1, AL);
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(CF);
CalculatePF(AL);
FixupAF();
SetAFAndFixup(AF);
}
void OpDispatchBuilder::DASOp(OpcodeArgs) {