void fixEmptyBlocks() { foreach (var block in methodBlocks.getAllBlocks()) { if (block.Instructions.Count == 0) { block.Instructions.Add(new Instr(OpCodes.Nop.ToInstruction())); } } }
public int optimizeLocals() { if (locals.Count == 0) { return(0); } var usedLocals = new Dictionary <Local, List <LocalVariableInfo> >(); foreach (var block in methodBlocks.getAllBlocks()) { for (int i = 0; i < block.Instructions.Count; i++) { var instr = block.Instructions[i]; Local local; switch (instr.OpCode.Code) { case Code.Ldloc: case Code.Ldloc_S: case Code.Ldloc_0: case Code.Ldloc_1: case Code.Ldloc_2: case Code.Ldloc_3: case Code.Stloc: case Code.Stloc_S: case Code.Stloc_0: case Code.Stloc_1: case Code.Stloc_2: case Code.Stloc_3: local = Instr.getLocalVar(locals, instr); break; case Code.Ldloca_S: case Code.Ldloca: local = (Local)instr.Operand; break; default: local = null; break; } if (local == null) { continue; } List <LocalVariableInfo> list; if (!usedLocals.TryGetValue(local, out list)) { usedLocals[local] = list = new List <LocalVariableInfo>(); } list.Add(new LocalVariableInfo(block, i)); if (usedLocals.Count == locals.Count) { return(0); } } } int newIndex = -1; var newLocals = new List <Local>(usedLocals.Count); var newLocalsDict = new Dictionary <Local, bool>(usedLocals.Count); foreach (var local in usedLocals.Keys) { newIndex++; newLocals.Add(local); newLocalsDict[local] = true; foreach (var info in usedLocals[local]) { info.block.Instructions[info.index] = new Instr(optimizeLocalInstr(info.block.Instructions[info.index], local, (uint)newIndex)); } } // We can't remove all locals. Locals that reference another assembly will // cause the CLR to load that assembly before the method is executed if it // hasn't been loaded yet. This is a side effect the program may depend on. // At least one program has this dependency and will crash if we remove the // unused local. This took a while to figure out... var keptAssemblies = new Dictionary <string, bool>(StringComparer.Ordinal); foreach (var local in locals) { if (newLocalsDict.ContainsKey(local)) { continue; } var defAsm = local.Type.DefinitionAssembly; if (defAsm == null) { continue; // eg. fnptr } if (defAsm == method.DeclaringType.Module.Assembly) { continue; // this assembly is always loaded } if (defAsm.IsCorLib()) { continue; // mscorlib is always loaded } var asmName = defAsm.FullName; if (keptAssemblies.ContainsKey(asmName)) { continue; } keptAssemblies[asmName] = true; newLocals.Add(local); } int numRemoved = locals.Count - newLocals.Count; locals.Clear(); foreach (var local in newLocals) { locals.Add(local); } return(numRemoved); }
public int optimizeLocals() { if (locals.Count == 0) { return(0); } var usedLocals = new Dictionary <VariableDefinition, List <LocalVariableInfo> >(); foreach (var block in methodBlocks.getAllBlocks()) { for (int i = 0; i < block.Instructions.Count; i++) { var instr = block.Instructions[i]; VariableDefinition local; switch (instr.OpCode.Code) { case Code.Ldloc: case Code.Ldloc_S: case Code.Ldloc_0: case Code.Ldloc_1: case Code.Ldloc_2: case Code.Ldloc_3: case Code.Stloc: case Code.Stloc_S: case Code.Stloc_0: case Code.Stloc_1: case Code.Stloc_2: case Code.Stloc_3: local = Instr.getLocalVar(locals, instr); break; case Code.Ldloca_S: case Code.Ldloca: local = (VariableDefinition)instr.Operand; break; default: local = null; break; } if (local == null) { continue; } List <LocalVariableInfo> list; if (!usedLocals.TryGetValue(local, out list)) { usedLocals[local] = list = new List <LocalVariableInfo>(); } list.Add(new LocalVariableInfo(block, i)); if (usedLocals.Count == locals.Count) { return(0); } } } int newIndex = -1; var newLocals = new List <VariableDefinition>(usedLocals.Count); foreach (var local in usedLocals.Keys) { newIndex++; newLocals.Add(local); foreach (var info in usedLocals[local]) { info.block.Instructions[info.index] = new Instr(optimizeLocalInstr(info.block.Instructions[info.index], local, (uint)newIndex)); } } int numRemoved = locals.Count - newLocals.Count; locals.Clear(); foreach (var local in newLocals) { locals.Add(local); } return(numRemoved); }