PageRenderTime 83ms CodeModel.GetById 20ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 1ms

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