/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

http://github.com/icsharpcode/ILSpy · C# · 1060 lines · 844 code · 110 blank · 106 comment · 311 complexity · 3cbbb2c9af6741c2724d6078130c5b7d MD5 · raw file

  1. // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Diagnostics;
  21. using System.Linq;
  22. using ICSharpCode.Decompiler.FlowAnalysis;
  23. using ICSharpCode.NRefactory.Utils;
  24. using Mono.Cecil;
  25. using Mono.Cecil.Cil;
  26. namespace ICSharpCode.Decompiler.ILAst
  27. {
  28. public enum ILAstOptimizationStep
  29. {
  30. RemoveRedundantCode,
  31. ReduceBranchInstructionSet,
  32. InlineVariables,
  33. CopyPropagation,
  34. YieldReturn,
  35. AsyncAwait,
  36. PropertyAccessInstructions,
  37. SplitToMovableBlocks,
  38. TypeInference,
  39. HandlePointerArithmetic,
  40. SimplifyShortCircuit,
  41. SimplifyTernaryOperator,
  42. SimplifyNullCoalescing,
  43. JoinBasicBlocks,
  44. SimplifyLogicNot,
  45. SimplifyShiftOperators,
  46. TypeConversionSimplifications,
  47. SimplifyLdObjAndStObj,
  48. SimplifyCustomShortCircuit,
  49. SimplifyLiftedOperators,
  50. TransformArrayInitializers,
  51. TransformMultidimensionalArrayInitializers,
  52. TransformObjectInitializers,
  53. MakeAssignmentExpression,
  54. IntroducePostIncrement,
  55. InlineExpressionTreeParameterDeclarations,
  56. InlineVariables2,
  57. FindLoops,
  58. FindConditions,
  59. FlattenNestedMovableBlocks,
  60. RemoveEndFinally,
  61. RemoveRedundantCode2,
  62. GotoRemoval,
  63. DuplicateReturns,
  64. GotoRemoval2,
  65. ReduceIfNesting,
  66. InlineVariables3,
  67. CachedDelegateInitialization,
  68. IntroduceFixedStatements,
  69. RecombineVariables,
  70. TypeInference2,
  71. RemoveRedundantCode3,
  72. None
  73. }
  74. public partial class ILAstOptimizer
  75. {
  76. int nextLabelIndex = 0;
  77. DecompilerContext context;
  78. TypeSystem typeSystem;
  79. ILBlock method;
  80. public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
  81. {
  82. this.context = context;
  83. this.typeSystem = context.CurrentMethod.Module.TypeSystem;
  84. this.method = method;
  85. if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode) return;
  86. RemoveRedundantCode(method);
  87. if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return;
  88. foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
  89. ReduceBranchInstructionSet(block);
  90. }
  91. // ReduceBranchInstructionSet runs before inlining because the non-aggressive inlining heuristic
  92. // looks at which type of instruction consumes the inlined variable.
  93. if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return;
  94. // Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X
  95. ILInlining inlining1 = new ILInlining(method);
  96. inlining1.InlineAllVariables();
  97. if (abortBeforeStep == ILAstOptimizationStep.CopyPropagation) return;
  98. inlining1.CopyPropagation();
  99. if (abortBeforeStep == ILAstOptimizationStep.YieldReturn) return;
  100. YieldReturnDecompiler.Run(context, method);
  101. AsyncDecompiler.RunStep1(context, method);
  102. if (abortBeforeStep == ILAstOptimizationStep.AsyncAwait) return;
  103. AsyncDecompiler.RunStep2(context, method);
  104. if (abortBeforeStep == ILAstOptimizationStep.PropertyAccessInstructions) return;
  105. IntroducePropertyAccessInstructions(method);
  106. if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return;
  107. foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
  108. SplitToBasicBlocks(block);
  109. }
  110. if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return;
  111. // Types are needed for the ternary operator optimization
  112. TypeAnalysis.Run(context, method);
  113. if (abortBeforeStep == ILAstOptimizationStep.HandlePointerArithmetic) return;
  114. HandlePointerArithmetic(method);
  115. foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
  116. bool modified;
  117. do {
  118. modified = false;
  119. if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return;
  120. modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyShortCircuit);
  121. if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return;
  122. modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyTernaryOperator);
  123. if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return;
  124. modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyNullCoalescing);
  125. if (abortBeforeStep == ILAstOptimizationStep.JoinBasicBlocks) return;
  126. modified |= block.RunOptimization(new SimpleControlFlow(context, method).JoinBasicBlocks);
  127. if (abortBeforeStep == ILAstOptimizationStep.SimplifyLogicNot) return;
  128. modified |= block.RunOptimization(SimplifyLogicNot);
  129. if (abortBeforeStep == ILAstOptimizationStep.SimplifyShiftOperators) return;
  130. modified |= block.RunOptimization(SimplifyShiftOperators);
  131. if (abortBeforeStep == ILAstOptimizationStep.TypeConversionSimplifications) return;
  132. modified |= block.RunOptimization(TypeConversionSimplifications);
  133. if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return;
  134. modified |= block.RunOptimization(SimplifyLdObjAndStObj);
  135. if (abortBeforeStep == ILAstOptimizationStep.SimplifyCustomShortCircuit) return;
  136. modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyCustomShortCircuit);
  137. if (abortBeforeStep == ILAstOptimizationStep.SimplifyLiftedOperators) return;
  138. modified |= block.RunOptimization(SimplifyLiftedOperators);
  139. if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return;
  140. modified |= block.RunOptimization(TransformArrayInitializers);
  141. if (abortBeforeStep == ILAstOptimizationStep.TransformMultidimensionalArrayInitializers) return;
  142. modified |= block.RunOptimization(TransformMultidimensionalArrayInitializers);
  143. if (abortBeforeStep == ILAstOptimizationStep.TransformObjectInitializers) return;
  144. modified |= block.RunOptimization(TransformObjectInitializers);
  145. if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return;
  146. if (context.Settings.MakeAssignmentExpressions) {
  147. modified |= block.RunOptimization(MakeAssignmentExpression);
  148. }
  149. modified |= block.RunOptimization(MakeCompoundAssignments);
  150. if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return;
  151. if (context.Settings.IntroduceIncrementAndDecrement) {
  152. modified |= block.RunOptimization(IntroducePostIncrement);
  153. }
  154. if (abortBeforeStep == ILAstOptimizationStep.InlineExpressionTreeParameterDeclarations) return;
  155. if (context.Settings.ExpressionTrees) {
  156. modified |= block.RunOptimization(InlineExpressionTreeParameterDeclarations);
  157. }
  158. if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
  159. modified |= new ILInlining(method).InlineAllInBlock(block);
  160. new ILInlining(method).CopyPropagation();
  161. } while(modified);
  162. }
  163. if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return;
  164. foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
  165. new LoopsAndConditions(context).FindLoops(block);
  166. }
  167. if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return;
  168. foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
  169. new LoopsAndConditions(context).FindConditions(block);
  170. }
  171. if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return;
  172. FlattenBasicBlocks(method);
  173. if (abortBeforeStep == ILAstOptimizationStep.RemoveEndFinally) return;
  174. RemoveEndFinally(method);
  175. if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode2) return;
  176. RemoveRedundantCode(method);
  177. if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return;
  178. new GotoRemoval().RemoveGotos(method);
  179. if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return;
  180. DuplicateReturnStatements(method);
  181. if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval2) return;
  182. new GotoRemoval().RemoveGotos(method);
  183. if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) return;
  184. ReduceIfNesting(method);
  185. if (abortBeforeStep == ILAstOptimizationStep.InlineVariables3) return;
  186. // The 2nd inlining pass is necessary because DuplicateReturns and the introduction of ternary operators
  187. // open up additional inlining possibilities.
  188. new ILInlining(method).InlineAllVariables();
  189. if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) return;
  190. if (context.Settings.AnonymousMethods) {
  191. foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
  192. for (int i = 0; i < block.Body.Count; i++) {
  193. // TODO: Move before loops
  194. CachedDelegateInitializationWithField(block, ref i);
  195. CachedDelegateInitializationWithLocal(block, ref i);
  196. }
  197. }
  198. }
  199. if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return;
  200. // we need post-order traversal, not pre-order, for "fixed" to work correctly
  201. foreach (ILBlock block in TreeTraversal.PostOrder<ILNode>(method, n => n.GetChildren()).OfType<ILBlock>()) {
  202. for (int i = block.Body.Count - 1; i >= 0; i--) {
  203. // TODO: Move before loops
  204. if (i < block.Body.Count)
  205. IntroduceFixedStatements(block.Body, i);
  206. }
  207. }
  208. if (abortBeforeStep == ILAstOptimizationStep.RecombineVariables) return;
  209. RecombineVariables(method);
  210. if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) return;
  211. TypeAnalysis.Reset(method);
  212. TypeAnalysis.Run(context, method);
  213. if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode3) return;
  214. GotoRemoval.RemoveRedundantCode(method);
  215. // ReportUnassignedILRanges(method);
  216. }
  217. /// <summary>
  218. /// Removes redundatant Br, Nop, Dup, Pop
  219. /// Ignore arguments of 'leave'
  220. /// </summary>
  221. /// <param name="method"></param>
  222. internal static void RemoveRedundantCode(ILBlock method)
  223. {
  224. Dictionary<ILLabel, int> labelRefCount = new Dictionary<ILLabel, int>();
  225. foreach (ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
  226. labelRefCount[target] = labelRefCount.GetOrDefault(target) + 1;
  227. }
  228. foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
  229. List<ILNode> body = block.Body;
  230. List<ILNode> newBody = new List<ILNode>(body.Count);
  231. for (int i = 0; i < body.Count; i++) {
  232. ILLabel target;
  233. ILExpression popExpr;
  234. if (body[i].Match(ILCode.Br, out target) && i+1 < body.Count && body[i+1] == target) {
  235. // Ignore the branch
  236. if (labelRefCount[target] == 1)
  237. i++; // Ignore the label as well
  238. } else if (body[i].Match(ILCode.Nop)){
  239. // Ignore nop
  240. } else if (body[i].Match(ILCode.Pop, out popExpr)) {
  241. ILVariable v;
  242. if (!popExpr.Match(ILCode.Ldloc, out v))
  243. throw new Exception("Pop should have just ldloc at this stage");
  244. // Best effort to move the ILRange to previous statement
  245. ILVariable prevVar;
  246. ILExpression prevExpr;
  247. if (i - 1 >= 0 && body[i - 1].Match(ILCode.Stloc, out prevVar, out prevExpr) && prevVar == v)
  248. prevExpr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges);
  249. // Ignore pop
  250. } else {
  251. ILLabel label = body[i] as ILLabel;
  252. if (label != null) {
  253. if (labelRefCount.GetOrDefault(label) > 0)
  254. newBody.Add(label);
  255. } else {
  256. newBody.Add(body[i]);
  257. }
  258. }
  259. }
  260. block.Body = newBody;
  261. }
  262. // Ignore arguments of 'leave'
  263. foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.Code == ILCode.Leave)) {
  264. if (expr.Arguments.Any(arg => !arg.Match(ILCode.Ldloc)))
  265. throw new Exception("Leave should have just ldloc at this stage");
  266. expr.Arguments.Clear();
  267. }
  268. // 'dup' removal
  269. foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
  270. for (int i = 0; i < expr.Arguments.Count; i++) {
  271. ILExpression child;
  272. if (expr.Arguments[i].Match(ILCode.Dup, out child)) {
  273. child.ILRanges.AddRange(expr.Arguments[i].ILRanges);
  274. expr.Arguments[i] = child;
  275. }
  276. }
  277. }
  278. }
  279. /// <summary>
  280. /// Reduces the branch codes to just br and brtrue.
  281. /// Moves ILRanges to the branch argument
  282. /// </summary>
  283. void ReduceBranchInstructionSet(ILBlock block)
  284. {
  285. for (int i = 0; i < block.Body.Count; i++) {
  286. ILExpression expr = block.Body[i] as ILExpression;
  287. if (expr != null && expr.Prefixes == null) {
  288. ILCode op;
  289. switch(expr.Code) {
  290. case ILCode.Switch:
  291. case ILCode.Brtrue:
  292. expr.Arguments.Single().ILRanges.AddRange(expr.ILRanges);
  293. expr.ILRanges.Clear();
  294. continue;
  295. case ILCode.__Brfalse: op = ILCode.LogicNot; break;
  296. case ILCode.__Beq: op = ILCode.Ceq; break;
  297. case ILCode.__Bne_Un: op = ILCode.Cne; break;
  298. case ILCode.__Bgt: op = ILCode.Cgt; break;
  299. case ILCode.__Bgt_Un: op = ILCode.Cgt_Un; break;
  300. case ILCode.__Ble: op = ILCode.Cle; break;
  301. case ILCode.__Ble_Un: op = ILCode.Cle_Un; break;
  302. case ILCode.__Blt: op = ILCode.Clt; break;
  303. case ILCode.__Blt_Un: op = ILCode.Clt_Un; break;
  304. case ILCode.__Bge: op = ILCode.Cge; break;
  305. case ILCode.__Bge_Un: op = ILCode.Cge_Un; break;
  306. default:
  307. continue;
  308. }
  309. var newExpr = new ILExpression(op, null, expr.Arguments);
  310. block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, newExpr);
  311. newExpr.ILRanges = expr.ILRanges;
  312. }
  313. }
  314. }
  315. /// <summary>
  316. /// Converts call and callvirt instructions that read/write properties into CallGetter/CallSetter instructions.
  317. ///
  318. /// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)".
  319. ///
  320. /// Also simplifies 'newobj(SomeDelegate, target, ldvirtftn(F, target))' to 'newobj(SomeDelegate, target, ldvirtftn(F))'
  321. /// </summary>
  322. void IntroducePropertyAccessInstructions(ILNode node)
  323. {
  324. ILExpression parentExpr = node as ILExpression;
  325. if (parentExpr != null) {
  326. for (int i = 0; i < parentExpr.Arguments.Count; i++) {
  327. ILExpression expr = parentExpr.Arguments[i];
  328. IntroducePropertyAccessInstructions(expr);
  329. IntroducePropertyAccessInstructions(expr, parentExpr, i);
  330. }
  331. } else {
  332. foreach (ILNode child in node.GetChildren()) {
  333. IntroducePropertyAccessInstructions(child);
  334. ILExpression expr = child as ILExpression;
  335. if (expr != null) {
  336. IntroducePropertyAccessInstructions(expr, null, -1);
  337. }
  338. }
  339. }
  340. }
  341. void IntroducePropertyAccessInstructions(ILExpression expr, ILExpression parentExpr, int posInParent)
  342. {
  343. if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
  344. MethodReference cecilMethod = (MethodReference)expr.Operand;
  345. if (cecilMethod.DeclaringType is ArrayType) {
  346. switch (cecilMethod.Name) {
  347. case "Get":
  348. expr.Code = ILCode.CallGetter;
  349. break;
  350. case "Set":
  351. expr.Code = ILCode.CallSetter;
  352. break;
  353. case "Address":
  354. ByReferenceType brt = cecilMethod.ReturnType as ByReferenceType;
  355. if (brt != null) {
  356. MethodReference getMethod = new MethodReference("Get", brt.ElementType, cecilMethod.DeclaringType);
  357. foreach (var p in cecilMethod.Parameters)
  358. getMethod.Parameters.Add(p);
  359. getMethod.HasThis = cecilMethod.HasThis;
  360. expr.Operand = getMethod;
  361. }
  362. expr.Code = ILCode.CallGetter;
  363. if (parentExpr != null) {
  364. parentExpr.Arguments[posInParent] = new ILExpression(ILCode.AddressOf, null, expr);
  365. }
  366. break;
  367. }
  368. } else {
  369. MethodDefinition cecilMethodDef = cecilMethod.Resolve();
  370. if (cecilMethodDef != null) {
  371. if (cecilMethodDef.IsGetter)
  372. expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter;
  373. else if (cecilMethodDef.IsSetter)
  374. expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter;
  375. }
  376. }
  377. } else if (expr.Code == ILCode.Newobj && expr.Arguments.Count == 2) {
  378. // Might be 'newobj(SomeDelegate, target, ldvirtftn(F, target))'.
  379. ILVariable target;
  380. if (expr.Arguments[0].Match(ILCode.Ldloc, out target)
  381. && expr.Arguments[1].Code == ILCode.Ldvirtftn
  382. && expr.Arguments[1].Arguments.Count == 1
  383. && expr.Arguments[1].Arguments[0].MatchLdloc(target))
  384. {
  385. // Remove the 'target' argument from the ldvirtftn instruction.
  386. // It's not needed in the translation to C#, and needs to be eliminated so that the target expression
  387. // can be inlined.
  388. expr.Arguments[1].Arguments.Clear();
  389. }
  390. }
  391. }
  392. /// <summary>
  393. /// Group input into a set of blocks that can be later arbitraliby schufled.
  394. /// The method adds necessary branches to make control flow between blocks
  395. /// explicit and thus order independent.
  396. /// </summary>
  397. void SplitToBasicBlocks(ILBlock block)
  398. {
  399. List<ILNode> basicBlocks = new List<ILNode>();
  400. ILLabel entryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
  401. ILBasicBlock basicBlock = new ILBasicBlock();
  402. basicBlocks.Add(basicBlock);
  403. basicBlock.Body.Add(entryLabel);
  404. block.EntryGoto = new ILExpression(ILCode.Br, entryLabel);
  405. if (block.Body.Count > 0) {
  406. if (block.Body[0] != entryLabel)
  407. basicBlock.Body.Add(block.Body[0]);
  408. for (int i = 1; i < block.Body.Count; i++) {
  409. ILNode lastNode = block.Body[i - 1];
  410. ILNode currNode = block.Body[i];
  411. // Start a new basic block if necessary
  412. if (currNode is ILLabel ||
  413. currNode is ILTryCatchBlock || // Counts as label
  414. lastNode.IsConditionalControlFlow() ||
  415. lastNode.IsUnconditionalControlFlow())
  416. {
  417. // Try to reuse the label
  418. ILLabel label = currNode as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++).ToString() };
  419. // Terminate the last block
  420. if (!lastNode.IsUnconditionalControlFlow()) {
  421. // Explicit branch from one block to other
  422. basicBlock.Body.Add(new ILExpression(ILCode.Br, label));
  423. }
  424. // Start the new block
  425. basicBlock = new ILBasicBlock();
  426. basicBlocks.Add(basicBlock);
  427. basicBlock.Body.Add(label);
  428. // Add the node to the basic block
  429. if (currNode != label)
  430. basicBlock.Body.Add(currNode);
  431. } else {
  432. basicBlock.Body.Add(currNode);
  433. }
  434. }
  435. }
  436. block.Body = basicBlocks;
  437. return;
  438. }
  439. void DuplicateReturnStatements(ILBlock method)
  440. {
  441. Dictionary<ILLabel, ILNode> nextSibling = new Dictionary<ILLabel, ILNode>();
  442. // Build navigation data
  443. foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
  444. for (int i = 0; i < block.Body.Count - 1; i++) {
  445. ILLabel curr = block.Body[i] as ILLabel;
  446. if (curr != null) {
  447. nextSibling[curr] = block.Body[i + 1];
  448. }
  449. }
  450. }
  451. // Duplicate returns
  452. foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
  453. for (int i = 0; i < block.Body.Count; i++) {
  454. ILLabel targetLabel;
  455. if (block.Body[i].Match(ILCode.Br, out targetLabel) || block.Body[i].Match(ILCode.Leave, out targetLabel)) {
  456. // Skip extra labels
  457. while(nextSibling.ContainsKey(targetLabel) && nextSibling[targetLabel] is ILLabel) {
  458. targetLabel = (ILLabel)nextSibling[targetLabel];
  459. }
  460. // Inline return statement
  461. ILNode target;
  462. List<ILExpression> retArgs;
  463. if (nextSibling.TryGetValue(targetLabel, out target)) {
  464. if (target.Match(ILCode.Ret, out retArgs)) {
  465. ILVariable locVar;
  466. object constValue;
  467. if (retArgs.Count == 0) {
  468. block.Body[i] = new ILExpression(ILCode.Ret, null);
  469. } else if (retArgs.Single().Match(ILCode.Ldloc, out locVar)) {
  470. block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar));
  471. } else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) {
  472. block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue));
  473. }
  474. }
  475. } else {
  476. if (method.Body.Count > 0 && method.Body.Last() == targetLabel) {
  477. // It exits the main method - so it is same as return;
  478. block.Body[i] = new ILExpression(ILCode.Ret, null);
  479. }
  480. }
  481. }
  482. }
  483. }
  484. }
  485. /// <summary>
  486. /// Flattens all nested basic blocks, except the the top level 'node' argument
  487. /// </summary>
  488. void FlattenBasicBlocks(ILNode node)
  489. {
  490. ILBlock block = node as ILBlock;
  491. if (block != null) {
  492. List<ILNode> flatBody = new List<ILNode>();
  493. foreach (ILNode child in block.GetChildren()) {
  494. FlattenBasicBlocks(child);
  495. ILBasicBlock childAsBB = child as ILBasicBlock;
  496. if (childAsBB != null) {
  497. if (!(childAsBB.Body.FirstOrDefault() is ILLabel))
  498. throw new Exception("Basic block has to start with a label. \n" + childAsBB.ToString());
  499. if (childAsBB.Body.LastOrDefault() is ILExpression && !childAsBB.Body.LastOrDefault().IsUnconditionalControlFlow())
  500. throw new Exception("Basci block has to end with unconditional control flow. \n" + childAsBB.ToString());
  501. flatBody.AddRange(childAsBB.GetChildren());
  502. } else {
  503. flatBody.Add(child);
  504. }
  505. }
  506. block.EntryGoto = null;
  507. block.Body = flatBody;
  508. } else if (node is ILExpression) {
  509. // Optimization - no need to check expressions
  510. } else if (node != null) {
  511. // Recursively find all ILBlocks
  512. foreach(ILNode child in node.GetChildren()) {
  513. FlattenBasicBlocks(child);
  514. }
  515. }
  516. }
  517. /// <summary>
  518. /// Replace endfinally with jump to the end of the finally block
  519. /// </summary>
  520. void RemoveEndFinally(ILBlock method)
  521. {
  522. // Go thought the list in reverse so that we do the nested blocks first
  523. foreach(var tryCatch in method.GetSelfAndChildrenRecursive<ILTryCatchBlock>(tc => tc.FinallyBlock != null).Reverse()) {
  524. ILLabel label = new ILLabel() { Name = "EndFinally_" + nextLabelIndex++ };
  525. tryCatch.FinallyBlock.Body.Add(label);
  526. foreach(var block in tryCatch.FinallyBlock.GetSelfAndChildrenRecursive<ILBlock>()) {
  527. for (int i = 0; i < block.Body.Count; i++) {
  528. if (block.Body[i].Match(ILCode.Endfinally)) {
  529. block.Body[i] = new ILExpression(ILCode.Br, label).WithILRanges(((ILExpression)block.Body[i]).ILRanges);
  530. }
  531. }
  532. }
  533. }
  534. }
  535. /// <summary>
  536. /// Reduce the nesting of conditions.
  537. /// It should be done on flat data that already had most gotos removed
  538. /// </summary>
  539. void ReduceIfNesting(ILNode node)
  540. {
  541. ILBlock block = node as ILBlock;
  542. if (block != null) {
  543. for (int i = 0; i < block.Body.Count; i++) {
  544. ILCondition cond = block.Body[i] as ILCondition;
  545. if (cond != null) {
  546. bool trueExits = cond.TrueBlock.Body.LastOrDefault().IsUnconditionalControlFlow();
  547. bool falseExits = cond.FalseBlock.Body.LastOrDefault().IsUnconditionalControlFlow();
  548. if (trueExits) {
  549. // Move the false block after the condition
  550. block.Body.InsertRange(i + 1, cond.FalseBlock.GetChildren());
  551. cond.FalseBlock = new ILBlock();
  552. } else if (falseExits) {
  553. // Move the true block after the condition
  554. block.Body.InsertRange(i + 1, cond.TrueBlock.GetChildren());
  555. cond.TrueBlock = new ILBlock();
  556. }
  557. // Eliminate empty true block
  558. if (!cond.TrueBlock.GetChildren().Any() && cond.FalseBlock.GetChildren().Any()) {
  559. // Swap bodies
  560. ILBlock tmp = cond.TrueBlock;
  561. cond.TrueBlock = cond.FalseBlock;
  562. cond.FalseBlock = tmp;
  563. cond.Condition = new ILExpression(ILCode.LogicNot, null, cond.Condition);
  564. }
  565. }
  566. }
  567. }
  568. // We are changing the number of blocks so we use plain old recursion to get all blocks
  569. foreach(ILNode child in node.GetChildren()) {
  570. if (child != null && !(child is ILExpression))
  571. ReduceIfNesting(child);
  572. }
  573. }
  574. void RecombineVariables(ILBlock method)
  575. {
  576. // Recombine variables that were split when the ILAst was created
  577. // This ensures that a single IL variable is a single C# variable (gets assigned only one name)
  578. // The DeclareVariables transformation might then split up the C# variable again if it is used indendently in two separate scopes.
  579. Dictionary<VariableDefinition, ILVariable> dict = new Dictionary<VariableDefinition, ILVariable>();
  580. ReplaceVariables(
  581. method,
  582. delegate(ILVariable v) {
  583. if (v.OriginalVariable == null)
  584. return v;
  585. ILVariable combinedVariable;
  586. if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) {
  587. dict.Add(v.OriginalVariable, v);
  588. combinedVariable = v;
  589. }
  590. return combinedVariable;
  591. });
  592. }
  593. void HandlePointerArithmetic(ILNode method)
  594. {
  595. foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
  596. List<ILExpression> args = expr.Arguments;
  597. switch (expr.Code) {
  598. case ILCode.Localloc:
  599. {
  600. PointerType type = expr.InferredType as PointerType;
  601. if (type != null) {
  602. ILExpression arg0 = args[0];
  603. ILExpression expr2 = expr;
  604. DivideOrMultiplyBySize(ref expr2, ref arg0, type.ElementType, true);
  605. // expr shouldn't change
  606. if (expr2 != expr)
  607. throw new InvalidOperationException();
  608. args[0] = arg0;
  609. }
  610. break;
  611. }
  612. case ILCode.Add:
  613. case ILCode.Add_Ovf:
  614. case ILCode.Add_Ovf_Un:
  615. {
  616. ILExpression arg0 = args[0];
  617. ILExpression arg1 = args[1];
  618. if (expr.InferredType is PointerType) {
  619. if (arg0.ExpectedType is PointerType) {
  620. DivideOrMultiplyBySize(ref arg0, ref arg1, ((PointerType)expr.InferredType).ElementType, true);
  621. } else if (arg1.ExpectedType is PointerType)
  622. DivideOrMultiplyBySize(ref arg1, ref arg0, ((PointerType)expr.InferredType).ElementType, true);
  623. }
  624. args[0] = arg0;
  625. args[1] = arg1;
  626. break;
  627. }
  628. case ILCode.Sub:
  629. case ILCode.Sub_Ovf:
  630. case ILCode.Sub_Ovf_Un:
  631. {
  632. ILExpression arg0 = args[0];
  633. ILExpression arg1 = args[1];
  634. if (expr.InferredType is PointerType) {
  635. if (arg0.ExpectedType is PointerType && !(arg1.InferredType is PointerType))
  636. DivideOrMultiplyBySize(ref arg0, ref arg1, ((PointerType)expr.InferredType).ElementType, true);
  637. }
  638. args[0] = arg0;
  639. args[1] = arg1;
  640. break;
  641. }
  642. case ILCode.Conv_I8:
  643. {
  644. ILExpression arg0 = args[0];
  645. // conv.i8(div:intptr(p0 - p1))
  646. if (arg0.Code == ILCode.Div && arg0.InferredType.FullName == "System.IntPtr")
  647. {
  648. ILExpression dividend = arg0.Arguments[0];
  649. if (dividend.InferredType.FullName == "System.IntPtr" &&
  650. (dividend.Code == ILCode.Sub || dividend.Code == ILCode.Sub_Ovf || dividend.Code == ILCode.Sub_Ovf_Un))
  651. {
  652. PointerType pointerType0 = dividend.Arguments[0].InferredType as PointerType;
  653. PointerType pointerType1 = dividend.Arguments[1].InferredType as PointerType;
  654. if (pointerType0 != null && pointerType1 != null) {
  655. if (pointerType0.ElementType.FullName == "System.Void" ||
  656. pointerType0.ElementType.FullName != pointerType1.ElementType.FullName) {
  657. pointerType0 = pointerType1 = new PointerType(typeSystem.Byte);
  658. dividend.Arguments[0] = Cast(dividend.Arguments[0], pointerType0);
  659. dividend.Arguments[1] = Cast(dividend.Arguments[1], pointerType1);
  660. }
  661. DivideOrMultiplyBySize(ref dividend, ref arg0, pointerType0.ElementType, false);
  662. // dividend shouldn't change
  663. if (args[0].Arguments[0] != dividend)
  664. throw new InvalidOperationException();
  665. }
  666. }
  667. }
  668. args[0] = arg0;
  669. break;
  670. }
  671. }
  672. }
  673. }
  674. static ILExpression UnwrapIntPtrCast(ILExpression expr)
  675. {
  676. if (expr.Code != ILCode.Conv_I && expr.Code != ILCode.Conv_U)
  677. return expr;
  678. ILExpression arg = expr.Arguments[0];
  679. switch (arg.InferredType.MetadataType) {
  680. case MetadataType.Byte:
  681. case MetadataType.SByte:
  682. case MetadataType.UInt16:
  683. case MetadataType.Int16:
  684. case MetadataType.UInt32:
  685. case MetadataType.Int32:
  686. case MetadataType.UInt64:
  687. case MetadataType.Int64:
  688. return arg;
  689. }
  690. return expr;
  691. }
  692. static ILExpression Cast(ILExpression expr, TypeReference type)
  693. {
  694. return new ILExpression(ILCode.Castclass, type, expr)
  695. {
  696. InferredType = type,
  697. ExpectedType = type
  698. };
  699. }
  700. void DivideOrMultiplyBySize(ref ILExpression pointerExpr, ref ILExpression adjustmentExpr, TypeReference elementType, bool divide)
  701. {
  702. adjustmentExpr = UnwrapIntPtrCast(adjustmentExpr);
  703. ILExpression sizeOfExpression;
  704. switch (TypeAnalysis.GetInformationAmount(elementType)) {
  705. case 0: // System.Void
  706. pointerExpr = Cast(pointerExpr, new PointerType(typeSystem.Byte));
  707. goto case 1;
  708. case 1:
  709. case 8:
  710. sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 1);
  711. break;
  712. case 16:
  713. sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 2);
  714. break;
  715. case 32:
  716. sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 4);
  717. break;
  718. case 64:
  719. sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 8);
  720. break;
  721. default:
  722. sizeOfExpression = new ILExpression(ILCode.Sizeof, elementType);
  723. break;
  724. }
  725. if (divide && (adjustmentExpr.Code == ILCode.Mul || adjustmentExpr.Code == ILCode.Mul_Ovf || adjustmentExpr.Code == ILCode.Mul_Ovf_Un) ||
  726. !divide && (adjustmentExpr.Code == ILCode.Div || adjustmentExpr.Code == ILCode.Div_Un)) {
  727. ILExpression mulArg = adjustmentExpr.Arguments[1];
  728. if (mulArg.Code == sizeOfExpression.Code && sizeOfExpression.Operand.Equals(mulArg.Operand)) {
  729. adjustmentExpr = UnwrapIntPtrCast(adjustmentExpr.Arguments[0]);
  730. return;
  731. }
  732. }
  733. if (adjustmentExpr.Code == sizeOfExpression.Code) {
  734. if (sizeOfExpression.Operand.Equals(adjustmentExpr.Operand)) {
  735. adjustmentExpr = new ILExpression(ILCode.Ldc_I4, 1);
  736. return;
  737. }
  738. if (adjustmentExpr.Code == ILCode.Ldc_I4) {
  739. int offsetInBytes = (int)adjustmentExpr.Operand;
  740. int elementSize = (int)sizeOfExpression.Operand;
  741. if (offsetInBytes % elementSize != 0) {
  742. pointerExpr = Cast(pointerExpr, new PointerType(typeSystem.Byte));
  743. return;
  744. }
  745. adjustmentExpr.Operand = offsetInBytes / elementSize;
  746. return;
  747. }
  748. }
  749. if (!(sizeOfExpression.Code == ILCode.Ldc_I4 && (int)sizeOfExpression.Operand == 1))
  750. adjustmentExpr = new ILExpression(divide ? ILCode.Div_Un : ILCode.Mul, null, adjustmentExpr, sizeOfExpression);
  751. }
  752. public static void ReplaceVariables(ILNode node, Func<ILVariable, ILVariable> variableMapping)
  753. {
  754. ILExpression expr = node as ILExpression;
  755. if (expr != null) {
  756. ILVariable v = expr.Operand as ILVariable;
  757. if (v != null)
  758. expr.Operand = variableMapping(v);
  759. foreach (ILExpression child in expr.Arguments)
  760. ReplaceVariables(child, variableMapping);
  761. } else {
  762. var catchBlock = node as ILTryCatchBlock.CatchBlock;
  763. if (catchBlock != null && catchBlock.ExceptionVariable != null) {
  764. catchBlock.ExceptionVariable = variableMapping(catchBlock.ExceptionVariable);
  765. }
  766. foreach (ILNode child in node.GetChildren())
  767. ReplaceVariables(child, variableMapping);
  768. }
  769. }
  770. void ReportUnassignedILRanges(ILBlock method)
  771. {
  772. var unassigned = ILRange.Invert(method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges), context.CurrentMethod.Body.CodeSize).ToList();
  773. if (unassigned.Count > 0)
  774. Debug.WriteLine(string.Format("Unassigned ILRanges for {0}.{1}: {2}", this.context.CurrentMethod.DeclaringType.Name, this.context.CurrentMethod.Name, string.Join(", ", unassigned.Select(r => r.ToString()))));
  775. }
  776. }
  777. public static class ILAstOptimizerExtensionMethods
  778. {
  779. /// <summary>
  780. /// Perform one pass of a given optimization on this block.
  781. /// This block must consist of only basicblocks.
  782. /// </summary>
  783. public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILBasicBlock, int, bool> optimization)
  784. {
  785. bool modified = false;
  786. List<ILNode> body = block.Body;
  787. for (int i = body.Count - 1; i >= 0; i--) {
  788. if (i < body.Count && optimization(body, (ILBasicBlock)body[i], i)) {
  789. modified = true;
  790. }
  791. }
  792. return modified;
  793. }
  794. public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILExpression, int, bool> optimization)
  795. {
  796. bool modified = false;
  797. foreach (ILBasicBlock bb in block.Body) {
  798. for (int i = bb.Body.Count - 1; i >= 0; i--) {
  799. ILExpression expr = bb.Body.ElementAtOrDefault(i) as ILExpression;
  800. if (expr != null && optimization(bb.Body, expr, i)) {
  801. modified = true;
  802. }
  803. }
  804. }
  805. return modified;
  806. }
  807. public static bool IsConditionalControlFlow(this ILNode node)
  808. {
  809. ILExpression expr = node as ILExpression;
  810. return expr != null && expr.Code.IsConditionalControlFlow();
  811. }
  812. public static bool IsUnconditionalControlFlow(this ILNode node)
  813. {
  814. ILExpression expr = node as ILExpression;
  815. return expr != null && expr.Code.IsUnconditionalControlFlow();
  816. }
  817. /// <summary>
  818. /// The expression has no effect on the program and can be removed
  819. /// if its return value is not needed.
  820. /// </summary>
  821. public static bool HasNoSideEffects(this ILExpression expr)
  822. {
  823. // Remember that if expression can throw an exception, it is a side effect
  824. switch(expr.Code) {
  825. case ILCode.Ldloc:
  826. case ILCode.Ldloca:
  827. case ILCode.Ldstr:
  828. case ILCode.Ldnull:
  829. case ILCode.Ldc_I4:
  830. case ILCode.Ldc_I8:
  831. case ILCode.Ldc_R4:
  832. case ILCode.Ldc_R8:
  833. case ILCode.Ldc_Decimal:
  834. return true;
  835. default:
  836. return false;
  837. }
  838. }
  839. public static bool IsStoreToArray(this ILCode code)
  840. {
  841. switch (code) {
  842. case ILCode.Stelem_Any:
  843. case ILCode.Stelem_I:
  844. case ILCode.Stelem_I1:
  845. case ILCode.Stelem_I2:
  846. case ILCode.Stelem_I4:
  847. case ILCode.Stelem_I8:
  848. case ILCode.Stelem_R4:
  849. case ILCode.Stelem_R8:
  850. case ILCode.Stelem_Ref:
  851. return true;
  852. default:
  853. return false;
  854. }
  855. }
  856. public static bool IsLoadFromArray(this ILCode code)
  857. {
  858. switch (code) {
  859. case ILCode.Ldelem_Any:
  860. case ILCode.Ldelem_I:
  861. case ILCode.Ldelem_I1:
  862. case ILCode.Ldelem_I2:
  863. case ILCode.Ldelem_I4:
  864. case ILCode.Ldelem_I8:
  865. case ILCode.Ldelem_U1:
  866. case ILCode.Ldelem_U2:
  867. case ILCode.Ldelem_U4:
  868. case ILCode.Ldelem_R4:
  869. case ILCode.Ldelem_R8:
  870. case ILCode.Ldelem_Ref:
  871. return true;
  872. default:
  873. return false;
  874. }
  875. }
  876. /// <summary>
  877. /// Can the expression be used as a statement in C#?
  878. /// </summary>
  879. public static bool CanBeExpressionStatement(this ILExpression expr)
  880. {
  881. switch(expr.Code) {
  882. case ILCode.Call:
  883. case ILCode.Callvirt:
  884. // property getters can't be expression statements, but all other method calls can be
  885. MethodReference mr = (MethodReference)expr.Operand;
  886. return !mr.Name.StartsWith("get_", StringComparison.Ordinal);
  887. case ILCode.CallSetter:
  888. case ILCode.CallvirtSetter:
  889. case ILCode.Newobj:
  890. case ILCode.Newarr:
  891. case ILCode.Stloc:
  892. case ILCode.Stobj:
  893. case ILCode.Stsfld:
  894. case ILCode.Stfld:
  895. case ILCode.Stind_Ref:
  896. case ILCode.Stelem_Any:
  897. case ILCode.Stelem_I:
  898. case ILCode.Stelem_I1:
  899. case ILCode.Stelem_I2:
  900. case ILCode.Stelem_I4:
  901. case ILCode.Stelem_I8:
  902. case ILCode.Stelem_R4:
  903. case ILCode.Stelem_R8:
  904. case ILCode.Stelem_Ref:
  905. return true;
  906. default:
  907. return false;
  908. }
  909. }
  910. public static ILExpression WithILRanges(this ILExpression expr, IEnumerable<ILRange> ilranges)
  911. {
  912. expr.ILRanges.AddRange(ilranges);
  913. return expr;
  914. }
  915. public static void RemoveTail(this List<ILNode> body, params ILCode[] codes)
  916. {
  917. for (int i = 0; i < codes.Length; i++) {
  918. if (((ILExpression)body[body.Count - codes.Length + i]).Code != codes[i])
  919. throw new Exception("Tailing code does not match expected.");
  920. }
  921. body.RemoveRange(body.Count - codes.Length, codes.Length);
  922. }
  923. public static V GetOrDefault<K,V>(this Dictionary<K, V> dict, K key)
  924. {
  925. V ret;
  926. dict.TryGetValue(key, out ret);
  927. return ret;
  928. }
  929. public static void RemoveOrThrow<T>(this ICollection<T> collection, T item)
  930. {
  931. if (!collection.Remove(item))
  932. throw new Exception("The item was not found in the collection");
  933. }
  934. public static void RemoveOrThrow<K,V>(this Dictionary<K,V> collection, K key)
  935. {
  936. if (!collection.Remove(key))
  937. throw new Exception("The key was not found in the dictionary");
  938. }
  939. public static bool ContainsReferenceTo(this ILExpression expr, ILVariable v)
  940. {
  941. if (expr.Operand == v)
  942. return true;
  943. foreach (var arg in expr.Arguments) {
  944. if (ContainsReferenceTo(arg, v))
  945. return true;
  946. }
  947. return false;
  948. }
  949. }
  950. }