/// <summary> /// Gets whether 'expressionBeingMoved' can be inlined into 'expr'. /// </summary> public bool CanInlineInto(AstExpression expr, AstVariable v, AstExpression expressionBeingMoved) { AstExpression parent; int pos; return(FindLoadInNext(expr, v, expressionBeingMoved, out parent, out pos) == true); }
private AssignmentStatement <TInstruction> CreateAssignment( Expression <TInstruction> expression, TInstruction instruction, IVariable[] writtenVariables) { // We will create stack slots for every push and "simulate" an assignment // to them. This way the resulting AST won't have the "concept" of a stack. int pushes = _context.Architecture.GetStackPushCount(instruction); var slots = GetStackSlots(pushes); var buffer = new AstVariable[slots.Length + writtenVariables.Length]; slots.CopyTo(buffer.AsSpan()); // Also assign to written variables. for (int i = 0; i < writtenVariables.Length; i++) { // Get the correct version of the variable to assign to. var variable = writtenVariables[i]; int version = _context.GetVariableVersion(variable); var snapshot = new VariableSnapshot(variable, version); var versionedVariable = _context.GetVersionedVariable(snapshot); buffer[slots.Length + i] = versionedVariable; } // Assign stack slots. _context.StackSlots[instruction] = slots; return(new AssignmentStatement <TInstruction>(buffer, expression)); }
bool ForwardScanInitializeArrayRuntimeHelper(List <AstNode> body, int pos, AstVariable array, XTypeReference arrayType, int arrayLength, out Array values, out int foundPos) { AstVariable v2; XMethodReference methodRef; AstExpression methodArg1; AstExpression methodArg2; XFieldReference fieldRef; if (body.ElementAtOrDefault(pos).Match(AstCode.Call, out methodRef, out methodArg1, out methodArg2) && methodRef.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers" && methodRef.Name == "InitializeArray" && methodArg1.Match(AstCode.Ldloc, out v2) && array == v2 && methodArg2.Match(AstCode.Ldtoken, out fieldRef)) { var fieldDef = fieldRef.Resolve(); //.ResolveWithinSameModule(); if (fieldDef != null && fieldDef.InitialValue != null) { Array newArr; if (DecodeArrayInitializer(arrayType.GetElementType(), (byte[])fieldDef.InitialValue, arrayLength, out newArr)) { values = newArr; foundPos = pos; return(true); } } } values = null; foundPos = -1; return(false); }
bool CanPerformCopyPropagation(AstExpression expr, AstVariable copyVariable) { switch (expr.Code) { case AstCode.Ldloca: case AstCode.Ldelema: case AstCode.Ldflda: case AstCode.Ldsflda: // All address-loading instructions always return the same value for a given operand/argument combination, // so they can be safely copied. return(true); case AstCode.Ldloc: AstVariable v = (AstVariable)expr.Operand; if (v.IsParameter) { // Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga) return(numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 0); } else { // Variables are be copied only if both they and the target copy variable are generated, // and if the variable has only a single assignment return(v.IsGenerated && copyVariable.IsGenerated && numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1); } default: return(false); } }
internal AstVariable GetVersionedVariable(VariableSnapshot snapshot) { if (VersionedVariables.TryGetValue(snapshot, out var variable)) { return(variable); } variable = new AstVariable(snapshot.ToString()); VersionedVariables.Add(snapshot, variable); return(variable); }
internal AstVariable GetExternalVariable(ExternalDataSourceNode <TInstruction> external) { if (ExternalSources.TryGetValue(external, out var variable)) { return(variable); } variable = new AstVariable(external.Name); ExternalSources.Add(external, variable); return(variable); }
/// <summary> /// Inlines 'expr' into 'next', if possible. /// </summary> bool InlineIfPossible(AstVariable v, AstExpression inlinedExpression, AstNode next, bool aggressive) { // ensure the variable is accessed only a single time if (numStloc.GetOrDefault(v) != 1) { return(false); } int ldloc = numLdloc.GetOrDefault(v); if (ldloc > 1 || ldloc + numLdloca.GetOrDefault(v) != 1) { return(false); } AstExpression parent; int pos; if (FindLoadInNext(next as AstExpression, v, inlinedExpression, out parent, out pos) == true) { if (ldloc == 0) { if (!IsGeneratedValueTypeTemporary((AstExpression)next, parent, pos, v, inlinedExpression)) { return(false); } } else { if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((AstExpression)next, parent, inlinedExpression)) { return(false); } } // Assign the ranges of the ldloc instruction: inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges); if (ldloc == 0) { // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' so that the types // comes out correctly parent.Arguments[pos] = new AstExpression(inlinedExpression.SourceLocation, AstCode.AddressOf, null, inlinedExpression); } else { parent.Arguments[pos] = inlinedExpression; } return(true); } return(false); }
/// <summary> /// Gets the register spec used for the given variable. /// </summary> internal RegisterSpec GetArgument(AstVariable variable) { if (variable.IsParameter) { return(arguments.First(x => (x.Parameter != null) && x.Parameter.Equals(variable))); } VariableRegisterSpec r; if (!variables.TryGetValue(variable, out r)) { r = (VariableRegisterSpec)Allocate(variable.Type.GetReference(targetPackage), false, RCategory.Variable, variable); variables.Add(variable, r); } return(r); }
private AstVariable[] GetStackSlots(int pushes) { if (pushes == 0) { return(Array.Empty <AstVariable>()); } var buffer = new AstVariable[pushes]; for (int i = 0; i < buffer.Length; i++) { buffer[i] = _context.CreateStackSlot(); } return(buffer); }
public AstVariableDeclaration ReadVariableDeclaration() { var flags = Read7BitEncodedInt(); var type = ReadExpression(); var vars = new AstVariable[flags >> (int)AstVariableModifier.Shift]; for (var i = 0; i < vars.Length; i++) { vars[i] = new AstVariable( ReadIdentifier(), ReadExpression()); } return(new AstVariableDeclaration( (AstVariableModifier)flags & AstVariableModifier.Mask, type, vars)); }
void ConvertParameters(List <ByteCode> body) { AstVariable thisParameter = null; if (methodDef.HasThis) { TypeReference type = methodDef.DeclaringType; thisParameter = new AstILVariable("this", XBuilder.AsTypeReference(module, type.IsValueType ? new ByReferenceType(type) : type), methodDef.Body.ThisParameter); } foreach (var p in methodDef.Parameters) { Parameters.Add(new AstILVariable(p.Name, XBuilder.AsTypeReference(module, p.ParameterType), p)); } if (Parameters.Count > 0 && (methodDef.IsSetter || methodDef.IsAddOn || methodDef.IsRemoveOn)) { // last parameter must be 'value', so rename it Parameters.Last().Name = "value"; } foreach (ByteCode byteCode in body) { ParameterDefinition p; switch (byteCode.Code) { case AstCode.__Ldarg: p = (ParameterDefinition)byteCode.Operand; byteCode.Code = AstCode.Ldloc; byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; break; case AstCode.__Starg: p = (ParameterDefinition)byteCode.Operand; byteCode.Code = AstCode.Stloc; byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; break; case AstCode.__Ldarga: p = (ParameterDefinition)byteCode.Operand; byteCode.Code = AstCode.Ldloca; byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; break; } } if (thisParameter != null) { this.Parameters.Add(thisParameter); } }
/// <summary> /// Runs a very simple form of copy propagation. /// Copy propagation is used in two cases: /// 1) assignments from arguments to local variables /// If the target variable is assigned to only once (so always is that argument) and the argument is never changed (no ldarga/starg), /// then we can replace the variable with the argument. /// 2) assignments of address-loading instructions to local variables /// </summary> public void CopyPropagation() { foreach (AstBlock block in method.GetSelfAndChildrenRecursive <AstBlock>()) { for (int i = 0; i < block.Body.Count; i++) { AstVariable v; AstExpression copiedExpr; if (block.Body[i].Match(AstCode.Stloc, out v, out copiedExpr) && !v.IsParameter && numStloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0 && CanPerformCopyPropagation(copiedExpr, v)) { // un-inline the arguments of the ldArg instruction var uninlinedArgs = new AstVariable[copiedExpr.Arguments.Count]; for (int j = 0; j < uninlinedArgs.Length; j++) { uninlinedArgs[j] = new AstGeneratedVariable(v.Name + "_cp_" + j, v.OriginalName); block.Body.Insert(i++, new AstExpression(copiedExpr.SourceLocation, AstCode.Stloc, uninlinedArgs[j], copiedExpr.Arguments[j])); } // perform copy propagation: foreach (var expr in method.GetSelfAndChildrenRecursive <AstExpression>()) { if (expr.Code == AstCode.Ldloc && expr.Operand == v) { expr.Code = copiedExpr.Code; expr.Operand = copiedExpr.Operand; for (int j = 0; j < uninlinedArgs.Length; j++) { expr.Arguments.Add(new AstExpression(copiedExpr.SourceLocation, AstCode.Ldloc, uninlinedArgs[j])); } } } block.Body.RemoveAt(i); if (uninlinedArgs.Length > 0) { // if we un-inlined stuff; we need to update the usage counters AnalyzeMethod(); } InlineInto(block.Body, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i] i -= uninlinedArgs.Length + 1; } } } }
void AnalyzeNode(AstNode node) { AstExpression expr = node as AstExpression; if (expr != null) { AstVariable locVar = expr.Operand as AstVariable; if (locVar != null) { if (expr.Code == AstCode.Stloc) { numStloc[locVar] = numStloc.GetOrDefault(locVar) + 1; } else if (expr.Code == AstCode.Ldloc) { numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + 1; } else if (expr.Code == AstCode.Ldloca) { numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + 1; } else { throw new NotSupportedException(expr.Code.ToString()); } } foreach (AstExpression child in expr.Arguments) { AnalyzeNode(child); } } else { var catchBlock = node as AstTryCatchBlock.CatchBlock; if (catchBlock != null && catchBlock.ExceptionVariable != null) { numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + 1; } foreach (AstNode child in node.GetChildren()) { AnalyzeNode(child); } } }
/// <summary> /// Gets the register spec used for the given variable. /// </summary> internal RegisterSpec GetArgument(AstVariable variable) { if (variable.IsParameter) { return(arguments.First(x => (x.Parameter != null) && x.Parameter.Equals(variable))); } VariableRegisterSpec r; if (!variables.TryGetValue(variable, out r)) { bool isPreventOpt = variable.PreventOptimizations; bool isTempVariable = !isPreventOpt && (variable.IsGenerated || variable.IsCompilerGenerated); var category = isPreventOpt ? RCategory.VariablePreventOptimization : isTempVariable ? RCategory.TempVariable : RCategory.Variable; r = (VariableRegisterSpec)Allocate(variable.Type.GetReference(targetPackage), false, category, variable); variables.Add(variable, r); } return(r); }
public bool InlineAllInBlock(AstBlock block) { bool modified = false; List <AstNode> body = block.Body; if (block is AstTryCatchBlock.CatchBlock && body.Count > 1) { AstVariable v = ((AstTryCatchBlock.CatchBlock)block).ExceptionVariable; if (v != null && v.IsGenerated) { if (numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1) { AstVariable v2; AstExpression ldException; if (body[0].Match(AstCode.Stloc, out v2, out ldException) && ldException.MatchLdloc(v)) { body.RemoveAt(0); ((AstTryCatchBlock.CatchBlock)block).ExceptionVariable = v2; modified = true; } } } } for (int i = 0; i < body.Count - 1;) { AstVariable locVar; AstExpression expr; if (body[i].Match(AstCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block.Body, i, aggressive: false)) { modified = true; i = Math.Max(0, i - 1); // Go back one step } else { i++; } } foreach (AstBasicBlock bb in body.OfType <AstBasicBlock>()) { modified |= InlineAllInBasicBlock(bb); } return(modified); }
/// <summary> /// Determines whether it is safe to move 'expressionBeingMoved' past 'expr' /// </summary> bool IsSafeForInlineOver(AstExpression expr, AstExpression expressionBeingMoved) { switch (expr.Code) { case AstCode.Ldloc: AstVariable loadedVar = (AstVariable)expr.Operand; if (numLdloca.GetOrDefault(loadedVar) != 0) { // abort, inlining is not possible return(false); } foreach (AstExpression potentialStore in expressionBeingMoved.GetSelfAndChildrenRecursive <AstExpression>()) { if (potentialStore.Code == AstCode.Stloc && potentialStore.Operand == loadedVar) { return(false); } } // the expression is loading a non-forbidden variable return(true); case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldelema: case AstCode.AddressOf: case AstCode.ValueOf: case AstCode.NullableOf: // address-loading instructions are safe if their arguments are safe foreach (AstExpression arg in expr.Arguments) { if (!IsSafeForInlineOver(arg, expressionBeingMoved)) { return(false); } } return(true); default: // instructions with no side-effects are safe (except for Ldloc and Ldloca which are handled separately) return(expr.HasNoSideEffects()); } }
/// <summary> /// Finds the position to inline to. /// </summary> /// <returns>true = found; false = cannot continue search; null = not found</returns> bool?FindLoadInNext(AstExpression expr, AstVariable v, AstExpression expressionBeingMoved, out AstExpression parent, out int pos) { parent = null; pos = 0; if (expr == null) { return(false); } for (int i = 0; i < expr.Arguments.Count; i++) { // Stop when seeing an opcode that does not guarantee that its operands will be evaluated. // Inlining in that case might result in the inlined expresion not being evaluted. if (i == 1 && (expr.Code == AstCode.NullCoalescing)) { return(false); } AstExpression arg = expr.Arguments[i]; if ((arg.Code == AstCode.Ldloc || arg.Code == AstCode.Ldloca) && arg.Operand == v) { parent = expr; pos = i; return(true); } bool?r = FindLoadInNext(arg, v, expressionBeingMoved, out parent, out pos); if (r != null) { return(r); } } if (IsSafeForInlineOver(expr, expressionBeingMoved)) { return(null); // continue searching } else { return(false); // abort, inlining not possible } }
/// <summary> /// Fill the parameters list. /// </summary> private void ConvertParameters(List <ByteCode> body) { AstVariable thisParameter = null; if (methodDef.HasThis) { var type = AsTypeReference(methodDef.DeclaringClass, XTypeUsageFlags.DeclaringType); thisParameter = new AstJavaVariable(type, codeAttr.ThisParameter); } foreach (var p in codeAttr.Parameters) { var type = AsTypeReference(p.Item1, XTypeUsageFlags.ParameterType); parameters.Add(new AstJavaVariable(type, p.Item2)); } if (thisParameter != null) { parameters.Add(thisParameter); } foreach (var byteCode in body) { LocalVariableReference p; switch (byteCode.Code) { case AstCode.Ldloc: case AstCode.Stloc: p = byteCode.Operand as LocalVariableReference; if (p != null) { var astP = parameters.Cast <AstJavaVariable>().FirstOrDefault(x => x.LocalVariableReference.Index == p.Index); if (astP != null) { byteCode.Operand = astP; } } break; } } }
bool StoreCanBeConvertedToAssignment(AstExpression store, AstVariable exprVar) { if (store == null) { return(false); } switch (store.Code) { case AstCode.Stloc: case AstCode.Stfld: case AstCode.Stsfld: case AstCode.Stobj: break; default: if (!store.Code.IsStoreToArray()) { return(false); } break; } return(store.Arguments.Last().Code == AstCode.Ldloc && store.Arguments.Last().Operand == exprVar); }
/// <summary> /// Runs a very simple form of copy propagation. /// Copy propagation is used in two cases: /// 1) assignments from arguments to local variables /// If the target variable is assigned to only once (so always is that argument) and the argument is never changed (no ldarga/starg), /// then we can replace the variable with the argument. /// 2) assignments of address-loading instructions to local variables /// </summary> public void CopyPropagation() { foreach (AstBlock block in method.GetSelfAndChildrenRecursive<AstBlock>()) { for (int i = 0; i < block.Body.Count; i++) { AstVariable v; AstExpression copiedExpr; if (block.Body[i].Match(AstCode.Stloc, out v, out copiedExpr) && !v.IsParameter && numStloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0 && CanPerformCopyPropagation(copiedExpr, v)) { // un-inline the arguments of the ldArg instruction var uninlinedArgs = new AstVariable[copiedExpr.Arguments.Count]; for (int j = 0; j < uninlinedArgs.Length; j++) { uninlinedArgs[j] = new AstGeneratedVariable(v.Name + "_cp_" + j, v.OriginalName); block.Body.Insert(i++, new AstExpression(copiedExpr.SourceLocation, AstCode.Stloc, uninlinedArgs[j], copiedExpr.Arguments[j])); } // perform copy propagation: foreach (var expr in method.GetSelfAndChildrenRecursive<AstExpression>()) { if (expr.Code == AstCode.Ldloc && expr.Operand == v) { expr.Code = copiedExpr.Code; expr.Operand = copiedExpr.Operand; for (int j = 0; j < uninlinedArgs.Length; j++) { expr.Arguments.Add(new AstExpression(copiedExpr.SourceLocation, AstCode.Ldloc, uninlinedArgs[j])); } } } block.Body.RemoveAt(i); if (uninlinedArgs.Length > 0) { // if we un-inlined stuff; we need to update the usage counters AnalyzeMethod(); } InlineInto(block.Body, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i] i -= uninlinedArgs.Length + 1; } } } }
public TrsVariable(string name, AstVariable astSource) { Name = name; AstSource = astSource; }
/// <summary> /// Gets the register spec used for the given variable. /// </summary> internal RegisterSpec GetArgument(AstVariable variable) { if (variable.IsParameter) { return arguments.First(x => (x.Parameter != null) && x.Parameter.Equals(variable)); } VariableRegisterSpec r; if (!variables.TryGetValue(variable, out r)) { bool isPreventOpt = variable.PreventOptimizations; bool isTempVariable = !isPreventOpt && (variable.IsGenerated || variable.IsCompilerGenerated); var category = isPreventOpt ? RCategory.VariablePreventOptimization : isTempVariable ? RCategory.TempVariable : RCategory.Variable; r = (VariableRegisterSpec) Allocate(variable.Type.GetReference(targetPackage), false, category, variable); variables.Add(variable, r); } return r; }
/// <summary> /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values /// </summary> /// <param name="next">The next top-level expression</param> /// <param name="parent">The direct parent of the load within 'next'</param> /// <param name="pos">Index of the load within 'parent'</param> /// <param name="v">The variable being inlined.</param> /// <param name="inlinedExpression">The expression being inlined</param> bool IsGeneratedValueTypeTemporary(AstExpression next, AstExpression parent, int pos, AstVariable v, AstExpression inlinedExpression) { if (pos == 0 && v.Type != null && v.Type.IsValueType) { // Inlining a value type variable is allowed only if the resulting code will maintain the semantics // that the method is operating on a copy. // Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers switch (inlinedExpression.Code) { case AstCode.Ldloc: case AstCode.Stloc: case AstCode.CompoundAssignment: case AstCode.Ldelem_Any: case AstCode.Ldelem_I: case AstCode.Ldelem_I1: case AstCode.Ldelem_I2: case AstCode.Ldelem_I4: case AstCode.Ldelem_I8: case AstCode.Ldelem_R4: case AstCode.Ldelem_R8: case AstCode.Ldelem_Ref: case AstCode.Ldelem_U1: case AstCode.Ldelem_U2: case AstCode.Ldelem_U4: case AstCode.Ldobj: case AstCode.Ldind_Ref: return(false); case AstCode.Ldfld: case AstCode.Stfld: case AstCode.Ldsfld: case AstCode.Stsfld: // allow inlining field access only if it's a readonly field var f = ((XFieldReference)inlinedExpression.Operand).Resolve(); if (!(f != null && f.IsReadOnly)) { return(false); } break; case AstCode.Call: // inlining runs both before and after IntroducePropertyAccessInstructions, // so we have to handle both 'call' and 'callgetter' var mr = (XMethodReference)inlinedExpression.Operand; // ensure that it's not an multi-dimensional array getter if (mr.DeclaringType is XArrayType) { return(false); } goto case AstCode.Callvirt; case AstCode.Callvirt: case AstCode.CallIntf: case AstCode.CallSpecial: // don't inline foreach loop variables: mr = (XMethodReference)inlinedExpression.Operand; if (mr.Name == "get_Current" && mr.HasThis) { return(false); } break; case AstCode.Castclass: case AstCode.Unbox_Any: // These are valid, but might occur as part of a foreach loop variable. AstExpression arg = inlinedExpression.Arguments[0]; if (arg.Code == AstCode.Call || arg.Code == AstCode.Callvirt || arg.Code == AstCode.CallIntf || arg.Code == AstCode.CallSpecial) { mr = (XMethodReference)arg.Operand; if (mr.Name == "get_Current" && mr.HasThis) { return(false); // looks like a foreach loop variable, so don't inline it } } break; } // inline the compiler-generated variable that are used when accessing a member on a value type: switch (parent.Code) { case AstCode.Call: case AstCode.Callvirt: case AstCode.CallIntf: case AstCode.CallSpecial: var mr = (XMethodReference)parent.Operand; return(mr.HasThis); case AstCode.Stfld: case AstCode.Ldfld: case AstCode.Ldflda: return(true); } } return(false); }
/// <summary> /// Gets the register spec used for the given variable. /// </summary> internal RegisterSpec GetArgument(AstVariable variable) { if (variable.IsParameter) { return arguments.First(x => (x.Parameter != null) && x.Parameter.Equals(variable)); } VariableRegisterSpec r; if (!variables.TryGetValue(variable, out r)) { r = (VariableRegisterSpec) Allocate(variable.Type.GetReference(targetPackage), false, RCategory.Variable, variable); variables.Add(variable, r); } return r; }
bool InlineExpressionTreeParameterDeclarations(List <AstNode> body, AstExpression expr, int pos) { // When there is a Expression.Lambda() call, and the parameters are declared in the // IL statement immediately prior to the one containing the Lambda() call, // using this code for the3 declaration: // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...))) // and the variables v are assigned only once (in that statements), and read only in a Expression::Lambda // call that immediately follows the assignment statements, then we will inline those assignments // into the Lambda call using AstCode.ExpressionTreeParameterDeclarations. // This is sufficient to allow inlining over the expression tree construction. The remaining translation // of expression trees into C# will be performed by a C# AST transformer. for (int i = expr.Arguments.Count - 1; i >= 0; i--) { if (InlineExpressionTreeParameterDeclarations(body, expr.Arguments[i], pos)) { return(true); } } XMethodReference mr; AstExpression lambdaBodyExpr, parameterArray; if (!(expr.Match(AstCode.Call, out mr, out lambdaBodyExpr, out parameterArray) && mr.Name == "Lambda")) { return(false); } if (!(parameterArray.Code == AstCode.InitArray && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression")) { return(false); } int firstParameterPos = pos - parameterArray.Arguments.Count; if (firstParameterPos < 0) { return(false); } AstExpression[] parameterInitExpressions = new AstExpression[parameterArray.Arguments.Count + 1]; for (int i = 0; i < parameterArray.Arguments.Count; i++) { parameterInitExpressions[i] = body[firstParameterPos + i] as AstExpression; if (!MatchParameterVariableAssignment(parameterInitExpressions[i])) { return(false); } AstVariable v = (AstVariable)parameterInitExpressions[i].Operand; if (!parameterArray.Arguments[i].MatchLdloc(v)) { return(false); } // TODO: validate that the variable is only used here and within 'body' } parameterInitExpressions[parameterInitExpressions.Length - 1] = lambdaBodyExpr; Debug.Assert(expr.Arguments[0] == lambdaBodyExpr); expr.Arguments[0] = new AstExpression(expr.SourceLocation, AstCode.ExpressionTreeParameterDeclarations, null, parameterInitExpressions); body.RemoveRange(firstParameterPos, parameterArray.Arguments.Count); return(true); }
private static bool IsModified(AstBlock block, AstVariable var) { return block.GetExpressions(AstCode.Stloc) .Any(a => a.Operand == var); }
/// <summary> /// Finds the position to inline to. /// </summary> /// <returns>true = found; false = cannot continue search; null = not found</returns> bool? FindLoadInNext(AstExpression expr, AstVariable v, AstExpression expressionBeingMoved, out AstExpression parent, out int pos) { parent = null; pos = 0; if (expr == null) return false; for (int i = 0; i < expr.Arguments.Count; i++) { // Stop when seeing an opcode that does not guarantee that its operands will be evaluated. // Inlining in that case might result in the inlined expresion not being evaluted. if (i == 1 && (expr.Code == AstCode.NullCoalescing)) return false; AstExpression arg = expr.Arguments[i]; if ((arg.Code == AstCode.Ldloc || arg.Code == AstCode.Ldloca) && arg.Operand == v) { parent = expr; pos = i; return true; } bool? r = FindLoadInNext(arg, v, expressionBeingMoved, out parent, out pos); if (r != null) return r; } if (IsSafeForInlineOver(expr, expressionBeingMoved)) return null; // continue searching else return false; // abort, inlining not possible }
bool StoreCanBeConvertedToAssignment(AstExpression store, AstVariable exprVar) { if (store == null) return false; switch (store.Code) { case AstCode.Stloc: case AstCode.Stfld: case AstCode.Stsfld: case AstCode.Stobj: break; default: if (!store.Code.IsStoreToArray()) return false; break; } return store.Arguments.Last().Code == AstCode.Ldloc && store.Arguments.Last().Operand == exprVar; }
/// <summary> /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values /// </summary> /// <param name="next">The next top-level expression</param> /// <param name="parent">The direct parent of the load within 'next'</param> /// <param name="pos">Index of the load within 'parent'</param> /// <param name="v">The variable being inlined.</param> /// <param name="inlinedExpression">The expression being inlined</param> bool IsGeneratedValueTypeTemporary(AstExpression next, AstExpression parent, int pos, AstVariable v, AstExpression inlinedExpression) { if (pos == 0 && v.Type != null && v.Type.IsValueType) { // Inlining a value type variable is allowed only if the resulting code will maintain the semantics // that the method is operating on a copy. // Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers switch (inlinedExpression.Code) { case AstCode.Ldloc: case AstCode.Stloc: case AstCode.CompoundAssignment: case AstCode.Ldelem_Any: case AstCode.Ldelem_I: case AstCode.Ldelem_I1: case AstCode.Ldelem_I2: case AstCode.Ldelem_I4: case AstCode.Ldelem_I8: case AstCode.Ldelem_R4: case AstCode.Ldelem_R8: case AstCode.Ldelem_Ref: case AstCode.Ldelem_U1: case AstCode.Ldelem_U2: case AstCode.Ldelem_U4: case AstCode.Ldobj: case AstCode.Ldind_Ref: return false; case AstCode.Ldfld: case AstCode.Stfld: case AstCode.Ldsfld: case AstCode.Stsfld: // allow inlining field access only if it's a readonly field var f = ((XFieldReference)inlinedExpression.Operand).Resolve(); if (!(f != null && f.IsReadOnly)) return false; break; case AstCode.Call: // inlining runs both before and after IntroducePropertyAccessInstructions, // so we have to handle both 'call' and 'callgetter' var mr = (XMethodReference)inlinedExpression.Operand; // ensure that it's not an multi-dimensional array getter if (mr.DeclaringType is XArrayType) return false; goto case AstCode.Callvirt; case AstCode.Callvirt: case AstCode.CallIntf: case AstCode.CallSpecial: // don't inline foreach loop variables: mr = (XMethodReference)inlinedExpression.Operand; if (mr.Name == "get_Current" && mr.HasThis) return false; break; case AstCode.Castclass: case AstCode.Unbox_Any: // These are valid, but might occur as part of a foreach loop variable. AstExpression arg = inlinedExpression.Arguments[0]; if (arg.Code == AstCode.Call || arg.Code == AstCode.Callvirt || arg.Code == AstCode.CallIntf || arg.Code == AstCode.CallSpecial) { mr = (XMethodReference)arg.Operand; if (mr.Name == "get_Current" && mr.HasThis) return false; // looks like a foreach loop variable, so don't inline it } break; } // inline the compiler-generated variable that are used when accessing a member on a value type: switch (parent.Code) { case AstCode.Call: case AstCode.Callvirt: case AstCode.CallIntf: case AstCode.CallSpecial: var mr = (XMethodReference)parent.Operand; return mr.HasThis; case AstCode.Stfld: case AstCode.Ldfld: case AstCode.Ldflda: return true; } } return false; }
/// <summary> /// Inlines 'expr' into 'next', if possible. /// </summary> bool InlineIfPossible(AstVariable v, AstExpression inlinedExpression, AstNode next, bool aggressive) { // ensure the variable is accessed only a single time if (numStloc.GetOrDefault(v) != 1) return false; int ldloc = numLdloc.GetOrDefault(v); if (ldloc > 1 || ldloc + numLdloca.GetOrDefault(v) != 1) return false; AstExpression parent; int pos; if (FindLoadInNext(next as AstExpression, v, inlinedExpression, out parent, out pos) == true) { if (ldloc == 0) { if (!IsGeneratedValueTypeTemporary((AstExpression)next, parent, pos, v, inlinedExpression)) return false; } else { if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((AstExpression)next, parent, inlinedExpression)) return false; } // Assign the ranges of the ldloc instruction: inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges); if (ldloc == 0) { // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' so that the types // comes out correctly parent.Arguments[pos] = new AstExpression(inlinedExpression.SourceLocation, AstCode.AddressOf, null, inlinedExpression); } else { parent.Arguments[pos] = inlinedExpression; } return true; } return false; }
public readonly AstVariable LoadFrom; // Variable used for storage of the value public StackSlot(ByteCode[] definitions, AstVariable loadFrom) { Definitions = definitions; LoadFrom = loadFrom; }
bool CanPerformCopyPropagation(AstExpression expr, AstVariable copyVariable) { switch (expr.Code) { case AstCode.Ldloca: case AstCode.Ldelema: case AstCode.Ldflda: case AstCode.Ldsflda: // All address-loading instructions always return the same value for a given operand/argument combination, // so they can be safely copied. return true; case AstCode.Ldloc: AstVariable v = (AstVariable)expr.Operand; if (v.IsParameter) { // Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga) return numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 0; } else { // Variables are be copied only if both they and the target copy variable are generated, // and if the variable has only a single assignment return v.IsGenerated && copyVariable.IsGenerated && numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1; } default: return false; } }
private static bool IsModified(AstBlock block, AstVariable var) { return(block.GetExpressions(AstCode.Stloc) .Any(a => a.Operand == var)); }
bool IntroducePostIncrementForVariables(List <AstNode> body, AstExpression expr, int pos) { // Works for variables and static fields/properties // expr = ldloc(i) // stloc(i, add(expr, ldc.i4(1))) // -> // expr = postincrement(1, ldloca(i)) AstVariable exprVar; AstExpression exprInit; if (!(expr.Match(AstCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated)) { return(false); } //The next expression AstExpression nextExpr = body.ElementAtOrDefault(pos + 1) as AstExpression; if (nextExpr == null) { return(false); } AstCode loadInstruction = exprInit.Code; AstCode storeInstruction = nextExpr.Code; bool recombineVariable = false; // We only recognise local variables, static fields, and static getters with no arguments switch (loadInstruction) { case AstCode.Ldloc: //Must be a matching store type if (storeInstruction != AstCode.Stloc) { return(false); } AstVariable loadVar = (AstVariable)exprInit.Operand; AstVariable storeVar = (AstVariable)nextExpr.Operand; if (loadVar != storeVar) { if (loadVar.OriginalVariable != null && loadVar.OriginalVariable == storeVar.OriginalVariable) { recombineVariable = true; } else { return(false); } } break; case AstCode.Ldsfld: if (storeInstruction != AstCode.Stsfld) { return(false); } if (exprInit.Operand != nextExpr.Operand) { return(false); } break; default: return(false); } AstExpression addExpr = nextExpr.Arguments[0]; int incrementAmount; AstCode incrementCode = GetIncrementCode(addExpr, out incrementAmount); if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar))) { return(false); } if (recombineVariable) { // Split local variable, unsplit these two instances // replace nextExpr.Operand with exprInit.Operand ReplaceVariables(method, oldVar => oldVar == nextExpr.Operand ? (AstVariable)exprInit.Operand : oldVar); } switch (loadInstruction) { case AstCode.Ldloc: exprInit.Code = AstCode.Ldloca; break; case AstCode.Ldsfld: exprInit.Code = AstCode.Ldsflda; break; } expr.Arguments[0] = new AstExpression(incrementCode, incrementAmount, exprInit); body.RemoveAt(pos + 1); // TODO ILRanges return(true); }
public static TrsVariable Convert(this AstVariable astIn) { var token = astIn.VariableName; return(new TrsVariable(token.TokenString, astIn)); }
/// <summary> /// Gets whether 'expressionBeingMoved' can be inlined into 'expr'. /// </summary> public bool CanInlineInto(AstExpression expr, AstVariable v, AstExpression expressionBeingMoved) { AstExpression parent; int pos; return FindLoadInNext(expr, v, expressionBeingMoved, out parent, out pos) == true; }
public readonly AstVariable LoadFrom; // Variable used for storage of the value public StackSlot(ByteCode[] definitions, AstVariable loadFrom) { Definitions = definitions; LoadFrom = loadFrom; }