diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64/ALUOps.cpp b/FEXCore/Source/Interface/Core/JIT/Arm64/ALUOps.cpp index 5ff017773..4677be52e 100644 --- a/FEXCore/Source/Interface/Core/JIT/Arm64/ALUOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/Arm64/ALUOps.cpp @@ -1128,19 +1128,24 @@ DEF_OP(Bfe) { auto Op = IROp->C(); LOGMAN_THROW_AA_FMT(IROp->Size <= 8, "OpSize is too large for BFE: {}", IROp->Size); LOGMAN_THROW_AA_FMT(Op->Width != 0, "Invalid BFE width of 0"); + const uint8_t OpSize = IROp->Size; + const auto EmitSize = OpSize == 8 ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit; const auto Dst = GetReg(Node); const auto Src = GetReg(Op->Src.ID()); - ubfx(ARMEmitter::Size::i64Bit, Dst, Src, Op->lsb, Op->Width); + ubfx(EmitSize, Dst, Src, Op->lsb, Op->Width); } DEF_OP(Sbfe) { - auto Op = IROp->C(); + auto Op = IROp->C(); + const uint8_t OpSize = IROp->Size; + const auto EmitSize = OpSize == 8 ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit; + const auto Dst = GetReg(Node); const auto Src = GetReg(Op->Src.ID()); - sbfx(ARMEmitter::Size::i64Bit, Dst, Src, Op->lsb, Op->Width); + sbfx(EmitSize, Dst, Src, Op->lsb, Op->Width); } ARMEmitter::Condition MapSelectCC(IR::CondClassType Cond) { diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64/MemoryOps.cpp b/FEXCore/Source/Interface/Core/JIT/Arm64/MemoryOps.cpp index 20e15114c..199360d21 100644 --- a/FEXCore/Source/Interface/Core/JIT/Arm64/MemoryOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/Arm64/MemoryOps.cpp @@ -133,7 +133,7 @@ DEF_OP(LoadRegister) { [[maybe_unused]] const auto regId = (Op->Offset / Core::CPUState::GPR_REG_SIZE) - 1; const auto regOffs = Op->Offset & 7; - LOGMAN_THROW_A_FMT(regId < StaticFPRegisters.size(), "out of range regId"); + LOGMAN_THROW_A_FMT(regId < StaticRegisters.size(), "out of range regId"); switch (OpSize) { case 1: @@ -297,7 +297,7 @@ DEF_OP(LoadRegisterSRA) { const auto regId = (Op->Offset - offsetof(Core::CpuStateFrame, State.gregs[0])) / Core::CPUState::GPR_REG_SIZE; const auto regOffs = Op->Offset & 7; - LOGMAN_THROW_A_FMT(regId < StaticFPRegisters.size(), "out of range regId"); + LOGMAN_THROW_A_FMT(regId < StaticRegisters.size(), "out of range regId"); const auto reg = StaticRegisters[regId]; diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 8956453b6..1e99536ee 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -447,7 +447,7 @@ void OpDispatchBuilder::ADCOp(OpcodeArgs) { OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[SrcIndex], Op->Flags, -1); uint8_t Size = GetDstSize(Op); - auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_LOC); + auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_LOC, Size); auto ALUOp = _Add(Src, CF); OrderedNode *Result{}; @@ -478,7 +478,7 @@ void OpDispatchBuilder::SBBOp(OpcodeArgs) { OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[SrcIndex], Op->Flags, -1); auto Size = GetDstSize(Op); - auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_LOC); + auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_LOC, Size); auto ALUOp = _Add(Src, CF); OrderedNode *Result{}; @@ -2474,6 +2474,8 @@ void OpDispatchBuilder::PEXT(OpcodeArgs) { } void OpDispatchBuilder::ADXOp(OpcodeArgs) { + const auto OperandSize = GetSrcSize(Op); + // Calculate flags early. CalculateDeferredFlags(); @@ -2483,9 +2485,9 @@ void OpDispatchBuilder::ADXOp(OpcodeArgs) { auto* Flag = [&]() -> OrderedNode* { if (IsADCX) { - return GetRFLAG(X86State::RFLAG_CF_LOC); + return GetRFLAG(X86State::RFLAG_CF_LOC, OperandSize); } else { - return GetRFLAG(X86State::RFLAG_OF_LOC); + return GetRFLAG(X86State::RFLAG_OF_LOC, OperandSize); } }(); @@ -2516,7 +2518,7 @@ void OpDispatchBuilder::RCROp1Bit(OpcodeArgs) { OrderedNode *Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1); const auto Size = GetSrcBitSize(Op); - auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_LOC); + auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_LOC, GetSrcSize(Op)); uint32_t Shift = 1; @@ -3281,7 +3283,6 @@ void OpDispatchBuilder::IMULOp(OpcodeArgs) { void OpDispatchBuilder::MULOp(OpcodeArgs) { const uint8_t Size = GetSrcSize(Op); - const uint8_t GPRSize = CTX->GetGPRSize(); OrderedNode *Src1 = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1); OrderedNode* Src2 = LoadGPRRegister(X86State::REG_RAX); @@ -3308,8 +3309,8 @@ void OpDispatchBuilder::MULOp(OpcodeArgs) { else if (Size == 4) { // 32bits stored in EAX // 32bits stored in EDX - OrderedNode *ResultLow = _Bfe(GPRSize, 32, 0, Result); - ResultHigh = _Bfe(GPRSize, 32, 32, Result); + OrderedNode *ResultLow = _Bfe(8, 32, 0, Result); + ResultHigh = _Bfe(8, 32, 32, Result); StoreGPRRegister(X86State::REG_RAX, ResultLow); StoreGPRRegister(X86State::REG_RDX, ResultHigh); } diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index 16850407b..65b9bf17e 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -1192,10 +1192,10 @@ private: void ZeroMultipleFlags(uint32_t BitMask); - OrderedNode *GetRFLAG(unsigned BitOffset) { + OrderedNode *GetRFLAG(unsigned BitOffset, int32_t Size = 8) { if (IsNZCV(BitOffset)) { if (!CachedNZCV || (PossiblySetNZCVBits & (1u << IndexNZCV(BitOffset)))) - return _Bfe(1, 1, IndexNZCV(BitOffset), GetNZCV()); + return _Bfe(Size, 1, IndexNZCV(BitOffset), GetNZCV()); else return _Constant(0); } else { diff --git a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp index 40cf617ac..755a6e0f4 100644 --- a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp @@ -548,7 +548,7 @@ namespace { auto IsPreWritable = [](uint8_t Size, RegisterClassType StaticClass) { LOGMAN_THROW_A_FMT(StaticClass == GPRFixedClass || StaticClass == FPRFixedClass, "Unexpected static class {}", StaticClass); if (StaticClass == GPRFixedClass) { - return Size == 8; + return Size == 8 || Size == 4; } else if (StaticClass == FPRFixedClass) { return Size == 16; } @@ -560,7 +560,7 @@ namespace { LOGMAN_THROW_A_FMT(StaticClass == GPRFixedClass || StaticClass == FPRFixedClass, "Unexpected static class {}", StaticClass); if (StaticClass == GPRFixedClass) { // We need more meta info to support not-size-of-reg - return (Size == 8 /*|| Size == 4*/) && ((Offset & 7) == 0); + return (Size == 8 || Size == 4) && ((Offset & 7) == 0); } else if (StaticClass == FPRFixedClass) { // We need more meta info to support not-size-of-reg return (Size == 16 /*|| Size == 8 || Size == 4*/) && ((Offset & 15) == 0); diff --git a/unittests/32Bit_ASM/FEX_bugs/adc.asm b/unittests/32Bit_ASM/FEX_bugs/adc.asm new file mode 100644 index 000000000..e5cb0a3e0 --- /dev/null +++ b/unittests/32Bit_ASM/FEX_bugs/adc.asm @@ -0,0 +1,26 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x00000000fffffffe", + "RBX": "0x0000000000000001" + }, + "Mode": "32BIT" +} +%endif + +; FEX had a bug where ADD or SUB with carry was generating results with garbage in the upper 32-bits. + +mov eax, -1 +mov ebx, -1 +mov edx, -1 + +clc +adc eax, edx +adc eax, edx +adc eax, edx + +clc +sbb ebx, edx +sbb ebx, edx +sbb ebx, edx +hlt