Esempio n. 1
0
        protected internal override void VisitBinaryNumericInstruction(BinaryNumericInstruction inst)
        {
            base.VisitBinaryNumericInstruction(inst);
            switch (inst.Operator)
            {
            case BinaryNumericOperator.ShiftLeft:
            case BinaryNumericOperator.ShiftRight:
                if (inst.Right.MatchBinaryNumericInstruction(BinaryNumericOperator.BitAnd, out var lhs, out var rhs) &&
                    rhs.MatchLdcI4(inst.ResultType == StackType.I8 ? 63 : 31))
                {
                    // a << (b & 31) => a << b
                    context.Step("Combine bit.and into shift", inst);
                    inst.Right = lhs;
                }
                break;

            case BinaryNumericOperator.BitAnd:
                if (inst.Left.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean) &&
                    inst.Right.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean))
                {
                    if (new NullableLiftingTransform(context).Run(inst))
                    {
                        // e.g. "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)"
                    }
                    else if (SemanticHelper.IsPure(inst.Right.Flags))
                    {
                        context.Step("Replace bit.and with logic.and", inst);
                        var expr = IfInstruction.LogicAnd(inst.Left, inst.Right);
                        inst.ReplaceWith(expr);
                        expr.AcceptVisitor(this);
                    }
                }
                break;
            }
        }
Esempio n. 2
0
        protected internal override void VisitBinaryNumericInstruction(BinaryNumericInstruction inst)
        {
            base.VisitBinaryNumericInstruction(inst);
            switch (inst.Operator)
            {
            case BinaryNumericOperator.ShiftLeft:
            case BinaryNumericOperator.ShiftRight:
                if (inst.Right.MatchBinaryNumericInstruction(BinaryNumericOperator.BitAnd, out var lhs, out var rhs) &&
                    rhs.MatchLdcI4(inst.ResultType == StackType.I8 ? 63 : 31))
                {
                    // a << (b & 31) => a << b
                    context.Step("Combine bit.and into shift", inst);
                    inst.Right = lhs;
                }
                break;

            case BinaryNumericOperator.BitAnd:
                if (IsBoolean(inst.Left) && IsBoolean(inst.Right) && SemanticHelper.IsPure(inst.Right.Flags))
                {
                    context.Step("Replace bit.and with logic.and", inst);
                    var expr = IfInstruction.LogicAnd(inst.Left, inst.Right);
                    inst.ReplaceWith(expr);
                    expr.AcceptVisitor(this);
                }
                break;
            }
        }
Esempio n. 3
0
        /// <code>
        /// stloc s(binary(callvirt(getter), value))
        /// callvirt (setter, ldloc s)
        /// (followed by single usage of s in next instruction)
        /// -->
        /// stloc s(compound.op.new(callvirt(getter), value))
        /// </code>
        bool TransformInlineCompoundAssignmentCall(Block block, int i)
        {
            var mainStLoc = block.Instructions[i] as StLoc;

            // in some cases it can be a compiler-generated local
            if (mainStLoc == null || (mainStLoc.Variable.Kind != VariableKind.StackSlot && mainStLoc.Variable.Kind != VariableKind.Local))
            {
                return(false);
            }
            BinaryNumericInstruction binary = mainStLoc.Value as BinaryNumericInstruction;
            ILVariable localVariable        = mainStLoc.Variable;

            if (!localVariable.IsSingleDefinition)
            {
                return(false);
            }
            if (localVariable.LoadCount != 2)
            {
                return(false);
            }
            var getterCall = binary?.Left as CallInstruction;
            var setterCall = block.Instructions.ElementAtOrDefault(i + 1) as CallInstruction;

            if (!MatchingGetterAndSetterCalls(getterCall, setterCall))
            {
                return(false);
            }
            if (!setterCall.Arguments.Last().MatchLdLoc(localVariable))
            {
                return(false);
            }

            var next = block.Instructions.ElementAtOrDefault(i + 2);

            if (next == null)
            {
                return(false);
            }
            if (next.Descendants.Where(d => d.MatchLdLoc(localVariable)).Count() != 1)
            {
                return(false);
            }
            if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType))
            {
                return(false);
            }
            context.Step($"Inline compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall);
            block.Instructions.RemoveAt(i + 1);             // remove setter call
            binary.ReplaceWith(new CompoundAssignmentInstruction(
                                   binary, getterCall, binary.Right,
                                   getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue));
            return(true);
        }
Esempio n. 4
0
        /// <summary>
        /// VS2017.8 / Roslyn 2.9 started optimizing some cases of
        ///   "a.GetValueOrDefault() == b.GetValueOrDefault() &amp;&amp; (a.HasValue &amp; b.HasValue)"
        /// to
        ///   "(a.GetValueOrDefault() == b.GetValueOrDefault()) &amp; (a.HasValue &amp; b.HasValue)"
        /// so this secondary entry point analyses logic.and as-if it was a short-circuiting &amp;&amp;.
        /// </summary>
        public bool Run(BinaryNumericInstruction bni)
        {
            Debug.Assert(!bni.IsLifted && bni.Operator == BinaryNumericOperator.BitAnd);
            // caller ensures that bni.Left/bni.Right are booleans
            var lifted = Lift(bni, bni.Left, bni.Right, new LdcI4(0));

            if (lifted != null)
            {
                bni.ReplaceWith(lifted);
                return(true);
            }
            return(false);
        }