diff --git a/FEXCore/Source/Interface/Core/JIT/MemoryOps.cpp b/FEXCore/Source/Interface/Core/JIT/MemoryOps.cpp index 075ee185f..d7429fcca 100644 --- a/FEXCore/Source/Interface/Core/JIT/MemoryOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/MemoryOps.cpp @@ -136,11 +136,8 @@ DEF_OP(LoadRegister) { if (Op->Class == IR::GPRClass) { LOGMAN_THROW_A_FMT(Op->Reg < StaticRegisters.size(), "out of range reg"); - const auto reg = StaticRegisters[Op->Reg]; - if (GetReg(Node).Idx() != reg.Idx()) { - mov(GetReg(Node).X(), reg.X()); - } + mov(GetReg(Node).X(), StaticRegisters[Op->Reg].X()); } else if (Op->Class == IR::FPRClass) { [[maybe_unused]] const auto regSize = HostSupportsAVX256 ? IR::OpSize::i256Bit : IR::OpSize::i128Bit; LOGMAN_THROW_A_FMT(Op->Reg < StaticFPRegisters.size(), "out of range reg"); @@ -149,12 +146,10 @@ DEF_OP(LoadRegister) { const auto guest = StaticFPRegisters[Op->Reg]; const auto host = GetVReg(Node); - if (host.Idx() != guest.Idx()) { - if (HostSupportsAVX256) { - mov(ARMEmitter::SubRegSize::i64Bit, host.Z(), PRED_TMP_32B.Merging(), guest.Z()); - } else { - mov(host.Q(), guest.Q()); - } + if (HostSupportsAVX256) { + mov(ARMEmitter::SubRegSize::i64Bit, host.Z(), PRED_TMP_32B.Merging(), guest.Z()); + } else { + mov(host.Q(), guest.Q()); } } else { LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", Op->Class); @@ -187,12 +182,9 @@ DEF_OP(StoreRegister) { LOGMAN_THROW_A_FMT(Reg < StaticRegisters.size(), "out of range reg"); const auto reg = StaticRegisters[Reg]; - const auto Src = GetReg(Op->Value); - if (Src.Idx() != reg.Idx()) { - // Always use 64-bit, it's faster. Upper bits ignored for 32-bit mode. - mov(ARMEmitter::Size::i64Bit, reg, Src); - } + // Always use 64-bit, it's faster. Upper bits ignored for 32-bit mode. + mov(ARMEmitter::Size::i64Bit, reg, GetReg(Op->Value)); } else if (Op->Class == IR::FPRClass) { [[maybe_unused]] const auto regSize = HostSupportsAVX256 ? IR::OpSize::i256Bit : IR::OpSize::i128Bit; LOGMAN_THROW_A_FMT(Op->Reg < StaticFPRegisters.size(), "reg out of range"); @@ -201,12 +193,10 @@ DEF_OP(StoreRegister) { const auto guest = StaticFPRegisters[Op->Reg]; const auto host = GetVReg(Op->Value); - if (guest.Idx() != host.Idx()) { - if (HostSupportsAVX256) { - mov(ARMEmitter::SubRegSize::i64Bit, guest.Z(), PRED_TMP_32B.Merging(), host.Z()); - } else { - mov(guest.Q(), host.Q()); - } + if (HostSupportsAVX256) { + mov(ARMEmitter::SubRegSize::i64Bit, guest.Z(), PRED_TMP_32B.Merging(), host.Z()); + } else { + mov(guest.Q(), host.Q()); } } else { LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", Op->Class); @@ -1525,6 +1515,29 @@ DEF_OP(Push) { } } +DEF_OP(PushTwo) { + const auto Op = IROp->C(); + const auto ValueSize = IR::OpSizeToSize(Op->ValueSize); + auto Src1 = GetReg(Op->Value1); + auto Src2 = GetReg(Op->Value2); + const auto Dst = GetReg(Op->Addr); + + switch (ValueSize) { + case 4: { + stp(Src1.W(), Src2.W(), Dst, -2 * ValueSize); + break; + } + case 8: { + stp(Src1.X(), Src2.X(), Dst, -2 * ValueSize); + break; + } + default: { + LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, ValueSize); + break; + } + } +} + DEF_OP(Pop) { const auto Op = IROp->C(); const auto Size = IR::OpSizeToSize(Op->Size); @@ -1557,6 +1570,37 @@ DEF_OP(Pop) { } } +DEF_OP(PopTwo) { + const auto Op = IROp->C(); + const auto Size = IR::OpSizeToSize(Op->Size); + const auto Addr = GetReg(Op->InoutAddr); + auto Dst1 = GetReg(Op->OutValue1); + const auto Dst2 = GetReg(Op->OutValue2); + + // ldp x, x is invalid. Explicitly discard the first destination to encode. + if (Dst1 == Dst2) { + Dst1 = ARMEmitter::Reg::zr; + } + + LOGMAN_THROW_A_FMT(Dst1 != Addr && Dst2 != Addr, "Invalid"); + LOGMAN_THROW_A_FMT(Dst1 != Dst2, "Invalid"); + + switch (Size) { + case 4: { + ldp(Dst1.W(), Dst2.W(), Addr, 2 * Size); + break; + } + case 8: { + ldp(Dst1.X(), Dst2.X(), Addr, 2 * Size); + break; + } + default: { + LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Op->Size); + break; + } + } +} + DEF_OP(StoreMem) { const auto Op = IROp->C(); const auto OpSize = IROp->Size; diff --git a/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp b/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp index a514900ad..4b94c828a 100644 --- a/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp @@ -17,10 +17,6 @@ $end_info$ namespace FEXCore::CPU { -DEF_OP(AllocateGPR) {} -DEF_OP(AllocateGPRAfter) {} -DEF_OP(AllocateFPR) {} - DEF_OP(GuestOpcode) { auto Op = IROp->C(); // metadata diff --git a/FEXCore/Source/Interface/Core/JIT/MoveOps.cpp b/FEXCore/Source/Interface/Core/JIT/MoveOps.cpp index aeb0c2988..9627e0cd4 100644 --- a/FEXCore/Source/Interface/Core/JIT/MoveOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/MoveOps.cpp @@ -15,13 +15,7 @@ DEF_OP(Copy) { } DEF_OP(RMWHandle) { - auto Op = IROp->C(); - auto Dest = GetReg(Node); - auto Src = GetReg(Op->Value); - - if (Dest != Src) { - mov(ARMEmitter::Size::i64Bit, Dest, Src); - } + mov(ARMEmitter::Size::i64Bit, GetReg(Node), GetReg(IROp->Args[0])); } DEF_OP(Swap1) { diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index a08ed2b00..b902e3858 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -355,68 +355,29 @@ void OpDispatchBuilder::SALCOp(OpcodeArgs) { void OpDispatchBuilder::PUSHOp(OpcodeArgs) { const auto Size = OpSizeFromSrc(Op); - Ref Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags); - Push(Size, Src); - FlushRegisterCache(); + Push(Size, LoadSource(GPRClass, Op, Op->Src[0], Op->Flags)); } void OpDispatchBuilder::PUSHREGOp(OpcodeArgs) { const auto Size = OpSizeFromSrc(Op); - Ref Src = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = true}); - - Push(Size, Src); - FlushRegisterCache(); + Push(Size, LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = true})); } void OpDispatchBuilder::PUSHAOp(OpcodeArgs) { // 32bit only const auto Size = OpSizeFromSrc(Op); - auto OldSP = LoadGPRRegister(X86State::REG_RSP); + Ref OldSP = _Copy(LoadGPRRegister(X86State::REG_RSP)); - // PUSHA order: - // Tmp = SP - // push EAX - // push ECX - // push EDX - // push EBX - // push Tmp - // push EBP - // push ESI - // push EDI - - Ref Src {}; - Ref NewSP = OldSP; - const auto GPRSize = CTX->GetGPROpSize(); - - Src = LoadGPRRegister(X86State::REG_RAX); - NewSP = _Push(GPRSize, Size, Src, NewSP); - - Src = LoadGPRRegister(X86State::REG_RCX); - NewSP = _Push(GPRSize, Size, Src, NewSP); - - Src = LoadGPRRegister(X86State::REG_RDX); - NewSP = _Push(GPRSize, Size, Src, NewSP); - - Src = LoadGPRRegister(X86State::REG_RBX); - NewSP = _Push(GPRSize, Size, Src, NewSP); - - // Push old-sp - NewSP = _Push(GPRSize, Size, OldSP, NewSP); - - Src = LoadGPRRegister(X86State::REG_RBP); - NewSP = _Push(GPRSize, Size, Src, NewSP); - - Src = LoadGPRRegister(X86State::REG_RSI); - NewSP = _Push(GPRSize, Size, Src, NewSP); - - Src = LoadGPRRegister(X86State::REG_RDI); - NewSP = _Push(GPRSize, Size, Src, NewSP); - - // Store the new stack pointer - StoreGPRRegister(X86State::REG_RSP, NewSP, OpSize::i32Bit); - FlushRegisterCache(); + Push(Size, LoadGPRRegister(X86State::REG_RAX)); + Push(Size, LoadGPRRegister(X86State::REG_RCX)); + Push(Size, LoadGPRRegister(X86State::REG_RDX)); + Push(Size, LoadGPRRegister(X86State::REG_RBX)); + Push(Size, OldSP); + Push(Size, LoadGPRRegister(X86State::REG_RBP)); + Push(Size, LoadGPRRegister(X86State::REG_RSI)); + Push(Size, LoadGPRRegister(X86State::REG_RDI)); } void OpDispatchBuilder::PUSHSegmentOp(OpcodeArgs, uint32_t SegmentReg) { @@ -3594,8 +3555,7 @@ void OpDispatchBuilder::BSWAPOp(OpcodeArgs) { void OpDispatchBuilder::PUSHFOp(OpcodeArgs) { const auto Size = OpSizeFromSrc(Op); - Ref Src = GetPackedRFLAG(); - Push(Size, Src); + Push(Size, GetPackedRFLAG()); } void OpDispatchBuilder::POPFOp(OpcodeArgs) { diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index c2915e90b..456e09cc4 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -2498,6 +2498,7 @@ private: auto OldSP = LoadGPRRegister(X86State::REG_RSP); auto NewSP = _Push(CTX->GetGPROpSize(), Size, Value, OldSP); StoreGPRRegister(X86State::REG_RSP, NewSP); + FlushRegisterCache(); } void InstallHostSpecificOpcodeHandlers(); diff --git a/FEXCore/Source/Interface/IR/IR.json b/FEXCore/Source/Interface/IR/IR.json index 6b8c0bbcc..5535f03af 100644 --- a/FEXCore/Source/Interface/IR/IR.json +++ b/FEXCore/Source/Interface/IR/IR.json @@ -254,19 +254,22 @@ "it cannot use a regular destination too. This ensures RA correctness.", "This is a kludge to deal with the IR's lack of multiple destinations", "If ForPair is set, RA will try to allocate the base of a register pair"], - "DestSize": "OpSize::i64Bit" + "DestSize": "OpSize::i64Bit", + "JITDispatch": false }, "FPR = AllocateFPR OpSize:#RegisterSize, OpSize:#ElementSize": { "Desc": ["Like AllocateGPR, but for FPR"], "DestSize": "RegisterSize", - "ElementSize": "ElementSize" + "ElementSize": "ElementSize", + "JITDispatch": false }, "GPR = AllocateGPRAfter GPR:$After": { "Desc": ["Silly pseudo-instruction to allocate a register for a future destination", "This is a kludge to deal with the IR's lack of multiple destinations", "RA will attempt to allocate to the register after $After.", "It may not succeed."], - "DestSize": "OpSize::i64Bit" + "DestSize": "OpSize::i64Bit", + "JITDispatch": false }, "GPR = RDRAND i1:$GetReseeded": { "Desc": ["Uses the hardware random number generator to generate a 64bit number", @@ -668,6 +671,13 @@ "HasSideEffects": true, "DestSize": "Size" }, + "PushTwo OpSize:#Size, OpSize:$ValueSize, GPR:$Value1, GPR:$Value2, GPR:$Addr": { + "Desc": [ + "Push two values to the address, incrementing the pointer in the place.", + "Fused post-RA so doesn't have a destination." + ], + "HasSideEffects": true + }, "GPR = RMWHandle GPR:$Value": { "Desc": [ "This is a special move that indicates the result will be poisoned by a non-SSA instruction writing to its result.", @@ -685,6 +695,11 @@ "HasSideEffects": true, "DestSize": "Size" }, + "GPR:$Addr, GPR:$Value1, GPR:$Value2 = PopTwo OpSize:$Size, GPR:$Addr": { + "Desc": ["Pop two values from the address. Fused post-RA."], + "HasSideEffects": true, + "DestSize": "Size" + }, "GPR = MemSet i1:$IsAtomic, OpSize:$Size, GPR:$Prefix, GPR:$Addr, GPR:$Value, GPR:$Length, GPR:$Direction": { "Desc": ["Duplicates behaviour of x86 STOS repeat", "Returns the final address that gets generated without the prefix appended." diff --git a/FEXCore/Source/Interface/IR/IREmitter.cpp b/FEXCore/Source/Interface/IR/IREmitter.cpp index 61d7d4bcb..1146edb28 100644 --- a/FEXCore/Source/Interface/IR/IREmitter.cpp +++ b/FEXCore/Source/Interface/IR/IREmitter.cpp @@ -146,6 +146,10 @@ void IREmitter::RemoveArgUses(Ref Node) { } } +void IREmitter::RemovePostRA(Ref Node) { + Node->Unlink(DualListData.ListBegin()); +} + void IREmitter::Remove(Ref Node) { RemoveArgUses(Node); diff --git a/FEXCore/Source/Interface/IR/IREmitter.h b/FEXCore/Source/Interface/IR/IREmitter.h index 4337de63d..5510fdbe1 100644 --- a/FEXCore/Source/Interface/IR/IREmitter.h +++ b/FEXCore/Source/Interface/IR/IREmitter.h @@ -219,6 +219,7 @@ public: void ReplaceNodeArgument(Ref Node, uint8_t Arg, Ref NewArg); void Remove(Ref Node); + void RemovePostRA(Ref Node); void SetPackedRFLAG(bool Lower8, Ref Src); Ref GetPackedRFLAG(bool Lower8); diff --git a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp index ea9633b45..b2bc1afd2 100644 --- a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp @@ -57,6 +57,7 @@ class ConstrainedRAPass final : public RegisterAllocationPass { public: void Run(IREmitter* IREmit) override; void AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) override; + bool TryPostRAMerge(Ref LastNode, Ref CodeNode, IROp_Header* IROp); private: RegisterClass Classes[INVALID_CLASS]; @@ -197,6 +198,18 @@ private: } }; + bool IsTrivial(Ref Node, const IROp_Header* Header) { + switch (Header->Op) { + case OP_ALLOCATEGPR: return true; + case OP_ALLOCATEGPRAFTER: return true; + case OP_ALLOCATEFPR: return true; + case OP_RMWHANDLE: return PhysicalRegister(Node) == PhysicalRegister(Header->Args[0]); + case OP_LOADREGISTER: return PhysicalRegister(Node) == DecodeSRAReg(Header); + case OP_STOREREGISTER: return PhysicalRegister(Header->Args[0]) == DecodeSRAReg(Header); + default: return false; + } + } + // Helper macro to walk the set bits b in a 32-bit word x, using ffs to get // the next set bit and then clearing on each iteration. #define foreach_bit(b, x) for (uint32_t __x = (x), b; ((b) = __builtin_ffs(__x) - 1, __x); __x &= ~(1 << (b))) @@ -362,10 +375,6 @@ private: unsigned Reg = std::countr_zero(Class->Available); SetReg(CodeNode, PhysicalRegister(ClassType, Reg)); }; - - bool IsRAOp(IROps Op) { - return Op == OP_SPILLREGISTER || Op == OP_FILLREGISTER || Op == OP_COPY; - }; }; void ConstrainedRAPass::AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) { @@ -374,6 +383,37 @@ void ConstrainedRAPass::AddRegisters(IR::RegisterClassType Class, uint32_t Regis Classes[Class].Count = RegisterCount; } +bool ConstrainedRAPass::TryPostRAMerge(Ref LastNode, Ref CodeNode, IROp_Header* IROp) { + if (IROp->Op == OP_PUSH) { + auto LastOp = IR->GetOp(LastNode); + if (LastOp->Op == OP_PUSH) { + auto SP = PhysicalRegister(CodeNode); + auto Push = IR->GetOp(CodeNode); + auto LastPush = IR->GetOp(LastNode); + + if (LastOp->Size == IROp->Size && LastPush->ValueSize == Push->ValueSize && SP == PhysicalRegister(LastNode) && + SP == PhysicalRegister(IROp->Args[1]) && SP == PhysicalRegister(LastOp->Args[1]) && SP != PhysicalRegister(IROp->Args[0]) && + SP != PhysicalRegister(LastOp->Args[0]) && Push->ValueSize >= OpSize::i32Bit) { + + IREmit->SetWriteCursorBefore(LastNode); + IREmit->_PushTwo(IROp->Size, Push->ValueSize, IROp->Args[0], LastOp->Args[0], IROp->Args[1]); + return true; + } + } + } else if (IROp->Op == OP_POP) { + auto LastOp = IR->GetOp(LastNode); + auto SP = PhysicalRegister(IROp->Args[0]); + + if (LastOp->Op == OP_POP && LastOp->Size == IROp->Size && IROp->Size >= OpSize::i32Bit && SP == PhysicalRegister(LastOp->Args[0])) { + IREmit->SetWriteCursorBefore(LastNode); + IREmit->_PopTwo(IROp->Size, IROp->Args[0], LastOp->Args[1], IROp->Args[1]); + return true; + } + } + + return false; +} + void ConstrainedRAPass::Run(IREmitter* IREmit_) { FEXCORE_PROFILE_SCOPED("PassManager::RA"); @@ -474,9 +514,17 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) { // SourcesNextUses is read backwards, this tracks the index int64_t SourceIndex = SourcesNextUses.size(); - // Forward pass: Assign registers, spilling as we go. + // Last nontrivial instruction, for merging as we go. + Ref LastNode = nullptr; + + // Forward pass: Assign registers, spilling & optimizing as we go. for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) { - LOGMAN_THROW_A_FMT(!IsRAOp(IROp->Op), "RA ops inserted before, so not seen iterating forward"); + // GuestOpcode does not read or write registers, and must be skipped for + // push/pop merging. Since we'd be doing this check anyway for merging, do + // the check now so we can skip the rest of the logic too. + if (IROp->Op == OP_GUESTOPCODE) { + continue; + } // Static registers must be consistent at SRA load/store. Evict to ensure. if (auto Node = DecodeSRANode(IROp, CodeNode); Node != nullptr) { @@ -555,8 +603,17 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) { AssignReg(IROp, CodeNode, IROp); } - LOGMAN_THROW_A_FMT(IP >= 1, "IP relative to end of block, iterating forward"); - --IP; + if (IsTrivial(CodeNode, IROp)) { + // Delete instructions that only exist for RA + IREmit->RemovePostRA(CodeNode); + } else if (LastNode && TryPostRAMerge(LastNode, CodeNode, IROp)) { + // Merge adjacent instructions + IREmit->RemovePostRA(CodeNode); + IREmit->RemovePostRA(LastNode); + LastNode = nullptr; + } else { + LastNode = CodeNode; + } } LOGMAN_THROW_A_FMT(SourceIndex == 0, "Consistent source count in block"); diff --git a/unittests/ASM/FEX_bugs/SelfPop.asm b/unittests/ASM/FEX_bugs/SelfPop.asm new file mode 100644 index 000000000..471013a3f --- /dev/null +++ b/unittests/ASM/FEX_bugs/SelfPop.asm @@ -0,0 +1,25 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "123", + "RBX": "456", + "RCX": "123" + } +} +%endif + +; FEX had a bug merging pops to the same register + +; Push some stuff +mov rsp, 0xe0000010 +mov rax, 123 +mov rbx, 456 +push rax +push rbx + +; Pop into the same register +pop rcx +pop rcx + +; rcx now equals rax +hlt diff --git a/unittests/InstructionCountCI/FEXOpt/MultiInst.json b/unittests/InstructionCountCI/FEXOpt/MultiInst.json index 9610c2947..4696cad39 100644 --- a/unittests/InstructionCountCI/FEXOpt/MultiInst.json +++ b/unittests/InstructionCountCI/FEXOpt/MultiInst.json @@ -34,7 +34,7 @@ }, "push rax, rbx": { "x86InstructionCount": 2, - "ExpectedInstructionCount": 2, + "ExpectedInstructionCount": 1, "Comment": [ "Mergable 64-bit pushes" ], @@ -43,8 +43,7 @@ "push rbx" ], "ExpectedArm64ASM": [ - "str x4, [x8, #-8]!", - "str x6, [x8, #-8]!" + "stp x6, x4, [x8, #-16]!" ] }, "adds xmm0, xmm1, xmm2": { @@ -1056,7 +1055,7 @@ }, "Sekiro spill block": { "x86InstructionCount": 119, - "ExpectedInstructionCount": 126, + "ExpectedInstructionCount": 118, "Comment": [ "This block of code came from the settings screen when it loaded", "It was originally at RIP: 0x14232cca0 and has been deobfuscated" @@ -1184,14 +1183,10 @@ ], "ExpectedArm64ASM": [ "str x7, [x8, #8]", - "str x6, [x8, #-8]!", - "str x9, [x8, #-8]!", - "str x10, [x8, #-8]!", - "str x11, [x8, #-8]!", - "str x16, [x8, #-8]!", - "str x17, [x8, #-8]!", - "str x19, [x8, #-8]!", - "str x29, [x8, #-8]!", + "stp x9, x6, [x8, #-16]!", + "stp x11, x10, [x8, #-16]!", + "stp x17, x16, [x8, #-16]!", + "stp x29, x19, [x8, #-16]!", "sub x8, x8, #0x18 (24)", "ldr w7, [x5, #36]", "ldr w10, [x5]", @@ -1300,14 +1295,10 @@ "mvn w27, w8", "adds x26, x8, #0x18 (24)", "mov x8, x26", - "ldr x29, [x8], #8", - "ldr x19, [x8], #8", - "ldr x17, [x8], #8", - "ldr x16, [x8], #8", - "ldr x11, [x8], #8", - "ldr x10, [x8], #8", - "ldr x9, [x8], #8", - "ldr x6, [x8], #8", + "ldp x29, x19, [x8], #16", + "ldp x17, x16, [x8], #16", + "ldp x11, x10, [x8], #16", + "ldp x9, x6, [x8], #16", "cfinv" ] }, diff --git a/unittests/InstructionCountCI/FlagM/HotBlocks.json b/unittests/InstructionCountCI/FlagM/HotBlocks.json index e57ceb3f2..e11d0ec42 100644 --- a/unittests/InstructionCountCI/FlagM/HotBlocks.json +++ b/unittests/InstructionCountCI/FlagM/HotBlocks.json @@ -253,7 +253,7 @@ }, "bytemark DivideInternalFPF": { "x86InstructionCount": 13, - "ExpectedInstructionCount": 15, + "ExpectedInstructionCount": 12, "Comment": [ "Saw this in bytemark" ], @@ -273,12 +273,9 @@ "cmp eax,0x18" ], "ExpectedArm64ASM": [ - "str x9, [x8, #-8]!", - "str x29, [x8, #-8]!", - "str x19, [x8, #-8]!", - "str x17, [x8, #-8]!", - "str x16, [x8, #-8]!", - "str x6, [x8, #-8]!", + "stp x29, x9, [x8, #-16]!", + "stp x17, x19, [x8, #-16]!", + "stp x6, x16, [x8, #-16]!", "sub x8, x8, #0x18 (24)", "str x5, [x8]", "ldrb w14, [x11]", diff --git a/unittests/InstructionCountCI/FlagM/HotBlocks_TSO_32Bit.json b/unittests/InstructionCountCI/FlagM/HotBlocks_TSO_32Bit.json index 6bbfd644e..307647d5e 100644 --- a/unittests/InstructionCountCI/FlagM/HotBlocks_TSO_32Bit.json +++ b/unittests/InstructionCountCI/FlagM/HotBlocks_TSO_32Bit.json @@ -17,7 +17,7 @@ "Instructions": { "The Sims 1 hot block": { "x86InstructionCount": 47, - "ExpectedInstructionCount": 98, + "ExpectedInstructionCount": 97, "Comment": [ "Hottest in-game block from The Sims 1, Legacy Collection", "Consumed 6.13% of a CPU core on Oryon-1", @@ -92,8 +92,7 @@ "stlur w4, [x9, #-4]", "ldapur w5, [x9, #8]", "nop", - "str w6, [x8, #-4]!", - "str w10, [x8, #-4]!", + "stp w10, w6, [x8, #-8]!", "ldapur w10, [x9, #24]", "nop", "str w11, [x8, #-4]!", diff --git a/unittests/InstructionCountCI/FlagM/Primary_32Bit.json b/unittests/InstructionCountCI/FlagM/Primary_32Bit.json index e4bac5ca6..8813249ea 100644 --- a/unittests/InstructionCountCI/FlagM/Primary_32Bit.json +++ b/unittests/InstructionCountCI/FlagM/Primary_32Bit.json @@ -247,67 +247,51 @@ ] }, "pusha": { - "ExpectedInstructionCount": 10, + "ExpectedInstructionCount": 5, "Comment": "0x60", "ExpectedArm64ASM": [ - "mov w20, w8", - "str w4, [x20, #-4]!", - "str w7, [x20, #-4]!", - "str w5, [x20, #-4]!", - "str w6, [x20, #-4]!", - "str w8, [x20, #-4]!", - "str w9, [x20, #-4]!", - "str w10, [x20, #-4]!", - "mov w8, w20", - "str w11, [x8, #-4]!" + "mov x20, x8", + "stp w7, w4, [x8, #-8]!", + "stp w6, w5, [x8, #-8]!", + "stp w9, w20, [x8, #-8]!", + "stp w11, w10, [x8, #-8]!" ] }, "pushad": { - "ExpectedInstructionCount": 10, + "ExpectedInstructionCount": 5, "Comment": "0x60", "ExpectedArm64ASM": [ - "mov w20, w8", - "str w4, [x20, #-4]!", - "str w7, [x20, #-4]!", - "str w5, [x20, #-4]!", - "str w6, [x20, #-4]!", - "str w8, [x20, #-4]!", - "str w9, [x20, #-4]!", - "str w10, [x20, #-4]!", - "mov w8, w20", - "str w11, [x8, #-4]!" + "mov x20, x8", + "stp w7, w4, [x8, #-8]!", + "stp w6, w5, [x8, #-8]!", + "stp w9, w20, [x8, #-8]!", + "stp w11, w10, [x8, #-8]!" ] }, "popa": { - "ExpectedInstructionCount": 10, + "ExpectedInstructionCount": 7, "Comment": "0x61", "ExpectedArm64ASM": [ "mov x20, x8", - "ldr w11, [x20], #4", - "ldr w10, [x20], #4", + "ldp w11, w10, [x20], #8", "ldr w9, [x20], #4", "add x20, x20, #0x4 (4)", "mov x8, x20", - "ldr w6, [x8], #4", - "ldr w5, [x8], #4", - "ldr w7, [x8], #4", - "ldr w4, [x8], #4" + "ldp w6, w5, [x8], #8", + "ldp w7, w4, [x8], #8" ] }, "popad": { - "ExpectedInstructionCount": 10, + "ExpectedInstructionCount": 7, "Comment": "0x61", "ExpectedArm64ASM": [ "mov x20, x8", - "ldr w11, [x20], #4", - "ldr w10, [x20], #4", + "ldp w11, w10, [x20], #8", "ldr w9, [x20], #4", "add x20, x20, #0x4 (4)", "mov x8, x20", - "ldr w6, [x8], #4", - "ldr w5, [x8], #4", - "ldr w7, [x8], #4", - "ldr w4, [x8], #4" + "ldp w6, w5, [x8], #8", + "ldp w7, w4, [x8], #8" ] }, "o16 pushf": { diff --git a/unittests/InstructionCountCI/FlagM/x87-Crysis2Max-fmodel.json b/unittests/InstructionCountCI/FlagM/x87-Crysis2Max-fmodel.json index 8c75d53d1..92c838b55 100644 --- a/unittests/InstructionCountCI/FlagM/x87-Crysis2Max-fmodel.json +++ b/unittests/InstructionCountCI/FlagM/x87-Crysis2Max-fmodel.json @@ -8585,8 +8585,7 @@ "fmov s2, s0", "str s2, [x7, #960]", "mov x8, x9", - "ldr w9, [x8], #4", - "ldr w20, [x8], #4", + "ldp w9, w20, [x8], #8", "ldrb w21, [x28, #1019]", "add w21, w21, #0x3 (3)", "and w21, w21, #0x7", @@ -8597,7 +8596,7 @@ "strb w21, [x28, #1298]" ], "x86InstructionCount": 809, - "ExpectedInstructionCount": 7770 + "ExpectedInstructionCount": 7769 } } } diff --git a/unittests/InstructionCountCI/FlagM/x87-HalfLife.json b/unittests/InstructionCountCI/FlagM/x87-HalfLife.json index 99139a050..4032800ca 100644 --- a/unittests/InstructionCountCI/FlagM/x87-HalfLife.json +++ b/unittests/InstructionCountCI/FlagM/x87-HalfLife.json @@ -1063,7 +1063,7 @@ }, "Block4": { "x86InstructionCount": 54, - "ExpectedInstructionCount": 80, + "ExpectedInstructionCount": 78, "x86Insts": [ "push ebp", "push edi", @@ -1121,10 +1121,8 @@ "fld dword [esp + 0x2c]" ], "ExpectedArm64ASM": [ - "str w9, [x8, #-4]!", - "str w11, [x8, #-4]!", - "str w10, [x8, #-4]!", - "str w6, [x8, #-4]!", + "stp w11, w9, [x8, #-8]!", + "stp w6, w10, [x8, #-8]!", "subs w26, w8, #0x4c (76)", "mov x27, x8", "mov x8, x26", @@ -1564,7 +1562,7 @@ }, "Block6": { "x86InstructionCount": 39, - "ExpectedInstructionCount": 297, + "ExpectedInstructionCount": 295, "x86Insts": [ "push ebp", "push edi", @@ -1607,10 +1605,8 @@ "fstp st1" ], "ExpectedArm64ASM": [ - "str w9, [x8, #-4]!", - "str w11, [x8, #-4]!", - "str w10, [x8, #-4]!", - "str w6, [x8, #-4]!", + "stp w11, w9, [x8, #-8]!", + "stp w6, w10, [x8, #-8]!", "subs w20, w8, #0x4 (4)", "mov x27, x8", "mov x8, x20", @@ -2611,7 +2607,7 @@ }, "Block10": { "x86InstructionCount": 125, - "ExpectedInstructionCount": 22, + "ExpectedInstructionCount": 21, "x86Insts": [ "push esi", "push ebx", @@ -2740,8 +2736,7 @@ "xor eax,dword gs:[0x14]" ], "ExpectedArm64ASM": [ - "str w10, [x8, #-4]!", - "str w6, [x8, #-4]!", + "stp w6, w10, [x8, #-8]!", "sub w8, w8, #0xa4 (164)", "ldr w6, [x8, #176]", "add w10, w8, #0x18 (24)", diff --git a/unittests/InstructionCountCI/FlagM/x87-Oblivion.json b/unittests/InstructionCountCI/FlagM/x87-Oblivion.json index 06ad7eb8d..caef346be 100644 --- a/unittests/InstructionCountCI/FlagM/x87-Oblivion.json +++ b/unittests/InstructionCountCI/FlagM/x87-Oblivion.json @@ -17705,7 +17705,7 @@ }, "Block4": { "x86InstructionCount": 2050, - "ExpectedInstructionCount": 40, + "ExpectedInstructionCount": 39, "x86Insts": [ "fldz", "push 0x0", @@ -19768,8 +19768,7 @@ "movk w21, #0x100, lsl #16", "str w21, [x8, #-4]!", "mov w21, #0x37", - "str w21, [x8, #-4]!", - "str w7, [x8, #-4]!", + "stp w7, w21, [x8, #-8]!", "str x30, [sp, #-16]!", "mov v0.16b, v2.16b", "ldr x0, [x28, #1456]", diff --git a/unittests/InstructionCountCI/FlagM/x87-Psychonauts.json b/unittests/InstructionCountCI/FlagM/x87-Psychonauts.json index 15ccf61e7..3975e25d1 100644 --- a/unittests/InstructionCountCI/FlagM/x87-Psychonauts.json +++ b/unittests/InstructionCountCI/FlagM/x87-Psychonauts.json @@ -26756,7 +26756,7 @@ }, "Block8": { "x86InstructionCount": 231, - "ExpectedInstructionCount": 3724, + "ExpectedInstructionCount": 3723, "x86Insts": [ "fadd dword [esp + 0x40]", "lea edx,[ecx + ecx*0x2]", @@ -30693,8 +30693,7 @@ "add w20, w20, #0x1 (1)", "and w20, w20, #0x7", "strb w20, [x28, #1019]", - "ldr w11, [x8], #4", - "ldr w10, [x8], #4", + "ldp w11, w10, [x8], #8", "ldrb w22, [x28, #1298]", "lsl w23, w21, w20", "bic w22, w22, w23", @@ -30719,7 +30718,7 @@ }, "Block9": { "x86InstructionCount": 222, - "ExpectedInstructionCount": 3646, + "ExpectedInstructionCount": 3645, "x86Insts": [ "fadd dword [esp + 0x40]", "lea edx,[ecx + ecx*0x2]", @@ -34569,8 +34568,7 @@ "add w20, w20, #0x1 (1)", "and w20, w20, #0x7", "strb w20, [x28, #1019]", - "ldr w11, [x8], #4", - "ldr w10, [x8], #4", + "ldp w11, w10, [x8], #8", "ldrb w22, [x28, #1298]", "lsl w23, w21, w20", "bic w22, w22, w23", diff --git a/unittests/InstructionCountCI/Primary_32Bit.json b/unittests/InstructionCountCI/Primary_32Bit.json index 2c27708d4..fa0b4a854 100644 --- a/unittests/InstructionCountCI/Primary_32Bit.json +++ b/unittests/InstructionCountCI/Primary_32Bit.json @@ -262,67 +262,51 @@ ] }, "pusha": { - "ExpectedInstructionCount": 10, + "ExpectedInstructionCount": 5, "Comment": "0x60", "ExpectedArm64ASM": [ - "mov w20, w8", - "str w4, [x20, #-4]!", - "str w7, [x20, #-4]!", - "str w5, [x20, #-4]!", - "str w6, [x20, #-4]!", - "str w8, [x20, #-4]!", - "str w9, [x20, #-4]!", - "str w10, [x20, #-4]!", - "mov w8, w20", - "str w11, [x8, #-4]!" + "mov x20, x8", + "stp w7, w4, [x8, #-8]!", + "stp w6, w5, [x8, #-8]!", + "stp w9, w20, [x8, #-8]!", + "stp w11, w10, [x8, #-8]!" ] }, "pushad": { - "ExpectedInstructionCount": 10, + "ExpectedInstructionCount": 5, "Comment": "0x60", "ExpectedArm64ASM": [ - "mov w20, w8", - "str w4, [x20, #-4]!", - "str w7, [x20, #-4]!", - "str w5, [x20, #-4]!", - "str w6, [x20, #-4]!", - "str w8, [x20, #-4]!", - "str w9, [x20, #-4]!", - "str w10, [x20, #-4]!", - "mov w8, w20", - "str w11, [x8, #-4]!" + "mov x20, x8", + "stp w7, w4, [x8, #-8]!", + "stp w6, w5, [x8, #-8]!", + "stp w9, w20, [x8, #-8]!", + "stp w11, w10, [x8, #-8]!" ] }, "popa": { - "ExpectedInstructionCount": 10, + "ExpectedInstructionCount": 7, "Comment": "0x61", "ExpectedArm64ASM": [ "mov x20, x8", - "ldr w11, [x20], #4", - "ldr w10, [x20], #4", + "ldp w11, w10, [x20], #8", "ldr w9, [x20], #4", "add x20, x20, #0x4 (4)", "mov x8, x20", - "ldr w6, [x8], #4", - "ldr w5, [x8], #4", - "ldr w7, [x8], #4", - "ldr w4, [x8], #4" + "ldp w6, w5, [x8], #8", + "ldp w7, w4, [x8], #8" ] }, "popad": { - "ExpectedInstructionCount": 10, + "ExpectedInstructionCount": 7, "Comment": "0x61", "ExpectedArm64ASM": [ "mov x20, x8", - "ldr w11, [x20], #4", - "ldr w10, [x20], #4", + "ldp w11, w10, [x20], #8", "ldr w9, [x20], #4", "add x20, x20, #0x4 (4)", "mov x8, x20", - "ldr w6, [x8], #4", - "ldr w5, [x8], #4", - "ldr w7, [x8], #4", - "ldr w4, [x8], #4" + "ldp w6, w5, [x8], #8", + "ldp w7, w4, [x8], #8" ] }, "aam": {