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); }