summaryrefslogtreecommitdiff
path: root/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs')
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs254
1 files changed, 254 insertions, 0 deletions
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs b/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs
new file mode 100644
index 00000000..47ff7bff
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs
@@ -0,0 +1,254 @@
+// 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>
+ internal 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
+
+ TransformToSsa(ControlFlowGraph cfg, SsaForm ssaForm)
+ {
+ this.cfg = cfg;
+ this.ssaForm = ssaForm;
+ writeToOriginalVariables = new List<SsaInstruction>[ssaForm.OriginalVariables.Count];
+ 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;
+ inputVariables = transform.ssaForm.OriginalVariables;
+ Debug.Assert(inputVariables.Count == processVariable.Length);
+ versionCounters = new int[inputVariables.Count];
+ 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
+ }
+}