diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp b/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp index 74996e83d..009320564 100644 --- a/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp @@ -19,6 +19,7 @@ namespace FEXCore::CPU { #define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header const* IROp, IR::NodeID Node) DEF_OP(AllocateGPR) {} +DEF_OP(AllocateGPRAfter) {} DEF_OP(GuestOpcode) { auto Op = IROp->C(); diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 4f085cd45..879949a18 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -3309,8 +3309,8 @@ void OpDispatchBuilder::MOVSOp(OpcodeArgs) { SrcAddr = _Add(OpSize::i64Bit, SrcAddr, SrcSegment); } - Ref Result_Src = _AllocateGPR(); - Ref Result_Dst = _AllocateGPR(); + Ref Result_Src = _AllocateGPR(false); + Ref Result_Dst = _AllocateGPR(false); _MemCpy(CTX->IsAtomicTSOEnabled(), Size, DstAddr, SrcAddr, Counter, LoadDir(1), Result_Dst, Result_Src); if (DstSegment) { @@ -3993,8 +3993,8 @@ void OpDispatchBuilder::CMPXCHGPairOp(OpcodeArgs) { // This will write to memory! Careful! // Third operand must be a calculated guest memory address - Ref Result_Lower = _AllocateGPR(); - Ref Result_Upper = _AllocateGPR(); + Ref Result_Lower = _AllocateGPR(true); + Ref Result_Upper = _AllocateGPRAfter(Result_Lower); _CASPair(IR::SizeToOpSize(Size), Expected_Lower, Expected_Upper, Desired_Lower, Desired_Upper, Src1, Result_Lower, Result_Upper); HandleNZCV_RMW(); diff --git a/FEXCore/Source/Interface/IR/IR.json b/FEXCore/Source/Interface/IR/IR.json index 0e72996a9..5afa0f7f3 100644 --- a/FEXCore/Source/Interface/IR/IR.json +++ b/FEXCore/Source/Interface/IR/IR.json @@ -252,9 +252,17 @@ "WalkFindRegClass($Value) != GPRPairClass" ] }, - "GPR = AllocateGPR": { + "GPR = AllocateGPR i1:$ForPair": { "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"], + "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": "8" + }, + "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": "8" }, "GPR = RDRAND i1:$GetReseeded": { diff --git a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp index 21d9e8d85..8c442ab1b 100644 --- a/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp +++ b/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp @@ -411,6 +411,25 @@ private: } } + // Try to coalesce reserved pairs. Just a heuristic to remove some moves. + if (IROp->Op == OP_ALLOCATEGPR) { + if (IROp->C()->ForPair) { + uint32_t Available = AvailableMask(&Classes[GPRClass], true); + if (Available) { + unsigned Reg = std::countr_zero(Available); + SetReg(CodeNode, PhysicalRegister(GPRClass, Reg)); + return; + } + } + } else if (IROp->Op == OP_ALLOCATEGPRAFTER) { + uint32_t Available = AvailableMask(&Classes[GPRClass], false); + auto After = SSAToReg[IR->GetID(IR->GetNode(IROp->Args[0])).Value]; + if ((After.Reg & 1) == 0 && Available & (1ull << (After.Reg + 1))) { + SetReg(CodeNode, PhysicalRegister(GPRClass, After.Reg + 1)); + return; + } + } + RegisterClassType OrigClassType = GetRegClassFromNode(IR, IROp); bool Pair = OrigClassType == GPRPairClass; RegisterClassType ClassType = Pair ? GPRClass : OrigClassType;