aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Translation/RegisterUsage.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ChocolArm64/Translation/RegisterUsage.cs')
-rw-r--r--ChocolArm64/Translation/RegisterUsage.cs299
1 files changed, 95 insertions, 204 deletions
diff --git a/ChocolArm64/Translation/RegisterUsage.cs b/ChocolArm64/Translation/RegisterUsage.cs
index 2e6829d5..fe0c8659 100644
--- a/ChocolArm64/Translation/RegisterUsage.cs
+++ b/ChocolArm64/Translation/RegisterUsage.cs
@@ -1,269 +1,160 @@
-using System;
+using ChocolArm64.IntermediateRepresentation;
+using ChocolArm64.State;
using System.Collections.Generic;
namespace ChocolArm64.Translation
{
class RegisterUsage
{
- public const long CallerSavedIntRegistersMask = 0x7fL << 9;
- public const long PStateNzcvFlagsMask = 0xfL << 60;
+ private const long CallerSavedIntRegistersMask = 0x7fL << 9;
+ private const long PStateNzcvFlagsMask = 0xfL << 60;
- public const long CallerSavedVecRegistersMask = 0xffffL << 16;
+ private const long CallerSavedVecRegistersMask = 0xffffL << 16;
- private class PathIo
+ private RegisterMask[] _inputs;
+ private RegisterMask[] _outputs;
+
+ public RegisterUsage(BasicBlock entryBlock, int blocksCount)
{
- private Dictionary<ILBlock, long> _allInputs;
- private Dictionary<ILBlock, long> _cmnOutputs;
+ _inputs = new RegisterMask[blocksCount];
+ _outputs = new RegisterMask[blocksCount];
- private long _allOutputs;
+ HashSet<BasicBlock> visited = new HashSet<BasicBlock>();
- public PathIo()
- {
- _allInputs = new Dictionary<ILBlock, long>();
- _cmnOutputs = new Dictionary<ILBlock, long>();
- }
+ Stack<BasicBlock> blockStack = new Stack<BasicBlock>();
+
+ List<BasicBlock> postOrderBlocks = new List<BasicBlock>(blocksCount);
+
+ visited.Add(entryBlock);
- public void Set(ILBlock entry, long inputs, long outputs)
+ blockStack.Push(entryBlock);
+
+ while (blockStack.TryPop(out BasicBlock block))
{
- if (!_allInputs.TryAdd(entry, inputs))
+ if (block.Next != null && visited.Add(block.Next))
{
- _allInputs[entry] |= inputs;
+ blockStack.Push(block);
+ blockStack.Push(block.Next);
}
-
- if (!_cmnOutputs.TryAdd(entry, outputs))
+ else if (block.Branch != null && visited.Add(block.Branch))
{
- _cmnOutputs[entry] &= outputs;
+ blockStack.Push(block);
+ blockStack.Push(block.Branch);
}
-
- _allOutputs |= outputs;
- }
-
- public long GetInputs(ILBlock entry)
- {
- if (_allInputs.TryGetValue(entry, out long inputs))
+ else
{
- //We also need to read the registers that may not be written
- //by all paths that can reach a exit point, to ensure that
- //the local variable will not remain uninitialized depending
- //on the flow path taken.
- return inputs | (_allOutputs & ~_cmnOutputs[entry]);
+ postOrderBlocks.Add(block);
}
-
- return 0;
}
- public long GetOutputs()
- {
- return _allOutputs;
- }
- }
-
- private Dictionary<ILBlock, PathIo> _intPaths;
- private Dictionary<ILBlock, PathIo> _vecPaths;
-
- private struct BlockIo : IEquatable<BlockIo>
- {
- public ILBlock Block { get; }
- public ILBlock Entry { get; }
-
- public long IntInputs { get; set; }
- public long VecInputs { get; set; }
- public long IntOutputs { get; set; }
- public long VecOutputs { get; set; }
+ RegisterMask[] cmnOutputMasks = new RegisterMask[blocksCount];
- public BlockIo(ILBlock block, ILBlock entry)
- {
- Block = block;
- Entry = entry;
+ bool modified;
- IntInputs = IntOutputs = 0;
- VecInputs = VecOutputs = 0;
- }
+ bool firstPass = true;
- public BlockIo(
- ILBlock block,
- ILBlock entry,
- long intInputs,
- long vecInputs,
- long intOutputs,
- long vecOutputs) : this(block, entry)
+ do
{
- IntInputs = intInputs;
- VecInputs = vecInputs;
- IntOutputs = intOutputs;
- VecOutputs = vecOutputs;
- }
+ modified = false;
- public override bool Equals(object obj)
- {
- if (!(obj is BlockIo other))
+ for (int blkIndex = postOrderBlocks.Count - 1; blkIndex >= 0; blkIndex--)
{
- return false;
- }
+ BasicBlock block = postOrderBlocks[blkIndex];
- return Equals(other);
- }
+ if (block.Predecessors.Count != 0 && !block.HasStateLoad)
+ {
+ BasicBlock predecessor = block.Predecessors[0];
- public bool Equals(BlockIo other)
- {
- return other.Block == Block &&
- other.Entry == Entry &&
- other.IntInputs == IntInputs &&
- other.VecInputs == VecInputs &&
- other.IntOutputs == IntOutputs &&
- other.VecOutputs == VecOutputs;
- }
+ RegisterMask cmnOutputs = predecessor.RegOutputs | cmnOutputMasks[predecessor.Index];
- public override int GetHashCode()
- {
- return HashCode.Combine(Block, Entry, IntInputs, VecInputs, IntOutputs, VecOutputs);
- }
+ RegisterMask outputs = _outputs[predecessor.Index];
- public static bool operator ==(BlockIo lhs, BlockIo rhs)
- {
- return lhs.Equals(rhs);
- }
+ for (int pIndex = 1; pIndex < block.Predecessors.Count; pIndex++)
+ {
+ predecessor = block.Predecessors[pIndex];
- public static bool operator !=(BlockIo lhs, BlockIo rhs)
- {
- return !(lhs == rhs);
- }
- }
+ cmnOutputs &= predecessor.RegOutputs | cmnOutputMasks[predecessor.Index];
- public RegisterUsage()
- {
- _intPaths = new Dictionary<ILBlock, PathIo>();
- _vecPaths = new Dictionary<ILBlock, PathIo>();
- }
+ outputs |= _outputs[predecessor.Index];
+ }
- public void BuildUses(ILBlock entry)
- {
- //This will go through all possible paths on the graph,
- //and store all inputs/outputs for each block. A register
- //that was previously written to already is not considered an input.
- //When a block can be reached by more than one path, then the
- //output from all paths needs to be set for this block, and
- //only outputs present in all of the parent blocks can be considered
- //when doing input elimination. Each block chain has a entry, that's where
- //the code starts executing. They are present on the subroutine start point,
- //and on call return points too (address written to X30 by BL).
- HashSet<BlockIo> visited = new HashSet<BlockIo>();
-
- Queue<BlockIo> unvisited = new Queue<BlockIo>();
-
- void Enqueue(BlockIo block)
- {
- if (visited.Add(block))
- {
- unvisited.Enqueue(block);
- }
- }
+ _inputs[block.Index] |= outputs & ~cmnOutputs;
- Enqueue(new BlockIo(entry, entry));
+ if (!firstPass)
+ {
+ cmnOutputs &= cmnOutputMasks[block.Index];
+ }
- while (unvisited.Count > 0)
- {
- BlockIo current = unvisited.Dequeue();
+ if (Exchange(cmnOutputMasks, block.Index, cmnOutputs))
+ {
+ modified = true;
+ }
- current.IntInputs |= current.Block.IntInputs & ~current.IntOutputs;
- current.VecInputs |= current.Block.VecInputs & ~current.VecOutputs;
- current.IntOutputs |= current.Block.IntOutputs;
- current.VecOutputs |= current.Block.VecOutputs;
+ outputs |= block.RegOutputs;
- //Check if this is a exit block
- //(a block that returns or calls another sub).
- if ((current.Block.Next == null &&
- current.Block.Branch == null) || current.Block.HasStateStore)
- {
- if (!_intPaths.TryGetValue(current.Block, out PathIo intPath))
- {
- _intPaths.Add(current.Block, intPath = new PathIo());
+ if (Exchange(_outputs, block.Index, _outputs[block.Index] | outputs))
+ {
+ modified = true;
+ }
}
-
- if (!_vecPaths.TryGetValue(current.Block, out PathIo vecPath))
+ else if (Exchange(_outputs, block.Index, block.RegOutputs))
{
- _vecPaths.Add(current.Block, vecPath = new PathIo());
+ modified = true;
}
-
- intPath.Set(current.Entry, current.IntInputs, current.IntOutputs);
- vecPath.Set(current.Entry, current.VecInputs, current.VecOutputs);
}
- void EnqueueFromCurrent(ILBlock block, bool retTarget)
+ firstPass = false;
+ }
+ while (modified);
+
+ do
+ {
+ modified = false;
+
+ for (int blkIndex = 0; blkIndex < postOrderBlocks.Count; blkIndex++)
{
- BlockIo blockIo;
+ BasicBlock block = postOrderBlocks[blkIndex];
+
+ RegisterMask inputs = block.RegInputs;
- if (retTarget)
+ if (block.Next != null)
{
- blockIo = new BlockIo(block, block);
+ inputs |= _inputs[block.Next.Index];
}
- else
+
+ if (block.Branch != null)
{
- blockIo = new BlockIo(
- block,
- current.Entry,
- current.IntInputs,
- current.VecInputs,
- current.IntOutputs,
- current.VecOutputs);
+ inputs |= _inputs[block.Branch.Index];
}
- Enqueue(blockIo);
- }
+ inputs &= ~cmnOutputMasks[block.Index];
- if (current.Block.Next != null)
- {
- EnqueueFromCurrent(current.Block.Next, current.Block.HasStateStore);
- }
-
- if (current.Block.Branch != null)
- {
- EnqueueFromCurrent(current.Block.Branch, false);
+ if (Exchange(_inputs, block.Index, _inputs[block.Index] | inputs))
+ {
+ modified = true;
+ }
}
}
+ while (modified);
}
- public long GetIntInputs(ILBlock entry) => GetInputsImpl(entry, _intPaths.Values);
- public long GetVecInputs(ILBlock entry) => GetInputsImpl(entry, _vecPaths.Values);
-
- private long GetInputsImpl(ILBlock entry, IEnumerable<PathIo> values)
+ private static bool Exchange(RegisterMask[] masks, int blkIndex, RegisterMask value)
{
- long inputs = 0;
+ RegisterMask oldValue = masks[blkIndex];
- foreach (PathIo path in values)
- {
- inputs |= path.GetInputs(entry);
- }
+ masks[blkIndex] = value;
- return inputs;
+ return oldValue != value;
}
- public long GetIntNotInputs(ILBlock entry) => GetNotInputsImpl(entry, _intPaths.Values);
- public long GetVecNotInputs(ILBlock entry) => GetNotInputsImpl(entry, _vecPaths.Values);
-
- private long GetNotInputsImpl(ILBlock entry, IEnumerable<PathIo> values)
- {
- //Returns a mask with registers that are written to
- //before being read. Only those registers that are
- //written in all paths, and is not read before being
- //written to on those paths, should be set on the mask.
- long mask = -1L;
-
- foreach (PathIo path in values)
- {
- mask &= path.GetOutputs() & ~path.GetInputs(entry);
- }
-
- return mask;
- }
+ public RegisterMask GetInputs(BasicBlock entryBlock) => _inputs[entryBlock.Index];
- public long GetIntOutputs(ILBlock block) => _intPaths[block].GetOutputs();
- public long GetVecOutputs(ILBlock block) => _vecPaths[block].GetOutputs();
+ public RegisterMask GetOutputs(BasicBlock block) => _outputs[block.Index];
- public static long ClearCallerSavedIntRegs(long mask, bool isAarch64)
+ public static long ClearCallerSavedIntRegs(long mask, ExecutionMode mode)
{
//TODO: ARM32 support.
- if (isAarch64)
+ if (mode == ExecutionMode.Aarch64)
{
mask &= ~(CallerSavedIntRegistersMask | PStateNzcvFlagsMask);
}
@@ -271,10 +162,10 @@ namespace ChocolArm64.Translation
return mask;
}
- public static long ClearCallerSavedVecRegs(long mask, bool isAarch64)
+ public static long ClearCallerSavedVecRegs(long mask, ExecutionMode mode)
{
//TODO: ARM32 support.
- if (isAarch64)
+ if (mode == ExecutionMode.Aarch64)
{
mask &= ~CallerSavedVecRegistersMask;
}