void CreateGraphStructure() { for (int i = 0; i < blocks.Length; i++) { blocks[i] = new SsaBlock(cfg.Nodes[i]); } for (int i = 0; i < blocks.Length; i++) { foreach (ControlFlowNode node in cfg.Nodes[i].Successors) { blocks[i].Successors.Add(blocks[node.BlockIndex]); blocks[node.BlockIndex].Predecessors.Add(blocks[i]); } } }
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); } } }
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); } }
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); } } } } }
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); } } }
void CreateInstructions(int blockIndex) { ControlFlowNode cfgNode = cfg.Nodes[blockIndex]; SsaBlock block = blocks[blockIndex]; int stackSize = stackSizeAtBlockStart[blockIndex]; Debug.Assert(stackSize >= 0); List <Instruction> prefixes = new List <Instruction>(); foreach (Instruction inst in cfgNode.Instructions) { if (inst.OpCode.OpCodeType == OpCodeType.Prefix) { prefixes.Add(inst); continue; } int popCount = inst.GetPopDelta() ?? stackSize; stackSize -= popCount; if (stackSize < 0) { throw new InvalidProgramException("IL stack underflow"); } int pushCount = inst.GetPushDelta(); if (stackSize + pushCount > stackLocations.Length) { throw new InvalidProgramException("IL stack overflow"); } SsaVariable target; SsaVariable[] operands; DetermineOperands(stackSize, inst, popCount, pushCount, out target, out operands); Instruction[] prefixArray = prefixes.Count > 0 ? prefixes.ToArray() : null; prefixes.Clear(); // ignore NOP instructions if (!(inst.OpCode == OpCodes.Nop || inst.OpCode == OpCodes.Pop)) { block.Instructions.Add(new SsaInstruction(block, inst, target, operands, prefixArray)); } stackSize += pushCount; } foreach (ControlFlowEdge edge in cfgNode.Outgoing) { int newStackSize; switch (edge.Type) { case JumpType.Normal: newStackSize = stackSize; break; case JumpType.EndFinally: if (stackSize != 0) { throw new NotSupportedException("stacksize must be 0 in endfinally edge"); } newStackSize = 0; break; case JumpType.JumpToExceptionHandler: switch (edge.Target.NodeType) { case ControlFlowNodeType.FinallyOrFaultHandler: newStackSize = 0; break; case ControlFlowNodeType.ExceptionalExit: case ControlFlowNodeType.CatchHandler: newStackSize = 1; break; default: throw new NotSupportedException("unsupported target node type: " + edge.Target.NodeType); } break; default: throw new NotSupportedException("unsupported jump type: " + edge.Type); } int nextStackSize = stackSizeAtBlockStart[edge.Target.BlockIndex]; if (nextStackSize == -1) { stackSizeAtBlockStart[edge.Target.BlockIndex] = newStackSize; CreateInstructions(edge.Target.BlockIndex); } else if (nextStackSize != newStackSize) { throw new InvalidProgramException("Stack size doesn't match"); } } }
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(); } } }
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); } }
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); } }
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(); } }