using ARMeilleure.Common; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using System; using System.Collections.Generic; using System.Diagnostics; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; namespace ARMeilleure.Translation { static partial class Ssa { private class DefMap { private readonly Dictionary _map; private readonly BitMap _phiMasks; public DefMap() { _map = new Dictionary(); _phiMasks = new BitMap(Allocators.Default, RegisterConsts.TotalCount); } public bool TryAddOperand(int key, Operand operand) { return _map.TryAdd(key, operand); } public bool TryGetOperand(int key, out Operand operand) { return _map.TryGetValue(key, out operand); } public bool AddPhi(int key) { return _phiMasks.Set(key); } public bool HasPhi(int key) { return _phiMasks.IsSet(key); } } public static void Construct(ControlFlowGraph cfg) { var globalDefs = new DefMap[cfg.Blocks.Count]; var localDefs = new Operand[cfg.LocalsCount + RegisterConsts.TotalCount]; var dfPhiBlocks = new Queue(); for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) { globalDefs[block.Index] = new DefMap(); } // First pass, get all defs and locals uses. for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) { for (Operation node = block.Operations.First; node != default; node = node.ListNext) { for (int index = 0; index < node.SourcesCount; index++) { Operand src = node.GetSource(index); if (TryGetId(src, out int srcKey)) { Operand local = localDefs[srcKey]; if (local == default) { local = src; } node.SetSource(index, local); } } Operand dest = node.Destination; if (TryGetId(dest, out int destKey)) { Operand local = Local(dest.Type); localDefs[destKey] = local; node.Destination = local; } } for (int key = 0; key < localDefs.Length; key++) { Operand local = localDefs[key]; if (local == default) { continue; } globalDefs[block.Index].TryAddOperand(key, local); dfPhiBlocks.Enqueue(block); while (dfPhiBlocks.TryDequeue(out BasicBlock dfPhiBlock)) { foreach (BasicBlock domFrontier in dfPhiBlock.DominanceFrontiers) { if (globalDefs[domFrontier.Index].AddPhi(key)) { dfPhiBlocks.Enqueue(domFrontier); } } } } Array.Clear(localDefs); } // Second pass, rename variables with definitions on different blocks. for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) { for (Operation node = block.Operations.First; node != default; node = node.ListNext) { for (int index = 0; index < node.SourcesCount; index++) { Operand src = node.GetSource(index); if (TryGetId(src, out int key)) { Operand local = localDefs[key]; if (local == default) { local = FindDef(globalDefs, block, src); localDefs[key] = local; } node.SetSource(index, local); } } } Array.Clear(localDefs); } } private static Operand FindDef(DefMap[] globalDefs, BasicBlock current, Operand operand) { if (globalDefs[current.Index].HasPhi(GetId(operand))) { return InsertPhi(globalDefs, current, operand); } if (current != current.ImmediateDominator) { return FindDefOnPred(globalDefs, current.ImmediateDominator, operand); } return Undef(); } private static Operand FindDefOnPred(DefMap[] globalDefs, BasicBlock current, Operand operand) { BasicBlock previous; do { DefMap defMap = globalDefs[current.Index]; int key = GetId(operand); if (defMap.TryGetOperand(key, out Operand lastDef)) { return lastDef; } if (defMap.HasPhi(key)) { return InsertPhi(globalDefs, current, operand); } previous = current; current = current.ImmediateDominator; } while (previous != current); return Undef(); } private static Operand InsertPhi(DefMap[] globalDefs, BasicBlock block, Operand operand) { // This block has a Phi that has not been materialized yet, but that // would define a new version of the variable we're looking for. We need // to materialize the Phi, add all the block/operand pairs into the Phi, and // then use the definition from that Phi. Operand local = Local(operand.Type); Operation operation = Operation.Factory.PhiOperation(local, block.Predecessors.Count); AddPhi(block, operation); globalDefs[block.Index].TryAddOperand(GetId(operand), local); PhiOperation phi = operation.AsPhi(); for (int index = 0; index < block.Predecessors.Count; index++) { BasicBlock predecessor = block.Predecessors[index]; phi.SetBlock(index, predecessor); phi.SetSource(index, FindDefOnPred(globalDefs, predecessor, operand)); } return local; } private static void AddPhi(BasicBlock block, Operation phi) { Operation node = block.Operations.First; if (node != default) { while (node.ListNext != default && node.ListNext.Instruction == Instruction.Phi) { node = node.ListNext; } } if (node != default && node.Instruction == Instruction.Phi) { block.Operations.AddAfter(node, phi); } else { block.Operations.AddFirst(phi); } } private static bool TryGetId(Operand operand, out int result) { if (operand != default) { if (operand.Kind == OperandKind.Register) { Register reg = operand.GetRegister(); if (reg.Type == RegisterType.Integer) { result = reg.Index; } else if (reg.Type == RegisterType.Vector) { result = RegisterConsts.IntRegsCount + reg.Index; } else if (reg.Type == RegisterType.Flag) { result = RegisterConsts.IntAndVecRegsCount + reg.Index; } else /* if (reg.Type == RegisterType.FpFlag) */ { result = RegisterConsts.FpFlagsOffset + reg.Index; } return true; } else if (operand.Kind == OperandKind.LocalVariable && operand.GetLocalNumber() > 0) { result = RegisterConsts.TotalCount + operand.GetLocalNumber() - 1; return true; } } result = -1; return false; } private static int GetId(Operand operand) { if (!TryGetId(operand, out int key)) { Debug.Fail("OperandKind must be Register or a numbered LocalVariable."); } return key; } } }