From ac5f4894ac389aafe073262ca3dbdb8fe1282651 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 31 Jul 2019 22:11:54 -0300 Subject: [PATCH] Add a new register allocator, higher quality code for hot code (tier up), and other tweaks --- .../RegisterAllocators/FastLinearScan.cs | 579 ------------------ .../RegisterAllocators/HybridAllocator.cs | 416 +++++++++++++ .../RegisterAllocators/IRegisterAllocator.cs | 12 + .../{LinearScan.cs => LinearScanAllocator.cs} | 7 +- ARMeilleure/CodeGen/X86/CodeGenerator.cs | 22 +- .../CodeGen/X86/HardwareCapabilities.cs | 6 +- ARMeilleure/CodeGen/X86/PreAllocator.cs | 10 + ARMeilleure/Instructions/InstEmitFlow.cs | 2 +- .../Instructions/InstEmitFlowHelper.cs | 6 +- ARMeilleure/Instructions/InstEmitSimdMove.cs | 4 +- ARMeilleure/Memory/MemoryManagerPal.cs | 6 +- ARMeilleure/Translation/Compiler.cs | 34 +- ARMeilleure/Translation/CompilerContext.cs | 6 +- ARMeilleure/Translation/CompilerOptions.cs | 16 + ARMeilleure/Translation/PriorityQueue.cs | 39 ++ ARMeilleure/Translation/RegisterToLocal.cs | 52 ++ ARMeilleure/Translation/RegisterUsage.cs | 10 +- ARMeilleure/Translation/SsaConstruction.cs | 2 +- ARMeilleure/Translation/TranslatedFunction.cs | 19 +- ARMeilleure/Translation/Translator.cs | 77 ++- 20 files changed, 698 insertions(+), 627 deletions(-) delete mode 100644 ARMeilleure/CodeGen/RegisterAllocators/FastLinearScan.cs create mode 100644 ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs create mode 100644 ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs rename ARMeilleure/CodeGen/RegisterAllocators/{LinearScan.cs => LinearScanAllocator.cs} (99%) create mode 100644 ARMeilleure/Translation/CompilerOptions.cs create mode 100644 ARMeilleure/Translation/PriorityQueue.cs create mode 100644 ARMeilleure/Translation/RegisterToLocal.cs diff --git a/ARMeilleure/CodeGen/RegisterAllocators/FastLinearScan.cs b/ARMeilleure/CodeGen/RegisterAllocators/FastLinearScan.cs deleted file mode 100644 index 20953130ad..0000000000 --- a/ARMeilleure/CodeGen/RegisterAllocators/FastLinearScan.cs +++ /dev/null @@ -1,579 +0,0 @@ -using ARMeilleure.Common; -using ARMeilleure.IntermediateRepresentation; -using ARMeilleure.Translation; -using System; -using System.Collections.Generic; -using System.Diagnostics; - -using static ARMeilleure.IntermediateRepresentation.OperandHelper; - -namespace ARMeilleure.CodeGen.RegisterAllocators -{ - class FastLinearScan - { - private const int InstructionGap = 2; - - private const int RegistersCount = 16; - - private const int MaxIROperands = 4; - - private class OperationInfo - { - public LinkedListNode Node { get; } - - public int IntSpillUsedRegisters { get; set; } - public int VecSpillUsedRegisters { get; set; } - - public OperationInfo(LinkedListNode node) - { - Node = node; - } - } - - private List _operationNodes; - - private int _intSpillTemps; - private int _vecSpillTemps; - - private List _intervals; - - private class CompareIntervalsEnd : IComparer - { - public int Compare(LiveInterval lhs, LiveInterval rhs) - { - if (lhs.GetEnd() == rhs.GetEnd()) - { - return 0; - } - else if (lhs.GetEnd() < rhs.GetEnd()) - { - return 1; - } - else - { - return -1; - } - } - } - - public AllocationResult RunPass(ControlFlowGraph cfg, StackAllocator stackAlloc, RegisterMasks regMasks) - { - BuildIntervals(cfg, regMasks); - - List[] fixedIntervals = new List[2]; - - fixedIntervals[0] = new List(); - fixedIntervals[1] = new List(); - - int intUsedRegisters = 0; - int vecUsedRegisters = 0; - - for (int index = 0; index < RegistersCount * 2; index++) - { - LiveInterval interval = _intervals[index]; - - if (!interval.IsEmpty) - { - if (interval.Register.Type == RegisterType.Integer) - { - intUsedRegisters |= 1 << interval.Register.Index; - } - else /* if (interval.Register.Type == RegisterType.Vector) */ - { - vecUsedRegisters |= 1 << interval.Register.Index; - } - - InsertSorted(fixedIntervals[index & 1], interval); - } - } - - List activeIntervals = new List(); - - CompareIntervalsEnd comparer = new CompareIntervalsEnd(); - - int intFreeRegisters = regMasks.IntAvailableRegisters; - int vecFreeRegisters = regMasks.VecAvailableRegisters; - - intFreeRegisters = ReserveSpillTemps(ref _intSpillTemps, intFreeRegisters); - vecFreeRegisters = ReserveSpillTemps(ref _vecSpillTemps, vecFreeRegisters); - - for (int index = RegistersCount * 2; index < _intervals.Count; index++) - { - LiveInterval current = _intervals[index]; - - while (activeIntervals.Count != 0 && - activeIntervals[activeIntervals.Count - 1].GetEnd() < current.GetStart()) - { - int iIndex = activeIntervals.Count - 1; - - LiveInterval interval = activeIntervals[iIndex]; - - if (interval.Register.Type == RegisterType.Integer) - { - intFreeRegisters |= 1 << interval.Register.Index; - } - else /* if (interval.Register.Type == RegisterType.Vector) */ - { - vecFreeRegisters |= 1 << interval.Register.Index; - } - - activeIntervals.RemoveAt(iIndex); - } - - Operand local = current.Local; - - bool localIsInteger = local.Type.IsInteger(); - - int freeMask = localIsInteger ? intFreeRegisters : vecFreeRegisters; - - if (freeMask != 0) - { - List fixedIntervalsForType = fixedIntervals[localIsInteger ? 0 : 1]; - - for (int iIndex = 0; iIndex < fixedIntervalsForType.Count; iIndex++) - { - LiveInterval interval = fixedIntervalsForType[iIndex]; - - if (interval.GetStart() >= current.GetEnd()) - { - break; - } - - if (interval.Overlaps(current)) - { - freeMask &= ~(1 << interval.Register.Index); - } - } - } - - if (freeMask != 0) - { - int selectedReg = BitUtils.LowestBitSet(freeMask); - - current.Register = new Register(selectedReg, local.Type.ToRegisterType()); - - int regMask = 1 << selectedReg; - - if (localIsInteger) - { - intUsedRegisters |= regMask; - intFreeRegisters &= ~regMask; - } - else - { - vecUsedRegisters |= regMask; - vecFreeRegisters &= ~regMask; - } - } - else - { - // Spill the interval that will free the register for the longest - // amount of time, as long there's no interference of the current - // interval with a fixed interval using the same register. - bool hasRegisterSelected = false; - - RegisterType regType = current.Local.Type.ToRegisterType(); - - for (int iIndex = 0; iIndex < activeIntervals.Count; iIndex++) - { - LiveInterval spillCandidate = activeIntervals[iIndex]; - - if (spillCandidate.Register.Type != regType) - { - continue; - } - - LiveInterval fixedInterval = _intervals[GetRegisterId(spillCandidate.Register)]; - - if (fixedInterval.IsEmpty || !fixedInterval.Overlaps(current)) - { - current.Register = spillCandidate.Register; - - spillCandidate.Spill(stackAlloc.Allocate(spillCandidate.Local.Type)); - - activeIntervals.RemoveAt(iIndex); - - hasRegisterSelected = true; - - break; - } - } - - Debug.Assert(hasRegisterSelected, "Failure allocating register with spill."); - } - - InsertSorted(activeIntervals, current, comparer); - } - - for (int index = RegistersCount * 2; index < _intervals.Count; index++) - { - LiveInterval interval = _intervals[index]; - - if (interval.IsSpilled) - { - ReplaceLocalWithSpill(interval, ref intUsedRegisters, ref vecUsedRegisters); - } - else - { - ReplaceLocalWithRegister(interval); - } - } - - return new AllocationResult(intUsedRegisters, vecUsedRegisters, stackAlloc.TotalSize); - } - - private int ReserveSpillTemps(ref int tempsMask, int availableRegs) - { - for (int index = 0; index < MaxIROperands; index++) - { - int selectedReg = BitUtils.HighestBitSet(availableRegs); - - tempsMask |= 1 << selectedReg; - - availableRegs &= ~(1 << selectedReg); - } - - return availableRegs; - } - - private void ReplaceLocalWithRegister(LiveInterval interval) - { - Operand register = GetRegister(interval); - - foreach (int usePosition in interval.UsePositions()) - { - Node operation = GetOperationInfo(usePosition).Node.Value; - - for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) - { - Operand source = operation.GetSource(srcIndex); - - if (source == interval.Local) - { - operation.SetSource(srcIndex, register); - } - } - - if (operation.Destination == interval.Local) - { - operation.Destination = register; - } - } - } - - private static Operand GetRegister(LiveInterval interval) - { - Debug.Assert(!interval.IsSpilled, "Spilled intervals are not allowed."); - - return new Operand( - interval.Register.Index, - interval.Register.Type, - interval.Local.Type); - } - - private void ReplaceLocalWithSpill( - LiveInterval interval, - ref int intUsedRegisters, - ref int vecUsedRegisters) - { - Operand local = interval.Local; - - int spillOffset = interval.SpillOffset; - - foreach (int usePosition in interval.UsePositions()) - { - OperationInfo info = GetOperationInfo(usePosition); - - int tempReg = GetSpillTemp(info, local.Type); - - if (local.Type.IsInteger()) - { - intUsedRegisters |= 1 << tempReg; - } - else - { - vecUsedRegisters |= 1 << tempReg; - } - - Operand temp = new Operand(tempReg, local.Type.ToRegisterType(), local.Type); - - LinkedListNode node = info.Node; - - Node operation = node.Value; - - for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) - { - Operand source = operation.GetSource(srcIndex); - - if (source == local) - { - Operation fillOp = new Operation(Instruction.Fill, temp, Const(spillOffset)); - - node.List.AddBefore(node, fillOp); - - operation.SetSource(srcIndex, temp); - } - } - - if (operation.Destination == local) - { - Operation spillOp = new Operation(Instruction.Spill, null, Const(spillOffset), temp); - - node.List.AddAfter(node, spillOp); - - operation.Destination = temp; - } - } - } - - private OperationInfo GetOperationInfo(int position) - { - return _operationNodes[position / InstructionGap]; - } - - private int GetSpillTemp(OperationInfo info, OperandType type) - { - int selectedReg; - - if (type.IsInteger()) - { - selectedReg = BitUtils.LowestBitSet(_intSpillTemps & ~info.IntSpillUsedRegisters); - - info.IntSpillUsedRegisters |= 1 << selectedReg; - } - else - { - selectedReg = BitUtils.LowestBitSet(_vecSpillTemps & ~info.VecSpillUsedRegisters); - - info.VecSpillUsedRegisters |= 1 << selectedReg; - } - - Debug.Assert(selectedReg != -1, "Out of spill temporary registers. " + (info.Node.Value as Operation).Instruction); - - return selectedReg; - } - - private static void InsertSorted( - List list, - LiveInterval interval, - IComparer comparer = null) - { - int insertIndex = list.BinarySearch(interval, comparer); - - if (insertIndex < 0) - { - insertIndex = ~insertIndex; - } - - list.Insert(insertIndex, interval); - } - - private void BuildIntervals(ControlFlowGraph cfg, RegisterMasks masks) - { - _operationNodes = new List(); - - _intervals = new List(); - - for (int index = 0; index < RegistersCount; index++) - { - _intervals.Add(new LiveInterval(new Register(index, RegisterType.Integer))); - _intervals.Add(new LiveInterval(new Register(index, RegisterType.Vector))); - } - - HashSet visited = new HashSet(); - - LiveInterval GetOrAddInterval(Operand operand) - { - LiveInterval interval; - - if (visited.Add(operand)) - { - operand.NumberLocal(_intervals.Count); - - interval = new LiveInterval(operand); - - _intervals.Add(interval); - } - else - { - interval = _intervals[GetOperandId(operand)]; - } - - return interval; - } - - int[] blockStarts = new int[cfg.Blocks.Count]; - - int operationPos = 0; - - List backwardsBranches = new List(); - - for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--) - { - BasicBlock block = cfg.PostOrderBlocks[index]; - - blockStarts[block.Index] = operationPos; - - for (LinkedListNode node = block.Operations.First; node != null; node = node.Next) - { - _operationNodes.Add(new OperationInfo(node)); - - Operation operation = node.Value as Operation; - - // Note: For fixed intervals, we must process sources first, in - // order to extend the live range of the fixed interval to the last - // use, in case the register is both used and assigned on the same - // instruction. - for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) - { - Operand source = operation.GetSource(srcIndex); - - if (source.Kind == OperandKind.LocalVariable) - { - LiveInterval interval = GetOrAddInterval(source); - - Debug.Assert(!interval.IsEmpty, "Interval is empty."); - - interval.SetEnd(operationPos + 1); - interval.AddUsePosition(operationPos); - } - else if (source.Kind == OperandKind.Register) - { - int iIndex = GetRegisterId(source.GetRegister()); - - LiveInterval interval = _intervals[iIndex]; - - if (interval.IsEmpty) - { - interval.SetStart(operationPos + 1); - } - else if (interval.GetEnd() < operationPos + 1) - { - interval.SetEnd(operationPos + 1); - } - } - } - - Operand dest = operation.Destination; - - if (dest != null) - { - if (dest.Kind == OperandKind.LocalVariable) - { - LiveInterval interval = GetOrAddInterval(dest); - - if (interval.IsEmpty) - { - interval.SetStart(operationPos + 1); - } - - interval.AddUsePosition(operationPos); - } - else if (dest.Kind == OperandKind.Register) - { - int iIndex = GetRegisterId(dest.GetRegister()); - - _intervals[iIndex].AddRange(operationPos + 1, operationPos + InstructionGap); - } - } - - if (operation.Instruction == Instruction.Call) - { - AddIntervalCallerSavedReg(masks.IntCallerSavedRegisters, operationPos, RegisterType.Integer); - AddIntervalCallerSavedReg(masks.VecCallerSavedRegisters, operationPos, RegisterType.Vector); - } - - operationPos += InstructionGap; - } - - foreach (BasicBlock successor in Successors(block)) - { - int branchIndex = cfg.PostOrderMap[block.Index]; - int targetIndex = cfg.PostOrderMap[successor.Index]; - - // Is the branch jumping backwards? - if (targetIndex >= branchIndex) - { - int targetPos = blockStarts[successor.Index]; - - backwardsBranches.Add(new LiveRange(targetPos, operationPos)); - } - } - } - - foreach (LiveRange backwardBranch in backwardsBranches) - { - for (int iIndex = RegistersCount * 2; iIndex < _intervals.Count; iIndex++) - { - LiveInterval interval = _intervals[iIndex]; - - int start = interval.GetStart(); - int end = interval.GetEnd(); - - if (backwardBranch.Start >= start && backwardBranch.Start < end) - { - if (interval.GetEnd() < backwardBranch.End) - { - interval.SetEnd(backwardBranch.End); - } - } - - if (start > backwardBranch.Start) - { - break; - } - } - } - } - - private void AddIntervalCallerSavedReg(int mask, int operationPos, RegisterType regType) - { - while (mask != 0) - { - int regIndex = BitUtils.LowestBitSet(mask); - - Register callerSavedReg = new Register(regIndex, regType); - - int rIndex = GetRegisterId(callerSavedReg); - - _intervals[rIndex].AddRange(operationPos + 1, operationPos + InstructionGap); - - mask &= ~(1 << regIndex); - } - } - - private static int GetOperandId(Operand operand) - { - if (operand.Kind == OperandKind.LocalVariable) - { - return operand.AsInt32(); - } - else if (operand.Kind == OperandKind.Register) - { - return GetRegisterId(operand.GetRegister()); - } - else - { - throw new ArgumentException($"Invalid operand kind \"{operand.Kind}\"."); - } - } - - private static int GetRegisterId(Register register) - { - return (register.Index << 1) | (register.Type == RegisterType.Vector ? 1 : 0); - } - - private static IEnumerable Successors(BasicBlock block) - { - if (block.Next != null) - { - yield return block.Next; - } - - if (block.Branch != null) - { - yield return block.Branch; - } - } - } -} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs b/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs new file mode 100644 index 0000000000..4eba78ae26 --- /dev/null +++ b/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs @@ -0,0 +1,416 @@ +using ARMeilleure.Common; +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +using static ARMeilleure.IntermediateRepresentation.OperandHelper; + +namespace ARMeilleure.CodeGen.RegisterAllocators +{ + class HybridAllocator : IRegisterAllocator + { + private const int RegistersCount = 16; + private const int MaxIROperands = 4; + + private struct BlockInfo + { + public bool HasCall; + public int IntFixedRegisters; + public int VecFixedRegisters; + } + + private class LocalInfo + { + public int Uses { get; set; } + public int UseCount { get; set; } + + public bool PreAllocated { get; set; } + public int Register { get; set; } + public int SpillOffset { get; set; } + + public int Sequence { get; set; } + + public Operand Temp { get; set; } + + public OperandType Type { get; } + + private int _first; + private int _last; + + public bool IsBlockLocal => _first == _last; + + public LocalInfo(OperandType type, int uses) + { + Uses = uses; + Type = type; + + _first = -1; + _last = -1; + } + + public void SetBlockIndex(int blkIndex) + { + if (_first == -1 || blkIndex < _first) + { + _first = blkIndex; + } + + if (_last == -1 || blkIndex > _last) + { + _last = blkIndex; + } + } + } + + public AllocationResult RunPass( + ControlFlowGraph cfg, + StackAllocator stackAlloc, + RegisterMasks regMasks) + { + int intUsedRegisters = 0; + int vecUsedRegisters = 0; + + int intFreeRegisters = regMasks.IntAvailableRegisters; + int vecFreeRegisters = regMasks.VecAvailableRegisters; + + BlockInfo[] blockInfo = new BlockInfo[cfg.Blocks.Count]; + + List locInfo = new List(); + + for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--) + { + BasicBlock block = cfg.PostOrderBlocks[index]; + + int intFixedRegisters = 0; + int vecFixedRegisters = 0; + + bool hasCall = false; + + foreach (Node node in block.Operations) + { + if (node is Operation operation && operation.Instruction == Instruction.Call) + { + hasCall = true; + } + + for (int srcIndex = 0; srcIndex < node.SourcesCount; srcIndex++) + { + Operand source = node.GetSource(srcIndex); + + if (source.Kind == OperandKind.LocalVariable) + { + locInfo[source.AsInt32() - 1].SetBlockIndex(block.Index); + } + } + + for (int dstIndex = 0; dstIndex < node.DestinationsCount; dstIndex++) + { + Operand dest = node.GetDestination(dstIndex); + + if (dest.Kind == OperandKind.LocalVariable) + { + LocalInfo info; + + if (dest.Value != 0) + { + info = locInfo[dest.AsInt32() - 1]; + } + else + { + dest.NumberLocal(locInfo.Count + 1); + + info = new LocalInfo(dest.Type, UsesCount(dest)); + + locInfo.Add(info); + } + + info.SetBlockIndex(block.Index); + } + else if (dest.Kind == OperandKind.Register) + { + if (dest.Type.IsInteger()) + { + intFixedRegisters |= 1 << dest.GetRegister().Index; + } + else + { + vecFixedRegisters |= 1 << dest.GetRegister().Index; + } + } + } + } + + blockInfo[block.Index] = new BlockInfo() + { + HasCall = hasCall, + IntFixedRegisters = intFixedRegisters, + VecFixedRegisters = vecFixedRegisters + }; + } + + int intReservedCount = 0; + int vecReservedCount = 0; + + foreach (LocalInfo info in locInfo.OrderByDescending(x => x.Uses)) + { + if (info.Type.IsInteger() && intReservedCount < 7) + { + int selectedReg = BitUtils.HighestBitSet(intFreeRegisters & ~regMasks.IntCallerSavedRegisters); + + int mask = 1 << selectedReg; + + intFreeRegisters &= ~mask; + intUsedRegisters |= mask; + + info.PreAllocated = true; + info.Register = selectedReg; + + intReservedCount++; + } + else if (!info.Type.IsInteger() && vecReservedCount < 7) + { + int selectedReg = BitUtils.HighestBitSet(vecFreeRegisters & ~regMasks.VecCallerSavedRegisters); + + int mask = 1 << selectedReg; + + vecFreeRegisters &= ~mask; + vecUsedRegisters |= mask; + + info.PreAllocated = true; + info.Register = selectedReg; + + vecReservedCount++; + } + + if (intReservedCount + vecReservedCount == 14) + { + break; + } + } + + int sequence = 0; + + for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--) + { + BasicBlock block = cfg.PostOrderBlocks[index]; + + BlockInfo blkInfo = blockInfo[block.Index]; + + int intLocalFreeRegisters = intFreeRegisters & ~blkInfo.IntFixedRegisters; + int vecLocalFreeRegisters = vecFreeRegisters & ~blkInfo.VecFixedRegisters; + + int intCallerSavedRegisters = blkInfo.HasCall ? regMasks.IntCallerSavedRegisters : 0; + int vecCallerSavedRegisters = blkInfo.HasCall ? regMasks.VecCallerSavedRegisters : 0; + + int intSpillTempRegisters = SelectSpillTemps(intCallerSavedRegisters, intLocalFreeRegisters); + int vecSpillTempRegisters = SelectSpillTemps(vecCallerSavedRegisters, vecLocalFreeRegisters); + + intLocalFreeRegisters &= ~(intSpillTempRegisters | intCallerSavedRegisters); + vecLocalFreeRegisters &= ~(vecSpillTempRegisters | vecCallerSavedRegisters); + + for (LinkedListNode llNode = block.Operations.First; llNode != null; llNode = llNode.Next) + { + Node node = llNode.Value; + + int intLocalUse = 0; + int vecLocalUse = 0; + + for (int srcIndex = 0; srcIndex < node.SourcesCount; srcIndex++) + { + Operand source = node.GetSource(srcIndex); + + if (source.Kind != OperandKind.LocalVariable) + { + continue; + } + + LocalInfo info = locInfo[source.AsInt32() - 1]; + + info.UseCount++; + + Debug.Assert(info.UseCount <= info.Uses); + + if (info.Register != -1) + { + node.SetSource(srcIndex, Register(info.Register, source.Type.ToRegisterType(), source.Type)); + + if (info.UseCount == info.Uses && !info.PreAllocated) + { + if (source.Type.IsInteger()) + { + intLocalFreeRegisters |= 1 << info.Register; + } + else + { + vecLocalFreeRegisters |= 1 << info.Register; + } + } + } + else + { + Operand temp = info.Temp; + + if (temp == null || info.Sequence != sequence) + { + temp = source.Type.IsInteger() + ? GetSpillTemp(source, intSpillTempRegisters, ref intLocalUse) + : GetSpillTemp(source, vecSpillTempRegisters, ref vecLocalUse); + + info.Sequence = sequence; + info.Temp = temp; + } + + node.SetSource(srcIndex, temp); + + Operation fillOp = new Operation(Instruction.Fill, temp, Const(info.SpillOffset)); + + block.Operations.AddBefore(llNode, fillOp); + } + } + + int intLocalAsg = 0; + int vecLocalAsg = 0; + + for (int dstIndex = 0; dstIndex < node.DestinationsCount; dstIndex++) + { + Operand dest = node.GetDestination(dstIndex); + + if (dest.Kind != OperandKind.LocalVariable) + { + continue; + } + + LocalInfo info = locInfo[dest.AsInt32() - 1]; + + if (info.UseCount == 0 && !info.PreAllocated) + { + int mask = dest.Type.IsInteger() + ? intLocalFreeRegisters + : vecLocalFreeRegisters; + + if (info.IsBlockLocal && mask != 0) + { + int selectedReg = BitUtils.LowestBitSet(mask); + + info.Register = selectedReg; + + if (dest.Type.IsInteger()) + { + intLocalFreeRegisters &= ~(1 << selectedReg); + intUsedRegisters |= 1 << selectedReg; + } + else + { + vecLocalFreeRegisters &= ~(1 << selectedReg); + vecUsedRegisters |= 1 << selectedReg; + } + } + else + { + info.Register = -1; + info.SpillOffset = stackAlloc.Allocate(dest.Type.GetSizeInBytes()); + } + } + + info.UseCount++; + + Debug.Assert(info.UseCount <= info.Uses); + + if (info.Register != -1) + { + node.SetDestination(dstIndex, Register(info.Register, dest.Type.ToRegisterType(), dest.Type)); + } + else + { + Operand temp = info.Temp; + + if (temp == null || info.Sequence != sequence) + { + temp = dest.Type.IsInteger() + ? GetSpillTemp(dest, intSpillTempRegisters, ref intLocalAsg) + : GetSpillTemp(dest, vecSpillTempRegisters, ref vecLocalAsg); + + info.Sequence = sequence; + info.Temp = temp; + } + + node.SetDestination(dstIndex, temp); + + Operation spillOp = new Operation(Instruction.Spill, null, Const(info.SpillOffset), temp); + + llNode = block.Operations.AddAfter(llNode, spillOp); + } + } + + sequence++; + + intUsedRegisters |= intLocalAsg | intLocalUse; + vecUsedRegisters |= vecLocalAsg | vecLocalUse; + } + } + + return new AllocationResult(intUsedRegisters, vecUsedRegisters, stackAlloc.TotalSize); + } + + private static int SelectSpillTemps(int mask0, int mask1) + { + int selection = 0; + int count = 0; + + while (count < MaxIROperands && mask0 != 0) + { + int mask = mask0 & -mask0; + + selection |= mask; + + mask0 &= ~mask; + + count++; + } + + while (count < MaxIROperands && mask1 != 0) + { + int mask = mask1 & -mask1; + + selection |= mask; + + mask1 &= ~mask; + + count++; + } + + Debug.Assert(count == MaxIROperands, "No enough registers for spill temps."); + + return selection; + } + + private static Operand GetSpillTemp(Operand local, int freeMask, ref int useMask) + { + int selectedReg = BitUtils.LowestBitSet(freeMask & ~useMask); + + useMask |= 1 << selectedReg; + + return Register(selectedReg, local.Type.ToRegisterType(), local.Type); + } + + private static int UsesCount(Operand local) + { + return local.Assignments.Count + local.Uses.Count; + } + + private static IEnumerable Successors(BasicBlock block) + { + if (block.Next != null) + { + yield return block.Next; + } + + if (block.Branch != null) + { + yield return block.Branch; + } + } + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs b/ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs new file mode 100644 index 0000000000..8f236c2533 --- /dev/null +++ b/ARMeilleure/CodeGen/RegisterAllocators/IRegisterAllocator.cs @@ -0,0 +1,12 @@ +using ARMeilleure.Translation; + +namespace ARMeilleure.CodeGen.RegisterAllocators +{ + interface IRegisterAllocator + { + AllocationResult RunPass( + ControlFlowGraph cfg, + StackAllocator stackAlloc, + RegisterMasks regMasks); + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LinearScan.cs b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs similarity index 99% rename from ARMeilleure/CodeGen/RegisterAllocators/LinearScan.cs rename to ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs index ae5a6edfdd..4eec2eca37 100644 --- a/ARMeilleure/CodeGen/RegisterAllocators/LinearScan.cs +++ b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs @@ -11,7 +11,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators // Based on: // "Linear Scan Register Allocation for the Java(tm) HotSpot Client Compiler". // http://www.christianwimmer.at/Publications/Wimmer04a/Wimmer04a.pdf - class LinearScan + class LinearScanAllocator : IRegisterAllocator { private const int InstructionGap = 2; private const int InstructionGapMask = InstructionGap - 1; @@ -71,7 +71,10 @@ namespace ARMeilleure.CodeGen.RegisterAllocators } } - public AllocationResult RunPass(ControlFlowGraph cfg, StackAllocator stackAlloc, RegisterMasks regMasks) + public AllocationResult RunPass( + ControlFlowGraph cfg, + StackAllocator stackAlloc, + RegisterMasks regMasks) { NumberLocals(cfg); diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs index 89c302f98e..d13dab8409 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -103,7 +103,11 @@ namespace ARMeilleure.CodeGen.X86 Logger.StartPass(PassName.Optimization); - Optimizer.RunPass(cfg); + if ((cctx.Options & CompilerOptions.SsaForm) != 0 && + (cctx.Options & CompilerOptions.Optimize) != 0) + { + Optimizer.RunPass(cfg); + } Logger.EndPass(PassName.Optimization, cfg); @@ -117,9 +121,21 @@ namespace ARMeilleure.CodeGen.X86 Logger.StartPass(PassName.RegisterAllocation); - Ssa.Deconstruct(cfg); + if ((cctx.Options & CompilerOptions.SsaForm) != 0) + { + Ssa.Deconstruct(cfg); + } - LinearScan regAlloc = new LinearScan(); + IRegisterAllocator regAlloc; + + if ((cctx.Options & CompilerOptions.Lsra) != 0) + { + regAlloc = new LinearScanAllocator(); + } + else + { + regAlloc = new HybridAllocator(); + } RegisterMasks regMasks = new RegisterMasks( CallingConvention.GetIntAvailableRegisters(), diff --git a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs index a5951de734..7f930d6b9c 100644 --- a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs +++ b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs @@ -40,7 +40,11 @@ namespace ARMeilleure.CodeGen.X86 OperandType[] argTypes = new OperandType[0]; - GetFeatureInfo getFeatureInfo = Compiler.Compile(cfg, argTypes, OperandType.I64); + GetFeatureInfo getFeatureInfo = Compiler.Compile( + cfg, + argTypes, + OperandType.I64, + CompilerOptions.HighCq); _featureInfo = getFeatureInfo(); } diff --git a/ARMeilleure/CodeGen/X86/PreAllocator.cs b/ARMeilleure/CodeGen/X86/PreAllocator.cs index ccfea24b86..12425ffecc 100644 --- a/ARMeilleure/CodeGen/X86/PreAllocator.cs +++ b/ARMeilleure/CodeGen/X86/PreAllocator.cs @@ -374,6 +374,12 @@ namespace ARMeilleure.CodeGen.X86 if (IsSameOperandDestSrc1(operation) && src1.Kind == OperandKind.LocalVariable && !threeOperandForm) { + // FIXME: We should support the same variable as dest being used on sources. + for (int srcIndex = 1; srcIndex < operation.SourcesCount; srcIndex++) + { + Debug.Assert(operation.GetSource(srcIndex) == dest); + } + Operation copyOp = new Operation(Instruction.Copy, dest, src1); node.List.AddBefore(node, copyOp); @@ -382,8 +388,12 @@ namespace ARMeilleure.CodeGen.X86 } else if (inst == Instruction.ConditionalSelect) { + Operand src2 = operation.GetSource(1); Operand src3 = operation.GetSource(2); + // FIXME: We should support the same variable as dest being used on sources. + Debug.Assert(src1 == dest || src2 == dest); + Operation copyOp = new Operation(Instruction.Copy, dest, src3); node.List.AddBefore(node, copyOp); diff --git a/ARMeilleure/Instructions/InstEmitFlow.cs b/ARMeilleure/Instructions/InstEmitFlow.cs index d9e770755a..93d36e1b94 100644 --- a/ARMeilleure/Instructions/InstEmitFlow.cs +++ b/ARMeilleure/Instructions/InstEmitFlow.cs @@ -71,7 +71,7 @@ namespace ARMeilleure.Instructions public static void Ret(ArmEmitterContext context) { - context.Return(GetIntOrZR(context, RegisterAlias.Lr)); + context.Return(context.BitwiseOr(GetIntOrZR(context, RegisterAlias.Lr), Const(CallFlag))); } public static void Tbnz(ArmEmitterContext context) => EmitTb(context, onNotZero: true); diff --git a/ARMeilleure/Instructions/InstEmitFlowHelper.cs b/ARMeilleure/Instructions/InstEmitFlowHelper.cs index f32001c6bc..a8eb21d33f 100644 --- a/ARMeilleure/Instructions/InstEmitFlowHelper.cs +++ b/ARMeilleure/Instructions/InstEmitFlowHelper.cs @@ -10,6 +10,8 @@ namespace ARMeilleure.Instructions { static class InstEmitFlowHelper { + public const ulong CallFlag = 1; + public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond) { if (cond != Condition.Al) @@ -140,7 +142,7 @@ namespace ARMeilleure.Instructions public static void EmitCall(ArmEmitterContext context, ulong immediate) { - context.Return(Const(immediate)); + context.Return(Const(immediate | CallFlag)); } public static void EmitVirtualCall(ArmEmitterContext context, Operand target) @@ -155,7 +157,7 @@ namespace ARMeilleure.Instructions private static void EmitVirtualCallOrJump(ArmEmitterContext context, Operand target, bool isJump) { - context.Return(target); + context.Return(context.BitwiseOr(target, Const(target.Type, (long)CallFlag))); } private static void EmitContinueOrReturnCheck(ArmEmitterContext context, Operand retVal) diff --git a/ARMeilleure/Instructions/InstEmitSimdMove.cs b/ARMeilleure/Instructions/InstEmitSimdMove.cs index b366b044c6..47359161fb 100644 --- a/ARMeilleure/Instructions/InstEmitSimdMove.cs +++ b/ARMeilleure/Instructions/InstEmitSimdMove.cs @@ -49,8 +49,8 @@ namespace ARMeilleure.Instructions { switch (op.Size) { - case 0: n = context.ZeroExtend8 (n.Type, n); n = context.Multiply(n, Const(0x01010101)); break; - case 1: n = context.ZeroExtend16(n.Type, n); n = context.Multiply(n, Const(0x00010001)); break; + case 0: n = context.ZeroExtend8 (n.Type, n); n = context.Multiply(n, Const(n.Type, 0x01010101)); break; + case 1: n = context.ZeroExtend16(n.Type, n); n = context.Multiply(n, Const(n.Type, 0x00010001)); break; case 2: n = context.ZeroExtend32(n.Type, n); break; } diff --git a/ARMeilleure/Memory/MemoryManagerPal.cs b/ARMeilleure/Memory/MemoryManagerPal.cs index cda7f6dbce..64191a0acb 100644 --- a/ARMeilleure/Memory/MemoryManagerPal.cs +++ b/ARMeilleure/Memory/MemoryManagerPal.cs @@ -66,7 +66,11 @@ namespace ARMeilleure.Memory OperandType.V128 }; - _compareExchange128 = Compiler.Compile(cfg, argTypes, OperandType.V128); + _compareExchange128 = Compiler.Compile( + cfg, + argTypes, + OperandType.V128, + CompilerOptions.HighCq); } } } diff --git a/ARMeilleure/Translation/Compiler.cs b/ARMeilleure/Translation/Compiler.cs index 11dde2f45a..4075a7f069 100644 --- a/ARMeilleure/Translation/Compiler.cs +++ b/ARMeilleure/Translation/Compiler.cs @@ -12,21 +12,8 @@ namespace ARMeilleure.Translation public static T Compile( ControlFlowGraph cfg, OperandType[] funcArgTypes, - OperandType funcReturnType) - { - CompilerContext cctx = GetCompilerContext(cfg, funcArgTypes, funcReturnType); - - CompiledFunction func = CodeGenerator.Generate(cctx); - - IntPtr codePtr = JitCache.Map(func); - - return Marshal.GetDelegateForFunctionPointer(codePtr); - } - - private static CompilerContext GetCompilerContext( - ControlFlowGraph cfg, - OperandType[] funcArgTypes, - OperandType funcReturnType) + OperandType funcReturnType, + CompilerOptions options) { Logger.StartPass(PassName.Dominance); @@ -37,11 +24,24 @@ namespace ARMeilleure.Translation Logger.StartPass(PassName.SsaConstruction); - Ssa.Construct(cfg); + if ((options & CompilerOptions.SsaForm) != 0) + { + Ssa.Construct(cfg); + } + else + { + RegisterToLocal.Rename(cfg); + } Logger.EndPass(PassName.SsaConstruction, cfg); - return new CompilerContext(cfg, funcArgTypes, funcReturnType); + CompilerContext cctx = new CompilerContext(cfg, funcArgTypes, funcReturnType, options); + + CompiledFunction func = CodeGenerator.Generate(cctx); + + IntPtr codePtr = JitCache.Map(func); + + return Marshal.GetDelegateForFunctionPointer(codePtr); } } } \ No newline at end of file diff --git a/ARMeilleure/Translation/CompilerContext.cs b/ARMeilleure/Translation/CompilerContext.cs index 82bbd302e2..cfe5ad1e50 100644 --- a/ARMeilleure/Translation/CompilerContext.cs +++ b/ARMeilleure/Translation/CompilerContext.cs @@ -9,14 +9,18 @@ namespace ARMeilleure.Translation public OperandType[] FuncArgTypes { get; } public OperandType FuncReturnType { get; } + public CompilerOptions Options { get; } + public CompilerContext( ControlFlowGraph cfg, OperandType[] funcArgTypes, - OperandType funcReturnType) + OperandType funcReturnType, + CompilerOptions options) { Cfg = cfg; FuncArgTypes = funcArgTypes; FuncReturnType = funcReturnType; + Options = options; } } } \ No newline at end of file diff --git a/ARMeilleure/Translation/CompilerOptions.cs b/ARMeilleure/Translation/CompilerOptions.cs new file mode 100644 index 0000000000..53998ec6f3 --- /dev/null +++ b/ARMeilleure/Translation/CompilerOptions.cs @@ -0,0 +1,16 @@ +using System; + +namespace ARMeilleure.Translation +{ + [Flags] + enum CompilerOptions + { + None = 0, + SsaForm = 1 << 0, + Optimize = 1 << 1, + Lsra = 1 << 2, + + MediumCq = SsaForm | Optimize, + HighCq = SsaForm | Optimize | Lsra + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PriorityQueue.cs b/ARMeilleure/Translation/PriorityQueue.cs new file mode 100644 index 0000000000..ab593dc07f --- /dev/null +++ b/ARMeilleure/Translation/PriorityQueue.cs @@ -0,0 +1,39 @@ +using System.Collections.Concurrent; + +namespace ARMeilleure.Translation +{ + class PriorityQueue + { + private ConcurrentQueue[] _queues; + + public PriorityQueue(int priorities) + { + _queues = new ConcurrentQueue[priorities]; + + for (int index = 0; index < priorities; index++) + { + _queues[index] = new ConcurrentQueue(); + } + } + + public void Enqueue(int priority, T value) + { + _queues[priority].Enqueue(value); + } + + public bool TryDequeue(out T value) + { + for (int index = 0; index < _queues.Length; index++) + { + if (_queues[index].TryDequeue(out value)) + { + return true; + } + } + + value = default(T); + + return false; + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/RegisterToLocal.cs b/ARMeilleure/Translation/RegisterToLocal.cs new file mode 100644 index 0000000000..aa91801824 --- /dev/null +++ b/ARMeilleure/Translation/RegisterToLocal.cs @@ -0,0 +1,52 @@ +using ARMeilleure.IntermediateRepresentation; +using System.Collections.Generic; + +using static ARMeilleure.IntermediateRepresentation.OperandHelper; + +namespace ARMeilleure.Translation +{ + static class RegisterToLocal + { + public static void Rename(ControlFlowGraph cfg) + { + Dictionary registerToLocalMap = new Dictionary(); + + Operand GetLocal(Operand op) + { + Register register = op.GetRegister(); + + if (!registerToLocalMap.TryGetValue(register, out Operand local)) + { + local = Local(op.Type); + + registerToLocalMap.Add(register, local); + } + + return local; + } + + foreach (BasicBlock block in cfg.Blocks) + { + foreach (Node node in block.Operations) + { + Operand dest = node.Destination; + + if (dest != null && dest.Kind == OperandKind.Register) + { + node.Destination = GetLocal(dest); + } + + for (int index = 0; index < node.SourcesCount; index++) + { + Operand source = node.GetSource(index); + + if (source.Kind == OperandKind.Register) + { + node.SetSource(index, GetLocal(source)); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/RegisterUsage.cs b/ARMeilleure/Translation/RegisterUsage.cs index e1f9030602..4164786b90 100644 --- a/ARMeilleure/Translation/RegisterUsage.cs +++ b/ARMeilleure/Translation/RegisterUsage.cs @@ -68,7 +68,7 @@ namespace ARMeilleure.Translation } } - public static void RunPass(ControlFlowGraph cfg) + public static void RunPass(ControlFlowGraph cfg, bool isCompleteFunction) { // Compute local register inputs and outputs used inside blocks. RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count]; @@ -218,8 +218,8 @@ namespace ARMeilleure.Translation if (EndsWithReturn(block) || hasContextStore) { - StoreLocals(block, globalOutputs[block.Index].IntMask, RegisterType.Integer); - StoreLocals(block, globalOutputs[block.Index].VecMask, RegisterType.Vector); + StoreLocals(block, globalOutputs[block.Index].IntMask, RegisterType.Integer, isCompleteFunction); + StoreLocals(block, globalOutputs[block.Index].VecMask, RegisterType.Vector, isCompleteFunction); } } } @@ -311,9 +311,9 @@ namespace ARMeilleure.Translation block.Operations.AddFirst(loadArg0); } - private static void StoreLocals(BasicBlock block, long outputs, RegisterType baseType) + private static void StoreLocals(BasicBlock block, long outputs, RegisterType baseType, bool isCompleteFunction) { - if (Optimizations.AssumeStrictAbiCompliance) + if (Optimizations.AssumeStrictAbiCompliance && isCompleteFunction) { if (baseType == RegisterType.Integer || baseType == RegisterType.Flag) { diff --git a/ARMeilleure/Translation/SsaConstruction.cs b/ARMeilleure/Translation/SsaConstruction.cs index 2737f49cdc..ccf5259154 100644 --- a/ARMeilleure/Translation/SsaConstruction.cs +++ b/ARMeilleure/Translation/SsaConstruction.cs @@ -284,7 +284,7 @@ namespace ARMeilleure.Translation { return new Register(id - RegisterConsts.IntRegsCount, RegisterType.Vector); } - else /* if (key < RegisterConsts.TotalCount) */ + else /* if (id < RegisterConsts.TotalCount) */ { return new Register(id - RegisterConsts.IntAndVecRegsCount, RegisterType.Flag); } diff --git a/ARMeilleure/Translation/TranslatedFunction.cs b/ARMeilleure/Translation/TranslatedFunction.cs index 9bb7ce0673..06069cf8fe 100644 --- a/ARMeilleure/Translation/TranslatedFunction.cs +++ b/ARMeilleure/Translation/TranslatedFunction.cs @@ -1,19 +1,30 @@ -using ARMeilleure.State; +using System.Threading; namespace ARMeilleure.Translation { class TranslatedFunction { + private const int MinCallsForRejit = 100; + private GuestFunction _func; - public TranslatedFunction(GuestFunction func) + private bool _rejit; + private int _callCount; + + public TranslatedFunction(GuestFunction func, bool rejit) { - _func = func; + _func = func; + _rejit = rejit; } - public ulong Execute(ExecutionContext context) + public ulong Execute(State.ExecutionContext context) { return _func(context.NativeContextPtr); } + + public bool ShouldRejit() + { + return _rejit && Interlocked.Increment(ref _callCount) == MinCallsForRejit; + } } } \ No newline at end of file diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 0b5549b381..8ef7532370 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -6,6 +6,7 @@ using ARMeilleure.Memory; using ARMeilleure.State; using System; using System.Collections.Concurrent; +using System.Threading; using static ARMeilleure.IntermediateRepresentation.OperandHelper; @@ -13,19 +14,56 @@ namespace ARMeilleure.Translation { public class Translator { + private const ulong CallFlag = InstEmitFlowHelper.CallFlag; + private MemoryManager _memory; private ConcurrentDictionary _funcs; + private PriorityQueue _backgroundQueue; + + private AutoResetEvent _backgroundTranslatorEvent; + + private volatile int _threadCount; + public Translator(MemoryManager memory) { _memory = memory; _funcs = new ConcurrentDictionary(); + + _backgroundQueue = new PriorityQueue(2); + + _backgroundTranslatorEvent = new AutoResetEvent(false); } - public void Execute(ExecutionContext context, ulong address) + private void TranslateQueuedSubs() { + while (_threadCount != 0) + { + if (_backgroundQueue.TryDequeue(out ulong address)) + { + TranslatedFunction func = Translate(address, ExecutionMode.Aarch64, highCq: true); + + _funcs.AddOrUpdate(address, func, (key, oldFunc) => func); + } + else + { + _backgroundTranslatorEvent.WaitOne(); + } + } + } + + public void Execute(State.ExecutionContext context, ulong address) + { + if (Interlocked.Increment(ref _threadCount) == 1) + { + Thread backgroundTranslatorThread = new Thread(TranslateQueuedSubs); + + backgroundTranslatorThread.Priority = ThreadPriority.Lowest; + backgroundTranslatorThread.Start(); + } + Statistics.InitializeTimer(); NativeInterface.RegisterThread(context, _memory); @@ -37,9 +75,14 @@ namespace ARMeilleure.Translation while (context.Running && address != 0); NativeInterface.UnregisterThread(); + + if (Interlocked.Decrement(ref _threadCount) == 0) + { + _backgroundTranslatorEvent.Set(); + } } - public ulong ExecuteSingle(ExecutionContext context, ulong address) + public ulong ExecuteSingle(State.ExecutionContext context, ulong address) { TranslatedFunction func = GetOrTranslate(address, context.ExecutionMode); @@ -54,23 +97,37 @@ namespace ARMeilleure.Translation private TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode) { + // TODO: Investigate how we should handle code at unaligned addresses. + // Currently, those low bits are used to store special flags. + bool isCallTarget = (address & CallFlag) != 0; + + address &= ~CallFlag; + if (!_funcs.TryGetValue(address, out TranslatedFunction func)) { - func = Translate(address, mode); + func = Translate(address, mode, highCq: false); _funcs.TryAdd(address, func); } + else if (isCallTarget && func.ShouldRejit()) + { + _backgroundQueue.Enqueue(0, address); + + _backgroundTranslatorEvent.Set(); + } return func; } - private TranslatedFunction Translate(ulong address, ExecutionMode mode) + private TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq) { ArmEmitterContext context = new ArmEmitterContext(_memory, Aarch32Mode.User); Logger.StartPass(PassName.Decoding); - Block[] blocks = Decoder.DecodeFunction(_memory, address, mode); + Block[] blocks = highCq + ? Decoder.DecodeFunction (_memory, address, mode) + : Decoder.DecodeBasicBlock(_memory, address, mode); Logger.EndPass(PassName.Decoding); @@ -89,15 +146,19 @@ namespace ARMeilleure.Translation Logger.StartPass(PassName.RegisterUsage); - RegisterUsage.RunPass(cfg); + RegisterUsage.RunPass(cfg, isCompleteFunction: false); Logger.EndPass(PassName.RegisterUsage); OperandType[] argTypes = new OperandType[] { OperandType.I64 }; - GuestFunction func = Compiler.Compile(cfg, argTypes, OperandType.I64); + CompilerOptions options = highCq + ? CompilerOptions.HighCq + : CompilerOptions.None; - return new TranslatedFunction(func); + GuestFunction func = Compiler.Compile(cfg, argTypes, OperandType.I64, options); + + return new TranslatedFunction(func, rejit: !highCq); } private static ControlFlowGraph EmitAndGetCFG(ArmEmitterContext context, Block[] blocks)