public override string ToString() { if (Exception != null) { return($"[{GetType ().Name}: {Target} {Exception.HandlerType}]"); } return($"[{GetType ().Name}: {Target} <== {OriginBlock} - {CecilHelper.Format (Origin)}]"); }
bool Scan() { LogDebug(1, $"SCAN: {Method}"); BasicBlock bb = null; if (DebugLevel > 0) { Context.Debug(); } if (!BlockList.Initialize()) { return(false); } for (int i = 0; i < Method.Body.Instructions.Count; i++) { var instruction = Method.Body.Instructions [i]; if (BlockList.TryGetBlock(instruction, out var newBB)) { if (bb != null && bb.BranchType != BranchType.None) { throw DebugHelpers.AssertFail(Method, bb, $"Found known basic block with unexpected branch type `{bb.BranchType}`"); } LogDebug(2, $" KNOWN BB: {newBB}"); bb = newBB; } else if (bb == null) { bb = BlockList.NewBlock(instruction); LogDebug(2, $" NEW BB: {bb}"); } else { bb.AddInstruction(instruction); } var type = CecilHelper.GetBranchType(instruction); LogDebug(2, $" {type}: {CecilHelper.Format (instruction)}"); if (instruction.OpCode.OperandType == OperandType.InlineMethod) { if (LinkerConditional.Scan(this, ref bb, ref i, instruction)) { FoundConditionals = true; } continue; } switch (type) { case BranchType.None: break; case BranchType.Conditional: case BranchType.False: case BranchType.True: case BranchType.Jump: BlockList.AddJumpOrigin(bb, instruction, (Instruction)instruction.Operand); bb = null; break; case BranchType.Exit: case BranchType.Return: case BranchType.EndFinally: bb = null; break; case BranchType.Switch: foreach (var label in (Instruction [])bb.LastInstruction.Operand) { BlockList.AddJumpOrigin(bb, instruction, label); } bb = null; break; default: throw new OptimizerAssertionException(); } } BlockList.ComputeOffsets(); DumpBlocks(); if (Context.Options.AnalyzeAll || FoundConditionals || DebugLevel > 3) { EliminateDeadBlocks(); DumpBlocks(); return(true); } return(true); }
internal void LogDebug(int level, string indent, string message, IReadOnlyCollection <Instruction> collection) { LogDebug(level, indent, message, collection, i => CecilHelper.Format(i)); }
void RemoveVariable(VariableEntry variable) { if (Scanner.DebugLevel > 1) { BlockList.ComputeOffsets(); Scanner.LogDebug(2, $"DELETE VARIABLE: {Method.Name} {variable}"); Scanner.DumpBlocks(2); } for (int i = 0; i < BlockList.Count; i++) { var block = BlockList [i]; for (int j = 0; j < block.Instructions.Count; j++) { var instruction = block.Instructions [j]; Scanner.LogDebug(2, $" {CecilHelper.Format (instruction)}"); switch (instruction.OpCode.Code) { case Code.Ldloc_0: if (variable.Index == 0 && variable.IsConstant) { BlockList.ReplaceInstructionAt(ref block, j, CecilHelper.CreateConstantLoad(variable.Value)); } break; case Code.Stloc_0: break; case Code.Ldloc_1: if (variable.Index < 1) { BlockList.ReplaceInstructionAt(ref block, j, Instruction.Create(OpCodes.Ldloc_0)); } else if (variable.Index == 1) { BlockList.ReplaceInstructionAt(ref block, j, CecilHelper.CreateConstantLoad(variable.Value)); } break; case Code.Stloc_1: if (variable.Index < 1) { BlockList.ReplaceInstructionAt(ref block, j, Instruction.Create(OpCodes.Stloc_0)); } break; case Code.Ldloc_2: if (variable.Index < 2) { BlockList.ReplaceInstructionAt(ref block, j, Instruction.Create(OpCodes.Ldloc_1)); } else if (variable.Index == 2) { BlockList.ReplaceInstructionAt(ref block, j, CecilHelper.CreateConstantLoad(variable.Value)); } break; case Code.Stloc_2: if (variable.Index < 2) { BlockList.ReplaceInstructionAt(ref block, j, Instruction.Create(OpCodes.Stloc_1)); } break; case Code.Ldloc_3: if (variable.Index < 3) { BlockList.ReplaceInstructionAt(ref block, j, Instruction.Create(OpCodes.Ldloc_2)); } else if (variable.Index == 3) { BlockList.ReplaceInstructionAt(ref block, j, CecilHelper.CreateConstantLoad(variable.Value)); } break; case Code.Stloc_3: if (variable.Index < 3) { BlockList.ReplaceInstructionAt(ref block, j, Instruction.Create(OpCodes.Stloc_2)); } break; case Code.Ldloc: case Code.Ldloc_S: var argument = CecilHelper.GetVariable(Method.Body, instruction); if (argument == variable.Variable) { BlockList.ReplaceInstructionAt(ref block, j, CecilHelper.CreateConstantLoad(variable.Value)); } break; } } } if (Scanner.DebugLevel > 1) { BlockList.ComputeOffsets(); Scanner.LogDebug(2, $"DELETE VARIABLE DONE: {Method.Name} {variable}"); Scanner.DumpBlocks(2); } }
public bool RemoveUnusedVariables() { Scanner.LogDebug(1, $"REMOVE VARIABLES: {Method.Name}"); if (Method.Body.HasExceptionHandlers) { return(false); } var removed = false; var variables = new Dictionary <VariableDefinition, VariableEntry> (); for (int i = 0; i < Method.Body.Variables.Count; i++) { var variable = new VariableEntry(Method.Body.Variables [i], i); variables.Add(variable.Variable, variable); if (Scanner.DebugLevel > 1) { Scanner.LogDebug(2, $" VARIABLE: {variable}"); } } foreach (var block in BlockList.Blocks) { Scanner.LogDebug(2, $"REMOVE VARIABLES #1: {block}"); for (int i = 0; i < block.Instructions.Count; i++) { var instruction = block.Instructions [i]; Scanner.LogDebug(2, $" {CecilHelper.Format (instruction)}"); var variable = CecilHelper.GetVariable(Method.Body, instruction); if (variable == null) { continue; } var entry = variables [variable]; if (entry == null) { throw DebugHelpers.AssertFail(Method, block, $"Cannot resolve variable from instruction `{CecilHelper.Format (instruction)}`."); } entry.Used = true; if (entry.Modified) { continue; } switch (instruction.OpCode.Code) { case Code.Ldloc_0: case Code.Ldloc_1: case Code.Ldloc_2: case Code.Ldloc_3: case Code.Ldloc: case Code.Ldloc_S: continue; case Code.Ldloca: case Code.Ldloca_S: entry.SetModified(); continue; case Code.Stloc: case Code.Stloc_0: case Code.Stloc_1: case Code.Stloc_2: case Code.Stloc_3: case Code.Stloc_S: break; default: throw DebugHelpers.AssertFailUnexpected(Method, block, instruction); } if (i == 0 || entry.IsConstant) { entry.SetModified(); continue; } var load = block.Instructions [i - 1]; switch (block.Instructions [i - 1].OpCode.Code) { case Code.Ldc_I4_0: entry.SetConstant(block, load, ConstantValue.Zero); break; case Code.Ldc_I4_1: entry.SetConstant(block, load, ConstantValue.One); break; case Code.Ldnull: entry.SetConstant(block, load, ConstantValue.Null); break; default: entry.SetModified(); break; } } } Scanner.LogDebug(1, $"REMOVE VARIABLES #1"); for (int i = Method.Body.Variables.Count - 1; i >= 0; i--) { var variable = variables [Method.Body.Variables [i]]; Scanner.LogDebug(2, $" VARIABLE #{i}: {variable}"); if (!variable.Used) { Scanner.LogDebug(2, $" --> REMOVE"); Scanner.LogDebug(1, $"REMOVE VARIABLES - REMOVE: {Method.Name}"); RemoveVariable(variable); Method.Body.Variables.RemoveAt(i); removed = true; continue; } if (variable.IsConstant) { Scanner.LogDebug(2, $" --> CONSTANT ({variable.Value}): {variable.Instruction}"); Scanner.LogDebug(1, $"REMOVE VARIABLES - CONSTANT: {Method.Name}"); Scanner.DumpBlock(2, variable.Block); var position = variable.Block.IndexOf(variable.Instruction); var block = variable.Block; BlockList.RemoveInstructionAt(ref block, position + 1); BlockList.RemoveInstructionAt(ref block, position); RemoveVariable(variable); Method.Body.Variables.RemoveAt(i); removed = true; continue; } } if (removed) { BlockList.ComputeOffsets(); Scanner.LogDebug(1, $"REMOVE VARIABLES DONE: {removed}"); Scanner.DumpBlocks(1); Scanner.Context.Options.OptimizerReport?.RemovedDeadVariables(Method); } return(removed); }
bool RemoveConstantJumps(ref BasicBlock block, int index) { if (Scanner.DebugLevel > 2) { Scanner.LogDebug(2, $" #{index}: {block}"); Scanner.LogDebug(2, " ", null, block.JumpOrigins); Scanner.LogDebug(2, " ", null, block.Instructions); Scanner.Context.Debug(); } bool constant; switch (block.BranchType) { case BranchType.True: if (block.Count < 2) { return(false); } if (!CecilHelper.IsConstantLoad(block.Instructions [block.Count - 2], out constant)) { return(false); } break; case BranchType.False: if (block.Count < 2) { return(false); } if (!CecilHelper.IsConstantLoad(block.Instructions [block.Count - 2], out constant)) { return(false); } constant = !constant; break; default: return(false); } var target = (Instruction)block.LastInstruction.Operand; Scanner.LogDebug(2, $" ELIMINATE CONSTANT JUMP: {constant} {block.LastInstruction} {CecilHelper.Format (target)}"); BlockList.RemoveInstructionAt(ref block, block.Count - 1); if (constant) { BlockList.ReplaceInstructionAt(ref block, block.Count - 1, Instruction.Create(OpCodes.Br, target)); } else { BlockList.RemoveInstructionAt(ref block, block.Count - 1); } return(true); }
public static VariableDefinition GetVariable(MethodBody body, Instruction instruction) { switch (instruction.OpCode.Code) { case Code.Ldloc_0: case Code.Stloc_0: return(body.Variables [0]); case Code.Ldloc_1: case Code.Stloc_1: return(body.Variables [1]); case Code.Ldloc_2: case Code.Stloc_2: return(body.Variables [2]); case Code.Ldloc_3: case Code.Stloc_3: return(body.Variables [3]); case Code.Ldloc: case Code.Ldloc_S: case Code.Ldloca: case Code.Ldloca_S: case Code.Stloc_S: case Code.Stloc: var variable = ((VariableReference)instruction.Operand).Resolve(); if (variable == null) { throw DebugHelpers.AssertFail(body.Method, $"Unable to resolve variable from `{CecilHelper.Format (instruction)}`"); } return(variable); default: return(null); } }