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);
        }
예제 #2
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);
 }
예제 #3
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])
                                                         .WithILRange(call);
                block.Instructions.RemoveAt(pos + 1);
                context.RequestRerun();                 // the 'stloc s' may now be eligible for inlining
                return(true);
            }
            return(false);
        }
예제 #4
0
 void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
 {
     if (LegacyPattern(block, pos, context))
     {
         return;
     }
     if (RoslynOptimized(block, pos, context))
     {
         return;
     }
 }
        public void Run(Block block, int pos, StatementTransformContext context)
        {
            if (!context.Settings.NamedArguments)
            {
                return;
            }
            var options = ILInlining.OptionsForBlock(block, pos);

            options |= InliningOptions.IntroduceNamedArguments;
            ILInlining.InlineOneIfPossible(block, pos, options, context: context);
        }
 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;
     }
 }
예제 #7
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--;
                }
            }
        }
예제 #8
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);
 }
 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);
 }
예제 #10
0
 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);
 }
 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;
         }
     } finally {
         this.context = null;
     }
 }
예제 #12
0
 void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
 {
     this.context = context;
     if (context.Settings.MakeAssignmentExpressions)
     {
         if (TransformInlineAssignmentStObjOrCall(block, pos) || TransformInlineAssignmentLocal(block, pos))
         {
             // both inline assignments create a top-level stloc which might affect inlining
             context.RequestRerun();
             return;
         }
     }
     if (context.Settings.IntroduceIncrementAndDecrement)
     {
         if (TransformPostIncDecOperatorWithInlineStore(block, pos) ||
             TransformPostIncDecOperator(block, pos) ||
             TransformPostIncDecOperatorLocal(block, pos))
         {
             // again, new top-level stloc might need inlining:
             context.RequestRerun();
             return;
         }
     }
 }
예제 #13
0
 public void Run(Block block, int pos, StatementTransformContext context)
 {
     TransformRefTypes(block, pos, context);
 }
예제 #14
0
 public void Run(Block block, int pos, StatementTransformContext context)
 {
     InlineOneIfPossible(block, pos, OptionsForBlock(block, pos), context: context);
 }
예제 #15
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));
 }
예제 #16
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 (!IsMatchingCompoundLoad(binary.Left, compoundStore, forbiddenVariable: storeInSetter?.Variable))
                {
                    return(false);
                }
                if (!ValidateCompoundAssign(binary, smallIntConv, targetType))
                {
                    return(false);
                }
                context.Step($"Compound assignment (binary.numeric)", compoundStore);
                newInst = new NumericCompoundAssign(
                    binary, binary.Left, binary.Right,
                    targetType, CompoundAssignmentType.EvaluatesToNewValue);
            }
            else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator)
            {
                if (operatorCall.Arguments.Count == 0)
                {
                    return(false);
                }
                if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, 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);
                newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundAssignmentType.EvaluatesToNewValue,
                                                        operatorCall.Arguments[0], rhs);
            }