mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							2038 lines
						
					
					
						
							70 KiB
						
					
					
				
			
		
		
	
	
							2038 lines
						
					
					
						
							70 KiB
						
					
					
				// Copyright (c) 2014 Daniel Grunwald | 
						|
//  | 
						|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this | 
						|
// software and associated documentation files (the "Software"), to deal in the Software | 
						|
// without restriction, including without limitation the rights to use, copy, modify, merge, | 
						|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons | 
						|
// to whom the Software is furnished to do so, subject to the following conditions: | 
						|
//  | 
						|
// The above copyright notice and this permission notice shall be included in all copies or | 
						|
// substantial portions of the Software. | 
						|
//  | 
						|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | 
						|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | 
						|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE | 
						|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | 
						|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
						|
// DEALINGS IN THE SOFTWARE. | 
						|
 | 
						|
using System; | 
						|
using System.Collections; | 
						|
using System.Collections.Generic; | 
						|
using System.Collections.Immutable; | 
						|
using System.Diagnostics; | 
						|
using System.Linq; | 
						|
using System.Reflection.Metadata; | 
						|
using System.Reflection.Metadata.Ecma335; | 
						|
using System.Threading; | 
						|
 | 
						|
using ICSharpCode.Decompiler.Disassembler; | 
						|
using ICSharpCode.Decompiler.TypeSystem; | 
						|
using ICSharpCode.Decompiler.Util; | 
						|
 | 
						|
using ArrayType = ICSharpCode.Decompiler.TypeSystem.ArrayType; | 
						|
using ByReferenceType = ICSharpCode.Decompiler.TypeSystem.ByReferenceType; | 
						|
using PinnedType = ICSharpCode.Decompiler.TypeSystem.Implementation.PinnedType; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.IL | 
						|
{ | 
						|
	/// <summary> | 
						|
	/// Reads IL bytecodes and converts them into ILAst instructions. | 
						|
	/// </summary> | 
						|
	/// <remarks> | 
						|
	/// Instances of this class are not thread-safe. Use separate instances to decompile multiple members in parallel. | 
						|
	/// </remarks> | 
						|
	public class ILReader | 
						|
	{ | 
						|
		readonly ICompilation compilation; | 
						|
		readonly MetadataModule module; | 
						|
		readonly MetadataReader metadata; | 
						|
 | 
						|
		public bool UseDebugSymbols { get; set; } | 
						|
		public DebugInfo.IDebugInfoProvider DebugInfo { get; set; } | 
						|
		public List<string> Warnings { get; } = new List<string>(); | 
						|
 | 
						|
		// List of candidate locations for sequence points. Includes empty il stack locations, any nop instructions, and the instruction following | 
						|
		// a call instruction.  | 
						|
		public List<int> SequencePointCandidates { get; private set; } | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Creates a new ILReader instance. | 
						|
		/// </summary> | 
						|
		/// <param name="module"> | 
						|
		/// The module used to resolve metadata tokens in the type system. | 
						|
		/// </param> | 
						|
		public ILReader(MetadataModule module) | 
						|
		{ | 
						|
			if (module == null) | 
						|
				throw new ArgumentNullException(nameof(module)); | 
						|
			this.module = module; | 
						|
			this.compilation = module.Compilation; | 
						|
			this.metadata = module.metadata; | 
						|
			this.SequencePointCandidates = new List<int>(); | 
						|
		} | 
						|
 | 
						|
		GenericContext genericContext; | 
						|
		IMethod method; | 
						|
		MethodBodyBlock body; | 
						|
		StackType methodReturnStackType; | 
						|
		BlobReader reader; | 
						|
		ImmutableStack<ILVariable> currentStack; | 
						|
		List<ILInstruction> expressionStack; | 
						|
		ILVariable[] parameterVariables; | 
						|
		ILVariable[] localVariables; | 
						|
		BitSet isBranchTarget; | 
						|
		BlockContainer mainContainer; | 
						|
		List<ILInstruction> instructionBuilder; | 
						|
		int currentInstructionStart; | 
						|
 | 
						|
		// Dictionary that stores stacks for each IL instruction | 
						|
		Dictionary<int, ImmutableStack<ILVariable>> stackByOffset; | 
						|
		Dictionary<ExceptionRegion, ILVariable> variableByExceptionHandler; | 
						|
		UnionFind<ILVariable> unionFind; | 
						|
		List<(ILVariable, ILVariable)> stackMismatchPairs; | 
						|
		IEnumerable<ILVariable> stackVariables; | 
						|
 | 
						|
		void Init(MethodDefinitionHandle methodDefinitionHandle, MethodBodyBlock body, GenericContext genericContext) | 
						|
		{ | 
						|
			if (body == null) | 
						|
				throw new ArgumentNullException(nameof(body)); | 
						|
			if (methodDefinitionHandle.IsNil) | 
						|
				throw new ArgumentException("methodDefinitionHandle.IsNil"); | 
						|
			this.method = module.GetDefinition(methodDefinitionHandle); | 
						|
			if (genericContext.ClassTypeParameters == null && genericContext.MethodTypeParameters == null) | 
						|
			{ | 
						|
				// no generic context specified: use the method's own type parameters | 
						|
				genericContext = new GenericContext(method); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// generic context specified, so specialize the method for it: | 
						|
				this.method = this.method.Specialize(genericContext.ToSubstitution()); | 
						|
			} | 
						|
			this.genericContext = genericContext; | 
						|
			this.body = body; | 
						|
			this.reader = body.GetILReader(); | 
						|
			this.currentStack = ImmutableStack<ILVariable>.Empty; | 
						|
			this.expressionStack = new List<ILInstruction>(); | 
						|
			this.unionFind = new UnionFind<ILVariable>(); | 
						|
			this.stackMismatchPairs = new List<(ILVariable, ILVariable)>(); | 
						|
			this.methodReturnStackType = method.ReturnType.GetStackType(); | 
						|
			InitParameterVariables(); | 
						|
			localVariables = InitLocalVariables(); | 
						|
			foreach (var v in localVariables) | 
						|
			{ | 
						|
				v.InitialValueIsInitialized = body.LocalVariablesInitialized; | 
						|
				v.UsesInitialValue = true; | 
						|
			} | 
						|
			this.mainContainer = new BlockContainer(expectedResultType: methodReturnStackType); | 
						|
			this.instructionBuilder = new List<ILInstruction>(); | 
						|
			this.isBranchTarget = new BitSet(reader.Length); | 
						|
			this.stackByOffset = new Dictionary<int, ImmutableStack<ILVariable>>(); | 
						|
			this.variableByExceptionHandler = new Dictionary<ExceptionRegion, ILVariable>(); | 
						|
		} | 
						|
 | 
						|
		EntityHandle ReadAndDecodeMetadataToken() | 
						|
		{ | 
						|
			int token = reader.ReadInt32(); | 
						|
			if (token <= 0) | 
						|
			{ | 
						|
				// SRM uses negative tokens as "virtual tokens" and can get confused | 
						|
				// if we manually create them. | 
						|
				// Row-IDs < 1 are always invalid. | 
						|
				throw new BadImageFormatException("Invalid metadata token"); | 
						|
			} | 
						|
			return MetadataTokens.EntityHandle(token); | 
						|
		} | 
						|
 | 
						|
		IType ReadAndDecodeTypeReference() | 
						|
		{ | 
						|
			var typeReference = ReadAndDecodeMetadataToken(); | 
						|
			return module.ResolveType(typeReference, genericContext); | 
						|
		} | 
						|
 | 
						|
		IMethod ReadAndDecodeMethodReference() | 
						|
		{ | 
						|
			var methodReference = ReadAndDecodeMetadataToken(); | 
						|
			return module.ResolveMethod(methodReference, genericContext); | 
						|
		} | 
						|
 | 
						|
		IField ReadAndDecodeFieldReference() | 
						|
		{ | 
						|
			var fieldReference = ReadAndDecodeMetadataToken(); | 
						|
			var f = module.ResolveEntity(fieldReference, genericContext) as IField; | 
						|
			if (f == null) | 
						|
				throw new BadImageFormatException("Invalid field token"); | 
						|
			return f; | 
						|
		} | 
						|
 | 
						|
		ILVariable[] InitLocalVariables() | 
						|
		{ | 
						|
			if (body.LocalSignature.IsNil) | 
						|
				return Empty<ILVariable>.Array; | 
						|
			ImmutableArray<IType> variableTypes; | 
						|
			try | 
						|
			{ | 
						|
				variableTypes = module.DecodeLocalSignature(body.LocalSignature, genericContext); | 
						|
			} | 
						|
			catch (BadImageFormatException ex) | 
						|
			{ | 
						|
				Warnings.Add("Error decoding local variables: " + ex.Message); | 
						|
				variableTypes = ImmutableArray<IType>.Empty; | 
						|
			} | 
						|
			var localVariables = new ILVariable[variableTypes.Length]; | 
						|
			foreach (var (index, type) in variableTypes.WithIndex()) | 
						|
			{ | 
						|
				localVariables[index] = CreateILVariable(index, type); | 
						|
			} | 
						|
			return localVariables; | 
						|
		} | 
						|
 | 
						|
		void InitParameterVariables() | 
						|
		{ | 
						|
			int popCount = method.Parameters.Count; | 
						|
			if (!method.IsStatic) | 
						|
				popCount++; | 
						|
			if (method.Parameters.LastOrDefault()?.Type == SpecialType.ArgList) | 
						|
				popCount--; | 
						|
			parameterVariables = new ILVariable[popCount]; | 
						|
			int paramIndex = 0; | 
						|
			int offset = 0; | 
						|
			if (!method.IsStatic) | 
						|
			{ | 
						|
				offset = 1; | 
						|
				IType declaringType = method.DeclaringType; | 
						|
				if (declaringType.IsUnbound()) | 
						|
				{ | 
						|
					// If method is a definition (and not specialized), the declaring type is also just a definition, | 
						|
					// and needs to be converted into a normally usable type. | 
						|
					declaringType = new ParameterizedType(declaringType, declaringType.TypeParameters); | 
						|
				} | 
						|
				ILVariable ilVar = CreateILVariable(-1, declaringType, "this"); | 
						|
				ilVar.IsRefReadOnly = method.ThisIsRefReadOnly; | 
						|
				parameterVariables[paramIndex++] = ilVar; | 
						|
			} | 
						|
			while (paramIndex < parameterVariables.Length) | 
						|
			{ | 
						|
				IParameter parameter = method.Parameters[paramIndex - offset]; | 
						|
				ILVariable ilVar = CreateILVariable(paramIndex - offset, parameter.Type, parameter.Name); | 
						|
				ilVar.IsRefReadOnly = parameter.IsIn; | 
						|
				parameterVariables[paramIndex] = ilVar; | 
						|
				paramIndex++; | 
						|
			} | 
						|
			Debug.Assert(paramIndex == parameterVariables.Length); | 
						|
		} | 
						|
 | 
						|
		ILVariable CreateILVariable(int index, IType type) | 
						|
		{ | 
						|
			VariableKind kind; | 
						|
			if (type.SkipModifiers() is PinnedType pinned) | 
						|
			{ | 
						|
				kind = VariableKind.PinnedLocal; | 
						|
				type = pinned.ElementType; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				kind = VariableKind.Local; | 
						|
			} | 
						|
			ILVariable ilVar = new ILVariable(kind, type, index); | 
						|
			if (!UseDebugSymbols || DebugInfo == null || !DebugInfo.TryGetName((MethodDefinitionHandle)method.MetadataToken, index, out string name)) | 
						|
			{ | 
						|
				ilVar.Name = "V_" + index; | 
						|
				ilVar.HasGeneratedName = true; | 
						|
			} | 
						|
			else if (string.IsNullOrWhiteSpace(name)) | 
						|
			{ | 
						|
				ilVar.Name = "V_" + index; | 
						|
				ilVar.HasGeneratedName = true; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				ilVar.Name = name; | 
						|
			} | 
						|
			return ilVar; | 
						|
		} | 
						|
 | 
						|
		ILVariable CreateILVariable(int index, IType parameterType, string name) | 
						|
		{ | 
						|
			Debug.Assert(!parameterType.IsUnbound()); | 
						|
			ITypeDefinition def = parameterType.GetDefinition(); | 
						|
			if (def != null && index < 0 && def.IsReferenceType == false) | 
						|
			{ | 
						|
				parameterType = new ByReferenceType(parameterType); | 
						|
			} | 
						|
			var ilVar = new ILVariable(VariableKind.Parameter, parameterType, index); | 
						|
			Debug.Assert(ilVar.StoreCount == 1); // count the initial store when the method is called with an argument | 
						|
			if (index < 0) | 
						|
				ilVar.Name = "this"; | 
						|
			else if (string.IsNullOrEmpty(name)) | 
						|
				ilVar.Name = "P_" + index; | 
						|
			else | 
						|
				ilVar.Name = name; | 
						|
			return ilVar; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Warn when invalid IL is detected. | 
						|
		/// ILSpy should be able to handle invalid IL; but this method can be helpful for debugging the ILReader, | 
						|
		/// as this method should not get called when processing valid IL. | 
						|
		/// </summary> | 
						|
		void Warn(string message) | 
						|
		{ | 
						|
			Warnings.Add(string.Format("IL_{0:x4}: {1}", currentInstructionStart, message)); | 
						|
		} | 
						|
 | 
						|
		ImmutableStack<ILVariable> MergeStacks(ImmutableStack<ILVariable> a, ImmutableStack<ILVariable> b) | 
						|
		{ | 
						|
			if (CheckStackCompatibleWithoutAdjustments(a, b)) | 
						|
			{ | 
						|
				// We only need to union the input variables, but can  | 
						|
				// otherwise re-use the existing stack. | 
						|
				ImmutableStack<ILVariable> output = a; | 
						|
				while (!a.IsEmpty && !b.IsEmpty) | 
						|
				{ | 
						|
					Debug.Assert(a.Peek().StackType == b.Peek().StackType); | 
						|
					unionFind.Merge(a.Peek(), b.Peek()); | 
						|
					a = a.Pop(); | 
						|
					b = b.Pop(); | 
						|
				} | 
						|
				return output; | 
						|
			} | 
						|
			else if (a.Count() != b.Count()) | 
						|
			{ | 
						|
				// Let's not try to merge mismatched stacks. | 
						|
				Warn("Incompatible stack heights: " + a.Count() + " vs " + b.Count()); | 
						|
				return a; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// The more complex case where the stacks don't match exactly. | 
						|
				var output = new List<ILVariable>(); | 
						|
				while (!a.IsEmpty && !b.IsEmpty) | 
						|
				{ | 
						|
					var varA = a.Peek(); | 
						|
					var varB = b.Peek(); | 
						|
					if (varA.StackType == varB.StackType) | 
						|
					{ | 
						|
						unionFind.Merge(varA, varB); | 
						|
						output.Add(varA); | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						if (!IsValidTypeStackTypeMerge(varA.StackType, varB.StackType)) | 
						|
						{ | 
						|
							Warn("Incompatible stack types: " + varA.StackType + " vs " + varB.StackType); | 
						|
						} | 
						|
						if (varA.StackType > varB.StackType) | 
						|
						{ | 
						|
							output.Add(varA); | 
						|
							// every store to varB should also store to varA | 
						|
							stackMismatchPairs.Add((varB, varA)); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							output.Add(varB); | 
						|
							// every store to varA should also store to varB | 
						|
							stackMismatchPairs.Add((varA, varB)); | 
						|
						} | 
						|
					} | 
						|
					a = a.Pop(); | 
						|
					b = b.Pop(); | 
						|
				} | 
						|
				// because we built up output by popping from the input stacks, we need to reverse it to get back the original order | 
						|
				output.Reverse(); | 
						|
				return ImmutableStack.CreateRange(output); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		static bool CheckStackCompatibleWithoutAdjustments(ImmutableStack<ILVariable> a, ImmutableStack<ILVariable> b) | 
						|
		{ | 
						|
			while (!a.IsEmpty && !b.IsEmpty) | 
						|
			{ | 
						|
				if (a.Peek().StackType != b.Peek().StackType) | 
						|
					return false; | 
						|
				a = a.Pop(); | 
						|
				b = b.Pop(); | 
						|
			} | 
						|
			return a.IsEmpty && b.IsEmpty; | 
						|
		} | 
						|
 | 
						|
		private bool IsValidTypeStackTypeMerge(StackType stackType1, StackType stackType2) | 
						|
		{ | 
						|
			if (stackType1 == StackType.I && stackType2 == StackType.I4) | 
						|
				return true; | 
						|
			if (stackType1 == StackType.I4 && stackType2 == StackType.I) | 
						|
				return true; | 
						|
			if (stackType1 == StackType.F4 && stackType2 == StackType.F8) | 
						|
				return true; | 
						|
			if (stackType1 == StackType.F8 && stackType2 == StackType.F4) | 
						|
				return true; | 
						|
			// allow merging unknown type with any other type | 
						|
			return stackType1 == StackType.Unknown || stackType2 == StackType.Unknown; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Stores the given stack for a branch to `offset`. | 
						|
		///  | 
						|
		/// The stack may be modified if stack adjustments are necessary. (e.g. implicit I4->I conversion) | 
						|
		/// </summary> | 
						|
		void StoreStackForOffset(int offset, ref ImmutableStack<ILVariable> stack) | 
						|
		{ | 
						|
			if (stackByOffset.TryGetValue(offset, out var existing)) | 
						|
			{ | 
						|
				stack = MergeStacks(existing, stack); | 
						|
				if (stack != existing) | 
						|
					stackByOffset[offset] = stack; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				stackByOffset.Add(offset, stack); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		void ReadInstructions(CancellationToken cancellationToken) | 
						|
		{ | 
						|
			reader.Reset(); | 
						|
			ILParser.SetBranchTargets(ref reader, isBranchTarget); | 
						|
			reader.Reset(); | 
						|
			PrepareBranchTargetsAndStacksForExceptionHandlers(); | 
						|
 | 
						|
			bool nextInstructionBeginsNewBlock = false; | 
						|
 | 
						|
			reader.Reset(); | 
						|
			while (reader.RemainingBytes > 0) | 
						|
			{ | 
						|
				cancellationToken.ThrowIfCancellationRequested(); | 
						|
				int start = reader.Offset; | 
						|
				if (isBranchTarget[start]) | 
						|
				{ | 
						|
					FlushExpressionStack(); | 
						|
					StoreStackForOffset(start, ref currentStack); | 
						|
				} | 
						|
				currentInstructionStart = start; | 
						|
				bool startedWithEmptyStack = CurrentStackIsEmpty(); | 
						|
				DecodedInstruction decodedInstruction; | 
						|
				try | 
						|
				{ | 
						|
					decodedInstruction = DecodeInstruction(); | 
						|
				} | 
						|
				catch (BadImageFormatException ex) | 
						|
				{ | 
						|
					decodedInstruction = new InvalidBranch(ex.Message); | 
						|
				} | 
						|
				var inst = decodedInstruction.Instruction; | 
						|
				if (inst.ResultType == StackType.Unknown && inst.OpCode != OpCode.InvalidBranch && inst.OpCode != OpCode.InvalidExpression) | 
						|
					Warn("Unknown result type (might be due to invalid IL or missing references)"); | 
						|
				inst.CheckInvariant(ILPhase.InILReader); | 
						|
				int end = reader.Offset; | 
						|
				inst.AddILRange(new Interval(start, end)); | 
						|
				if (!decodedInstruction.PushedOnExpressionStack) | 
						|
				{ | 
						|
					// Flush to avoid re-ordering of side-effects | 
						|
					FlushExpressionStack(); | 
						|
					instructionBuilder.Add(inst); | 
						|
				} | 
						|
				else if (isBranchTarget[start] || nextInstructionBeginsNewBlock) | 
						|
				{ | 
						|
					// If this instruction is the first in a new block, avoid it being inlined | 
						|
					// into the next instruction. | 
						|
					// This is necessary because the BlockBuilder uses inst.StartILOffset to | 
						|
					// detect block starts, and doesn't search nested instructions. | 
						|
					FlushExpressionStack(); | 
						|
				} | 
						|
				if (inst.HasDirectFlag(InstructionFlags.EndPointUnreachable)) | 
						|
				{ | 
						|
					FlushExpressionStack(); | 
						|
					if (!stackByOffset.TryGetValue(end, out currentStack)) | 
						|
					{ | 
						|
						currentStack = ImmutableStack<ILVariable>.Empty; | 
						|
					} | 
						|
					nextInstructionBeginsNewBlock = true; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					nextInstructionBeginsNewBlock = inst.HasFlag(InstructionFlags.MayBranch); | 
						|
				} | 
						|
 | 
						|
				if ((!decodedInstruction.PushedOnExpressionStack && IsSequencePointInstruction(inst)) || startedWithEmptyStack) | 
						|
				{ | 
						|
					this.SequencePointCandidates.Add(inst.StartILOffset); | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			FlushExpressionStack(); | 
						|
 | 
						|
			var visitor = new CollectStackVariablesVisitor(unionFind); | 
						|
			for (int i = 0; i < instructionBuilder.Count; i++) | 
						|
			{ | 
						|
				instructionBuilder[i] = instructionBuilder[i].AcceptVisitor(visitor); | 
						|
			} | 
						|
			stackVariables = visitor.variables; | 
						|
			InsertStackAdjustments(); | 
						|
		} | 
						|
 | 
						|
		private bool CurrentStackIsEmpty() | 
						|
		{ | 
						|
			return currentStack.IsEmpty && expressionStack.Count == 0; | 
						|
		} | 
						|
 | 
						|
		private void PrepareBranchTargetsAndStacksForExceptionHandlers() | 
						|
		{ | 
						|
			// Fill isBranchTarget and branchStackDict based on exception handlers | 
						|
			foreach (var eh in body.ExceptionRegions) | 
						|
			{ | 
						|
				// Always mark the start of the try block as a "branch target". | 
						|
				// We don't actually need to store the stack state here, | 
						|
				// but we need to ensure that no ILInstructions are inlined | 
						|
				// into the try-block. | 
						|
				isBranchTarget[eh.TryOffset] = true; | 
						|
 | 
						|
				ImmutableStack<ILVariable> ehStack; | 
						|
				if (eh.Kind == ExceptionRegionKind.Catch) | 
						|
				{ | 
						|
					var catchType = module.ResolveType(eh.CatchType, genericContext); | 
						|
					var v = new ILVariable(VariableKind.ExceptionStackSlot, catchType, eh.HandlerOffset) { | 
						|
						Name = "E_" + eh.HandlerOffset, | 
						|
						HasGeneratedName = true | 
						|
					}; | 
						|
					variableByExceptionHandler.Add(eh, v); | 
						|
					ehStack = ImmutableStack.Create(v); | 
						|
				} | 
						|
				else if (eh.Kind == ExceptionRegionKind.Filter) | 
						|
				{ | 
						|
					var v = new ILVariable(VariableKind.ExceptionStackSlot, compilation.FindType(KnownTypeCode.Object), eh.HandlerOffset) { | 
						|
						Name = "E_" + eh.HandlerOffset, | 
						|
						HasGeneratedName = true | 
						|
					}; | 
						|
					variableByExceptionHandler.Add(eh, v); | 
						|
					ehStack = ImmutableStack.Create(v); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					ehStack = ImmutableStack<ILVariable>.Empty; | 
						|
				} | 
						|
				if (eh.FilterOffset != -1) | 
						|
				{ | 
						|
					isBranchTarget[eh.FilterOffset] = true; | 
						|
					StoreStackForOffset(eh.FilterOffset, ref ehStack); | 
						|
				} | 
						|
				if (eh.HandlerOffset != -1) | 
						|
				{ | 
						|
					isBranchTarget[eh.HandlerOffset] = true; | 
						|
					StoreStackForOffset(eh.HandlerOffset, ref ehStack); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private bool IsSequencePointInstruction(ILInstruction instruction) | 
						|
		{ | 
						|
			if (instruction.OpCode == OpCode.Nop || | 
						|
				(instructionBuilder.Count > 0 | 
						|
				&& instructionBuilder.Last().OpCode is OpCode.Call | 
						|
													or OpCode.CallIndirect | 
						|
													or OpCode.CallVirt)) | 
						|
			{ | 
						|
 | 
						|
				return true; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return false; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		void InsertStackAdjustments() | 
						|
		{ | 
						|
			if (stackMismatchPairs.Count == 0) | 
						|
				return; | 
						|
			var dict = new MultiDictionary<ILVariable, ILVariable>(); | 
						|
			foreach (var (origA, origB) in stackMismatchPairs) | 
						|
			{ | 
						|
				var a = unionFind.Find(origA); | 
						|
				var b = unionFind.Find(origB); | 
						|
				Debug.Assert(a.StackType < b.StackType); | 
						|
				// For every store to a, insert a converting store to b. | 
						|
				if (!dict[a].Contains(b)) | 
						|
					dict.Add(a, b); | 
						|
			} | 
						|
			var newInstructions = new List<ILInstruction>(); | 
						|
			foreach (var inst in instructionBuilder) | 
						|
			{ | 
						|
				newInstructions.Add(inst); | 
						|
				if (inst is StLoc store) | 
						|
				{ | 
						|
					foreach (var additionalVar in dict[store.Variable]) | 
						|
					{ | 
						|
						ILInstruction value = new LdLoc(store.Variable); | 
						|
						value = new Conv(value, additionalVar.StackType.ToPrimitiveType(), false, Sign.Signed); | 
						|
						newInstructions.Add(new StLoc(additionalVar, value) { | 
						|
							IsStackAdjustment = true, | 
						|
						}.WithILRange(inst)); | 
						|
					} | 
						|
				} | 
						|
			} | 
						|
			instructionBuilder = newInstructions; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Debugging helper: writes the decoded instruction stream interleaved with the inferred evaluation stack layout. | 
						|
		/// </summary> | 
						|
		public void WriteTypedIL(MethodDefinitionHandle method, MethodBodyBlock body, | 
						|
			ITextOutput output, GenericContext genericContext = default, CancellationToken cancellationToken = default) | 
						|
		{ | 
						|
			Init(method, body, genericContext); | 
						|
			ReadInstructions(cancellationToken); | 
						|
			foreach (var inst in instructionBuilder) | 
						|
			{ | 
						|
				if (inst is StLoc stloc && stloc.IsStackAdjustment) | 
						|
				{ | 
						|
					output.Write("          "); | 
						|
					inst.WriteTo(output, new ILAstWritingOptions()); | 
						|
					output.WriteLine(); | 
						|
					continue; | 
						|
				} | 
						|
				if (stackByOffset.TryGetValue(inst.StartILOffset, out var stack)) | 
						|
				{ | 
						|
					output.Write("   ["); | 
						|
					bool isFirstElement = true; | 
						|
					foreach (var element in stack) | 
						|
					{ | 
						|
						if (isFirstElement) | 
						|
							isFirstElement = false; | 
						|
						else | 
						|
							output.Write(", "); | 
						|
						output.WriteLocalReference(element.Name, element); | 
						|
						output.Write(":"); | 
						|
						output.Write(element.StackType); | 
						|
					} | 
						|
					output.Write(']'); | 
						|
					output.WriteLine(); | 
						|
				} | 
						|
				if (isBranchTarget[inst.StartILOffset]) | 
						|
					output.Write('*'); | 
						|
				else | 
						|
					output.Write(' '); | 
						|
				output.WriteLocalReference("IL_" + inst.StartILOffset.ToString("x4"), inst.StartILOffset, isDefinition: true); | 
						|
				output.Write(": "); | 
						|
				inst.WriteTo(output, new ILAstWritingOptions()); | 
						|
				output.WriteLine(); | 
						|
			} | 
						|
			new Disassembler.MethodBodyDisassembler(output, cancellationToken) { DetectControlStructure = false } | 
						|
				.WriteExceptionHandlers(module.PEFile, method, body); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Decodes the specified method body and returns an ILFunction. | 
						|
		/// </summary> | 
						|
		public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, ILFunctionKind kind = ILFunctionKind.TopLevelFunction, CancellationToken cancellationToken = default) | 
						|
		{ | 
						|
			cancellationToken.ThrowIfCancellationRequested(); | 
						|
			Init(method, body, genericContext); | 
						|
			ReadInstructions(cancellationToken); | 
						|
			var blockBuilder = new BlockBuilder(body, variableByExceptionHandler, compilation); | 
						|
			blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken); | 
						|
			var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind); | 
						|
			function.Variables.AddRange(parameterVariables); | 
						|
			function.Variables.AddRange(localVariables); | 
						|
			function.Variables.AddRange(stackVariables); | 
						|
			function.Variables.AddRange(variableByExceptionHandler.Values); | 
						|
			function.Variables.AddRange(blockBuilder.OnErrorDispatcherVariables); | 
						|
			function.AddRef(); // mark the root node | 
						|
			var removedBlocks = new List<Block>(); | 
						|
			foreach (var c in function.Descendants.OfType<BlockContainer>()) | 
						|
			{ | 
						|
				var newOrder = c.TopologicalSort(deleteUnreachableBlocks: true); | 
						|
				if (newOrder.Count < c.Blocks.Count) | 
						|
				{ | 
						|
					removedBlocks.AddRange(c.Blocks.Except(newOrder)); | 
						|
				} | 
						|
				c.Blocks.ReplaceList(newOrder); | 
						|
			} | 
						|
			if (removedBlocks.Count > 0) | 
						|
			{ | 
						|
				removedBlocks.SortBy(b => b.StartILOffset); | 
						|
				function.Warnings.Add("Discarded unreachable code: " | 
						|
							+ string.Join(", ", removedBlocks.Select(b => $"IL_{b.StartILOffset:x4}"))); | 
						|
			} | 
						|
 | 
						|
			this.SequencePointCandidates.Sort(); | 
						|
			function.SequencePointCandidates = this.SequencePointCandidates; | 
						|
 | 
						|
			function.Warnings.AddRange(Warnings); | 
						|
			return function; | 
						|
		} | 
						|
 | 
						|
		DecodedInstruction Neg() | 
						|
		{ | 
						|
			switch (PeekStackType()) | 
						|
			{ | 
						|
				case StackType.I4: | 
						|
					return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcI4(0), Pop(), checkForOverflow: false, sign: Sign.None)); | 
						|
				case StackType.I: | 
						|
					return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None), Pop(), checkForOverflow: false, sign: Sign.None)); | 
						|
				case StackType.I8: | 
						|
					return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcI8(0), Pop(), checkForOverflow: false, sign: Sign.None)); | 
						|
				case StackType.F4: | 
						|
					return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcF4(0), Pop(), checkForOverflow: false, sign: Sign.None)); | 
						|
				case StackType.F8: | 
						|
					return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcF8(0), Pop(), checkForOverflow: false, sign: Sign.None)); | 
						|
				default: | 
						|
					Warn("Unsupported input type for neg."); | 
						|
					goto case StackType.I4; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		struct DecodedInstruction | 
						|
		{ | 
						|
			public ILInstruction Instruction; | 
						|
			public bool PushedOnExpressionStack; | 
						|
 | 
						|
			public static implicit operator DecodedInstruction(ILInstruction instruction) | 
						|
			{ | 
						|
				return new DecodedInstruction { Instruction = instruction }; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		DecodedInstruction DecodeInstruction() | 
						|
		{ | 
						|
			if (reader.RemainingBytes == 0) | 
						|
				return new InvalidBranch("Unexpected end of body"); | 
						|
			var opCode = ILParser.DecodeOpCode(ref reader); | 
						|
			switch (opCode) | 
						|
			{ | 
						|
				case ILOpCode.Constrained: | 
						|
					return DecodeConstrainedCall(); | 
						|
				case ILOpCode.Readonly: | 
						|
					return DecodeReadonly(); | 
						|
				case ILOpCode.Tail: | 
						|
					return DecodeTailCall(); | 
						|
				case ILOpCode.Unaligned: | 
						|
					return DecodeUnaligned(); | 
						|
				case ILOpCode.Volatile: | 
						|
					return DecodeVolatile(); | 
						|
				case ILOpCode.Add: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Add); | 
						|
				case ILOpCode.Add_ovf: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Add, true, Sign.Signed); | 
						|
				case ILOpCode.Add_ovf_un: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Add, true, Sign.Unsigned); | 
						|
				case ILOpCode.And: | 
						|
					return BinaryNumeric(BinaryNumericOperator.BitAnd); | 
						|
				case ILOpCode.Arglist: | 
						|
					return Push(new Arglist()); | 
						|
				case ILOpCode.Beq: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.Equality); | 
						|
				case ILOpCode.Beq_s: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.Equality); | 
						|
				case ILOpCode.Bge: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThanOrEqual); | 
						|
				case ILOpCode.Bge_s: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThanOrEqual); | 
						|
				case ILOpCode.Bge_un: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThanOrEqual, un: true); | 
						|
				case ILOpCode.Bge_un_s: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThanOrEqual, un: true); | 
						|
				case ILOpCode.Bgt: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThan); | 
						|
				case ILOpCode.Bgt_s: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThan); | 
						|
				case ILOpCode.Bgt_un: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThan, un: true); | 
						|
				case ILOpCode.Bgt_un_s: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.GreaterThan, un: true); | 
						|
				case ILOpCode.Ble: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.LessThanOrEqual); | 
						|
				case ILOpCode.Ble_s: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.LessThanOrEqual); | 
						|
				case ILOpCode.Ble_un: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.LessThanOrEqual, un: true); | 
						|
				case ILOpCode.Ble_un_s: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.LessThanOrEqual, un: true); | 
						|
				case ILOpCode.Blt: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.LessThan); | 
						|
				case ILOpCode.Blt_s: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.LessThan); | 
						|
				case ILOpCode.Blt_un: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.LessThan, un: true); | 
						|
				case ILOpCode.Blt_un_s: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.LessThan, un: true); | 
						|
				case ILOpCode.Bne_un: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.Inequality, un: true); | 
						|
				case ILOpCode.Bne_un_s: | 
						|
					return DecodeComparisonBranch(opCode, ComparisonKind.Inequality, un: true); | 
						|
				case ILOpCode.Br: | 
						|
					return DecodeUnconditionalBranch(opCode); | 
						|
				case ILOpCode.Br_s: | 
						|
					return DecodeUnconditionalBranch(opCode); | 
						|
				case ILOpCode.Break: | 
						|
					return new DebugBreak(); | 
						|
				case ILOpCode.Brfalse: | 
						|
					return DecodeConditionalBranch(opCode, true); | 
						|
				case ILOpCode.Brfalse_s: | 
						|
					return DecodeConditionalBranch(opCode, true); | 
						|
				case ILOpCode.Brtrue: | 
						|
					return DecodeConditionalBranch(opCode, false); | 
						|
				case ILOpCode.Brtrue_s: | 
						|
					return DecodeConditionalBranch(opCode, false); | 
						|
				case ILOpCode.Call: | 
						|
					return DecodeCall(OpCode.Call); | 
						|
				case ILOpCode.Callvirt: | 
						|
					return DecodeCall(OpCode.CallVirt); | 
						|
				case ILOpCode.Calli: | 
						|
					return DecodeCallIndirect(); | 
						|
				case ILOpCode.Ceq: | 
						|
					return Push(Comparison(ComparisonKind.Equality)); | 
						|
				case ILOpCode.Cgt: | 
						|
					return Push(Comparison(ComparisonKind.GreaterThan)); | 
						|
				case ILOpCode.Cgt_un: | 
						|
					return Push(Comparison(ComparisonKind.GreaterThan, un: true)); | 
						|
				case ILOpCode.Clt: | 
						|
					return Push(Comparison(ComparisonKind.LessThan)); | 
						|
				case ILOpCode.Clt_un: | 
						|
					return Push(Comparison(ComparisonKind.LessThan, un: true)); | 
						|
				case ILOpCode.Ckfinite: | 
						|
					return new Ckfinite(Peek()); | 
						|
				case ILOpCode.Conv_i1: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I1, false, Sign.None)); | 
						|
				case ILOpCode.Conv_i2: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I2, false, Sign.None)); | 
						|
				case ILOpCode.Conv_i4: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I4, false, Sign.None)); | 
						|
				case ILOpCode.Conv_i8: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I8, false, Sign.None)); | 
						|
				case ILOpCode.Conv_r4: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.R4, false, Sign.Signed)); | 
						|
				case ILOpCode.Conv_r8: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.R8, false, Sign.Signed)); | 
						|
				case ILOpCode.Conv_u1: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U1, false, Sign.None)); | 
						|
				case ILOpCode.Conv_u2: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U2, false, Sign.None)); | 
						|
				case ILOpCode.Conv_u4: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U4, false, Sign.None)); | 
						|
				case ILOpCode.Conv_u8: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U8, false, Sign.None)); | 
						|
				case ILOpCode.Conv_i: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I, false, Sign.None)); | 
						|
				case ILOpCode.Conv_u: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U, false, Sign.None)); | 
						|
				case ILOpCode.Conv_r_un: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.R, false, Sign.Unsigned)); | 
						|
				case ILOpCode.Conv_ovf_i1: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I1, true, Sign.Signed)); | 
						|
				case ILOpCode.Conv_ovf_i2: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I2, true, Sign.Signed)); | 
						|
				case ILOpCode.Conv_ovf_i4: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I4, true, Sign.Signed)); | 
						|
				case ILOpCode.Conv_ovf_i8: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I8, true, Sign.Signed)); | 
						|
				case ILOpCode.Conv_ovf_u1: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U1, true, Sign.Signed)); | 
						|
				case ILOpCode.Conv_ovf_u2: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U2, true, Sign.Signed)); | 
						|
				case ILOpCode.Conv_ovf_u4: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U4, true, Sign.Signed)); | 
						|
				case ILOpCode.Conv_ovf_u8: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U8, true, Sign.Signed)); | 
						|
				case ILOpCode.Conv_ovf_i: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I, true, Sign.Signed)); | 
						|
				case ILOpCode.Conv_ovf_u: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U, true, Sign.Signed)); | 
						|
				case ILOpCode.Conv_ovf_i1_un: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I1, true, Sign.Unsigned)); | 
						|
				case ILOpCode.Conv_ovf_i2_un: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I2, true, Sign.Unsigned)); | 
						|
				case ILOpCode.Conv_ovf_i4_un: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I4, true, Sign.Unsigned)); | 
						|
				case ILOpCode.Conv_ovf_i8_un: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I8, true, Sign.Unsigned)); | 
						|
				case ILOpCode.Conv_ovf_u1_un: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U1, true, Sign.Unsigned)); | 
						|
				case ILOpCode.Conv_ovf_u2_un: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U2, true, Sign.Unsigned)); | 
						|
				case ILOpCode.Conv_ovf_u4_un: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U4, true, Sign.Unsigned)); | 
						|
				case ILOpCode.Conv_ovf_u8_un: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U8, true, Sign.Unsigned)); | 
						|
				case ILOpCode.Conv_ovf_i_un: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.I, true, Sign.Unsigned)); | 
						|
				case ILOpCode.Conv_ovf_u_un: | 
						|
					return Push(new Conv(Pop(), PrimitiveType.U, true, Sign.Unsigned)); | 
						|
				case ILOpCode.Cpblk: | 
						|
					// This preserves the evaluation order because the ILAst will run | 
						|
					// destAddress; sourceAddress; size. | 
						|
					return new Cpblk(size: Pop(StackType.I4), sourceAddress: PopPointer(), destAddress: PopPointer()); | 
						|
				case ILOpCode.Div: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Div, false, Sign.Signed); | 
						|
				case ILOpCode.Div_un: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Div, false, Sign.Unsigned); | 
						|
				case ILOpCode.Dup: | 
						|
					return Push(Peek()); | 
						|
				case ILOpCode.Endfilter: | 
						|
					return new Leave(null, Pop()); | 
						|
				case ILOpCode.Endfinally: | 
						|
					return new Leave(null); | 
						|
				case ILOpCode.Initblk: | 
						|
					// This preserves the evaluation order because the ILAst will run | 
						|
					// address; value; size. | 
						|
					return new Initblk(size: Pop(StackType.I4), value: Pop(StackType.I4), address: PopPointer()); | 
						|
				case ILOpCode.Jmp: | 
						|
					return DecodeJmp(); | 
						|
				case ILOpCode.Ldarg: | 
						|
				case ILOpCode.Ldarg_s: | 
						|
					return Push(Ldarg(ILParser.DecodeIndex(ref reader, opCode))); | 
						|
				case ILOpCode.Ldarg_0: | 
						|
					return Push(Ldarg(0)); | 
						|
				case ILOpCode.Ldarg_1: | 
						|
					return Push(Ldarg(1)); | 
						|
				case ILOpCode.Ldarg_2: | 
						|
					return Push(Ldarg(2)); | 
						|
				case ILOpCode.Ldarg_3: | 
						|
					return Push(Ldarg(3)); | 
						|
				case ILOpCode.Ldarga: | 
						|
				case ILOpCode.Ldarga_s: | 
						|
					return Push(Ldarga(ILParser.DecodeIndex(ref reader, opCode))); | 
						|
				case ILOpCode.Ldc_i4: | 
						|
					return Push(new LdcI4(reader.ReadInt32())); | 
						|
				case ILOpCode.Ldc_i8: | 
						|
					return Push(new LdcI8(reader.ReadInt64())); | 
						|
				case ILOpCode.Ldc_r4: | 
						|
					return Push(new LdcF4(reader.ReadSingle())); | 
						|
				case ILOpCode.Ldc_r8: | 
						|
					return Push(new LdcF8(reader.ReadDouble())); | 
						|
				case ILOpCode.Ldc_i4_m1: | 
						|
					return Push(new LdcI4(-1)); | 
						|
				case ILOpCode.Ldc_i4_0: | 
						|
					return Push(new LdcI4(0)); | 
						|
				case ILOpCode.Ldc_i4_1: | 
						|
					return Push(new LdcI4(1)); | 
						|
				case ILOpCode.Ldc_i4_2: | 
						|
					return Push(new LdcI4(2)); | 
						|
				case ILOpCode.Ldc_i4_3: | 
						|
					return Push(new LdcI4(3)); | 
						|
				case ILOpCode.Ldc_i4_4: | 
						|
					return Push(new LdcI4(4)); | 
						|
				case ILOpCode.Ldc_i4_5: | 
						|
					return Push(new LdcI4(5)); | 
						|
				case ILOpCode.Ldc_i4_6: | 
						|
					return Push(new LdcI4(6)); | 
						|
				case ILOpCode.Ldc_i4_7: | 
						|
					return Push(new LdcI4(7)); | 
						|
				case ILOpCode.Ldc_i4_8: | 
						|
					return Push(new LdcI4(8)); | 
						|
				case ILOpCode.Ldc_i4_s: | 
						|
					return Push(new LdcI4(reader.ReadSByte())); | 
						|
				case ILOpCode.Ldnull: | 
						|
					return Push(new LdNull()); | 
						|
				case ILOpCode.Ldstr: | 
						|
					return Push(DecodeLdstr()); | 
						|
				case ILOpCode.Ldftn: | 
						|
					return Push(new LdFtn(ReadAndDecodeMethodReference())); | 
						|
				case ILOpCode.Ldind_i1: | 
						|
					return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.SByte))); | 
						|
				case ILOpCode.Ldind_i2: | 
						|
					return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int16))); | 
						|
				case ILOpCode.Ldind_i4: | 
						|
					return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int32))); | 
						|
				case ILOpCode.Ldind_i8: | 
						|
					return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Int64))); | 
						|
				case ILOpCode.Ldind_u1: | 
						|
					return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Byte))); | 
						|
				case ILOpCode.Ldind_u2: | 
						|
					return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.UInt16))); | 
						|
				case ILOpCode.Ldind_u4: | 
						|
					return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.UInt32))); | 
						|
				case ILOpCode.Ldind_r4: | 
						|
					return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Single))); | 
						|
				case ILOpCode.Ldind_r8: | 
						|
					return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Double))); | 
						|
				case ILOpCode.Ldind_i: | 
						|
					return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.IntPtr))); | 
						|
				case ILOpCode.Ldind_ref: | 
						|
					return Push(new LdObj(PopPointer(), compilation.FindType(KnownTypeCode.Object))); | 
						|
				case ILOpCode.Ldloc: | 
						|
				case ILOpCode.Ldloc_s: | 
						|
					return Push(Ldloc(ILParser.DecodeIndex(ref reader, opCode))); | 
						|
				case ILOpCode.Ldloc_0: | 
						|
					return Push(Ldloc(0)); | 
						|
				case ILOpCode.Ldloc_1: | 
						|
					return Push(Ldloc(1)); | 
						|
				case ILOpCode.Ldloc_2: | 
						|
					return Push(Ldloc(2)); | 
						|
				case ILOpCode.Ldloc_3: | 
						|
					return Push(Ldloc(3)); | 
						|
				case ILOpCode.Ldloca: | 
						|
				case ILOpCode.Ldloca_s: | 
						|
					return Push(Ldloca(ILParser.DecodeIndex(ref reader, opCode))); | 
						|
				case ILOpCode.Leave: | 
						|
					return DecodeUnconditionalBranch(opCode, isLeave: true); | 
						|
				case ILOpCode.Leave_s: | 
						|
					return DecodeUnconditionalBranch(opCode, isLeave: true); | 
						|
				case ILOpCode.Localloc: | 
						|
					return Push(new LocAlloc(Pop())); | 
						|
				case ILOpCode.Mul: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Mul, false, Sign.None); | 
						|
				case ILOpCode.Mul_ovf: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Mul, true, Sign.Signed); | 
						|
				case ILOpCode.Mul_ovf_un: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Mul, true, Sign.Unsigned); | 
						|
				case ILOpCode.Neg: | 
						|
					return Neg(); | 
						|
				case ILOpCode.Newobj: | 
						|
					return DecodeCall(OpCode.NewObj); | 
						|
				case ILOpCode.Nop: | 
						|
					return new Nop(); | 
						|
				case ILOpCode.Not: | 
						|
					return Push(new BitNot(Pop())); | 
						|
				case ILOpCode.Or: | 
						|
					return BinaryNumeric(BinaryNumericOperator.BitOr); | 
						|
				case ILOpCode.Pop: | 
						|
					FlushExpressionStack(); // discard only the value, not the side-effects | 
						|
					Pop(); | 
						|
					return new Nop() { Kind = NopKind.Pop }; | 
						|
				case ILOpCode.Rem: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Rem, false, Sign.Signed); | 
						|
				case ILOpCode.Rem_un: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Rem, false, Sign.Unsigned); | 
						|
				case ILOpCode.Ret: | 
						|
					return Return(); | 
						|
				case ILOpCode.Shl: | 
						|
					return BinaryNumeric(BinaryNumericOperator.ShiftLeft, false, Sign.None); | 
						|
				case ILOpCode.Shr: | 
						|
					return BinaryNumeric(BinaryNumericOperator.ShiftRight, false, Sign.Signed); | 
						|
				case ILOpCode.Shr_un: | 
						|
					return BinaryNumeric(BinaryNumericOperator.ShiftRight, false, Sign.Unsigned); | 
						|
				case ILOpCode.Starg: | 
						|
				case ILOpCode.Starg_s: | 
						|
					return Starg(ILParser.DecodeIndex(ref reader, opCode)); | 
						|
				case ILOpCode.Stind_i1: | 
						|
					// target will run before value, thus preserving the evaluation order | 
						|
					return new StObj(value: Pop(StackType.I4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.SByte)); | 
						|
				case ILOpCode.Stind_i2: | 
						|
					return new StObj(value: Pop(StackType.I4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Int16)); | 
						|
				case ILOpCode.Stind_i4: | 
						|
					return new StObj(value: Pop(StackType.I4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Int32)); | 
						|
				case ILOpCode.Stind_i8: | 
						|
					return new StObj(value: Pop(StackType.I8), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Int64)); | 
						|
				case ILOpCode.Stind_r4: | 
						|
					return new StObj(value: Pop(StackType.F4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Single)); | 
						|
				case ILOpCode.Stind_r8: | 
						|
					return new StObj(value: Pop(StackType.F8), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Double)); | 
						|
				case ILOpCode.Stind_i: | 
						|
					return new StObj(value: Pop(StackType.I), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.IntPtr)); | 
						|
				case ILOpCode.Stind_ref: | 
						|
					return new StObj(value: Pop(StackType.O), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Object)); | 
						|
				case ILOpCode.Stloc: | 
						|
				case ILOpCode.Stloc_s: | 
						|
					return Stloc(ILParser.DecodeIndex(ref reader, opCode)); | 
						|
				case ILOpCode.Stloc_0: | 
						|
					return Stloc(0); | 
						|
				case ILOpCode.Stloc_1: | 
						|
					return Stloc(1); | 
						|
				case ILOpCode.Stloc_2: | 
						|
					return Stloc(2); | 
						|
				case ILOpCode.Stloc_3: | 
						|
					return Stloc(3); | 
						|
				case ILOpCode.Sub: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Sub, false, Sign.None); | 
						|
				case ILOpCode.Sub_ovf: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Sub, true, Sign.Signed); | 
						|
				case ILOpCode.Sub_ovf_un: | 
						|
					return BinaryNumeric(BinaryNumericOperator.Sub, true, Sign.Unsigned); | 
						|
				case ILOpCode.Switch: | 
						|
					return DecodeSwitch(); | 
						|
				case ILOpCode.Xor: | 
						|
					return BinaryNumeric(BinaryNumericOperator.BitXor); | 
						|
				case ILOpCode.Box: | 
						|
				{ | 
						|
					var type = ReadAndDecodeTypeReference(); | 
						|
					return Push(new Box(Pop(type.GetStackType()), type)); | 
						|
				} | 
						|
				case ILOpCode.Castclass: | 
						|
					return Push(new CastClass(Pop(StackType.O), ReadAndDecodeTypeReference())); | 
						|
				case ILOpCode.Cpobj: | 
						|
				{ | 
						|
					var type = ReadAndDecodeTypeReference(); | 
						|
					// OK, 'target' runs before 'value: ld' | 
						|
					var ld = new LdObj(PopPointer(), type); | 
						|
					return new StObj(PopStObjTarget(), ld, type); | 
						|
				} | 
						|
				case ILOpCode.Initobj: | 
						|
					return InitObj(PopStObjTarget(), ReadAndDecodeTypeReference()); | 
						|
				case ILOpCode.Isinst: | 
						|
					return Push(new IsInst(Pop(StackType.O), ReadAndDecodeTypeReference())); | 
						|
				case ILOpCode.Ldelem: | 
						|
					return LdElem(ReadAndDecodeTypeReference()); | 
						|
				case ILOpCode.Ldelem_i1: | 
						|
					return LdElem(compilation.FindType(KnownTypeCode.SByte)); | 
						|
				case ILOpCode.Ldelem_i2: | 
						|
					return LdElem(compilation.FindType(KnownTypeCode.Int16)); | 
						|
				case ILOpCode.Ldelem_i4: | 
						|
					return LdElem(compilation.FindType(KnownTypeCode.Int32)); | 
						|
				case ILOpCode.Ldelem_i8: | 
						|
					return LdElem(compilation.FindType(KnownTypeCode.Int64)); | 
						|
				case ILOpCode.Ldelem_u1: | 
						|
					return LdElem(compilation.FindType(KnownTypeCode.Byte)); | 
						|
				case ILOpCode.Ldelem_u2: | 
						|
					return LdElem(compilation.FindType(KnownTypeCode.UInt16)); | 
						|
				case ILOpCode.Ldelem_u4: | 
						|
					return LdElem(compilation.FindType(KnownTypeCode.UInt32)); | 
						|
				case ILOpCode.Ldelem_r4: | 
						|
					return LdElem(compilation.FindType(KnownTypeCode.Single)); | 
						|
				case ILOpCode.Ldelem_r8: | 
						|
					return LdElem(compilation.FindType(KnownTypeCode.Double)); | 
						|
				case ILOpCode.Ldelem_i: | 
						|
					return LdElem(compilation.FindType(KnownTypeCode.IntPtr)); | 
						|
				case ILOpCode.Ldelem_ref: | 
						|
					return LdElem(compilation.FindType(KnownTypeCode.Object)); | 
						|
				case ILOpCode.Ldelema: | 
						|
					// LdElema will evalute the array before the indices, so we're preserving the evaluation order | 
						|
					return Push(new LdElema(indices: Pop(), array: Pop(), type: ReadAndDecodeTypeReference())); | 
						|
				case ILOpCode.Ldfld: | 
						|
				{ | 
						|
					var field = ReadAndDecodeFieldReference(); | 
						|
					return Push(new LdObj(new LdFlda(PopLdFldTarget(field), field) { DelayExceptions = true }, field.Type)); | 
						|
				} | 
						|
				case ILOpCode.Ldflda: | 
						|
				{ | 
						|
					var field = ReadAndDecodeFieldReference(); | 
						|
					return Push(new LdFlda(PopFieldTarget(field), field)); | 
						|
				} | 
						|
				case ILOpCode.Stfld: | 
						|
				{ | 
						|
					var field = ReadAndDecodeFieldReference(); | 
						|
					return new StObj(value: Pop(field.Type.GetStackType()), target: new LdFlda(PopFieldTarget(field), field) { DelayExceptions = true }, type: field.Type); | 
						|
				} | 
						|
				case ILOpCode.Ldlen: | 
						|
					return Push(new LdLen(StackType.I, Pop(StackType.O))); | 
						|
				case ILOpCode.Ldobj: | 
						|
					return Push(new LdObj(PopPointer(), ReadAndDecodeTypeReference())); | 
						|
				case ILOpCode.Ldsfld: | 
						|
				{ | 
						|
					var field = ReadAndDecodeFieldReference(); | 
						|
					return Push(new LdObj(new LdsFlda(field), field.Type)); | 
						|
				} | 
						|
				case ILOpCode.Ldsflda: | 
						|
					return Push(new LdsFlda(ReadAndDecodeFieldReference())); | 
						|
				case ILOpCode.Stsfld: | 
						|
				{ | 
						|
					var field = ReadAndDecodeFieldReference(); | 
						|
					return new StObj(value: Pop(field.Type.GetStackType()), target: new LdsFlda(field), type: field.Type); | 
						|
				} | 
						|
				case ILOpCode.Ldtoken: | 
						|
					return Push(LdToken(ReadAndDecodeMetadataToken())); | 
						|
				case ILOpCode.Ldvirtftn: | 
						|
					return Push(new LdVirtFtn(Pop(), ReadAndDecodeMethodReference())); | 
						|
				case ILOpCode.Mkrefany: | 
						|
					return Push(new MakeRefAny(PopPointer(), ReadAndDecodeTypeReference())); | 
						|
				case ILOpCode.Newarr: | 
						|
					return Push(new NewArr(ReadAndDecodeTypeReference(), Pop())); | 
						|
				case ILOpCode.Refanytype: | 
						|
					return Push(new RefAnyType(Pop())); | 
						|
				case ILOpCode.Refanyval: | 
						|
					return Push(new RefAnyValue(Pop(), ReadAndDecodeTypeReference())); | 
						|
				case ILOpCode.Rethrow: | 
						|
					return new Rethrow(); | 
						|
				case ILOpCode.Sizeof: | 
						|
					return Push(new SizeOf(ReadAndDecodeTypeReference())); | 
						|
				case ILOpCode.Stelem: | 
						|
					return StElem(ReadAndDecodeTypeReference()); | 
						|
				case ILOpCode.Stelem_i1: | 
						|
					return StElem(compilation.FindType(KnownTypeCode.SByte)); | 
						|
				case ILOpCode.Stelem_i2: | 
						|
					return StElem(compilation.FindType(KnownTypeCode.Int16)); | 
						|
				case ILOpCode.Stelem_i4: | 
						|
					return StElem(compilation.FindType(KnownTypeCode.Int32)); | 
						|
				case ILOpCode.Stelem_i8: | 
						|
					return StElem(compilation.FindType(KnownTypeCode.Int64)); | 
						|
				case ILOpCode.Stelem_r4: | 
						|
					return StElem(compilation.FindType(KnownTypeCode.Single)); | 
						|
				case ILOpCode.Stelem_r8: | 
						|
					return StElem(compilation.FindType(KnownTypeCode.Double)); | 
						|
				case ILOpCode.Stelem_i: | 
						|
					return StElem(compilation.FindType(KnownTypeCode.IntPtr)); | 
						|
				case ILOpCode.Stelem_ref: | 
						|
					return StElem(compilation.FindType(KnownTypeCode.Object)); | 
						|
				case ILOpCode.Stobj: | 
						|
				{ | 
						|
					var type = ReadAndDecodeTypeReference(); | 
						|
					// OK, target runs before value | 
						|
					return new StObj(value: Pop(type.GetStackType()), target: PopStObjTarget(), type: type); | 
						|
				} | 
						|
				case ILOpCode.Throw: | 
						|
					return new Throw(Pop()); | 
						|
				case ILOpCode.Unbox: | 
						|
					return Push(new Unbox(Pop(), ReadAndDecodeTypeReference())); | 
						|
				case ILOpCode.Unbox_any: | 
						|
					return Push(new UnboxAny(Pop(), ReadAndDecodeTypeReference())); | 
						|
				default: | 
						|
					return new InvalidBranch($"Unknown opcode: 0x{(int)opCode:X2}"); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		StackType PeekStackType() | 
						|
		{ | 
						|
			if (expressionStack.Count > 0) | 
						|
				return expressionStack.Last().ResultType; | 
						|
			if (currentStack.IsEmpty) | 
						|
				return StackType.Unknown; | 
						|
			else | 
						|
				return currentStack.Peek().StackType; | 
						|
		} | 
						|
 | 
						|
		class CollectStackVariablesVisitor : ILVisitor<ILInstruction> | 
						|
		{ | 
						|
			readonly UnionFind<ILVariable> unionFind; | 
						|
			internal readonly HashSet<ILVariable> variables = new HashSet<ILVariable>(); | 
						|
 | 
						|
			public CollectStackVariablesVisitor(UnionFind<ILVariable> unionFind) | 
						|
			{ | 
						|
				Debug.Assert(unionFind != null); | 
						|
				this.unionFind = unionFind; | 
						|
			} | 
						|
 | 
						|
			protected override ILInstruction Default(ILInstruction inst) | 
						|
			{ | 
						|
				foreach (var child in inst.Children) | 
						|
				{ | 
						|
					var newChild = child.AcceptVisitor(this); | 
						|
					if (newChild != child) | 
						|
						child.ReplaceWith(newChild); | 
						|
				} | 
						|
				return inst; | 
						|
			} | 
						|
 | 
						|
			protected internal override ILInstruction VisitLdLoc(LdLoc inst) | 
						|
			{ | 
						|
				base.VisitLdLoc(inst); | 
						|
				if (inst.Variable.Kind == VariableKind.StackSlot) | 
						|
				{ | 
						|
					var variable = unionFind.Find(inst.Variable); | 
						|
					if (variables.Add(variable)) | 
						|
						variable.Name = "S_" + (variables.Count - 1); | 
						|
					return new LdLoc(variable).WithILRange(inst); | 
						|
				} | 
						|
				return inst; | 
						|
			} | 
						|
 | 
						|
			protected internal override ILInstruction VisitStLoc(StLoc inst) | 
						|
			{ | 
						|
				base.VisitStLoc(inst); | 
						|
				if (inst.Variable.Kind == VariableKind.StackSlot) | 
						|
				{ | 
						|
					var variable = unionFind.Find(inst.Variable); | 
						|
					if (variables.Add(variable)) | 
						|
						variable.Name = "S_" + (variables.Count - 1); | 
						|
					return new StLoc(variable, inst.Value).WithILRange(inst); | 
						|
				} | 
						|
				return inst; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		DecodedInstruction Push(ILInstruction inst) | 
						|
		{ | 
						|
			expressionStack.Add(inst); | 
						|
			return new DecodedInstruction { | 
						|
				Instruction = inst, | 
						|
				PushedOnExpressionStack = true | 
						|
			}; | 
						|
		} | 
						|
 | 
						|
		ILInstruction Peek() | 
						|
		{ | 
						|
			FlushExpressionStack(); | 
						|
			if (currentStack.IsEmpty) | 
						|
			{ | 
						|
				return new InvalidExpression("Stack underflow").WithILRange(new Interval(reader.Offset, reader.Offset)); | 
						|
			} | 
						|
			return new LdLoc(currentStack.Peek()); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Pops a value/instruction from the evaluation stack. | 
						|
		/// Note that instructions popped from the stack must be evaluated in the order they | 
						|
		/// were pushed (so in reverse order of the pop calls!). | 
						|
		///  | 
						|
		/// For instructions like 'conv' that pop a single element and then push their result, | 
						|
		/// it's fine to pop just one element as the instruction itself will end up on the stack, | 
						|
		/// thus maintaining the evaluation order. | 
						|
		/// For instructions like 'call' that pop multiple arguments, it's critical that | 
						|
		/// the evaluation order of the resulting ILAst will be reverse from the order of the push | 
						|
		/// calls. | 
						|
		/// For instructions like 'brtrue', it's fine to pop only a part of the stack because | 
						|
		/// ReadInstructions() will flush the evaluation stack before outputting the brtrue instruction. | 
						|
		///  | 
						|
		/// Use FlushExpressionStack() to ensure that following Pop() calls do not return | 
						|
		/// instructions that involve side-effects. This way evaluation order is preserved | 
						|
		/// no matter which order the ILAst will execute the popped instructions in. | 
						|
		/// </summary> | 
						|
		ILInstruction Pop() | 
						|
		{ | 
						|
			if (expressionStack.Count > 0) | 
						|
			{ | 
						|
				var inst = expressionStack.Last(); | 
						|
				expressionStack.RemoveAt(expressionStack.Count - 1); | 
						|
				return inst; | 
						|
			} | 
						|
			if (currentStack.IsEmpty) | 
						|
			{ | 
						|
				return new InvalidExpression("Stack underflow").WithILRange(new Interval(reader.Offset, reader.Offset)); | 
						|
			} | 
						|
			ILVariable v; | 
						|
			currentStack = currentStack.Pop(out v); | 
						|
			return new LdLoc(v); | 
						|
		} | 
						|
 | 
						|
		ILInstruction Pop(StackType expectedType) | 
						|
		{ | 
						|
			ILInstruction inst = Pop(); | 
						|
			return Cast(inst, expectedType, Warnings, reader.Offset); | 
						|
		} | 
						|
 | 
						|
		internal static ILInstruction Cast(ILInstruction inst, StackType expectedType, List<string> warnings, int ilOffset) | 
						|
		{ | 
						|
			if (expectedType != inst.ResultType) | 
						|
			{ | 
						|
				if (inst is InvalidExpression) | 
						|
				{ | 
						|
					((InvalidExpression)inst).ExpectedResultType = expectedType; | 
						|
				} | 
						|
				else if (expectedType == StackType.I && inst.ResultType == StackType.I4) | 
						|
				{ | 
						|
					// IL allows implicit I4->I conversions | 
						|
					inst = new Conv(inst, PrimitiveType.I, false, Sign.None); | 
						|
				} | 
						|
				else if (expectedType == StackType.I4 && inst.ResultType == StackType.I) | 
						|
				{ | 
						|
					// C++/CLI also sometimes implicitly converts in the other direction: | 
						|
					inst = new Conv(inst, PrimitiveType.I4, false, Sign.None); | 
						|
				} | 
						|
				else if (expectedType == StackType.Unknown) | 
						|
				{ | 
						|
					inst = new Conv(inst, PrimitiveType.Unknown, false, Sign.None); | 
						|
				} | 
						|
				else if (inst.ResultType == StackType.Ref) | 
						|
				{ | 
						|
					// Implicitly stop GC tracking; this occurs when passing the result of 'ldloca' or 'ldsflda' | 
						|
					// to a method expecting a native pointer. | 
						|
					inst = new Conv(inst, PrimitiveType.I, false, Sign.None); | 
						|
					switch (expectedType) | 
						|
					{ | 
						|
						case StackType.I4: | 
						|
							inst = new Conv(inst, PrimitiveType.I4, false, Sign.None); | 
						|
							break; | 
						|
						case StackType.I: | 
						|
							break; | 
						|
						case StackType.I8: | 
						|
							inst = new Conv(inst, PrimitiveType.I8, false, Sign.None); | 
						|
							break; | 
						|
						default: | 
						|
							Warn($"Expected {expectedType}, but got {StackType.Ref}"); | 
						|
							inst = new Conv(inst, expectedType.ToPrimitiveType(), false, Sign.None); | 
						|
							break; | 
						|
					} | 
						|
				} | 
						|
				else if (expectedType == StackType.Ref) | 
						|
				{ | 
						|
					// implicitly start GC tracking / object to interior | 
						|
					if (!inst.ResultType.IsIntegerType() && inst.ResultType != StackType.O) | 
						|
					{ | 
						|
						// We also handle the invalid to-ref cases here because the else case | 
						|
						// below uses expectedType.ToKnownTypeCode(), which doesn't work for Ref. | 
						|
						Warn($"Expected {expectedType}, but got {inst.ResultType}"); | 
						|
					} | 
						|
					inst = new Conv(inst, PrimitiveType.Ref, false, Sign.None); | 
						|
				} | 
						|
				else if (expectedType == StackType.F8 && inst.ResultType == StackType.F4) | 
						|
				{ | 
						|
					// IL allows implicit F4->F8 conversions, because in IL F4 and F8 are the same. | 
						|
					inst = new Conv(inst, PrimitiveType.R8, false, Sign.Signed); | 
						|
				} | 
						|
				else if (expectedType == StackType.F4 && inst.ResultType == StackType.F8) | 
						|
				{ | 
						|
					// IL allows implicit F8->F4 conversions, because in IL F4 and F8 are the same. | 
						|
					inst = new Conv(inst, PrimitiveType.R4, false, Sign.Signed); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					Warn($"Expected {expectedType}, but got {inst.ResultType}"); | 
						|
					inst = new Conv(inst, expectedType.ToPrimitiveType(), false, Sign.Signed); | 
						|
				} | 
						|
			} | 
						|
			return inst; | 
						|
 | 
						|
			void Warn(string message) | 
						|
			{ | 
						|
				if (warnings != null) | 
						|
				{ | 
						|
					warnings.Add(string.Format("IL_{0:x4}: {1}", ilOffset, message)); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		ILInstruction PopPointer() | 
						|
		{ | 
						|
			ILInstruction inst = Pop(); | 
						|
			switch (inst.ResultType) | 
						|
			{ | 
						|
				case StackType.I4: | 
						|
				case StackType.I8: | 
						|
				case StackType.Unknown: | 
						|
					return new Conv(inst, PrimitiveType.I, false, Sign.None); | 
						|
				case StackType.I: | 
						|
				case StackType.Ref: | 
						|
					return inst; | 
						|
				default: | 
						|
					Warn("Expected native int or pointer, but got " + inst.ResultType); | 
						|
					return new Conv(inst, PrimitiveType.I, false, Sign.None); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		ILInstruction PopStObjTarget() | 
						|
		{ | 
						|
			// stobj has a special invariant (StObj.CheckTargetSlot) | 
						|
			// that prohibits inlining LdElema/LdFlda. | 
						|
			if (expressionStack.LastOrDefault() is LdElema or LdFlda) | 
						|
			{ | 
						|
				FlushExpressionStack(); | 
						|
			} | 
						|
			return PopPointer(); | 
						|
		} | 
						|
 | 
						|
		ILInstruction PopFieldTarget(IField field) | 
						|
		{ | 
						|
			switch (field.DeclaringType.IsReferenceType) | 
						|
			{ | 
						|
				case true: | 
						|
					return Pop(StackType.O); | 
						|
				case false: | 
						|
					return PopPointer(); | 
						|
				default: | 
						|
					// field in unresolved type | 
						|
					var stackType = PeekStackType(); | 
						|
					if (stackType == StackType.O || stackType == StackType.Unknown) | 
						|
						return Pop(); | 
						|
					else | 
						|
						return PopPointer(); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Like PopFieldTarget, but supports ldfld's special behavior for fields of temporary value types. | 
						|
		/// </summary> | 
						|
		ILInstruction PopLdFldTarget(IField field) | 
						|
		{ | 
						|
			switch (field.DeclaringType.IsReferenceType) | 
						|
			{ | 
						|
				case true: | 
						|
					return Pop(StackType.O); | 
						|
				case false: | 
						|
					// field of value type: ldfld can handle temporaries | 
						|
					if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown) | 
						|
						return new AddressOf(Pop(), field.DeclaringType); | 
						|
					else | 
						|
						return PopPointer(); | 
						|
				default: | 
						|
					// field in unresolved type | 
						|
					if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown) | 
						|
						return Pop(); | 
						|
					else | 
						|
						return PopPointer(); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private ILInstruction Return() | 
						|
		{ | 
						|
			if (methodReturnStackType == StackType.Void) | 
						|
				return new IL.Leave(mainContainer); | 
						|
			else | 
						|
				return new IL.Leave(mainContainer, Pop(methodReturnStackType)); | 
						|
		} | 
						|
 | 
						|
		private ILInstruction DecodeLdstr() | 
						|
		{ | 
						|
			return new LdStr(ILParser.DecodeUserString(ref reader, metadata)); | 
						|
		} | 
						|
 | 
						|
		private ILInstruction Ldarg(int v) | 
						|
		{ | 
						|
			if (v >= 0 && v < parameterVariables.Length) | 
						|
			{ | 
						|
				return new LdLoc(parameterVariables[v]); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return new InvalidExpression($"ldarg {v} (out-of-bounds)"); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private ILInstruction Ldarga(int v) | 
						|
		{ | 
						|
			if (v >= 0 && v < parameterVariables.Length) | 
						|
			{ | 
						|
				return new LdLoca(parameterVariables[v]); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return new InvalidExpression($"ldarga {v} (out-of-bounds)"); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private ILInstruction Starg(int v) | 
						|
		{ | 
						|
			if (v >= 0 && v < parameterVariables.Length) | 
						|
			{ | 
						|
				return new StLoc(parameterVariables[v], Pop(parameterVariables[v].StackType)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				FlushExpressionStack(); | 
						|
				Pop(); | 
						|
				return new InvalidExpression($"starg {v} (out-of-bounds)"); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private ILInstruction Ldloc(int v) | 
						|
		{ | 
						|
			if (v >= 0 && v < localVariables.Length) | 
						|
			{ | 
						|
				return new LdLoc(localVariables[v]); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return new InvalidExpression($"ldloc {v} (out-of-bounds)"); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private ILInstruction Ldloca(int v) | 
						|
		{ | 
						|
			if (v >= 0 && v < localVariables.Length) | 
						|
			{ | 
						|
				return new LdLoca(localVariables[v]); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return new InvalidExpression($"ldloca {v} (out-of-bounds)"); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private ILInstruction Stloc(int v) | 
						|
		{ | 
						|
			if (v >= 0 && v < localVariables.Length) | 
						|
			{ | 
						|
				return new StLoc(localVariables[v], Pop(localVariables[v].StackType)) { | 
						|
					ILStackWasEmpty = CurrentStackIsEmpty() | 
						|
				}; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				FlushExpressionStack(); | 
						|
				Pop(); | 
						|
				return new InvalidExpression($"stloc {v} (out-of-bounds)"); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private DecodedInstruction LdElem(IType type) | 
						|
		{ | 
						|
			return Push(new LdObj(new LdElema(indices: Pop(), array: Pop(), type: type) { DelayExceptions = true }, type)); | 
						|
		} | 
						|
 | 
						|
		private ILInstruction StElem(IType type) | 
						|
		{ | 
						|
			var value = Pop(type.GetStackType()); | 
						|
			var index = Pop(); | 
						|
			var array = Pop(); | 
						|
			// OK, evaluation order is array, index, value | 
						|
			return new StObj(new LdElema(type, array, index) { DelayExceptions = true }, value, type); | 
						|
		} | 
						|
 | 
						|
		ILInstruction InitObj(ILInstruction target, IType type) | 
						|
		{ | 
						|
			var value = new DefaultValue(type); | 
						|
			value.ILStackWasEmpty = CurrentStackIsEmpty(); | 
						|
			return new StObj(target, value, type); | 
						|
		} | 
						|
 | 
						|
		IType constrainedPrefix; | 
						|
 | 
						|
		private DecodedInstruction DecodeConstrainedCall() | 
						|
		{ | 
						|
			constrainedPrefix = ReadAndDecodeTypeReference(); | 
						|
			var inst = DecodeInstruction(); | 
						|
			var call = inst.Instruction as CallInstruction; | 
						|
			if (call != null) | 
						|
				Debug.Assert(call.ConstrainedTo == constrainedPrefix); | 
						|
			else | 
						|
				Warn("Ignored invalid 'constrained' prefix"); | 
						|
			constrainedPrefix = null; | 
						|
			return inst; | 
						|
		} | 
						|
 | 
						|
		private DecodedInstruction DecodeTailCall() | 
						|
		{ | 
						|
			var inst = DecodeInstruction(); | 
						|
			var call = inst.Instruction as CallInstruction; | 
						|
			if (call != null) | 
						|
				call.IsTail = true; | 
						|
			else | 
						|
				Warn("Ignored invalid 'tail' prefix"); | 
						|
			return inst; | 
						|
		} | 
						|
 | 
						|
		private DecodedInstruction DecodeUnaligned() | 
						|
		{ | 
						|
			byte alignment = reader.ReadByte(); | 
						|
			var inst = DecodeInstruction(); | 
						|
			var sup = inst.Instruction as ISupportsUnalignedPrefix; | 
						|
			if (sup != null) | 
						|
				sup.UnalignedPrefix = alignment; | 
						|
			else | 
						|
				Warn("Ignored invalid 'unaligned' prefix"); | 
						|
			return inst; | 
						|
		} | 
						|
 | 
						|
		private DecodedInstruction DecodeVolatile() | 
						|
		{ | 
						|
			var inst = DecodeInstruction(); | 
						|
			var svp = inst.Instruction as ISupportsVolatilePrefix; | 
						|
			if (svp != null) | 
						|
				svp.IsVolatile = true; | 
						|
			else | 
						|
				Warn("Ignored invalid 'volatile' prefix"); | 
						|
			return inst; | 
						|
		} | 
						|
 | 
						|
		private DecodedInstruction DecodeReadonly() | 
						|
		{ | 
						|
			var inst = DecodeInstruction(); | 
						|
			var ldelema = inst.Instruction as LdElema; | 
						|
			if (ldelema != null) | 
						|
				ldelema.IsReadOnly = true; | 
						|
			else | 
						|
				Warn("Ignored invalid 'readonly' prefix"); | 
						|
			return inst; | 
						|
		} | 
						|
 | 
						|
		DecodedInstruction DecodeCall(OpCode opCode) | 
						|
		{ | 
						|
			var method = ReadAndDecodeMethodReference(); | 
						|
			ILInstruction[] arguments; | 
						|
			switch (method.DeclaringType.Kind) | 
						|
			{ | 
						|
				case TypeKind.Array: | 
						|
				{ | 
						|
					arguments = PrepareArguments(firstArgumentIsStObjTarget: false); | 
						|
					var elementType = ((ArrayType)method.DeclaringType).ElementType; | 
						|
					if (opCode == OpCode.NewObj) | 
						|
						return Push(new NewArr(elementType, arguments)); | 
						|
					if (method.Name == "Set") | 
						|
					{ | 
						|
						var target = arguments[0]; | 
						|
						var indices = arguments.Skip(1).Take(arguments.Length - 2).ToArray(); | 
						|
						var value = arguments.Last(); | 
						|
						// preserves evaluation order target,indices,value | 
						|
						return new StObj(new LdElema(elementType, target, indices) { DelayExceptions = true }, value, elementType); | 
						|
					} | 
						|
					if (method.Name == "Get") | 
						|
					{ | 
						|
						var target = arguments[0]; | 
						|
						var indices = arguments.Skip(1).ToArray(); | 
						|
						// preserves evaluation order target,indices | 
						|
						return Push(new LdObj(new LdElema(elementType, target, indices) { DelayExceptions = true }, elementType)); | 
						|
					} | 
						|
					if (method.Name == "Address") | 
						|
					{ | 
						|
						var target = arguments[0]; | 
						|
						var indices = arguments.Skip(1).ToArray(); | 
						|
						// preserves evaluation order target,indices | 
						|
						return Push(new LdElema(elementType, target, indices)); | 
						|
					} | 
						|
					Warn("Unknown method called on array type: " + method.Name); | 
						|
					goto default; | 
						|
				} | 
						|
				case TypeKind.Struct when method.IsConstructor && !method.IsStatic && opCode == OpCode.Call | 
						|
					&& method.ReturnType.Kind == TypeKind.Void: | 
						|
				{ | 
						|
					// "call Struct.ctor(target, ...)" doesn't exist in C#, | 
						|
					// the next best equivalent is an assignment `*target = new Struct(...);`. | 
						|
					// So we represent this call as "stobj Struct(target, newobj Struct.ctor(...))". | 
						|
					// This needs to happen early (not as a transform) because the StObj.TargetSlot has | 
						|
					// restricted inlining (doesn't accept ldflda when exceptions aren't delayed). | 
						|
					arguments = PrepareArguments(firstArgumentIsStObjTarget: true); | 
						|
					var newobj = new NewObj(method); | 
						|
					newobj.ILStackWasEmpty = CurrentStackIsEmpty(); | 
						|
					newobj.ConstrainedTo = constrainedPrefix; | 
						|
					newobj.Arguments.AddRange(arguments.Skip(1)); | 
						|
					return new StObj(arguments[0], newobj, method.DeclaringType); | 
						|
				} | 
						|
				default: | 
						|
					arguments = PrepareArguments(firstArgumentIsStObjTarget: false); | 
						|
					var call = CallInstruction.Create(opCode, method); | 
						|
					call.ILStackWasEmpty = CurrentStackIsEmpty(); | 
						|
					call.ConstrainedTo = constrainedPrefix; | 
						|
					call.Arguments.AddRange(arguments); | 
						|
					if (call.ResultType != StackType.Void) | 
						|
						return Push(call); | 
						|
					return call; | 
						|
			} | 
						|
 | 
						|
			ILInstruction[] PrepareArguments(bool firstArgumentIsStObjTarget) | 
						|
			{ | 
						|
				int firstArgument = (opCode != OpCode.NewObj && !method.IsStatic) ? 1 : 0; | 
						|
				var arguments = new ILInstruction[firstArgument + method.Parameters.Count]; | 
						|
				for (int i = method.Parameters.Count - 1; i >= 0; i--) | 
						|
				{ | 
						|
					arguments[firstArgument + i] = Pop(method.Parameters[i].Type.GetStackType()); | 
						|
				} | 
						|
				if (firstArgument == 1) | 
						|
				{ | 
						|
					arguments[0] = firstArgumentIsStObjTarget | 
						|
						? PopStObjTarget() | 
						|
						: Pop(CallInstruction.ExpectedTypeForThisPointer(constrainedPrefix ?? method.DeclaringType)); | 
						|
				} | 
						|
				// arguments is in reverse order of the Pop calls, thus | 
						|
				// arguments is now in the correct evaluation order. | 
						|
				return arguments; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		DecodedInstruction DecodeCallIndirect() | 
						|
		{ | 
						|
			var signatureHandle = (StandaloneSignatureHandle)ReadAndDecodeMetadataToken(); | 
						|
			var (header, fpt) = module.DecodeMethodSignature(signatureHandle, genericContext); | 
						|
			var functionPointer = Pop(StackType.I); | 
						|
			int firstArgument = header.IsInstance ? 1 : 0; | 
						|
			var arguments = new ILInstruction[firstArgument + fpt.ParameterTypes.Length]; | 
						|
			for (int i = fpt.ParameterTypes.Length - 1; i >= 0; i--) | 
						|
			{ | 
						|
				arguments[firstArgument + i] = Pop(fpt.ParameterTypes[i].GetStackType()); | 
						|
			} | 
						|
			if (firstArgument == 1) | 
						|
			{ | 
						|
				arguments[0] = Pop(); | 
						|
			} | 
						|
			// arguments is in reverse order of the Pop calls, thus | 
						|
			// arguments is now in the correct evaluation order. | 
						|
			var call = new CallIndirect( | 
						|
				header.IsInstance, | 
						|
				header.HasExplicitThis, | 
						|
				fpt, | 
						|
				functionPointer, | 
						|
				arguments | 
						|
			); | 
						|
			if (call.ResultType != StackType.Void) | 
						|
				return Push(call); | 
						|
			else | 
						|
				return call; | 
						|
		} | 
						|
 | 
						|
		ILInstruction Comparison(ComparisonKind kind, bool un = false) | 
						|
		{ | 
						|
			var right = Pop(); | 
						|
			var left = Pop(); | 
						|
			// left will run before right, thus preserving the evaluation order | 
						|
 | 
						|
			if ((left.ResultType == StackType.O || left.ResultType == StackType.Ref) && right.ResultType.IsIntegerType()) | 
						|
			{ | 
						|
				// C++/CLI sometimes compares object references with integers. | 
						|
				// Also happens with Ref==I in Unsafe.IsNullRef(). | 
						|
				if (right.ResultType == StackType.I4) | 
						|
				{ | 
						|
					// ensure we compare at least native integer size | 
						|
					right = new Conv(right, PrimitiveType.I, false, Sign.None); | 
						|
				} | 
						|
				left = new Conv(left, right.ResultType.ToPrimitiveType(), false, Sign.None); | 
						|
			} | 
						|
			else if ((right.ResultType == StackType.O || right.ResultType == StackType.Ref) && left.ResultType.IsIntegerType()) | 
						|
			{ | 
						|
				if (left.ResultType == StackType.I4) | 
						|
				{ | 
						|
					left = new Conv(left, PrimitiveType.I, false, Sign.None); | 
						|
				} | 
						|
				right = new Conv(right, left.ResultType.ToPrimitiveType(), false, Sign.None); | 
						|
			} | 
						|
 | 
						|
			// make implicit integer conversions explicit: | 
						|
			MakeExplicitConversion(sourceType: StackType.I4, targetType: StackType.I, conversionType: PrimitiveType.I); | 
						|
			MakeExplicitConversion(sourceType: StackType.I4, targetType: StackType.I8, conversionType: PrimitiveType.I8); | 
						|
			MakeExplicitConversion(sourceType: StackType.I, targetType: StackType.I8, conversionType: PrimitiveType.I8); | 
						|
 | 
						|
			// Based on Table 4: Binary Comparison or Branch Operation | 
						|
			if (left.ResultType.IsFloatType() && right.ResultType.IsFloatType()) | 
						|
			{ | 
						|
				if (left.ResultType != right.ResultType) | 
						|
				{ | 
						|
					// make the implicit F4->F8 conversion explicit: | 
						|
					MakeExplicitConversion(StackType.F4, StackType.F8, PrimitiveType.R8); | 
						|
				} | 
						|
				if (un) | 
						|
				{ | 
						|
					// for floats, 'un' means 'unordered' | 
						|
					return Comp.LogicNot(new Comp(kind.Negate(), Sign.None, left, right)); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					return new Comp(kind, Sign.None, left, right); | 
						|
				} | 
						|
			} | 
						|
			else if (left.ResultType.IsIntegerType() && right.ResultType.IsIntegerType() && !kind.IsEqualityOrInequality()) | 
						|
			{ | 
						|
				// integer comparison where the sign matters | 
						|
				Debug.Assert(right.ResultType.IsIntegerType()); | 
						|
				return new Comp(kind, un ? Sign.Unsigned : Sign.Signed, left, right); | 
						|
			} | 
						|
			else if (left.ResultType == right.ResultType) | 
						|
			{ | 
						|
				// integer equality, object reference or managed reference comparison | 
						|
				return new Comp(kind, Sign.None, left, right); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				Warn($"Invalid comparison between {left.ResultType} and {right.ResultType}"); | 
						|
				if (left.ResultType < right.ResultType) | 
						|
				{ | 
						|
					left = new Conv(left, right.ResultType.ToPrimitiveType(), false, Sign.Signed); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					right = new Conv(right, left.ResultType.ToPrimitiveType(), false, Sign.Signed); | 
						|
				} | 
						|
				return new Comp(kind, Sign.None, left, right); | 
						|
			} | 
						|
 | 
						|
			void MakeExplicitConversion(StackType sourceType, StackType targetType, PrimitiveType conversionType) | 
						|
			{ | 
						|
				if (left.ResultType == sourceType && right.ResultType == targetType) | 
						|
				{ | 
						|
					left = new Conv(left, conversionType, false, Sign.None); | 
						|
				} | 
						|
				else if (left.ResultType == targetType && right.ResultType == sourceType) | 
						|
				{ | 
						|
					right = new Conv(right, conversionType, false, Sign.None); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		bool IsInvalidBranch(int target) => target < 0 || target >= reader.Length; | 
						|
 | 
						|
		ILInstruction DecodeComparisonBranch(ILOpCode opCode, ComparisonKind kind, bool un = false) | 
						|
		{ | 
						|
			int start = reader.Offset - 1; // opCode is always one byte in this case | 
						|
			int target = ILParser.DecodeBranchTarget(ref reader, opCode); | 
						|
			var condition = Comparison(kind, un); | 
						|
			condition.AddILRange(new Interval(start, reader.Offset)); | 
						|
			if (!IsInvalidBranch(target)) | 
						|
			{ | 
						|
				MarkBranchTarget(target); | 
						|
				return new IfInstruction(condition, new Branch(target)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return new IfInstruction(condition, new InvalidBranch("Invalid branch target")); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		ILInstruction DecodeConditionalBranch(ILOpCode opCode, bool negate) | 
						|
		{ | 
						|
			int target = ILParser.DecodeBranchTarget(ref reader, opCode); | 
						|
			ILInstruction condition = Pop(); | 
						|
			switch (condition.ResultType) | 
						|
			{ | 
						|
				case StackType.O: | 
						|
					// introduce explicit comparison with null | 
						|
					condition = new Comp( | 
						|
						negate ? ComparisonKind.Equality : ComparisonKind.Inequality, | 
						|
						Sign.None, condition, new LdNull()); | 
						|
					break; | 
						|
				case StackType.I: | 
						|
					// introduce explicit comparison with 0 | 
						|
					condition = new Comp( | 
						|
						negate ? ComparisonKind.Equality : ComparisonKind.Inequality, | 
						|
						Sign.None, condition, new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None)); | 
						|
					break; | 
						|
				case StackType.I8: | 
						|
					// introduce explicit comparison with 0 | 
						|
					condition = new Comp( | 
						|
						negate ? ComparisonKind.Equality : ComparisonKind.Inequality, | 
						|
						Sign.None, condition, new LdcI8(0)); | 
						|
					break; | 
						|
				case StackType.Ref: | 
						|
					// introduce explicit comparison with null ref | 
						|
					condition = new Comp( | 
						|
						negate ? ComparisonKind.Equality : ComparisonKind.Inequality, | 
						|
						Sign.None, new Conv(condition, PrimitiveType.I, false, Sign.None), new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None)); | 
						|
					break; | 
						|
				case StackType.I4: | 
						|
					if (negate) | 
						|
					{ | 
						|
						condition = Comp.LogicNot(condition); | 
						|
					} | 
						|
					break; | 
						|
				default: | 
						|
					condition = new Conv(condition, PrimitiveType.I4, false, Sign.None); | 
						|
					if (negate) | 
						|
					{ | 
						|
						condition = Comp.LogicNot(condition); | 
						|
					} | 
						|
					break; | 
						|
			} | 
						|
			if (!IsInvalidBranch(target)) | 
						|
			{ | 
						|
				MarkBranchTarget(target); | 
						|
				return new IfInstruction(condition, new Branch(target)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return new IfInstruction(condition, new InvalidBranch("Invalid branch target")); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		ILInstruction DecodeUnconditionalBranch(ILOpCode opCode, bool isLeave = false) | 
						|
		{ | 
						|
			int target = ILParser.DecodeBranchTarget(ref reader, opCode); | 
						|
			if (isLeave) | 
						|
			{ | 
						|
				FlushExpressionStack(); | 
						|
				currentStack = currentStack.Clear(); | 
						|
			} | 
						|
			if (!IsInvalidBranch(target)) | 
						|
			{ | 
						|
				MarkBranchTarget(target); | 
						|
				return new Branch(target); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return new InvalidBranch("Invalid branch target"); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		void MarkBranchTarget(int targetILOffset) | 
						|
		{ | 
						|
			FlushExpressionStack(); | 
						|
			Debug.Assert(isBranchTarget[targetILOffset]); | 
						|
			StoreStackForOffset(targetILOffset, ref currentStack); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// The expression stack holds ILInstructions that might have side-effects | 
						|
		/// that should have already happened (in the order of the pushes). | 
						|
		/// This method forces these instructions to be added to the instructionBuilder. | 
						|
		/// This is used e.g. to avoid moving side-effects past branches. | 
						|
		/// </summary> | 
						|
		private void FlushExpressionStack() | 
						|
		{ | 
						|
			foreach (var inst in expressionStack) | 
						|
			{ | 
						|
				Debug.Assert(inst.ResultType != StackType.Void); | 
						|
				IType type = compilation.FindType(inst.ResultType); | 
						|
				var v = new ILVariable(VariableKind.StackSlot, type, inst.ResultType); | 
						|
				v.HasGeneratedName = true; | 
						|
				currentStack = currentStack.Push(v); | 
						|
				instructionBuilder.Add(new StLoc(v, inst).WithILRange(inst)); | 
						|
			} | 
						|
			expressionStack.Clear(); | 
						|
		} | 
						|
 | 
						|
		ILInstruction DecodeSwitch() | 
						|
		{ | 
						|
			var targets = ILParser.DecodeSwitchTargets(ref reader); | 
						|
			var instr = new SwitchInstruction(Pop(StackType.I4)); | 
						|
 | 
						|
			for (int i = 0; i < targets.Length; i++) | 
						|
			{ | 
						|
				var section = new SwitchSection(); | 
						|
				section.Labels = new LongSet(i); | 
						|
				int target = targets[i]; | 
						|
				if (!IsInvalidBranch(target)) | 
						|
				{ | 
						|
					MarkBranchTarget(target); | 
						|
					section.Body = new Branch(target); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					section.Body = new InvalidBranch("Invalid branch target"); | 
						|
				} | 
						|
				instr.Sections.Add(section); | 
						|
			} | 
						|
			var defaultSection = new SwitchSection(); | 
						|
			defaultSection.Labels = new LongSet(new LongInterval(0, targets.Length)).Invert(); | 
						|
			defaultSection.Body = new Nop(); | 
						|
			instr.Sections.Add(defaultSection); | 
						|
			return instr; | 
						|
		} | 
						|
 | 
						|
		DecodedInstruction BinaryNumeric(BinaryNumericOperator @operator, bool checkForOverflow = false, Sign sign = Sign.None) | 
						|
		{ | 
						|
			var right = Pop(); | 
						|
			var left = Pop(); | 
						|
			// left will run before right, thus preserving the evaluation order | 
						|
			if (@operator != BinaryNumericOperator.Add && @operator != BinaryNumericOperator.Sub) | 
						|
			{ | 
						|
				// we are treating all Refs as I, make the conversion explicit | 
						|
				if (left.ResultType == StackType.Ref) | 
						|
				{ | 
						|
					left = new Conv(left, PrimitiveType.I, false, Sign.None); | 
						|
				} | 
						|
				if (right.ResultType == StackType.Ref) | 
						|
				{ | 
						|
					right = new Conv(right, PrimitiveType.I, false, Sign.None); | 
						|
				} | 
						|
			} | 
						|
			if (@operator != BinaryNumericOperator.ShiftLeft && @operator != BinaryNumericOperator.ShiftRight) | 
						|
			{ | 
						|
				// make the implicit I4->I conversion explicit: | 
						|
				MakeExplicitConversion(sourceType: StackType.I4, targetType: StackType.I, conversionType: PrimitiveType.I); | 
						|
				// I4->I8 conversion: | 
						|
				MakeExplicitConversion(sourceType: StackType.I4, targetType: StackType.I8, conversionType: PrimitiveType.I8); | 
						|
				// I->I8 conversion: | 
						|
				MakeExplicitConversion(sourceType: StackType.I, targetType: StackType.I8, conversionType: PrimitiveType.I8); | 
						|
				// F4->F8 conversion: | 
						|
				MakeExplicitConversion(sourceType: StackType.F4, targetType: StackType.F8, conversionType: PrimitiveType.R8); | 
						|
			} | 
						|
			return Push(new BinaryNumericInstruction(@operator, left, right, checkForOverflow, sign)); | 
						|
 | 
						|
			void MakeExplicitConversion(StackType sourceType, StackType targetType, PrimitiveType conversionType) | 
						|
			{ | 
						|
				if (left.ResultType == sourceType && right.ResultType == targetType) | 
						|
				{ | 
						|
					left = new Conv(left, conversionType, false, Sign.None); | 
						|
				} | 
						|
				else if (left.ResultType == targetType && right.ResultType == sourceType) | 
						|
				{ | 
						|
					right = new Conv(right, conversionType, false, Sign.None); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		ILInstruction DecodeJmp() | 
						|
		{ | 
						|
			IMethod method = ReadAndDecodeMethodReference(); | 
						|
			// Translate jmp into tail call: | 
						|
			Call call = new Call(method); | 
						|
			call.IsTail = true; | 
						|
			call.ILStackWasEmpty = true; | 
						|
			if (!method.IsStatic) | 
						|
			{ | 
						|
				call.Arguments.Add(Ldarg(0)); | 
						|
			} | 
						|
			foreach (var p in method.Parameters) | 
						|
			{ | 
						|
				call.Arguments.Add(Ldarg(call.Arguments.Count)); | 
						|
			} | 
						|
			return new Leave(mainContainer, call); | 
						|
		} | 
						|
 | 
						|
		ILInstruction LdToken(EntityHandle token) | 
						|
		{ | 
						|
			if (token.Kind.IsTypeKind()) | 
						|
				return new LdTypeToken(module.ResolveType(token, genericContext)); | 
						|
			if (token.Kind.IsMemberKind()) | 
						|
			{ | 
						|
				var entity = module.ResolveEntity(token, genericContext); | 
						|
				if (entity is IMember member) | 
						|
					return new LdMemberToken(member); | 
						|
			} | 
						|
			throw new BadImageFormatException("Invalid metadata token for ldtoken instruction."); | 
						|
		} | 
						|
	} | 
						|
}
 | 
						|
 |