Beispiel #1
0
        static bool MatchSpanTCtorWithPointerAndSize(NewObj newObj, StatementTransformContext context, out IType elementType, out FieldDefinition field, out int size)
        {
            field       = default;
            size        = default;
            elementType = null;
            IType type = newObj.Method.DeclaringType;

            if (!type.IsKnownType(KnownTypeCode.SpanOfT) && !type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
            {
                return(false);
            }
            if (newObj.Arguments.Count != 2 || type.TypeArguments.Count != 1)
            {
                return(false);
            }
            elementType = type.TypeArguments[0];
            if (!newObj.Arguments[0].UnwrapConv(ConversionKind.StopGCTracking).MatchLdsFlda(out var member))
            {
                return(false);
            }
            if (member.MetadataToken.IsNil)
            {
                return(false);
            }
            if (!newObj.Arguments[1].MatchLdcI4(out size))
            {
                return(false);
            }
            field = context.PEFile.Metadata.GetFieldDefinition((FieldDefinitionHandle)member.MetadataToken);
            return(true);
        }
Beispiel #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);
        }
Beispiel #3
0
        /*
         *      stloc tuple(call MakeIntIntTuple(ldloc this))
         * ----
         *      stloc myInt(call op_Implicit(ldfld Item2(ldloca tuple)))
         *      stloc a(ldfld Item1(ldloca tuple))
         *      stloc b(ldloc myInt)
         * ==>
         *      deconstruct {
         *              init:
         *                      <empty>
         *              deconstruct:
         *                      match.deconstruct(temp = ldloca tuple) {
         *                              match(result0 = deconstruct.result 0(temp)),
         *                              match(result1 = deconstruct.result 1(temp))
         *                      }
         *              conversions: {
         *                      stloc conv2(call op_Implicit(ldloc result1))
         *              }
         *              assignments: {
         *                      stloc a(ldloc result0)
         *                      stloc b(ldloc conv2)
         *              }
         *      }
         * */
        void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
        {
            if (!context.Settings.Deconstruction)
            {
                return;
            }

            try
            {
                this.context = context;
                Reset();

                if (TransformDeconstruction(block, pos))
                {
                    return;
                }
                if (InlineDeconstructionInitializer(block, pos))
                {
                    return;
                }
            }
            finally
            {
                this.context = null;
                Reset();
            }
        }
Beispiel #4
0
 void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
 {
     if (!context.Settings.ArrayInitializers)
     {
         return;
     }
     this.context = context;
     try {
         if (DoTransform(context.Function, block, pos))
         {
             return;
         }
         if (DoTransformMultiDim(context.Function, block, pos))
         {
             return;
         }
         if (context.Settings.StackAllocInitializers && DoTransformStackAllocInitializer(block, pos))
         {
             return;
         }
         if (DoTransformInlineRuntimeHelpersInitializeArray(block, pos))
         {
             return;
         }
     } finally {
         this.context = null;
     }
 }
 /// <summary>
 /// Handles NullCoalescingInstruction case 1: reference types.
 ///
 /// stloc s(valueInst)
 /// if (comp(ldloc s == ldnull)) {
 ///		stloc s(fallbackInst)
 /// }
 /// =>
 /// stloc s(if.notnull(valueInst, fallbackInst))
 /// </summary>
 bool TransformRefTypes(Block block, int pos, StatementTransformContext context)
 {
     if (!(block.Instructions[pos] is StLoc stloc))
     {
         return(false);
     }
     if (stloc.Variable.Kind != VariableKind.StackSlot)
     {
         return(false);
     }
     if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst))
     {
         return(false);
     }
     trueInst = Block.Unwrap(trueInst);
     if (condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull() &&
         trueInst.MatchStLoc(stloc.Variable, out var fallbackValue)
         )
     {
         context.Step("NullCoalescingTransform (reference types)", stloc);
         stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue);
         block.Instructions.RemoveAt(pos + 1);                 // remove if instruction
         ILInlining.InlineOneIfPossible(block, pos, false, context);
         return(true);
     }
     return(false);
 }
Beispiel #6
0
 public void Run(Block block, int pos, StatementTransformContext context)
 {
     this.context = context;
     context.StepStartGroup($"ExpressionTransforms ({block.Label}:{pos})", block.Instructions[pos]);
     block.Instructions[pos].AcceptVisitor(this);
     context.StepEndGroup(keepIfEmpty: true);
 }
Beispiel #7
0
 public void Run(Block block, int pos, StatementTransformContext context)
 {
     if (!TransformRefTypes(block, pos, context))
     {
         TransformThrowExpressionValueTypes(block, pos, context);
     }
 }
 void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
 {
     this.context = context;
     try {
         DoTransform(block, pos);
     } finally {
         this.context = null;
     }
 }
Beispiel #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 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);
        }
Beispiel #10
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));
        }
        public void Run(Block block, int pos, StatementTransformContext context)
        {
            if (!context.Settings.NamedArguments)
            {
                return;
            }
            var options = ILInlining.OptionsForBlock(block, pos, context);

            options |= InliningOptions.IntroduceNamedArguments;
            ILInlining.InlineOneIfPossible(block, pos, options, context: context);
        }
Beispiel #12
0
 void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
 {
     if (LegacyPattern(block, pos, context))
     {
         return;
     }
     if (RoslynOptimized(block, pos, context))
     {
         return;
     }
 }
Beispiel #13
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--;
                }
            }
        }
Beispiel #14
0
 void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
 {
     if (!context.Settings.ObjectOrCollectionInitializers)
     {
         return;
     }
     this.context = context;
     try {
         DoTransform(block, pos);
     } finally {
         this.context = null;
     }
 }
Beispiel #15
0
 /// <summary>
 /// Handles NullCoalescingInstruction case 1: reference types.
 ///
 /// stloc s(valueInst)
 /// if (comp(ldloc s == ldnull)) {
 ///		stloc s(fallbackInst)
 /// }
 /// =>
 /// stloc s(if.notnull(valueInst, fallbackInst))
 /// </summary>
 bool TransformRefTypes(Block block, int pos, StatementTransformContext context)
 {
     if (!(block.Instructions[pos] is StLoc stloc))
     {
         return(false);
     }
     if (stloc.Variable.Kind != VariableKind.StackSlot)
     {
         return(false);
     }
     if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst))
     {
         return(false);
     }
     if (!(condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull()))
     {
         return(false);
     }
     trueInst = Block.Unwrap(trueInst);
     if (trueInst.MatchStLoc(stloc.Variable, out var fallbackValue))
     {
         context.Step("NullCoalescingTransform: simple (reference types)", stloc);
         stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue);
         block.Instructions.RemoveAt(pos + 1);                 // remove if instruction
         ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context);
         return(true);
     }
     // sometimes the compiler generates:
     // stloc s(valueInst)
     // if (comp(ldloc s == ldnull)) {
     //		stloc v(fallbackInst)
     //      stloc s(ldloc v)
     // }
     // v must be single-assign and single-use.
     if (trueInst is Block trueBlock && trueBlock.Instructions.Count == 2 &&
         trueBlock.Instructions[0].MatchStLoc(out var temporary, out fallbackValue) &&
         temporary.IsSingleDefinition && temporary.LoadCount == 1 &&
         trueBlock.Instructions[1].MatchStLoc(stloc.Variable, out var useOfTemporary) &&
         useOfTemporary.MatchLdLoc(temporary))
     {
         context.Step("NullCoalescingTransform: with temporary variable (reference types)", stloc);
         stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue);
         block.Instructions.RemoveAt(pos + 1);                 // remove if instruction
         ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context);
         return(true);
     }
     return(false);
 }
 void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
 {
     if (!context.Settings.ArrayInitializers)
     {
         return;
     }
     this.context = context;
     try {
         if (!DoTransform(block, pos))
         {
             DoTransformMultiDim(block, pos);
         }
     } finally {
         this.context = null;
     }
 }
Beispiel #17
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;
     }
 }
Beispiel #18
0
 internal static bool TransformSpanTArrayInitialization(NewObj inst, StatementTransformContext context, out Block block)
 {
     block = null;
     if (MatchSpanTCtorWithPointerAndSize(inst, context, out var elementType, out var field, out var size))
     {
         if (field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA))
         {
             var valuesList   = new List <ILInstruction>();
             var initialValue = field.GetInitialValue(context.PEFile.Reader, context.TypeSystem);
             if (DecodeArrayInitializer(elementType, initialValue, new[] { size }, valuesList))
             {
                 var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.TypeSystem, elementType));
                 block = BlockFromInitializer(tempStore, elementType, new[] { size }, valuesList.ToArray());
                 return(true);
             }
         }
     }
     return(false);
 }
        /// 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;
            }
        }
Beispiel #20
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);
        }
        void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
        {
            if (!context.Settings.StringInterpolation)
            {
                return;
            }
            int           interpolationStart = pos;
            int           interpolationEnd;
            ILInstruction insertionPoint;

            // stloc v(newobj DefaultInterpolatedStringHandler..ctor(ldc.i4 literalLength, ldc.i4 formattedCount))
            if (block.Instructions[pos] is StLoc {
                Variable : ILVariable {
                    Kind : VariableKind.Local
                } v,
                Value : NewObj {
                    Arguments : { Count : 2 }
                } newObj
            } stloc &&
                v.Type.IsKnownType(KnownTypeCode.DefaultInterpolatedStringHandler) &&
                newObj.Method.DeclaringType.IsKnownType(KnownTypeCode.DefaultInterpolatedStringHandler) &&
                newObj.Arguments[0].MatchLdcI4(out _) &&
                newObj.Arguments[1].MatchLdcI4(out _))
            {
                // { call MethodName(ldloca v, ...) }
                do
                {
                    pos++;
                }while (IsKnownCall(block, pos, v));
                interpolationEnd = pos;
                // ... call ToStringAndClear(ldloca v) ...
                if (!FindToStringAndClear(block, pos, interpolationStart, interpolationEnd, v, out insertionPoint))
                {
                    return;
                }
                if (!(v.StoreCount == 1 && v.AddressCount == interpolationEnd - interpolationStart && v.LoadCount == 0))
                {
                    return;
                }
            }
 bool RoslynOptimized(Block block, int pos, StatementTransformContext context)
 {
     // Roslyn, optimized pattern in combination with return statement:
     //   if (logic.not(call op_False(ldloc lhsVar))) leave IL_0000 (call op_BitwiseAnd(ldloc lhsVar, rhsInst))
     //   leave IL_0000(ldloc lhsVar)
     // ->
     //   user.logic op_BitwiseAnd(ldloc lhsVar, rhsInst)
     if (!block.Instructions[pos].MatchIfInstructionPositiveCondition(out var condition, out var trueInst, out var falseInst))
     {
         return(false);
     }
     if (trueInst.OpCode == OpCode.Nop)
     {
         trueInst = block.Instructions[pos + 1];
     }
     else if (falseInst.OpCode == OpCode.Nop)
     {
         falseInst = block.Instructions[pos + 1];
     }
     else
     {
         return(false);
     }
     if (trueInst.MatchReturn(out var trueValue) && falseInst.MatchReturn(out var falseValue))
     {
         var transformed = Transform(condition, trueValue, falseValue);
         if (transformed == null)
         {
             transformed = TransformDynamic(condition, trueValue, falseValue);
         }
         if (transformed != null)
         {
             context.Step("User-defined short-circuiting logic operator (optimized return)", condition);
             ((Leave)block.Instructions[pos + 1]).Value = transformed;
             block.Instructions.RemoveAt(pos);
             return(true);
         }
     }
     return(false);
 }
Beispiel #23
0
 public void Run(Block block, int pos, StatementTransformContext context)
 {
     InlineOneIfPossible(block, pos, OptionsForBlock(block, pos, context), context: context);
 }
Beispiel #24
0
 internal static void TransformDynamicSetMemberInstruction(DynamicSetMemberInstruction inst, StatementTransformContext context)
 {
     if (!inst.BinderFlags.HasFlag(CSharpBinderFlags.ValueFromCompoundAssignment))
     {
         return;
     }
     if (!(inst.Value is DynamicBinaryOperatorInstruction binaryOp))
     {
         return;
     }
     if (!(binaryOp.Left is DynamicGetMemberInstruction dynamicGetMember))
     {
         return;
     }
     if (!dynamicGetMember.Target.Match(inst.Target).Success)
     {
         return;
     }
     if (!SemanticHelper.IsPure(dynamicGetMember.Target.Flags))
     {
         return;
     }
     if (inst.Name != dynamicGetMember.Name || !DynamicCompoundAssign.IsExpressionTypeSupported(binaryOp.Operation))
     {
         return;
     }
     context.Step("dynamic.setmember.compound -> dynamic.compound.op", inst);
     inst.ReplaceWith(new DynamicCompoundAssign(binaryOp.Operation, binaryOp.BinderFlags, binaryOp.Left, binaryOp.LeftArgumentInfo, binaryOp.Right, binaryOp.RightArgumentInfo));
 }
Beispiel #25
0
 public void Run(Block block, int pos, StatementTransformContext context)
 {
     InlineOneIfPossible(block, pos, aggressive: IsCatchWhenBlock(block), context: context);
 }
 public void Run(Block block, int pos, StatementTransformContext context)
 {
     TransformRefTypes(block, pos, context);
 }
Beispiel #27
0
        /// <summary>
        /// Transform compound assignments where the return value is not being used,
        /// or where there's an inlined assignment within the setter call.
        ///
        /// Patterns handled:
        /// 1.
        ///   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)
        /// 2.
        ///   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))
        /// 3.
        ///   stobj(target, binary.op(ldobj(target), ...))
        ///     where target is pure
        ///   => compound.op(target, ...)
        /// </summary>
        /// <remarks>
        /// Called by ExpressionTransforms, or after the inline-assignment transform for setters.
        /// </remarks>
        internal static bool HandleCompoundAssign(ILInstruction compoundStore, StatementTransformContext context)
        {
            if (!context.Settings.MakeAssignmentExpressions || !context.Settings.IntroduceIncrementAndDecrement)
            {
                return(false);
            }
            if (compoundStore is CallInstruction && compoundStore.SlotInfo != Block.InstructionSlot)
            {
                // replacing 'call set_Property' with a compound assignment instruction
                // changes the return value of the expression, so this is only valid on block-level.
                return(false);
            }
            if (!IsCompoundStore(compoundStore, out var targetType, out var setterValue, context.TypeSystem))
            {
                return(false);
            }
            // targetType = The type of the property/field/etc. being stored to.
            // setterValue = The value being stored.
            var storeInSetter = setterValue as StLoc;

            if (storeInSetter != null)
            {
                // We'll move the stloc to top-level:
                // 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;
                if (storeInSetter.Variable.Type.IsSmallIntegerType())
                {
                    // 'stloc v' implicitly truncates the value.
                    // Ensure that type of 'v' matches the type of the property:
                    if (storeInSetter.Variable.Type.GetSize() != targetType.GetSize())
                    {
                        return(false);
                    }
                    if (storeInSetter.Variable.Type.GetSign() != targetType.GetSign())
                    {
                        return(false);
                    }
                }
            }
            ILInstruction newInst;

            if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary)
            {
                if (compoundStore is StLoc)
                {
                    // transform local variables only for user-defined operators
                    return(false);
                }
                if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable))
                {
                    return(false);
                }
                if (!ValidateCompoundAssign(binary, smallIntConv, targetType))
                {
                    return(false);
                }
                context.Step($"Compound assignment (binary.numeric)", compoundStore);
                finalizeMatch?.Invoke(context);
                newInst = new NumericCompoundAssign(
                    binary, target, targetKind, binary.Right,
                    targetType, CompoundEvalMode.EvaluatesToNewValue);
            }
            else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator)
            {
                if (operatorCall.Arguments.Count == 0)
                {
                    return(false);
                }
                if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable))
                {
                    return(false);
                }
                ILInstruction rhs;
                if (operatorCall.Arguments.Count == 2)
                {
                    if (CSharp.ExpressionBuilder.GetAssignmentOperatorTypeFromMetadataName(operatorCall.Method.Name) == null)
                    {
                        return(false);
                    }
                    rhs = operatorCall.Arguments[1];
                }
                else if (operatorCall.Arguments.Count == 1)
                {
                    if (!(operatorCall.Method.Name == "op_Increment" || operatorCall.Method.Name == "op_Decrement"))
                    {
                        return(false);
                    }
                    // use a dummy node so that we don't need a dedicated instruction for user-defined unary operator calls
                    rhs = new LdcI4(1);
                }
                else
                {
                    return(false);
                }
                if (operatorCall.IsLifted)
                {
                    return(false);                    // TODO: add tests and think about whether nullables need special considerations
                }
                context.Step($"Compound assignment (user-defined binary)", compoundStore);
                finalizeMatch?.Invoke(context);
                newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue,
                                                        target, targetKind, rhs);
            }
Beispiel #28
0
        /// <summary>
        /// stloc v(value)
        /// if (logic.not(call get_HasValue(ldloca v))) throw(...)
        /// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ...
        /// =>
        /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ...
        /// </summary>
        bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context)
        {
            if (pos + 2 >= block.Instructions.Count)
            {
                return(false);
            }
            if (!(block.Instructions[pos] is StLoc stloc))
            {
                return(false);
            }
            ILVariable v = stloc.Variable;

            if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2))
            {
                return(false);
            }
            if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst))
            {
                return(false);
            }
            if (!(Block.Unwrap(trueInst) is Throw throwInst))
            {
                return(false);
            }
            if (!condition.MatchLogicNot(out var arg))
            {
                return(false);
            }
            if (!(arg is CallInstruction call && NullableLiftingTransform.MatchHasValueCall(call, v)))
            {
                return(false);
            }
            var throwInstParent         = throwInst.Parent;
            var throwInstChildIndex     = throwInst.ChildIndex;
            var nullCoalescingWithThrow = new NullCoalescingInstruction(
                NullCoalescingKind.NullableWithValueFallback,
                stloc.Value,
                throwInst);
            var resultType = NullableType.GetUnderlyingType(call.Method.DeclaringType).GetStackType();

            nullCoalescingWithThrow.UnderlyingResultType = resultType;
            var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullCoalescingWithThrow, InliningOptions.None);

            if (result.Type == ILInlining.FindResultType.Found &&
                NullableLiftingTransform.MatchGetValueOrDefault(result.LoadInst.Parent, v))
            {
                context.Step("NullCoalescingTransform (value types + throw expression)", stloc);
                throwInst.resultType = resultType;
                result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow);
                block.Instructions.RemoveRange(pos, 2);                 // remove store(s) and if instruction
                return(true);
            }
            else
            {
                // reset the primary position (see remarks on ILInstruction.Parent)
                stloc.Value = stloc.Value;
                var children = throwInstParent.Children;
                children[throwInstChildIndex] = throwInst;
                return(false);
            }
        }