Beispiel #1
0
        void PostOptimizeAssignment(JSBinaryOperatorExpression boe, JSExpression rhs)
        {
            var rhsCopy = rhs as JSStructCopyExpression;

            if (rhsCopy == null)
            {
                return;
            }

            var expr = rhsCopy.Struct;
            var rre  = expr as JSResultReferenceExpression;

            if (rre != null)
            {
                expr = rre.Referent;
            }

            var invocation = expr as JSInvocationExpression;

            if (invocation == null)
            {
                return;
            }

            var invokeMethod = invocation.JSMethod;

            if (invokeMethod == null)
            {
                return;
            }

            var invocationSecondPass = GetSecondPass(invokeMethod);

            if (invocationSecondPass == null)
            {
                return;
            }

            bool   eliminateCopy   = false;
            string eliminateReason = null;

            if (invocationSecondPass.ResultIsNew)
            {
                // If this expression is the return value of a function invocation, we can eliminate struct
                //  copies if the return value is a 'new' expression.
                eliminateCopy   = true;
                eliminateReason = "Result is new";
            }
            else if (invocationSecondPass.ResultVariable != null)
            {
                // We can also eliminate a return value copy if the return value is one of the function's
                //  arguments, and we are sure that argument does not escape (other than through the return
                //  statement, that is).

                var parameters     = invokeMethod.Method.Parameters;
                int parameterIndex = -1;

                for (var i = 0; i < parameters.Length; i++)
                {
                    if (parameters[i].Name != invocationSecondPass.ResultVariable)
                    {
                        continue;
                    }

                    parameterIndex = i;
                    break;
                }

                // We found a single parameter that acts as the result of this function call.
                if (parameterIndex >= 0)
                {
                    GenericParameter relevantParameter;
                    var innerValue = invocation.Arguments[parameterIndex];

                    // Identify any local variables that are a dependency of the result parameter.
                    var localVariableDependencies = StaticAnalyzer.ExtractInvolvedVariables(
                        innerValue,
                        (n) =>
                        // If a variable is inside a copy expression we can ignore it as a dependency.
                        // The copy ensures that the dependency is resolved at the time of invocation.
                        (n is JSStructCopyExpression) ||
                        // If a variable is inside an invocation expression we can ignore it as a dependency.
                        // Dependency resolution would have occurred for the invocation already.
                        (n is JSInvocationExpression)
                        );

                    // Was the result parameter already copied when invoking the function?
                    // If so, we can eliminate the copy of the result because all dependencies have been resolved.
                    // Note that this is separate from the variable dependency extraction above -
                    //  if an invocation has multiple variable dependencies, the above merely narrows them down
                    //  while this detects cases where there are effectively *no* dependencies of any kind.
                    // This also detects cases where the argument is the result of an invocation or property access
                    //  and the result was copied.
                    var isAlreadyCopied = innerValue is JSStructCopyExpression;

                    var dependenciesModified = localVariableDependencies.Any(iv => SecondPass.IsVariableModified(iv.Name));

                    var copyNeededForTarget = IsCopyNeededForAssignmentTarget(boe.Left);

                    var escapes  = invocationSecondPass.DoesVariableEscape(invocationSecondPass.ResultVariable, false);
                    var modified = invocationSecondPass.IsVariableModified(invocationSecondPass.ResultVariable);

                    var traceMessage = (Action)(() => {
                        if (TracePostOptimizeDecisions)
                        {
                            Console.WriteLine(
                                "< {0}: {1} > escapes:{2} modified:{3} copyNeededForTarget({4}):{5} inputsModified:{6}",
                                parameters[parameterIndex].Name, innerValue, escapes, modified, boe.Left, copyNeededForTarget, dependenciesModified
                                );
                        }
                    });

                    if (isAlreadyCopied)
                    {
                        eliminateCopy   = true;
                        eliminateReason = "Result was already copied as part of argument list";
                    }
                    else if (!escapes && !dependenciesModified)
                    {
                        if (!copyNeededForTarget)
                        {
                            eliminateCopy   = true;
                            eliminateReason = "Target does not require a copy and other criteria are met";
                        }
                        else if (!modified)
                        {
                            eliminateCopy   = true;
                            eliminateReason = "Input not modified, doesn't escape, no dependency changes";
                        }

                        traceMessage();
                    }
                    else
                    {
                        traceMessage();
                    }
                }
            }

            if (eliminateCopy)
            {
                if (TracePostOptimizedCopies)
                {
                    Console.WriteLine("Post-optimized assignment {0} because {1}", boe, eliminateReason);
                }

                boe.ReplaceChild(rhsCopy, rhsCopy.Struct);
            }
        }
Beispiel #2
0
        public void VisitNode(JSBinaryOperatorExpression boe)
        {
            if (boe.Operator != JSOperator.Assignment)
            {
                base.VisitNode(boe);
                return;
            }

            bool doPostOptimization = false;

            GenericParameter relevantParameter;

            if (IsCopyNeeded(boe.Right, out relevantParameter))
            {
                var rightVars = new HashSet <JSVariable>(StaticAnalyzer.ExtractInvolvedVariables(boe.Right));

                // Even if the assignment target is never modified, if the assignment *source*
                //  gets modified, we need to make a copy here, because the target is probably
                //  being used as a back-up copy.
                var rightVarsModified      = (rightVars.Any((rv) => SecondPass.IsVariableModified(rv.Name)));
                var rightVarsAreReferences = rightVars.Any((rv) => rv.IsReference);

                bool rightVarIsEffectivelyConstantHere =
                    IsVarEffectivelyConstantHere(boe.Right as JSVariable);

                if (
                    (
                        rightVarsModified ||
                        IsCopyNeededForAssignmentTarget(boe.Left) ||
                        rightVarsAreReferences
                    ) &&
                    !IsCopyAlwaysUnnecessaryForAssignmentTarget(boe.Left) &&
                    !rightVarIsEffectivelyConstantHere
                    )
                {
                    if (TraceInsertedCopies)
                    {
                        Console.WriteLine("struct copy introduced for assignment {0} = {1}", boe.Left, boe.Right);
                    }

                    doPostOptimization = true;
                    boe.Right          = MakeCopyForExpression(boe.Right, relevantParameter);
                }
                else
                {
                    if (TraceElidedCopies)
                    {
                        Console.WriteLine(
                            "struct copy elided for assignment {0} = {1}{2}", boe.Left, boe.Right,
                            rightVarIsEffectivelyConstantHere ? " (rhs is effectively constant due to following all other accesses)" : ""
                            );
                    }
                }
            }
            else
            {
                if (TraceElidedCopies && TypeUtil.IsStruct(boe.Right.GetActualType(TypeSystem)))
                {
                    Console.WriteLine(String.Format("no copy needed for assignment {0} = {1}", boe.Left, boe.Right));
                }
            }

            VisitChildren(boe);

            if (doPostOptimization)
            {
                PostOptimizeAssignment(boe, boe.Right);
            }
        }