private SsaFormBuilder(MethodDefinition method, ControlFlowGraph cfg) { this.method = method; this.cfg = cfg; this.blocks = new SsaBlock[cfg.Nodes.Count]; this.stackSizeAtBlockStart = new int[cfg.Nodes.Count]; for (int i = 0; i < stackSizeAtBlockStart.Length; i++) { stackSizeAtBlockStart[i] = -1; } stackSizeAtBlockStart[cfg.EntryPoint.BlockIndex] = 0; this.parameters = new SsaVariable[method.Parameters.Count + (method.HasThis ? 1 : 0)]; if (method.HasThis) { parameters[0] = new SsaVariable(method.Body.ThisParameter); } for (int i = 0; i < method.Parameters.Count; i++) { parameters[i + (method.HasThis ? 1 : 0)] = new SsaVariable(method.Parameters[i]); } this.locals = new SsaVariable[method.Body.Variables.Count]; for (int i = 0; i < locals.Length; i++) { locals[i] = new SsaVariable(method.Body.Variables[i]); } this.stackLocations = new SsaVariable[method.Body.MaxStackSize]; for (int i = 0; i < stackLocations.Length; i++) { stackLocations[i] = new SsaVariable(i); } }
private SsaFormBuilder(MethodDefinition method, ControlFlowGraph cfg) { this.method = method; this.cfg = cfg; this.blocks = new SsaBlock[cfg.Nodes.Count]; this.stackSizeAtBlockStart = new int[cfg.Nodes.Count]; for (int i = 0; i < stackSizeAtBlockStart.Length; i++) { stackSizeAtBlockStart[i] = -1; } stackSizeAtBlockStart[cfg.EntryPoint.BlockIndex] = 0; this.parameters = new SsaVariable[method.Parameters.Count + (method.HasThis ? 1 : 0)]; if (method.HasThis) parameters[0] = new SsaVariable(method.Body.ThisParameter); for (int i = 0; i < method.Parameters.Count; i++) parameters[i + (method.HasThis ? 1 : 0)] = new SsaVariable(method.Parameters[i]); this.locals = new SsaVariable[method.Body.Variables.Count]; for (int i = 0; i < locals.Length; i++) locals[i] = new SsaVariable(method.Body.Variables[i]); this.stackLocations = new SsaVariable[method.Body.MaxStackSize]; for (int i = 0; i < stackLocations.Length; i++) { stackLocations[i] = new SsaVariable(i); } }
public SsaVariable(SsaVariable original, string newName) { Name = newName; IsStackLocation = original.IsStackLocation; OriginalVariableIndex = original.OriginalVariableIndex; Parameter = original.Parameter; Variable = original.Variable; }
public SsaVariable(SsaVariable original, string newName) { this.Name = newName; this.IsStackLocation = original.IsStackLocation; this.OriginalVariableIndex = original.OriginalVariableIndex; this.Parameter = original.Parameter; this.Variable = original.Variable; }
public void ReplaceVariableInOperands(SsaVariable oldVar, SsaVariable newVar) { for (int i = 0; i < this.Operands.Length; i++) { if (this.Operands[i] == oldVar) { this.Operands[i] = newVar; } } }
public static void RemoveDeadAssignments(SsaForm ssaForm) { HashSet <SsaVariable> liveVariables = new HashSet <SsaVariable>(); // find variables that are used directly foreach (SsaBlock block in ssaForm.Blocks) { foreach (SsaInstruction inst in block.Instructions) { if (!CanRemoveAsDeadCode(inst)) { if (inst.Target != null) { liveVariables.Add(inst.Target); } foreach (SsaVariable op in inst.Operands) { liveVariables.Add(op); } } } } Queue <SsaVariable> queue = new Queue <SsaVariable>(liveVariables); // find variables that are used indirectly while (queue.Count > 0) { SsaVariable v = queue.Dequeue(); if (v.IsSingleAssignment) { foreach (SsaVariable op in v.Definition.Operands) { if (liveVariables.Add(op)) { queue.Enqueue(op); } } } } // remove assignments to all unused variables foreach (SsaBlock block in ssaForm.Blocks) { block.Instructions.RemoveAll( inst => { if (inst.Target != null && !liveVariables.Contains(inst.Target)) { Debug.Assert(inst.Target.IsSingleAssignment); return(true); } return(false); }); } ssaForm.ComputeVariableUsage(); // update usage after we modified stuff }
void DetermineOperands(int stackSize, Instruction inst, int popCount, int pushCount, out SsaVariable target, out SsaVariable[] operands) { switch (inst.OpCode.Code) { case Code.Ldarg: operands = new SsaVariable[] { ssaForm.GetOriginalVariable((ParameterReference)inst.Operand) }; target = stackLocations[stackSize]; break; case Code.Starg: operands = new SsaVariable[] { stackLocations[stackSize] }; target = ssaForm.GetOriginalVariable((ParameterReference)inst.Operand); break; case Code.Ldloc: operands = new SsaVariable[] { ssaForm.GetOriginalVariable((VariableReference)inst.Operand) }; target = stackLocations[stackSize]; break; case Code.Stloc: operands = new SsaVariable[] { stackLocations[stackSize] }; target = ssaForm.GetOriginalVariable((VariableReference)inst.Operand); break; case Code.Dup: operands = new SsaVariable[] { stackLocations[stackSize] }; target = stackLocations[stackSize + 1]; break; default: operands = new SsaVariable[popCount]; for (int i = 0; i < popCount; i++) { operands[i] = stackLocations[stackSize + i]; } switch (pushCount) { case 0: target = null; break; case 1: target = stackLocations[stackSize]; break; default: throw new NotSupportedException("unsupported pushCount=" + pushCount); } break; } }
public SsaInstruction(SsaBlock parentBlock, Instruction instruction, SsaVariable target, SsaVariable[] operands, Instruction[] prefixes = null, SpecialOpCode specialOpCode = SpecialOpCode.None, TypeReference typeOperand = null) { this.ParentBlock = parentBlock; this.Instruction = instruction; this.Prefixes = prefixes ?? emptyInstructionArray; this.Target = target; this.Operands = operands ?? emptyVariableArray; this.SpecialOpCode = specialOpCode; this.TypeOperand = typeOperand; Debug.Assert((typeOperand != null) == (specialOpCode == SpecialOpCode.Exception || specialOpCode == SpecialOpCode.InitObj)); }
internal SsaForm(SsaBlock[] blocks, SsaVariable[] parameters, SsaVariable[] locals, SsaVariable[] stackLocations, bool methodHasThis) { this.parameters = parameters; this.locals = locals; this.Blocks = new ReadOnlyCollection<SsaBlock>(blocks); this.OriginalVariables = new ReadOnlyCollection<SsaVariable>(parameters.Concat(locals).Concat(stackLocations).ToList()); this.methodHasThis = methodHasThis; Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint); Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit); Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit); for (int i = 0; i < this.OriginalVariables.Count; i++) { this.OriginalVariables[i].OriginalVariableIndex = i; } }
private void MakeByRefCallSimple(SsaBlock block, ref int instructionIndexInBlock, IMethodSignature targetMethod) { SsaInstruction inst = block.Instructions[instructionIndexInBlock]; for (int i = 0; i < inst.Operands.Length; i++) { SsaVariable operand = inst.Operands[i]; if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) { // address is used for this method call only Instruction loadAddressInstruction = operand.Definition.Instruction; // find target parameter type: bool isOut; if (i == 0 && targetMethod.HasThis) { isOut = false; } else { ParameterDefinition parameter = targetMethod.Parameters[i - (targetMethod.HasThis ? 1 : 0)]; isOut = parameter.IsOut; } SsaVariable addressTakenOf = GetVariableFromLoadAddressInstruction(loadAddressInstruction); // insert "Prepare" instruction on front SpecialOpCode loadOpCode = isOut ? SpecialOpCode.PrepareByOutCall : SpecialOpCode.PrepareByRefCall; block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction( block, null, operand, new SsaVariable[] { addressTakenOf }, specialOpCode: loadOpCode)); // insert "WriteAfterByRefOrOutCall" instruction after call block.Instructions.Insert(instructionIndexInBlock + 1, new SsaInstruction( block, null, addressTakenOf, new SsaVariable[] { operand }, specialOpCode: SpecialOpCode.WriteAfterByRefOrOutCall)); couldSimplifySomething = true; // remove the loadAddressInstruction later // (later because it might be defined in the current block and we don't want instructionIndex to become invalid) redundantLoadAddressInstructions.Add(operand.Definition); } } }
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)); } } }
private void MakeLoadFieldCallSimple(SsaBlock block, ref int instructionIndexInBlock) { SsaInstruction inst = block.Instructions[instructionIndexInBlock]; Debug.Assert(inst.Operands.Length == 1); SsaVariable operand = inst.Operands[0]; if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) { // insert special "PrepareForFieldAccess" instruction in front block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction( inst.ParentBlock, null, operand, new SsaVariable[] { GetVariableFromLoadAddressInstruction(operand.Definition.Instruction) }, specialOpCode: SpecialOpCode.PrepareForFieldAccess)); couldSimplifySomething = true; // remove the loadAddressInstruction later redundantLoadAddressInstructions.Add(operand.Definition); } }
/// <summary> /// When any instructions stores its result in a stack location that's used only once in a 'stloc' or 'starg' instruction, /// we optimize this to directly store in the target location. /// As optimization this is redundant (does the same as copy propagation), but it'll make us keep the variables named /// after locals instead of keeping the temps as using only the simple copy propagation would do. /// </summary> public static void DirectlyStoreToVariables(SsaForm ssaForm) { foreach (SsaBlock block in ssaForm.Blocks) { block.Instructions.RemoveAll( inst => { if (inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Stloc || inst.Instruction.OpCode == OpCodes.Starg)) { SsaVariable target = inst.Target; SsaVariable temp = inst.Operands[0]; if (target.IsSingleAssignment && temp.IsSingleAssignment && temp.Usage.Count == 1 && temp.IsStackLocation) { temp.Definition.Target = target; return(true); } } return(false); }); } ssaForm.ComputeVariableUsage(); // update usage after we modified stuff }
private void MakeInitObjCallSimple(SsaBlock block, ref int instructionIndexInBlock) { SsaInstruction inst = block.Instructions[instructionIndexInBlock]; Debug.Assert(inst.Operands.Length == 1); SsaVariable operand = inst.Operands[0]; if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) { // replace instruction with special "InitObj" instruction block.Instructions[instructionIndexInBlock] = new SsaInstruction( inst.ParentBlock, null, GetVariableFromLoadAddressInstruction(operand.Definition.Instruction), null, specialOpCode: SpecialOpCode.InitObj, typeOperand: (TypeReference)inst.Instruction.Operand); couldSimplifySomething = true; // remove the loadAddressInstruction later redundantLoadAddressInstructions.Add(operand.Definition); } }
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); } } } } }
public void ReplaceVariableInOperands(SsaVariable oldVar, SsaVariable newVar) { for (int i = 0; i < this.Operands.Length; i++) { if (this.Operands[i] == oldVar) this.Operands[i] = newVar; } }
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); } } } } }