Example #1
0
 bool MatchSwitchVar(ILInstruction inst)
 {
     if (switchVar != null)
     {
         return(inst.MatchLdLoc(switchVar));
     }
     else
     {
         return(inst.MatchLdLoc(out switchVar));
     }
 }
Example #2
0
        /// <summary>
        /// Finds the position to inline to.
        /// </summary>
        /// <returns>true = found; false = cannot continue search; null = not found</returns>
        static bool?FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, out ILInstruction loadInst)
        {
            loadInst = null;
            if (expr == null)
            {
                return(false);
            }
            if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v))
            {
                // Match found, we can inline
                loadInst = expr;
                return(true);
            }
            else if (expr is Block block && block.Instructions.Count > 0)
            {
                // Inlining into inline-blocks? only for some block types, and only into the first instruction.
                switch (block.Kind)
                {
                case BlockKind.ArrayInitializer:
                case BlockKind.CollectionInitializer:
                case BlockKind.ObjectInitializer:
                    return(FindLoadInNext(block.Instructions[0], v, expressionBeingMoved, out loadInst) ?? false);

                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the block.
                default:
                    return(false);
                }
            }
 bool IsDisplayClassLoad(ILInstruction target, out ILVariable variable)
 {
     if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable))
     {
         return(true);
     }
     return(false);
 }
Example #4
0
 private bool IsDisplayClassLoad(ILInstruction target, out ILVariable variable)
 {
     // We cannot use MatchLdLocRef here because local functions use ref parameters
     if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable))
     {
         return(true);
     }
     return(false);
 }
Example #5
0
        /// <summary>
        /// Finds the position to inline to.
        /// </summary>
        /// <returns>true = found; false = cannot continue search; null = not found</returns>
        static bool?FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, out ILInstruction loadInst)
        {
            loadInst = null;
            if (expr == null)
            {
                return(false);
            }
            if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v))
            {
                // Match found, we can inline
                loadInst = expr;
                return(true);
            }
            else if (expr is Block block && block.Instructions.Count > 0)
            {
                // Inlining into inline-blocks? only for some block types, and only into the first instruction.
                switch (block.Type)
                {
                case BlockType.ArrayInitializer:
                case BlockType.CollectionInitializer:
                case BlockType.ObjectInitializer:
                    return(FindLoadInNext(block.Instructions[0], v, expressionBeingMoved, out loadInst) ?? false);

                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the block.
                default:
                    return(false);
                }
            }
            foreach (var child in expr.Children)
            {
                if (!child.SlotInfo.CanInlineInto)
                {
                    return(false);
                }

                // Recursively try to find the load instruction
                bool?r = FindLoadInNext(child, v, expressionBeingMoved, out loadInst);
                if (r != null)
                {
                    return(r);
                }
            }
            if (IsSafeForInlineOver(expr, expressionBeingMoved))
            {
                return(null);                // continue searching
            }
            else
            {
                return(false);                // abort, inlining not possible
            }
        }
Example #6
0
        /// <summary>
        /// Finds the position to inline to.
        /// </summary>
        /// <returns>true = found; false = cannot continue search; null = not found</returns>
        internal static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, InliningOptions options)
        {
            if (expr == null)
            {
                return(FindResult.Stop);
            }
            if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v))
            {
                // Match found, we can inline
                if (expr.SlotInfo == StObj.TargetSlot && !((StObj)expr.Parent).CanInlineIntoTargetSlot(expressionBeingMoved))
                {
                    // special case: the StObj.TargetSlot does not accept some kinds of expressions
                    return(FindResult.Stop);
                }
                return(FindResult.Found(expr));
            }
            else if (expr is Block block)
            {
                // Inlining into inline-blocks?
                switch (block.Kind)
                {
                case BlockKind.ArrayInitializer:
                case BlockKind.CollectionInitializer:
                case BlockKind.ObjectInitializer:
                case BlockKind.CallInlineAssign:
                    // Allow inlining into the first instruction of the block
                    if (block.Instructions.Count == 0)
                    {
                        return(FindResult.Stop);
                    }
                    return(NoContinue(FindLoadInNext(block.Instructions[0], v, expressionBeingMoved, options)));

                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the block.
                case BlockKind.CallWithNamedArgs:
                    return(NamedArgumentTransform.CanExtendNamedArgument(block, v, expressionBeingMoved));

                default:
                    return(FindResult.Stop);
                }
            }
            else if (expr is BlockContainer container && container.EntryPoint.IncomingEdgeCount == 1)
            {
                // Possibly a switch-container, allow inlining into the switch instruction:
                return(NoContinue(FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, options)));
                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the blockcontainer.
            }
Example #7
0
        /// <summary>
        /// Finds the position to inline to.
        /// </summary>
        /// <returns>true = found; false = cannot continue search; null = not found</returns>
        internal static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, out ILInstruction loadInst)
        {
            loadInst = null;
            if (expr == null)
            {
                return(FindResult.Stop);
            }
            if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v))
            {
                // Match found, we can inline
                loadInst = expr;
                return(FindResult.Found);
            }
            else if (expr is Block block)
            {
                // Inlining into inline-blocks?
                switch (block.Kind)
                {
                case BlockKind.ArrayInitializer:
                case BlockKind.CollectionInitializer:
                case BlockKind.ObjectInitializer:
                case BlockKind.CallInlineAssign:
                    // Allow inlining into the first instruction of the block
                    if (block.Instructions.Count == 0)
                    {
                        return(FindResult.Stop);
                    }
                    return(NoContinue(FindLoadInNext(block.Instructions[0], v, expressionBeingMoved, out loadInst)));

                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the block.
                case BlockKind.CallWithNamedArgs:
                    return(NamedArgumentTransform.CanExtendNamedArgument(block, v, expressionBeingMoved, out loadInst));

                default:
                    return(FindResult.Stop);
                }
            }
            else if (expr is BlockContainer container && container.EntryPoint.IncomingEdgeCount == 1)
            {
                // Possibly a switch-container, allow inlining into the switch instruction:
                return(NoContinue(FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, out loadInst)));
                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the blockcontainer.
            }
Example #8
0
        /// <summary>
        ///       if (call op_False(ldloc lhsVar)) ldloc lhsVar else call op_BitwiseAnd(ldloc lhsVar, rhsInst)
        ///    -> user.logic op_BitwiseAnd(ldloc lhsVar, rhsInst)
        /// or
        ///       if (call op_True(ldloc lhsVar)) ldloc lhsVar else call op_BitwiseOr(ldloc lhsVar, rhsInst)
        ///    -> user.logic op_BitwiseOr(ldloc lhsVar, rhsInst)
        /// </summary>
        public static ILInstruction Transform(ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst)
        {
            if (!MatchCondition(condition, out var lhsVar, out var conditionMethodName))
            {
                return(null);
            }
            if (!trueInst.MatchLdLoc(lhsVar))
            {
                return(null);
            }
            var call = falseInst as Call;

            if (!MatchBitwiseCall(call, lhsVar, conditionMethodName))
            {
                return(null);
            }

            var result = new UserDefinedLogicOperator(call.Method, call.Arguments[0], call.Arguments[1]);

            result.AddILRange(condition);
            result.AddILRange(trueInst);
            result.AddILRange(call);
            return(result);
        }
Example #9
0
        /// <summary>
        /// Finds the position to inline to.
        /// </summary>
        /// <returns>true = found; false = cannot continue search; null = not found</returns>
        static bool?FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, out ILInstruction loadInst)
        {
            loadInst = null;
            if (expr == null)
            {
                return(false);
            }
            if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v))
            {
                // Match found, we can inline
                loadInst = expr;
                return(true);
            }
            foreach (var child in expr.Children)
            {
                if (!child.SlotInfo.CanInlineInto)
                {
                    return(false);
                }

                // Recursively try to find the load instruction
                bool?r = FindLoadInNext(child, v, expressionBeingMoved, out loadInst);
                if (r != null)
                {
                    return(r);
                }
            }
            if (IsSafeForInlineOver(expr, expressionBeingMoved))
            {
                return(null);                // continue searching
            }
            else
            {
                return(false);                // abort, inlining not possible
            }
        }
Example #10
0
        /// <summary>
        /// Performs nullable lifting.
        ///
        /// Produces a lifted instruction with semantics equivalent to:
        ///   (v1 != null && ... && vn != null) ? trueInst : falseInst,
        /// where the v1,...,vn are the <c>this.nullableVars</c>.
        /// If lifting fails, returns <c>null</c>.
        /// </summary>
        ILInstruction LiftNormal(ILInstruction trueInst, ILInstruction falseInst, Interval ilrange)
        {
            bool isNullCoalescingWithNonNullableFallback = false;

            if (!MatchNullableCtor(trueInst, out var utype, out var exprToLift))
            {
                isNullCoalescingWithNonNullableFallback = true;
                utype      = context.TypeSystem.Compilation.FindType(trueInst.ResultType.ToKnownTypeCode());
                exprToLift = trueInst;
                if (nullableVars.Count == 1 && exprToLift.MatchLdLoc(nullableVars[0]))
                {
                    // v.HasValue ? ldloc v : fallback
                    // => v ?? fallback
                    context.Step("v.HasValue ? v : fallback => v ?? fallback", trueInst);
                    return(new NullCoalescingInstruction(NullCoalescingKind.Nullable, trueInst, falseInst)
                    {
                        UnderlyingResultType = NullableType.GetUnderlyingType(nullableVars[0].Type).GetStackType(),
                        ILRange = ilrange
                    });
                }
                else if (trueInst is Call call && !call.IsLifted &&
                         CSharp.Resolver.CSharpOperators.IsComparisonOperator(call.Method) &&
                         call.Method.Name != "op_Equality" && call.Method.Name != "op_Inequality" &&
                         falseInst.MatchLdcI4(0))
                {
                    // (v1 != null && ... && vn != null) ? call op_LessThan(lhs, rhs) : ldc.i4(0)
                    var liftedOperator = CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method);
                    if (liftedOperator != null)
                    {
                        var(left, right, bits) = DoLiftBinary(call.Arguments[0], call.Arguments[1]);
                        if (left != null && right != null && bits.All(0, nullableVars.Count))
                        {
                            return(new Call(liftedOperator)
                            {
                                Arguments = { left, right },
                                ConstrainedTo = call.ConstrainedTo,
                                ILRange = call.ILRange,
                                ILStackWasEmpty = call.ILStackWasEmpty,
                                IsTail = call.IsTail
                            });
                        }
                    }
                }
            }
            ILInstruction lifted;

            if (nullableVars.Count == 1 && MatchGetValueOrDefault(exprToLift, nullableVars[0]))
            {
                // v.HasValue ? call GetValueOrDefault(ldloca v) : fallback
                // => conv.nop.lifted(ldloc v) ?? fallback
                // This case is handled separately from DoLift() because
                // that doesn't introduce nop-conversions.
                context.Step("v.HasValue ? v.GetValueOrDefault() : fallback => v ?? fallback", trueInst);
                var inputUType = NullableType.GetUnderlyingType(nullableVars[0].Type);
                lifted = new LdLoc(nullableVars[0]);
                if (!inputUType.Equals(utype) && utype.ToPrimitiveType() != PrimitiveType.None)
                {
                    // While the ILAst allows implicit conversions between short and int
                    // (because both map to I4); it does not allow implicit conversions
                    // between short? and int? (structs of different types).
                    // So use 'conv.nop.lifted' to allow the conversion.
                    lifted = new Conv(
                        lifted,
                        inputUType.GetStackType(), inputUType.GetSign(), utype.ToPrimitiveType(),
                        checkForOverflow: false,
                        isLifted: true
                        )
                    {
                        ILRange = ilrange
                    };
                }
            }
            else
            {
                context.Step("NullableLiftingTransform.DoLift", trueInst);
                BitSet bits;
                (lifted, bits) = DoLift(exprToLift);
                if (lifted == null)
                {
                    return(null);
                }
                if (!bits.All(0, nullableVars.Count))
                {
                    // don't lift if a nullableVar doesn't contribute to the result
                    return(null);
                }
                Debug.Assert(lifted is ILiftableInstruction liftable && liftable.IsLifted &&
                             liftable.UnderlyingResultType == exprToLift.ResultType);
            }
            if (isNullCoalescingWithNonNullableFallback)
            {
                lifted = new NullCoalescingInstruction(NullCoalescingKind.NullableWithValueFallback, lifted, falseInst)
                {
                    UnderlyingResultType = exprToLift.ResultType,
                    ILRange = ilrange
                };
            }
            else if (!MatchNull(falseInst, utype))
            {
                // Normal lifting, but the falseInst isn't `default(utype?)`
                // => use the `??` operator to provide the fallback value.
                lifted = new NullCoalescingInstruction(NullCoalescingKind.Nullable, lifted, falseInst)
                {
                    UnderlyingResultType = exprToLift.ResultType,
                    ILRange = ilrange
                };
            }
            return(lifted);
        }
Example #11
0
        ILInstruction Lift(IfInstruction ifInst, ILInstruction trueInst, ILInstruction falseInst)
        {
            ILInstruction condition = ifInst.Condition;

            while (condition.MatchLogicNot(out var arg))
            {
                condition = arg;
                Swap(ref trueInst, ref falseInst);
            }
            if (AnalyzeCondition(condition))
            {
                // (v1 != null && ... && vn != null) ? trueInst : falseInst
                // => normal lifting
                return(LiftNormal(trueInst, falseInst, ilrange: ifInst.ILRange));
            }
            if (condition is Comp comp && !comp.IsLifted)
            {
                // This might be a C#-style lifted comparison
                // (C# checks the underlying value before checking the HasValue bits)
                if (comp.Kind.IsEqualityOrInequality())
                {
                    // for equality/inequality, the HasValue bits must also compare equal/inequal
                    if (comp.Kind == ComparisonKind.Inequality)
                    {
                        // handle inequality by swapping one last time
                        Swap(ref trueInst, ref falseInst);
                    }
                    if (falseInst.MatchLdcI4(0))
                    {
                        // (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue == b.HasValue) : false
                        // => a == b
                        return(LiftCSharpEqualityComparison(comp, ComparisonKind.Equality, trueInst));
                    }
                    else if (falseInst.MatchLdcI4(1))
                    {
                        // (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue != b.HasValue) : true
                        // => a != b
                        return(LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst));
                    }
                    else if (IsGenericNewPattern(condition, trueInst, falseInst))
                    {
                        // (default(T) == null) ? Activator.CreateInstance<T>() : default(T)
                        // => Activator.CreateInstance<T>()
                        return(trueInst);
                    }
                }
                else
                {
                    // Not (in)equality, but one of < <= > >=.
                    // Returns false unless all HasValue bits are true.
                    if (falseInst.MatchLdcI4(0) && AnalyzeCondition(trueInst))
                    {
                        // comp(lhs, rhs) ? (v1 != null && ... && vn != null) : false
                        // => comp.lifted[C#](lhs, rhs)
                        return(LiftCSharpComparison(comp, comp.Kind));
                    }
                    else if (trueInst.MatchLdcI4(0) && AnalyzeCondition(falseInst))
                    {
                        // comp(lhs, rhs) ? false : (v1 != null && ... && vn != null)
                        return(LiftCSharpComparison(comp, comp.Kind.Negate()));
                    }
                }
            }
            ILVariable v;

            if (MatchGetValueOrDefault(condition, out v) &&
                NullableType.GetUnderlyingType(v.Type).IsKnownType(KnownTypeCode.Boolean))
            {
                if (MatchHasValueCall(trueInst, v) && falseInst.MatchLdcI4(0))
                {
                    // v.GetValueOrDefault() ? v.HasValue : false
                    // ==> v == true
                    context.Step("NullableLiftingTransform: v == true", ifInst);
                    return(new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp,
                                    StackType.I4, Sign.None,
                                    new LdLoc(v)
                    {
                        ILRange = trueInst.ILRange
                    },
                                    new LdcI4(1)
                    {
                        ILRange = falseInst.ILRange
                    }
                                    )
                    {
                        ILRange = ifInst.ILRange
                    });
                }
                else if (trueInst.MatchLdcI4(0) && MatchHasValueCall(falseInst, v))
                {
                    // v.GetValueOrDefault() ? false : v.HasValue
                    // ==> v == false
                    context.Step("NullableLiftingTransform: v == false", ifInst);
                    return(new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp,
                                    StackType.I4, Sign.None,
                                    new LdLoc(v)
                    {
                        ILRange = falseInst.ILRange
                    },
                                    trueInst             // LdcI4(0)
                                    )
                    {
                        ILRange = ifInst.ILRange
                    });
                }
                else if (MatchNegatedHasValueCall(trueInst, v) && falseInst.MatchLdcI4(1))
                {
                    // v.GetValueOrDefault() ? !v.HasValue : true
                    // ==> v != true
                    context.Step("NullableLiftingTransform: v != true", ifInst);
                    return(new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp,
                                    StackType.I4, Sign.None,
                                    new LdLoc(v)
                    {
                        ILRange = trueInst.ILRange
                    },
                                    falseInst             // LdcI4(1)
                                    )
                    {
                        ILRange = ifInst.ILRange
                    });
                }
                else if (trueInst.MatchLdcI4(1) && MatchNegatedHasValueCall(falseInst, v))
                {
                    // v.GetValueOrDefault() ? true : !v.HasValue
                    // ==> v != false
                    context.Step("NullableLiftingTransform: v != false", ifInst);
                    return(new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp,
                                    StackType.I4, Sign.None,
                                    new LdLoc(v)
                    {
                        ILRange = falseInst.ILRange
                    },
                                    new LdcI4(0)
                    {
                        ILRange = trueInst.ILRange
                    }
                                    )
                    {
                        ILRange = ifInst.ILRange
                    });
                }
            }
            if (trueInst.MatchLdLoc(out v))
            {
                if (MatchNullableCtor(falseInst, out var utype, out var arg) &&
                    utype.IsKnownType(KnownTypeCode.Boolean) && arg.MatchLdcI4(0))
                {
                    // condition ? v : (bool?)false
                    // => condition & v
                    context.Step("NullableLiftingTransform: 3vl.logic.and(bool, bool?)", ifInst);
                    return(new ThreeValuedLogicAnd(condition, trueInst)
                    {
                        ILRange = ifInst.ILRange
                    });
                }
                if (falseInst.MatchLdLoc(out var v2))
                {
                    // condition ? v : v2
                    if (MatchThreeValuedLogicConditionPattern(condition, out var nullable1, out var nullable2))
                    {
                        // (nullable1.GetValueOrDefault() || (!nullable2.GetValueOrDefault() && !nullable1.HasValue)) ? v : v2
                        if (v == nullable1 && v2 == nullable2)
                        {
                            context.Step("NullableLiftingTransform: 3vl.logic.or(bool?, bool?)", ifInst);
                            return(new ThreeValuedLogicOr(trueInst, falseInst)
                            {
                                ILRange = ifInst.ILRange
                            });
                        }
                        else if (v == nullable2 && v2 == nullable1)
                        {
                            context.Step("NullableLiftingTransform: 3vl.logic.and(bool?, bool?)", ifInst);
                            return(new ThreeValuedLogicAnd(falseInst, trueInst)
                            {
                                ILRange = ifInst.ILRange
                            });
                        }
                    }
                }
            }
            else if (falseInst.MatchLdLoc(out v))
            {
                if (MatchNullableCtor(trueInst, out var utype, out var arg) &&
                    utype.IsKnownType(KnownTypeCode.Boolean) && arg.MatchLdcI4(1))
                {
                    // condition ? (bool?)true : v
                    // => condition | v
                    context.Step("NullableLiftingTransform: 3vl.logic.or(bool, bool?)", ifInst);
                    return(new ThreeValuedLogicOr(condition, falseInst)
                    {
                        ILRange = ifInst.ILRange
                    });
                }
            }
            return(null);
        }
Example #12
0
        /// <summary>
        /// Finds the position to inline to.
        /// </summary>
        /// <returns>true = found; false = cannot continue search; null = not found</returns>
        internal static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, InliningOptions options)
        {
            if (expr == null)
            {
                return(FindResult.Stop);
            }
            if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v))
            {
                // Match found, we can inline
                if (expr.SlotInfo == StObj.TargetSlot && !((StObj)expr.Parent).CanInlineIntoTargetSlot(expressionBeingMoved))
                {
                    if ((options & InliningOptions.AllowChangingOrderOfEvaluationForExceptions) != 0)
                    {
                        // Intentionally change code semantics so that we can avoid a ref local
                        if (expressionBeingMoved is LdFlda ldflda)
                        {
                            ldflda.DelayExceptions = true;
                        }
                        else if (expressionBeingMoved is LdElema ldelema)
                        {
                            ldelema.DelayExceptions = true;
                        }
                    }
                    else
                    {
                        // special case: the StObj.TargetSlot does not accept some kinds of expressions
                        return(FindResult.Stop);
                    }
                }
                return(FindResult.Found(expr));
            }
            else if (expr is Block block)
            {
                // Inlining into inline-blocks?
                switch (block.Kind)
                {
                case BlockKind.ControlFlow when block.Parent is BlockContainer:
                case BlockKind.ArrayInitializer:
                case BlockKind.CollectionInitializer:
                case BlockKind.ObjectInitializer:
                case BlockKind.CallInlineAssign:
                    // Allow inlining into the first instruction of the block
                    if (block.Instructions.Count == 0)
                    {
                        return(FindResult.Stop);
                    }
                    return(NoContinue(FindLoadInNext(block.Instructions[0], v, expressionBeingMoved, options)));

                // If FindLoadInNext() returns null, we still can't continue searching
                // because we can't inline over the remainder of the block.
                case BlockKind.CallWithNamedArgs:
                    return(NamedArgumentTransform.CanExtendNamedArgument(block, v, expressionBeingMoved));

                default:
                    return(FindResult.Stop);
                }
            }
            else if (options.HasFlag(InliningOptions.FindDeconstruction) && expr is DeconstructInstruction di)
            {
                return(FindResult.Deconstruction(di));
            }
            foreach (var child in expr.Children)
            {
                if (!expr.CanInlineIntoSlot(child.ChildIndex, expressionBeingMoved))
                {
                    return(FindResult.Stop);
                }

                // Recursively try to find the load instruction
                FindResult r = FindLoadInNext(child, v, expressionBeingMoved, options);
                if (r.Type != FindResultType.Continue)
                {
                    if (r.Type == FindResultType.Stop && (options & InliningOptions.IntroduceNamedArguments) != 0 && expr is CallInstruction call)
                    {
                        return(NamedArgumentTransform.CanIntroduceNamedArgument(call, child, v, expressionBeingMoved));
                    }
                    return(r);
                }
            }
            if (IsSafeForInlineOver(expr, expressionBeingMoved))
            {
                return(FindResult.Continue);                // continue searching
            }
            else
            {
                return(FindResult.Stop);                // abort, inlining not possible
            }
        }
Example #13
0
        /// <summary>
        /// Main entry point for lifting; called by both the expression-transform
        /// and the block transform.
        /// </summary>
        ILInstruction Lift(ILInstruction ifInst, ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst)
        {
            // ifInst is usually the IfInstruction to which condition belongs;
            // but can also be a BinaryNumericInstruction.
            while (condition.MatchLogicNot(out var arg))
            {
                condition = arg;
                ExtensionMethods.Swap(ref trueInst, ref falseInst);
            }
            if (context.Settings.NullPropagation && !NullPropagationTransform.IsProtectedIfInst(ifInst as IfInstruction))
            {
                var nullPropagated = new NullPropagationTransform(context)
                                     .Run(condition, trueInst, falseInst)?.WithILRange(ifInst);
                if (nullPropagated != null)
                {
                    return(nullPropagated);
                }
            }
            if (!context.Settings.LiftNullables)
            {
                return(null);
            }
            if (AnalyzeCondition(condition))
            {
                // (v1 != null && ... && vn != null) ? trueInst : falseInst
                // => normal lifting
                return(LiftNormal(trueInst, falseInst)?.WithILRange(ifInst));
            }
            if (MatchCompOrDecimal(condition, out var comp))
            {
                // This might be a C#-style lifted comparison
                // (C# checks the underlying value before checking the HasValue bits)
                if (comp.Kind.IsEqualityOrInequality())
                {
                    // for equality/inequality, the HasValue bits must also compare equal/inequal
                    if (comp.Kind == ComparisonKind.Inequality)
                    {
                        // handle inequality by swapping one last time
                        ExtensionMethods.Swap(ref trueInst, ref falseInst);
                    }
                    if (falseInst.MatchLdcI4(0))
                    {
                        // (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue == b.HasValue) : false
                        // => a == b
                        return(LiftCSharpEqualityComparison(comp, ComparisonKind.Equality, trueInst)
                               ?? LiftCSharpUserEqualityComparison(comp, ComparisonKind.Equality, trueInst));
                    }
                    else if (falseInst.MatchLdcI4(1))
                    {
                        // (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue != b.HasValue) : true
                        // => a != b
                        return(LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst)
                               ?? LiftCSharpUserEqualityComparison(comp, ComparisonKind.Inequality, trueInst));
                    }
                    else if (IsGenericNewPattern(comp.Left, comp.Right, trueInst, falseInst))
                    {
                        // (default(T) == null) ? Activator.CreateInstance<T>() : default(T)
                        // => Activator.CreateInstance<T>()
                        return(trueInst);
                    }
                }
                else
                {
                    // Not (in)equality, but one of < <= > >=.
                    // Returns false unless all HasValue bits are true.
                    if (falseInst.MatchLdcI4(0) && AnalyzeCondition(trueInst))
                    {
                        // comp(lhs, rhs) ? (v1 != null && ... && vn != null) : false
                        // => comp.lifted[C#](lhs, rhs)
                        return(LiftCSharpComparison(comp, comp.Kind));
                    }
                    else if (trueInst.MatchLdcI4(0) && AnalyzeCondition(falseInst))
                    {
                        // comp(lhs, rhs) ? false : (v1 != null && ... && vn != null)
                        return(LiftCSharpComparison(comp, comp.Kind.Negate()));
                    }
                }
            }
            ILVariable v;

            // Handle equality comparisons with bool?:
            if (MatchGetValueOrDefault(condition, out v) &&
                NullableType.GetUnderlyingType(v.Type).IsKnownType(KnownTypeCode.Boolean))
            {
                if (MatchHasValueCall(trueInst, v) && falseInst.MatchLdcI4(0))
                {
                    // v.GetValueOrDefault() ? v.HasValue : false
                    // ==> v == true
                    context.Step("NullableLiftingTransform: v == true", ifInst);
                    return(new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp,
                                    StackType.I4, Sign.None,
                                    new LdLoc(v).WithILRange(trueInst),
                                    new LdcI4(1).WithILRange(falseInst)
                                    ).WithILRange(ifInst));
                }
                else if (trueInst.MatchLdcI4(0) && MatchHasValueCall(falseInst, v))
                {
                    // v.GetValueOrDefault() ? false : v.HasValue
                    // ==> v == false
                    context.Step("NullableLiftingTransform: v == false", ifInst);
                    return(new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp,
                                    StackType.I4, Sign.None,
                                    new LdLoc(v).WithILRange(falseInst),
                                    trueInst             // LdcI4(0)
                                    ).WithILRange(ifInst));
                }
                else if (MatchNegatedHasValueCall(trueInst, v) && falseInst.MatchLdcI4(1))
                {
                    // v.GetValueOrDefault() ? !v.HasValue : true
                    // ==> v != true
                    context.Step("NullableLiftingTransform: v != true", ifInst);
                    return(new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp,
                                    StackType.I4, Sign.None,
                                    new LdLoc(v).WithILRange(trueInst),
                                    falseInst             // LdcI4(1)
                                    ).WithILRange(ifInst));
                }
                else if (trueInst.MatchLdcI4(1) && MatchNegatedHasValueCall(falseInst, v))
                {
                    // v.GetValueOrDefault() ? true : !v.HasValue
                    // ==> v != false
                    context.Step("NullableLiftingTransform: v != false", ifInst);
                    return(new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp,
                                    StackType.I4, Sign.None,
                                    new LdLoc(v).WithILRange(falseInst),
                                    new LdcI4(0).WithILRange(trueInst)
                                    ).WithILRange(ifInst));
                }
            }
            // Handle & and | on bool?:
            if (trueInst.MatchLdLoc(out v))
            {
                if (MatchNullableCtor(falseInst, out var utype, out var arg) &&
                    utype.IsKnownType(KnownTypeCode.Boolean) && arg.MatchLdcI4(0))
                {
                    // condition ? v : (bool?)false
                    // => condition & v
                    context.Step("NullableLiftingTransform: 3vl.bool.and(bool, bool?)", ifInst);
                    return(new ThreeValuedBoolAnd(condition, trueInst).WithILRange(ifInst));
                }
                if (falseInst.MatchLdLoc(out var v2))
                {
                    // condition ? v : v2
                    if (MatchThreeValuedLogicConditionPattern(condition, out var nullable1, out var nullable2))
                    {
                        // (nullable1.GetValueOrDefault() || (!nullable2.GetValueOrDefault() && !nullable1.HasValue)) ? v : v2
                        if (v == nullable1 && v2 == nullable2)
                        {
                            context.Step("NullableLiftingTransform: 3vl.bool.or(bool?, bool?)", ifInst);
                            return(new ThreeValuedBoolOr(trueInst, falseInst).WithILRange(ifInst));
                        }
                        else if (v == nullable2 && v2 == nullable1)
                        {
                            context.Step("NullableLiftingTransform: 3vl.bool.and(bool?, bool?)", ifInst);
                            return(new ThreeValuedBoolAnd(falseInst, trueInst).WithILRange(ifInst));
                        }
                    }
                }
            }
            else if (falseInst.MatchLdLoc(out v))
            {
                if (MatchNullableCtor(trueInst, out var utype, out var arg) &&
                    utype.IsKnownType(KnownTypeCode.Boolean) && arg.MatchLdcI4(1))
                {
                    // condition ? (bool?)true : v
                    // => condition | v
                    context.Step("NullableLiftingTransform: 3vl.logic.or(bool, bool?)", ifInst);
                    return(new ThreeValuedBoolOr(condition, falseInst).WithILRange(ifInst));
                }
            }
            return(null);
        }
Example #14
0
 bool MatchesTargetOrCopyLoad(ILInstruction inst)
 {
     return(targetAndCopies.Any(v => inst.MatchLdLoc(v)));
 }
 /// <summary>
 /// Block entryPoint (incoming: 1)  {
 ///   stloc temp(isinst exceptionType(ldloc exceptionVar))
 ///   if (comp(ldloc temp != ldnull)) br whenConditionBlock
 ///   br falseBlock
 /// }
 /// </summary>
 bool MatchCatchWhenEntryPoint(ILVariable exceptionVar, BlockContainer container, Block entryPoint, out IType exceptionType, out ILInstruction exceptionSlot, out Block whenConditionBlock)
 {
     exceptionType      = null;
     exceptionSlot      = null;
     whenConditionBlock = null;
     if (entryPoint == null || entryPoint.IncomingEdgeCount != 1)
     {
         return(false);
     }
     if (entryPoint.Instructions.Count == 3)
     {
         // stloc temp(isinst exceptionType(ldloc exceptionVar))
         // if (comp(ldloc temp != ldnull)) br whenConditionBlock
         // br falseBlock
         if (!entryPoint.Instructions[0].MatchStLoc(out var temp, out var isinst) ||
             temp.Kind != VariableKind.StackSlot || !isinst.MatchIsInst(out exceptionSlot, out exceptionType))
         {
             return(false);
         }
         if (!exceptionSlot.MatchLdLoc(exceptionVar))
         {
             return(false);
         }
         if (!entryPoint.Instructions[1].MatchIfInstruction(out var condition, out var branch))
         {
             return(false);
         }
         if (!condition.MatchCompNotEquals(out var left, out var right))
         {
             return(false);
         }
         if (!entryPoint.Instructions[2].MatchBranch(out var falseBlock) || !MatchFalseBlock(container, falseBlock, out var returnVar, out var exitBlock))
         {
             return(false);
         }
         if ((left.MatchLdNull() && right.MatchLdLoc(temp)) || (right.MatchLdNull() && left.MatchLdLoc(temp)))
         {
             return(branch.MatchBranch(out whenConditionBlock));
         }
     }
     else if (entryPoint.Instructions.Count == 2)
     {
         // if (comp(isinst exceptionType(ldloc exceptionVar) != ldnull)) br whenConditionBlock
         // br falseBlock
         if (!entryPoint.Instructions[0].MatchIfInstruction(out var condition, out var branch))
         {
             return(false);
         }
         if (!condition.MatchCompNotEquals(out var left, out var right))
         {
             return(false);
         }
         if (!entryPoint.Instructions[1].MatchBranch(out var falseBlock) || !MatchFalseBlock(container, falseBlock, out var returnVar, out var exitBlock))
         {
             return(false);
         }
         if (!left.MatchIsInst(out exceptionSlot, out exceptionType))
         {
             return(false);
         }
         if (!exceptionSlot.MatchLdLoc(exceptionVar))
         {
             return(false);
         }
         if (right.MatchLdNull())
         {
             return(branch.MatchBranch(out whenConditionBlock));
         }
     }
     return(false);
 }
Example #16
0
        /// <summary>
        /// Performs nullable lifting.
        ///
        /// Produces a lifted instruction with semantics equivalent to:
        ///   (v1 != null && ... && vn != null) ? trueInst : falseInst,
        /// where the v1,...,vn are the <c>this.nullableVars</c>.
        /// If lifting fails, returns <c>null</c>.
        /// </summary>
        ILInstruction LiftNormal(ILInstruction trueInst, ILInstruction falseInst, Interval ilrange)
        {
            bool isNullCoalescingWithNonNullableFallback = false;

            if (!MatchNullableCtor(trueInst, out var utype, out var exprToLift))
            {
                isNullCoalescingWithNonNullableFallback = true;
                utype      = context.TypeSystem.Compilation.FindType(trueInst.ResultType.ToKnownTypeCode());
                exprToLift = trueInst;
                if (nullableVars.Count == 1 && exprToLift.MatchLdLoc(nullableVars[0]))
                {
                    // v.HasValue ? ldloc v : fallback
                    // => v ?? fallback
                    context.Step("v.HasValue ? v : fallback => v ?? fallback", trueInst);
                    return(new NullCoalescingInstruction(NullCoalescingKind.Nullable, trueInst, falseInst)
                    {
                        UnderlyingResultType = NullableType.GetUnderlyingType(nullableVars[0].Type).GetStackType(),
                        ILRange = ilrange
                    });
                }
            }
            ILInstruction lifted;

            if (nullableVars.Count == 1 && MatchGetValueOrDefault(exprToLift, nullableVars[0]))
            {
                // v.HasValue ? call GetValueOrDefault(ldloca v) : fallback
                // => conv.nop.lifted(ldloc v) ?? fallback
                // This case is handled separately from DoLift() because
                // that doesn't introduce nop-conversions.
                context.Step("v.HasValue ? v.GetValueOrDefault() : fallback => v ?? fallback", trueInst);
                var inputUType = NullableType.GetUnderlyingType(nullableVars[0].Type);
                lifted = new LdLoc(nullableVars[0]);
                if (!inputUType.Equals(utype) && utype.ToPrimitiveType() != PrimitiveType.None)
                {
                    // While the ILAst allows implicit conversions between short and int
                    // (because both map to I4); it does not allow implicit conversions
                    // between short? and int? (structs of different types).
                    // So use 'conv.nop.lifted' to allow the conversion.
                    lifted = new Conv(
                        lifted,
                        inputUType.GetStackType(), inputUType.GetSign(), utype.ToPrimitiveType(),
                        checkForOverflow: false,
                        isLifted: true
                        )
                    {
                        ILRange = ilrange
                    };
                }
            }
            else
            {
                context.Step("NullableLiftingTransform.DoLift", trueInst);
                BitSet bits;
                (lifted, bits) = DoLift(exprToLift);
                if (lifted == null)
                {
                    return(null);
                }
                if (!bits.All(0, nullableVars.Count))
                {
                    // don't lift if a nullableVar doesn't contribute to the result
                    return(null);
                }
                Debug.Assert(lifted is ILiftableInstruction liftable && liftable.IsLifted &&
                             liftable.UnderlyingResultType == exprToLift.ResultType);
            }
            if (isNullCoalescingWithNonNullableFallback)
            {
                lifted = new NullCoalescingInstruction(NullCoalescingKind.NullableWithValueFallback, lifted, falseInst)
                {
                    UnderlyingResultType = exprToLift.ResultType,
                    ILRange = ilrange
                };
            }
            else if (!MatchNull(falseInst, utype))
            {
                // Normal lifting, but the falseInst isn't `default(utype?)`
                // => use the `??` operator to provide the fallback value.
                lifted = new NullCoalescingInstruction(NullCoalescingKind.Nullable, lifted, falseInst)
                {
                    UnderlyingResultType = exprToLift.ResultType,
                    ILRange = ilrange
                };
            }
            return(lifted);
        }