protected internal override void VisitStLoc(StLoc inst) { base.VisitStLoc(inst); if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc) { context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst); inst.ReplaceWith(inst.Value); } }
protected internal override void VisitStLoc(StLoc inst) { DisplayClass displayClass; if (inst.Parent is Block parentBlock && inst.Variable.IsSingleDefinition) { if ((inst.Variable.Kind == VariableKind.Local || inst.Variable.Kind == VariableKind.StackSlot) && inst.Variable.LoadCount == 0) { // traverse pre-order, so that we do not have to deal with more special cases afterwards base.VisitStLoc(inst); if (inst.Value is StLoc || inst.Value is CompoundAssignmentInstruction) { context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst); inst.ReplaceWith(inst.Value); } return; } if (displayClasses.TryGetValue(inst.Variable, out displayClass) && displayClass.Initializer == inst) { // inline contents of object initializer block if (inst.Value is Block initBlock && initBlock.Kind == BlockKind.ObjectInitializer) { context.Step($"Remove initializer of {inst.Variable.Name}", inst); for (int i = 1; i < initBlock.Instructions.Count; i++) { var stobj = (StObj)initBlock.Instructions[i]; var variable = displayClass.VariablesToDeclare[(IField)((LdFlda)stobj.Target).Field.MemberDefinition]; parentBlock.Instructions.Insert(inst.ChildIndex + i, new StLoc(variable.GetOrDeclare(), stobj.Value).WithILRange(stobj)); } } context.Step($"Remove initializer of {inst.Variable.Name}", inst); parentBlock.Instructions.Remove(inst); return; } if (inst.Value is LdLoc || inst.Value is LdObj) { // in some cases (e.g. if inlining fails), there can be a reference to a display class in a stack slot, // in that case it is necessary to resolve the reference and iff it can be propagated, replace all loads // of the single-definition. var referencedDisplayClass = ResolveVariableToPropagate(inst.Value); if (referencedDisplayClass != null && displayClasses.TryGetValue(referencedDisplayClass, out _)) { context.Step($"Propagate reference to {referencedDisplayClass.Name} in {inst.Variable}", inst); foreach (var ld in inst.Variable.LoadInstructions.ToArray()) { ld.ReplaceWith(new LdLoc(referencedDisplayClass).WithILRange(ld)); } parentBlock.Instructions.Remove(inst); return; } } } base.VisitStLoc(inst); }
protected internal override void VisitStLoc(StLoc inst) { base.VisitStLoc(inst); if (inst.Parent is Block && inst.Variable.IsSingleDefinition) { if (inst.Variable.Kind == VariableKind.Local && inst.Variable.LoadCount == 0 && inst.Value is StLoc) { context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst); inst.ReplaceWith(inst.Value); return; } if (inst.Value.MatchLdLoc(out var displayClassVariable) && displayClasses.TryGetValue(displayClassVariable, out var displayClass)) { context.Step($"Found copy-assignment of display-class variable {displayClassVariable.Name}", inst); displayClasses.Add(inst.Variable, displayClass); instructionsToRemove.Add(inst); return; } } }
/// <summary> /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction. /// /// Note that this method does not check whether 'v' has only one use; /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'. /// </summary> public static bool InlineOne(StLoc stloc, InliningOptions options, ILTransformContext context) { ILVariable v = stloc.Variable; Block block = (Block)stloc.Parent; int pos = stloc.ChildIndex; if (DoInline(v, stloc.Value, block.Instructions.ElementAtOrDefault(pos + 1), options, context)) { // Assign the ranges of the stloc instruction: stloc.Value.AddILRange(stloc); // Remove the stloc instruction: Debug.Assert(block.Instructions[pos] == stloc); block.Instructions.RemoveAt(pos); return(true); } else if (v.LoadCount == 0 && v.AddressCount == 0) { // The variable is never loaded if (SemanticHelper.IsPure(stloc.Value.Flags)) { // Remove completely if the instruction has no effects // (except for reading locals) context.Step("Remove dead store without side effects", stloc); block.Instructions.RemoveAt(pos); return(true); } else if (v.Kind == VariableKind.StackSlot) { context.Step("Remove dead store, but keep expression", stloc); // Assign the ranges of the stloc instruction: stloc.Value.AddILRange(stloc); // Remove the stloc, but keep the inner expression stloc.ReplaceWith(stloc.Value); return(true); } } return(false); }
bool CreatePinnedRegion(Block block, StLoc stLoc) { // Collect the blocks to be moved into the region: BlockContainer sourceContainer = (BlockContainer)block.Parent; int[] reachedEdgesPerBlock = new int[sourceContainer.Blocks.Count]; Queue <Block> workList = new Queue <Block>(); Block entryBlock = ((Branch)block.Instructions.Last()).TargetBlock; if (entryBlock.Parent != sourceContainer) { // we didn't find a single block to be added to the pinned region return(false); } reachedEdgesPerBlock[entryBlock.ChildIndex]++; workList.Enqueue(entryBlock); while (workList.Count > 0) { Block workItem = workList.Dequeue(); StLoc workStLoc = workItem.Instructions.SecondToLastOrDefault() as StLoc; int instructionCount; if (workStLoc != null && workStLoc.Variable == stLoc.Variable && IsNullOrZero(workStLoc.Value)) { // found unpin instruction: only consider branches prior to that instruction instructionCount = workStLoc.ChildIndex; } else { instructionCount = workItem.Instructions.Count; } for (int i = 0; i < instructionCount; i++) { foreach (var branch in workItem.Instructions[i].Descendants.OfType <Branch>()) { if (branch.TargetBlock.Parent == sourceContainer) { if (branch.TargetBlock == block) { // pin instruction is within a loop, and can loop around without an unpin instruction // This should never happen for C#-compiled code, but may happen with C++/CLI code. return(false); } if (reachedEdgesPerBlock[branch.TargetBlock.ChildIndex]++ == 0) { // detected first edge to that block: add block as work item workList.Enqueue(branch.TargetBlock); } } } } } // Validate that all uses of a block consistently are inside or outside the pinned region. // (we cannot do this anymore after we start moving blocks around) for (int i = 0; i < sourceContainer.Blocks.Count; i++) { if (reachedEdgesPerBlock[i] != 0 && reachedEdgesPerBlock[i] != sourceContainer.Blocks[i].IncomingEdgeCount) { return(false); } } context.Step("CreatePinnedRegion", block); BlockContainer body = new BlockContainer(); for (int i = 0; i < sourceContainer.Blocks.Count; i++) { if (reachedEdgesPerBlock[i] > 0) { var innerBlock = sourceContainer.Blocks[i]; Branch br = innerBlock.Instructions.LastOrDefault() as Branch; if (br != null && br.TargetContainer == sourceContainer && reachedEdgesPerBlock[br.TargetBlock.ChildIndex] == 0) { // branch that leaves body. // Should have an instruction that resets the pin; delete that instruction: StLoc innerStLoc = innerBlock.Instructions.SecondToLastOrDefault() as StLoc; if (innerStLoc != null && innerStLoc.Variable == stLoc.Variable && IsNullOrZero(innerStLoc.Value)) { innerBlock.Instructions.RemoveAt(innerBlock.Instructions.Count - 2); } } body.Blocks.Add(innerBlock); // move block into body sourceContainer.Blocks[i] = new Block(); // replace with dummy block // we'll delete the dummy block later } } var pinnedRegion = new PinnedRegion(stLoc.Variable, stLoc.Value, body).WithILRange(stLoc); stLoc.ReplaceWith(pinnedRegion); block.Instructions.RemoveAt(block.Instructions.Count - 1); // remove branch into body ProcessPinnedRegion(pinnedRegion); return(true); }