Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
0afa8f2c14 | |||
d25a084858 | |||
311ca3c3f1 |
@ -251,7 +251,20 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
}
|
||||
}
|
||||
|
||||
int selectedReg = GetHighestValueIndex(freePositions);
|
||||
// If this is a copy destination variable, we prefer the register used for the copy source.
|
||||
// If the register is available, then the copy can be eliminated later as both source
|
||||
// and destination will use the same register.
|
||||
int selectedReg;
|
||||
|
||||
if (current.TryGetCopySourceRegister(out int preferredReg) && freePositions[preferredReg] >= current.GetEnd())
|
||||
{
|
||||
selectedReg = preferredReg;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedReg = GetHighestValueIndex(freePositions);
|
||||
}
|
||||
|
||||
int selectedNextUse = freePositions[selectedReg];
|
||||
|
||||
// Intervals starts and ends at odd positions, unless they span an entire
|
||||
@ -431,7 +444,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetHighestValueIndex(Span<int> span)
|
||||
private static int GetHighestValueIndex(ReadOnlySpan<int> span)
|
||||
{
|
||||
int highest = int.MinValue;
|
||||
|
||||
@ -798,12 +811,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
// The "visited" state is stored in the MSB of the local's value.
|
||||
const ulong VisitedMask = 1ul << 63;
|
||||
|
||||
bool IsVisited(Operand local)
|
||||
static bool IsVisited(Operand local)
|
||||
{
|
||||
return (local.GetValueUnsafe() & VisitedMask) != 0;
|
||||
}
|
||||
|
||||
void SetVisited(Operand local)
|
||||
static void SetVisited(Operand local)
|
||||
{
|
||||
local.GetValueUnsafe() |= VisitedMask;
|
||||
}
|
||||
@ -826,9 +839,25 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
dest.NumberLocal(_intervals.Count);
|
||||
|
||||
_intervals.Add(new LiveInterval(dest));
|
||||
LiveInterval interval = new LiveInterval(dest);
|
||||
_intervals.Add(interval);
|
||||
|
||||
SetVisited(dest);
|
||||
|
||||
// If this is a copy (or copy-like operation), set the copy source interval as well.
|
||||
// This is used for register preferencing later on, which allows the copy to be eliminated
|
||||
// in some cases.
|
||||
if (node.Instruction == Instruction.Copy || node.Instruction == Instruction.ZeroExtend32)
|
||||
{
|
||||
Operand source = node.GetSource(0);
|
||||
|
||||
if (source.Kind == OperandKind.LocalVariable &&
|
||||
source.GetLocalNumber() > 0 &&
|
||||
(node.Instruction == Instruction.Copy || source.Type == OperandType.I32))
|
||||
{
|
||||
interval.SetCopySource(_intervals[source.GetLocalNumber()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
public LiveRange CurrRange;
|
||||
|
||||
public LiveInterval Parent;
|
||||
public LiveInterval CopySource;
|
||||
|
||||
public UseList Uses;
|
||||
public LiveIntervalList Children;
|
||||
@ -37,6 +38,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
private ref LiveRange CurrRange => ref _data->CurrRange;
|
||||
private ref LiveRange PrevRange => ref _data->PrevRange;
|
||||
private ref LiveInterval Parent => ref _data->Parent;
|
||||
private ref LiveInterval CopySource => ref _data->CopySource;
|
||||
private ref UseList Uses => ref _data->Uses;
|
||||
private ref LiveIntervalList Children => ref _data->Children;
|
||||
|
||||
@ -78,6 +80,25 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
Register = register;
|
||||
}
|
||||
|
||||
public void SetCopySource(LiveInterval copySource)
|
||||
{
|
||||
CopySource = copySource;
|
||||
}
|
||||
|
||||
public bool TryGetCopySourceRegister(out int copySourceRegIndex)
|
||||
{
|
||||
if (CopySource._data != null)
|
||||
{
|
||||
copySourceRegIndex = CopySource.Register.Index;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
copySourceRegIndex = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
PrevRange = default;
|
||||
|
@ -11,7 +11,7 @@ namespace ARMeilleure.Translation
|
||||
private int[] _postOrderMap;
|
||||
|
||||
public int LocalsCount { get; private set; }
|
||||
public BasicBlock Entry { get; }
|
||||
public BasicBlock Entry { get; private set; }
|
||||
public IntrusiveList<BasicBlock> Blocks { get; }
|
||||
public BasicBlock[] PostOrderBlocks => _postOrderBlocks;
|
||||
public int[] PostOrderMap => _postOrderMap;
|
||||
@ -34,6 +34,15 @@ namespace ARMeilleure.Translation
|
||||
return result;
|
||||
}
|
||||
|
||||
public void UpdateEntry(BasicBlock newEntry)
|
||||
{
|
||||
newEntry.AddSuccessor(Entry);
|
||||
|
||||
Entry = newEntry;
|
||||
Blocks.AddFirst(newEntry);
|
||||
Update();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
RemoveUnreachableBlocks(Blocks);
|
||||
|
@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 6634; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 6950; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
|
@ -89,6 +89,17 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode)
|
||||
{
|
||||
if (cfg.Entry.Predecessors.Count != 0)
|
||||
{
|
||||
// We expect the entry block to have no predecessors.
|
||||
// This is required because we have a implicit context load at the start of the function,
|
||||
// but if there is a jump to the start of the function, the context load would trash the modified values.
|
||||
// Here we insert a new entry block that will jump to the existing entry block.
|
||||
BasicBlock newEntry = new BasicBlock(cfg.Blocks.Count);
|
||||
|
||||
cfg.UpdateEntry(newEntry);
|
||||
}
|
||||
|
||||
// Compute local register inputs and outputs used inside blocks.
|
||||
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
|
||||
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
|
||||
@ -201,7 +212,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
// The only block without any predecessor should be the entry block.
|
||||
// It always needs a context load as it is the first block to run.
|
||||
if (block.Predecessors.Count == 0 || hasContextLoad)
|
||||
if (block == cfg.Entry || hasContextLoad)
|
||||
{
|
||||
long vecMask = globalInputs[block.Index].VecMask;
|
||||
long intMask = globalInputs[block.Index].IntMask;
|
||||
|
@ -124,7 +124,7 @@ namespace Ryujinx.Common.Memory
|
||||
|
||||
if (array is not null)
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
ArrayPool<T>.Shared.Return(array, RuntimeHelpers.IsReferenceOrContainsReferences<T>());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ namespace Ryujinx.Common.Memory
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose()
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(_array);
|
||||
ArrayPool<T>.Shared.Return(_array, RuntimeHelpers.IsReferenceOrContainsReferences<T>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -616,7 +616,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
}
|
||||
|
||||
ArrayPool<KSynchronizationObject>.Shared.Return(syncObjsArray);
|
||||
ArrayPool<KSynchronizationObject>.Shared.Return(syncObjsArray, true);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
}
|
||||
}
|
||||
|
||||
ArrayPool<LinkedListNode<KThread>>.Shared.Return(syncNodesArray);
|
||||
ArrayPool<LinkedListNode<KThread>>.Shared.Return(syncNodesArray, true);
|
||||
}
|
||||
|
||||
_context.CriticalSection.Leave();
|
||||
|
Reference in New Issue
Block a user