예제 #1
0
        /// <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);
        }
예제 #2
0
        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));
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
            }
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        /// <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);
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        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);
        }
예제 #10
0
파일: AstReader.cs 프로젝트: mortend/uno
        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));
        }
예제 #11
0
        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);
            }
        }
예제 #12
0
        /// <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;
                    }
                }
            }
        }
예제 #13
0
        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);
                }
            }
        }
예제 #14
0
        /// <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);
        }
예제 #15
0
        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);
        }
예제 #16
0
        /// <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());
            }
        }
예제 #17
0
        /// <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
            }
        }
예제 #18
0
        /// <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;
                }
            }
        }
예제 #19
0
        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);
        }
예제 #20
0
		/// <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;
					}
				}
			}
		}
예제 #21
0
 public TrsVariable(string name, AstVariable astSource)
 {
     Name      = name;
     AstSource = astSource;
 }
예제 #22
0
        /// <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;
        }
예제 #23
0
        /// <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);
        }
예제 #24
0
 /// <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;
 }
예제 #25
0
        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);
        }
예제 #26
0
 private static bool IsModified(AstBlock block, AstVariable var)
 {
     return block.GetExpressions(AstCode.Stloc)
                 .Any(a => a.Operand == var);
 }
예제 #27
0
		/// <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
		}
예제 #28
0
 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;
 }
예제 #29
0
		/// <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;
		}
예제 #30
0
		/// <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;
		}
예제 #31
0
            public readonly AstVariable LoadFrom;               // Variable used for storage of the value

            public StackSlot(ByteCode[] definitions, AstVariable loadFrom)
            {
                Definitions = definitions;
                LoadFrom    = loadFrom;
            }
예제 #32
0
		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;
			}
		}
예제 #33
0
 private static bool IsModified(AstBlock block, AstVariable var)
 {
     return(block.GetExpressions(AstCode.Stloc)
            .Any(a => a.Operand == var));
 }
예제 #34
0
        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);
        }
예제 #35
0
        public static TrsVariable Convert(this AstVariable astIn)
        {
            var token = astIn.VariableName;

            return(new TrsVariable(token.TokenString, astIn));
        }
예제 #36
0
		/// <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;
		}
예제 #37
0
            public readonly AstVariable LoadFrom;     // Variable used for storage of the value

            public StackSlot(ByteCode[] definitions, AstVariable loadFrom)
            {
                Definitions = definitions;
                LoadFrom = loadFrom;
            }