Ryujinx/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs
FICTURE7 89791ba68d
Add inlined on translation call counting (#2190)
* Add EntryTable<TEntry>

* Add on translation call counting

* Add Counter

* Add PPTC support

* Make Counter a generic & use a 32-bit counter instead

* Return false on overflow

* Set PPTC version

* Print more information about the rejit queue

* Make Counter<T> disposable

* Remove Block.TailCall since it is not used anymore

* Apply suggestions from code review

Address gdkchan's feedback

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Fix more stale docs

* Remove rejit requests queue logging

* Make Counter<T> finalizable

Most certainly quite an odd use case.

* Make EntryTable<T>.TryAllocate set entry to default

* Re-trigger CI

* Dispose Counters before they hit the finalizer queue

* Re-trigger CI

Just for good measure...

* Make EntryTable<T> expandable

* EntryTable is now expandable instead of being a fixed slab.
* Remove EntryTable<T>.TryAllocate
* Remove Counter<T>.TryCreate

Address LDj3SNuD's feedback

* Apply suggestions from code review

Address LDj3SNuD's feedback

Co-authored-by: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com>

* Remove useless return

* POH approach, but the sequel

* Revert "POH approach, but the sequel"

This reverts commit 5f5abaa24735726ff2db367dc74f98055d4f4cba.

The sequel got shelved

* Add extra documentation

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com>
2021-04-18 23:43:53 +02:00

88 lines
2.9 KiB
C#

using System;
using System.Collections.Generic;
namespace ARMeilleure.Decoders.Optimizations
{
static class TailCallRemover
{
public static Block[] RunPass(ulong entryAddress, List<Block> blocks)
{
// Detect tail calls:
// - Assume this function spans the space covered by contiguous code blocks surrounding the entry address.
// - A jump to an area outside this contiguous region will be treated as an exit block.
// - Include a small allowance for jumps outside the contiguous range.
if (!Decoder.BinarySearch(blocks, entryAddress, out int entryBlockId))
{
throw new InvalidOperationException("Function entry point is not contained in a block.");
}
const ulong allowance = 4;
Block entryBlock = blocks[entryBlockId];
Block startBlock = entryBlock;
Block endBlock = entryBlock;
int startBlockIndex = entryBlockId;
int endBlockIndex = entryBlockId;
for (int i = entryBlockId + 1; i < blocks.Count; i++) // Search forwards.
{
Block block = blocks[i];
if (endBlock.EndAddress < block.Address - allowance)
{
break; // End of contiguous function.
}
endBlock = block;
endBlockIndex = i;
}
for (int i = entryBlockId - 1; i >= 0; i--) // Search backwards.
{
Block block = blocks[i];
if (startBlock.Address > block.EndAddress + allowance)
{
break; // End of contiguous function.
}
startBlock = block;
startBlockIndex = i;
}
if (startBlockIndex == 0 && endBlockIndex == blocks.Count - 1)
{
return blocks.ToArray(); // Nothing to do here.
}
// Mark branches whose target is outside of the contiguous region as an exit block.
for (int i = startBlockIndex; i <= endBlockIndex; i++)
{
Block block = blocks[i];
if (block.Branch != null && (block.Branch.Address > endBlock.EndAddress || block.Branch.EndAddress < startBlock.Address))
{
block.Branch.Exit = true;
}
}
var newBlocks = new List<Block>(blocks.Count);
// Finally, rebuild decoded block list, ignoring blocks outside the contiguous range.
for (int i = 0; i < blocks.Count; i++)
{
Block block = blocks[i];
if (block.Exit || (i >= startBlockIndex && i <= endBlockIndex))
{
newBlocks.Add(block);
}
}
return newBlocks.ToArray();
}
}
}