mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
9.4 KiB
254 lines
9.4 KiB
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.Collections.ObjectModel; |
|
using System.Diagnostics; |
|
using System.Linq; |
|
|
|
using Mono.Cecil; |
|
using Mono.Cecil.Cil; |
|
|
|
namespace ICSharpCode.Decompiler.FlowAnalysis |
|
{ |
|
/// <summary> |
|
/// Convers a method to static single assignment form. |
|
/// </summary> |
|
sealed class TransformToSsa |
|
{ |
|
public static void Transform(ControlFlowGraph cfg, SsaForm ssa, bool optimize = true) |
|
{ |
|
TransformToSsa transform = new TransformToSsa(cfg, ssa); |
|
transform.ConvertVariablesToSsa(); |
|
SsaOptimization.RemoveDeadAssignments(ssa); // required so that 'MakeByRefCallsSimple' can detect more cases |
|
if (SimplifyByRefCalls.MakeByRefCallsSimple(ssa)) { |
|
transform.ConvertVariablesToSsa(); |
|
} |
|
if (optimize) |
|
SsaOptimization.Optimize(ssa); |
|
} |
|
|
|
readonly ControlFlowGraph cfg; |
|
readonly SsaForm ssaForm; |
|
readonly List<SsaInstruction>[] writeToOriginalVariables; // array index -> SsaVariable OriginalVariableIndex |
|
readonly bool[] addressTaken; // array index -> SsaVariable OriginalVariableIndex; value = whether ldloca instruction was used with variable |
|
|
|
private TransformToSsa(ControlFlowGraph cfg, SsaForm ssaForm) |
|
{ |
|
this.cfg = cfg; |
|
this.ssaForm = ssaForm; |
|
this.writeToOriginalVariables = new List<SsaInstruction>[ssaForm.OriginalVariables.Count]; |
|
this.addressTaken = new bool[ssaForm.OriginalVariables.Count]; |
|
} |
|
|
|
#region CollectInformationAboutOriginalVariableUse |
|
void CollectInformationAboutOriginalVariableUse() |
|
{ |
|
Debug.Assert(addressTaken.Length == writeToOriginalVariables.Length); |
|
for (int i = 0; i < writeToOriginalVariables.Length; i++) { |
|
Debug.Assert(ssaForm.OriginalVariables[i].OriginalVariableIndex == i); |
|
|
|
addressTaken[i] = false; |
|
// writeToOriginalVariables is only used when placing phi functions |
|
// we don't need to do that anymore for variables that are already in SSA form |
|
if (ssaForm.OriginalVariables[i].IsSingleAssignment) |
|
writeToOriginalVariables[i] = null; |
|
else |
|
writeToOriginalVariables[i] = new List<SsaInstruction>(); |
|
} |
|
foreach (SsaBlock block in ssaForm.Blocks) { |
|
foreach (SsaInstruction inst in block.Instructions) { |
|
if (inst.Target != null ) { |
|
var list = writeToOriginalVariables[inst.Target.OriginalVariableIndex]; |
|
if (list != null) |
|
list.Add(inst); |
|
} |
|
if (inst.Instruction != null) { |
|
if (inst.Instruction.OpCode == OpCodes.Ldloca) { |
|
addressTaken[ssaForm.GetOriginalVariable((VariableDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true; |
|
} else if (inst.Instruction.OpCode == OpCodes.Ldarga) { |
|
addressTaken[ssaForm.GetOriginalVariable((ParameterDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
#region ConvertToSsa |
|
void ConvertVariablesToSsa() |
|
{ |
|
CollectInformationAboutOriginalVariableUse(); |
|
bool[] processVariable = new bool[ssaForm.OriginalVariables.Count]; |
|
foreach (SsaVariable variable in ssaForm.OriginalVariables) { |
|
if (!variable.IsSingleAssignment && !addressTaken[variable.OriginalVariableIndex]) { |
|
PlacePhiFunctions(variable); |
|
processVariable[variable.OriginalVariableIndex] = true; |
|
} |
|
} |
|
RenameVariables(processVariable); |
|
foreach (SsaVariable variable in ssaForm.OriginalVariables) { |
|
if (!addressTaken[variable.OriginalVariableIndex]) { |
|
Debug.Assert(variable.IsSingleAssignment && variable.Definition != null); |
|
} |
|
} |
|
ssaForm.ComputeVariableUsage(); |
|
} |
|
#endregion |
|
|
|
#region PlacePhiFunctions |
|
void PlacePhiFunctions(SsaVariable variable) |
|
{ |
|
cfg.ResetVisited(); |
|
HashSet<SsaBlock> blocksWithPhi = new HashSet<SsaBlock>(); |
|
Queue<ControlFlowNode> worklist = new Queue<ControlFlowNode>(); |
|
foreach (SsaInstruction writeInstruction in writeToOriginalVariables[variable.OriginalVariableIndex]) { |
|
ControlFlowNode cfgNode = cfg.Nodes[writeInstruction.ParentBlock.BlockIndex]; |
|
if (!cfgNode.Visited) { |
|
cfgNode.Visited = true; |
|
worklist.Enqueue(cfgNode); |
|
} |
|
} |
|
while (worklist.Count > 0) { |
|
ControlFlowNode cfgNode = worklist.Dequeue(); |
|
foreach (ControlFlowNode dfNode in cfgNode.DominanceFrontier) { |
|
// we don't need phi functions in the exit node |
|
if (dfNode.NodeType == ControlFlowNodeType.RegularExit || dfNode.NodeType == ControlFlowNodeType.ExceptionalExit) |
|
continue; |
|
SsaBlock y = ssaForm.Blocks[dfNode.BlockIndex]; |
|
if (blocksWithPhi.Add(y)) { |
|
// add a phi instruction in y |
|
SsaVariable[] operands = Enumerable.Repeat(variable, dfNode.Incoming.Count).ToArray(); |
|
y.Instructions.Insert(0, new SsaInstruction(y, null, variable, operands, specialOpCode: SpecialOpCode.Phi)); |
|
if (!dfNode.Visited) { |
|
dfNode.Visited = true; |
|
worklist.Enqueue(dfNode); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
#region RenameVariable |
|
int tempVariableCounter = 1; |
|
|
|
void RenameVariables(bool[] processVariable) |
|
{ |
|
VariableRenamer r = new VariableRenamer(this, processVariable); |
|
r.Visit(ssaForm.EntryPoint); |
|
} |
|
|
|
sealed class VariableRenamer |
|
{ |
|
readonly TransformToSsa transform; |
|
readonly ReadOnlyCollection<SsaVariable> inputVariables; |
|
internal readonly Stack<SsaVariable>[] versionStacks; |
|
int[] versionCounters; // specifies for each input variable the next version number |
|
|
|
// processVariable = specifies for each input variable whether we should rename it |
|
public VariableRenamer(TransformToSsa transform, bool[] processVariable) |
|
{ |
|
this.transform = transform; |
|
this.inputVariables = transform.ssaForm.OriginalVariables; |
|
Debug.Assert(inputVariables.Count == processVariable.Length); |
|
this.versionCounters = new int[inputVariables.Count]; |
|
this.versionStacks = new Stack<SsaVariable>[inputVariables.Count]; |
|
for (int i = 0; i < versionStacks.Length; i++) { |
|
if (processVariable[i]) { |
|
Debug.Assert(inputVariables[i].IsSingleAssignment == false); |
|
// only create version stacks for the variables that we need to process and that weren't already processed earlier |
|
versionStacks[i] = new Stack<SsaVariable>(); |
|
versionStacks[i].Push(inputVariables[i]); |
|
} |
|
} |
|
} |
|
|
|
SsaVariable MakeNewVersion(int variableIndex) |
|
{ |
|
int versionCounter = ++versionCounters[variableIndex]; |
|
SsaVariable x = inputVariables[variableIndex]; |
|
if (versionCounter == 1) { |
|
return x; |
|
} else { |
|
if (x.IsStackLocation) { |
|
return new SsaVariable(x, "temp" + (transform.tempVariableCounter++)); |
|
} else { |
|
return new SsaVariable(x, x.Name + "_" + versionCounter); |
|
} |
|
} |
|
} |
|
|
|
internal void Visit(SsaBlock block) |
|
{ |
|
// duplicate top of all stacks |
|
foreach (var stack in versionStacks) { |
|
if (stack != null) |
|
stack.Push(stack.Peek()); |
|
} |
|
|
|
foreach (SsaInstruction s in block.Instructions) { |
|
// replace all uses of variables being processed with their current version. |
|
if (s.SpecialOpCode != SpecialOpCode.Phi) { |
|
for (int i = 0; i < s.Operands.Length; i++) { |
|
var stack = versionStacks[s.Operands[i].OriginalVariableIndex]; |
|
if (stack != null) |
|
s.Operands[i] = stack.Peek(); |
|
} |
|
} |
|
// if we're writing to a variable we should process: |
|
if (s.Target != null) { |
|
int targetIndex = s.Target.OriginalVariableIndex; |
|
if (versionStacks[targetIndex] != null) { |
|
s.Target = MakeNewVersion(targetIndex); |
|
s.Target.IsSingleAssignment = true; |
|
s.Target.Definition = s; |
|
|
|
// we already pushed our entry for this SsaBlock at the beginning (where we duplicated all stacks), |
|
// so now replace the top element |
|
versionStacks[targetIndex].Pop(); |
|
versionStacks[targetIndex].Push(s.Target); |
|
} |
|
} |
|
} |
|
|
|
foreach (SsaBlock succ in block.Successors) { |
|
int j = succ.Predecessors.IndexOf(block); |
|
Debug.Assert(j >= 0); |
|
foreach (SsaInstruction f in succ.Instructions) { |
|
if (f.SpecialOpCode == SpecialOpCode.Phi) { |
|
var stack = versionStacks[f.Target.OriginalVariableIndex]; |
|
if (stack != null) { |
|
f.Operands[j] = stack.Peek(); |
|
} |
|
} |
|
} |
|
} |
|
foreach (ControlFlowNode child in transform.cfg.Nodes[block.BlockIndex].DominatorTreeChildren) |
|
Visit(transform.ssaForm.Blocks[child.BlockIndex]); |
|
// restore stacks: |
|
foreach (var stack in versionStacks) { |
|
if (stack != null) |
|
stack.Pop(); |
|
} |
|
} |
|
} |
|
#endregion |
|
} |
|
}
|
|
|