예제 #1
0
 protected internal override void VisitStObj(StObj inst)
 {
     base.VisitStObj(inst);
     if (EarlyExpressionTransforms.StObjToStLoc(inst, context))
     {
         context.RequestRerun();
         return;
     }
     TransformAssignment.HandleCompoundAssign(inst, context);
 }
예제 #2
0
        bool LegacyPattern(Block block, int pos, StatementTransformContext context)
        {
            // Legacy csc pattern:
            //   stloc s(lhsInst)
            //   if (logic.not(call op_False(ldloc s))) Block {
            //     stloc s(call op_BitwiseAnd(ldloc s, rhsInst))
            //   }
            // ->
            //   stloc s(user.logic op_BitwiseAnd(lhsInst, rhsInst))
            if (!block.Instructions[pos].MatchStLoc(out var s, out var lhsInst))
            {
                return(false);
            }
            if (!(s.Kind == VariableKind.StackSlot))
            {
                return(false);
            }
            if (!(block.Instructions[pos + 1] is IfInstruction ifInst))
            {
                return(false);
            }
            if (!ifInst.Condition.MatchLogicNot(out var condition))
            {
                return(false);
            }
            if (!(MatchCondition(condition, out var s2, out string conditionMethodName) && s2 == s))
            {
                return(false);
            }
            if (ifInst.FalseInst.OpCode != OpCode.Nop)
            {
                return(false);
            }
            var trueInst = Block.Unwrap(ifInst.TrueInst);

            if (!trueInst.MatchStLoc(s, out var storeValue))
            {
                return(false);
            }
            if (storeValue is Call call)
            {
                if (!MatchBitwiseCall(call, s, conditionMethodName))
                {
                    return(false);
                }
                if (s.IsUsedWithin(call.Arguments[1]))
                {
                    return(false);
                }
                context.Step("User-defined short-circuiting logic operator (legacy pattern)", condition);
                ((StLoc)block.Instructions[pos]).Value = new UserDefinedLogicOperator(call.Method, lhsInst, call.Arguments[1])
                {
                    ILRange = call.ILRange
                };
                block.Instructions.RemoveAt(pos + 1);
                context.RequestRerun();                 // the 'stloc s' may now be eligible for inlining
                return(true);
            }
            return(false);
        }
예제 #3
0
 void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
 {
     this.context = context;
     if (TransformInlineAssignmentStObjOrCall(block, pos) || TransformInlineAssignmentLocal(block, pos))
     {
         // both inline assignments create a top-level stloc which might affect inlining
         context.RequestRerun();
         return;
     }
     if (TransformPostIncDecOperatorWithInlineStore(block, pos) ||
         TransformPostIncDecOperator(block, pos) ||
         TransformPostIncDecOperatorLocal(block, pos))
     {
         // again, new top-level stloc might need inlining:
         context.RequestRerun();
         return;
     }
 }
예제 #4
0
        /// <summary>
        /// Transform compound assignments where the return value is not being used,
        /// or where there's an inlined assignment within the setter call.
        /// </summary>
        /// <remarks>
        /// Called by ExpressionTransforms.
        /// </remarks>
        internal static bool HandleCallCompoundAssign(CallInstruction setterCall, StatementTransformContext context)
        {
            // callvirt set_Property(ldloc S_1, binary.op(callvirt get_Property(ldloc S_1), value))
            // ==> compound.op.new(callvirt get_Property(ldloc S_1), value)
            var setterValue   = setterCall.Arguments.LastOrDefault();
            var storeInSetter = setterValue as StLoc;

            if (storeInSetter != null)
            {
                // callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value)))
                // ==> stloc v(compound.op.new(callvirt get_Property(ldloc S_1), value))
                setterValue = storeInSetter.Value;
            }
            setterValue = UnwrapSmallIntegerConv(setterValue, out var conv);
            if (!(setterValue is BinaryNumericInstruction binary))
            {
                return(false);
            }
            var getterCall = binary.Left as CallInstruction;

            if (!MatchingGetterAndSetterCalls(getterCall, setterCall))
            {
                return(false);
            }
            IType targetType = getterCall.Method.ReturnType;

            if (!ValidateCompoundAssign(binary, conv, targetType))
            {
                return(false);
            }
            if (storeInSetter != null && storeInSetter.Variable.Type.IsSmallIntegerType())
            {
                // 'stloc v' implicitly truncates.
                // Ensure that type of 'v' must match type of the property:
                if (storeInSetter.Variable.Type.GetSize() != targetType.GetSize())
                {
                    return(false);
                }
                if (storeInSetter.Variable.Type.GetSign() != targetType.GetSign())
                {
                    return(false);
                }
            }
            context.Step($"Compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall);
            ILInstruction newInst = new CompoundAssignmentInstruction(
                binary, getterCall, binary.Right,
                getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue);

            if (storeInSetter != null)
            {
                storeInSetter.Value = newInst;
                newInst             = storeInSetter;
                context.RequestRerun();                 // moving stloc to top-level might trigger inlining
            }
            setterCall.ReplaceWith(newInst);
            return(true);
        }
예제 #5
0
        public void Run(Block block, BlockTransformContext context)
        {
            var ctx = new StatementTransformContext(context);
            int pos = 0;

            ctx.rerunPosition = block.Instructions.Count - 1;
            while (pos >= 0)
            {
                if (ctx.rerunPosition != null)
                {
                    Debug.Assert(ctx.rerunPosition >= pos);
#if DEBUG
                    for (; pos < ctx.rerunPosition; ++pos)
                    {
                        block.Instructions[pos].ResetDirty();
                    }
#else
                    pos = ctx.rerunPosition.Value;
#endif
                    Debug.Assert(pos == ctx.rerunPosition);
                    ctx.rerunPosition = null;
                }
                foreach (var transform in children)
                {
                    transform.Run(block, pos, ctx);
#if DEBUG
                    block.Instructions[pos].CheckInvariant(ILPhase.Normal);
                    for (int i = Math.Max(0, pos - 100); i < pos; ++i)
                    {
                        if (block.Instructions[i].IsDirty)
                        {
                            Debug.Fail($"{transform.GetType().Name} modified an instruction before pos");
                        }
                    }
#endif
                    if (ctx.rerunCurrentPosition)
                    {
                        ctx.rerunCurrentPosition = false;
                        ctx.RequestRerun(pos);
                    }
                    if (ctx.rerunPosition != null)
                    {
                        break;
                    }
                }
                if (ctx.rerunPosition == null)
                {
                    pos--;
                }
            }
            // This invariant can be surprisingly expensive to check if the block has thousands
            // of instructions and is frequently modified by transforms (invalidating the flags each time)
            // so we'll check this only once at the end of the block.
            Debug.Assert(block.HasFlag(InstructionFlags.EndPointUnreachable));
        }
예제 #6
0
        public void Run(Block block, BlockTransformContext context)
        {
            var ctx = new StatementTransformContext(context);
            int pos = 0;

            ctx.rerunPosition = block.Instructions.Count - 1;
            while (pos >= 0)
            {
                if (ctx.rerunPosition != null)
                {
                    Debug.Assert(ctx.rerunPosition >= pos);
#if DEBUG
                    for (; pos < ctx.rerunPosition; ++pos)
                    {
                        block.Instructions[pos].ResetDirty();
                    }
#else
                    pos = ctx.rerunPosition.Value;
#endif
                    Debug.Assert(pos == ctx.rerunPosition);
                    ctx.rerunPosition = null;
                }
                foreach (var transform in children)
                {
                    Debug.Assert(block.HasFlag(InstructionFlags.EndPointUnreachable));
                    transform.Run(block, pos, ctx);
#if DEBUG
                    block.Instructions[pos].CheckInvariant(ILPhase.Normal);
                    for (int i = Math.Max(0, pos - 100); i < pos; ++i)
                    {
                        if (block.Instructions[i].IsDirty)
                        {
                            Debug.Fail($"{transform.GetType().Name} modified an instruction before pos");
                        }
                    }
#endif
                    if (ctx.rerunCurrentPosition)
                    {
                        ctx.rerunCurrentPosition = false;
                        ctx.RequestRerun(pos);
                    }
                    if (ctx.rerunPosition != null)
                    {
                        break;
                    }
                }
                if (ctx.rerunPosition == null)
                {
                    pos--;
                }
            }
        }
예제 #7
0
        protected internal override void VisitStObj(StObj inst)
        {
            base.VisitStObj(inst);
            if (EarlyExpressionTransforms.StObjToStLoc(inst, context))
            {
                context.RequestRerun();
                return;
            }

            if (inst.Value is BinaryNumericInstruction binary &&
                binary.Left.MatchLdObj(out ILInstruction target, out IType t) &&
                inst.Target.Match(target).Success &&
                SemanticHelper.IsPure(target.Flags) &&
                CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, t))
            {
                context.Step("compound assignment", inst);
                // stobj(target, binary.op(ldobj(target), ...))
                // => compound.op(target, ...)
                inst.ReplaceWith(new CompoundAssignmentInstruction(
                                     binary, binary.Left, binary.Right,
                                     t, CompoundAssignmentType.EvaluatesToNewValue));
            }
        }
        /// stloc V_1(dynamic.isevent (target))
        /// if (logic.not(ldloc V_1)) Block IL_004a {
        ///     stloc V_2(dynamic.getmember B(target))
        /// }
        /// [stloc copyOfValue(value)]
        /// if (logic.not(ldloc V_1)) Block IL_0149 {
        ///     dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(ldloc V_2,  value))
        /// } else Block IL_0151 {
        ///     dynamic.invokemember.invokespecial.discard add_B(target, value)
        /// }
        /// =>
        /// if (logic.not(dynamic.isevent (target))) Block IL_0149 {
        ///     dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(dynamic.getmember B(target),  value))
        /// } else Block IL_0151 {
        ///     dynamic.invokemember.invokespecial.discard add_B(target, value)
        /// }
        public void Run(Block block, int pos, StatementTransformContext context)
        {
            if (!(pos + 3 < block.Instructions.Count && block.Instructions[pos].MatchStLoc(out var flagVar, out var inst) && inst is DynamicIsEventInstruction isEvent))
            {
                return;
            }
            if (!(flagVar.IsSingleDefinition && flagVar.LoadCount == 2))
            {
                return;
            }
            if (!MatchLhsCacheIfInstruction(block.Instructions[pos + 1], flagVar, out var dynamicGetMemberStore))
            {
                return;
            }
            if (!(dynamicGetMemberStore.MatchStLoc(out var getMemberVar, out inst) && inst is DynamicGetMemberInstruction getMemberInst))
            {
                return;
            }
            int offset = 2;

            if (block.Instructions[pos + offset].MatchStLoc(out var valueVariable) &&
                pos + 4 < block.Instructions.Count && valueVariable.IsSingleDefinition && valueVariable.LoadCount == 2 &&
                valueVariable.LoadInstructions.All(ld => ld.Parent is DynamicInstruction))
            {
                offset++;
            }
            foreach (var descendant in block.Instructions[pos + offset].Descendants)
            {
                if (!MatchIsEventAssignmentIfInstruction(descendant, isEvent, flagVar, getMemberVar, out var setMemberInst, out var getMemberVarUse, out var isEventConditionUse))
                {
                    continue;
                }
                context.Step("DynamicIsEventAssignmentTransform", block.Instructions[pos]);
                // Collapse duplicate condition
                getMemberVarUse.ReplaceWith(getMemberInst);
                isEventConditionUse.ReplaceWith(isEvent);
                block.Instructions.RemoveRange(pos, 2);
                // Reuse ExpressionTransforms
                ExpressionTransforms.TransformDynamicSetMemberInstruction(setMemberInst, context);
                context.RequestRerun();
                break;
            }
        }
예제 #9
0
        /// <summary>
        /// Transform compound assignments where the return value is not being used,
        /// or where there's an inlined assignment within the setter call.
        /// </summary>
        /// <remarks>
        /// Called by ExpressionTransforms.
        /// </remarks>
        internal static bool HandleCallCompoundAssign(CallInstruction setterCall, StatementTransformContext context)
        {
            // callvirt set_Property(ldloc S_1, binary.op(callvirt get_Property(ldloc S_1), value))
            // ==> compound.op.new(callvirt(callvirt get_Property(ldloc S_1)), value)
            var setterValue   = setterCall.Arguments.LastOrDefault();
            var storeInSetter = setterValue as StLoc;

            if (storeInSetter != null)
            {
                // callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value)))
                // ==> stloc v(compound.op.new(callvirt(callvirt get_Property(ldloc S_1)), value))
                setterValue = storeInSetter.Value;
            }
            if (!(setterValue is BinaryNumericInstruction binary))
            {
                return(false);
            }
            var getterCall = binary.Left as CallInstruction;

            if (!MatchingGetterAndSetterCalls(getterCall, setterCall))
            {
                return(false);
            }
            if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType))
            {
                return(false);
            }
            context.Step($"Compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall);
            ILInstruction newInst = new CompoundAssignmentInstruction(
                binary, getterCall, binary.Right,
                getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue);

            if (storeInSetter != null)
            {
                storeInSetter.Value = newInst;
                newInst             = storeInSetter;
                context.RequestRerun();                 // moving stloc to top-level might trigger inlining
            }
            setterCall.ReplaceWith(newInst);
            return(true);
        }