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)"
                    }
                }
                break;
            }
        }
        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;
            }
        }
Exemple #3
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;
            }
        }
Exemple #4
0
 static bool ValidateCompoundAssign(BinaryNumericInstruction binary, Conv conv, IType targetType)
 {
     if (!NumericCompoundAssign.IsBinaryCompatibleWithType(binary, targetType))
     {
         return(false);
     }
     if (conv != null && !(conv.TargetType == targetType.ToPrimitiveType() && conv.CheckForOverflow == binary.CheckForOverflow))
     {
         return(false);                // conv does not match binary operation
     }
     return(true);
 }
        /// <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);
        }
        /// <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);
        }
Exemple #7
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;
     }
 }
Exemple #8
0
 /// <summary>
 /// Recursive function that lifts the specified instruction.
 /// The input instruction is expected to a subexpression of the trueInst
 /// (so that all nullableVars are guaranteed non-null within this expression).
 ///
 /// Creates a new lifted instruction without modifying the input instruction.
 /// On success, returns (new lifted instruction, bitset).
 /// If lifting fails, returns (null, null).
 ///
 /// The returned bitset specifies which nullableVars were considered "relevant" for this instruction.
 /// bitSet[i] == true means nullableVars[i] was relevant.
 ///
 /// The new lifted instruction will have equivalent semantics to the input instruction
 /// if all relevant variables are non-null [except that the result will be wrapped in a Nullable{T} struct].
 /// If any relevant variable is null, the new instruction is guaranteed to evaluate to <c>null</c>
 /// without having any other effect.
 /// </summary>
 (ILInstruction, BitSet) DoLift(ILInstruction inst)
 {
     if (MatchGetValueOrDefault(inst, out ILVariable inputVar))
     {
         // n.GetValueOrDefault() lifted => n.
         BitSet foundIndices = new BitSet(nullableVars.Count);
         for (int i = 0; i < nullableVars.Count; i++)
         {
             if (nullableVars[i] == inputVar)
             {
                 foundIndices[i] = true;
             }
         }
         if (foundIndices.Any())
         {
             return(new LdLoc(inputVar)
             {
                 ILRange = inst.ILRange
             }, foundIndices);
         }
         else
         {
             return(null, null);
         }
     }
     else if (inst is Conv conv)
     {
         var(arg, bits) = DoLift(conv.Argument);
         if (arg != null)
         {
             if (conv.HasFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count))
             {
                 // Cannot execute potentially-throwing instruction unless all
                 // the nullableVars are arguments to the instruction
                 // (thus causing it not to throw when any of them is null).
                 return(null, null);
             }
             var newInst = new Conv(arg, conv.InputType, conv.InputSign, conv.TargetType, conv.CheckForOverflow, isLifted: true)
             {
                 ILRange = conv.ILRange
             };
             return(newInst, bits);
         }
     }
     else if (inst is BitNot bitnot)
     {
         var(arg, bits) = DoLift(bitnot.Argument);
         if (arg != null)
         {
             var newInst = new BitNot(arg, isLifted: true, stackType: bitnot.ResultType)
             {
                 ILRange = bitnot.ILRange
             };
             return(newInst, bits);
         }
     }
     else if (inst is BinaryNumericInstruction binary)
     {
         var(left, right, bits) = DoLiftBinary(binary.Left, binary.Right);
         if (left != null && right != null)
         {
             if (binary.HasFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count))
             {
                 // Cannot execute potentially-throwing instruction unless all
                 // the nullableVars are arguments to the instruction
                 // (thus causing it not to throw when any of them is null).
                 return(null, null);
             }
             var newInst = new BinaryNumericInstruction(
                 binary.Operator, left, right,
                 binary.LeftInputType, binary.RightInputType,
                 binary.CheckForOverflow, binary.Sign,
                 isLifted: true
                 )
             {
                 ILRange = binary.ILRange
             };
             return(newInst, bits);
         }
     }
     else if (inst is Comp comp && !comp.IsLifted && comp.Kind == ComparisonKind.Equality &&
              MatchGetValueOrDefault(comp.Left, out ILVariable v) &&
              NullableType.GetUnderlyingType(v.Type).IsKnownType(KnownTypeCode.Boolean) &&
              comp.Right.MatchLdcI4(0)
              )
     {
         // C# doesn't support ComparisonLiftingKind.ThreeValuedLogic,
         // except for operator! on bool?.
         var(arg, bits) = DoLift(comp.Left);
         Debug.Assert(arg != null);
         var newInst = new Comp(comp.Kind, ComparisonLiftingKind.ThreeValuedLogic, comp.InputType, comp.Sign, arg, comp.Right.Clone())
         {
             ILRange = comp.ILRange
         };
         return(newInst, bits);
     }
        /// <code>
        /// stloc s(ldflda)
        /// stloc s2(ldobj(ldflda(ldloc s)))
        /// stloc l(ldloc s2)
        /// stobj (ldflda(ldloc s), binary.add(ldloc s2, ldc.i4 1))
        /// -->
        /// stloc l(compound.op.old(ldobj(ldflda(ldflda)), ldc.i4 1))
        /// </code>
        bool TransformCSharp4PostIncDecOperatorOnAddress(Block block, int i)
        {
            var baseFieldAddress      = block.Instructions[i] as StLoc;
            var fieldValue            = block.Instructions.ElementAtOrDefault(i + 1) as StLoc;
            var fieldValueCopyToLocal = block.Instructions.ElementAtOrDefault(i + 2) as StLoc;
            var stobj = block.Instructions.ElementAtOrDefault(i + 3) as StObj;

            if (baseFieldAddress == null || fieldValue == null || fieldValueCopyToLocal == null || stobj == null)
            {
                return(false);
            }
            if (baseFieldAddress.Variable.Kind != VariableKind.StackSlot || fieldValue.Variable.Kind != VariableKind.StackSlot || fieldValueCopyToLocal.Variable.Kind != VariableKind.Local)
            {
                return(false);
            }
            IType         t;
            IField        targetField;
            ILInstruction targetFieldLoad, baseFieldAddressLoad2;

            if (!fieldValue.Value.MatchLdObj(out targetFieldLoad, out t))
            {
                return(false);
            }
            ILInstruction baseAddress;

            if (baseFieldAddress.Value is LdFlda)
            {
                IField        targetField2;
                ILInstruction baseFieldAddressLoad3;
                if (!targetFieldLoad.MatchLdFlda(out baseFieldAddressLoad2, out targetField) || !baseFieldAddressLoad2.MatchLdLoc(baseFieldAddress.Variable))
                {
                    return(false);
                }
                if (!stobj.Target.MatchLdFlda(out baseFieldAddressLoad3, out targetField2) || !baseFieldAddressLoad3.MatchLdLoc(baseFieldAddress.Variable) || !IsSameMember(targetField, targetField2))
                {
                    return(false);
                }
                baseAddress = new LdFlda(baseFieldAddress.Value, targetField);
            }
            else if (baseFieldAddress.Value is LdElema)
            {
                if (!targetFieldLoad.MatchLdLoc(baseFieldAddress.Variable) || !stobj.Target.MatchLdLoc(baseFieldAddress.Variable))
                {
                    return(false);
                }
                baseAddress = baseFieldAddress.Value;
            }
            else
            {
                return(false);
            }
            BinaryNumericInstruction binary = stobj.Value as BinaryNumericInstruction;

            if (binary == null || !binary.Left.MatchLdLoc(fieldValue.Variable) || !binary.Right.MatchLdcI4(1) ||
                (binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub))
            {
                return(false);
            }
            context.Step($"TransformCSharp4PostIncDecOperatorOnAddress", baseFieldAddress);
            var assignment = new CompoundAssignmentInstruction(binary, new LdObj(baseAddress, t), binary.Right, t, CompoundAssignmentType.EvaluatesToOldValue);

            stobj.ReplaceWith(new StLoc(fieldValueCopyToLocal.Variable, assignment));
            block.Instructions.RemoveAt(i + 2);
            block.Instructions.RemoveAt(i + 1);
            return(true);
        }
Exemple #10
0
 /// <summary>
 /// Recursive function that lifts the specified instruction.
 /// The input instruction is expected to a subexpression of the trueInst
 /// (so that all nullableVars are guaranteed non-null within this expression).
 ///
 /// Creates a new lifted instruction without modifying the input instruction.
 /// On success, returns (new lifted instruction, bitset).
 /// If lifting fails, returns (null, null).
 ///
 /// The returned bitset specifies which nullableVars were considered "relevant" for this instruction.
 /// bitSet[i] == true means nullableVars[i] was relevant.
 ///
 /// The new lifted instruction will have equivalent semantics to the input instruction
 /// if all relevant variables are non-null [except that the result will be wrapped in a Nullable{T} struct].
 /// If any relevant variable is null, the new instruction is guaranteed to evaluate to <c>null</c>
 /// without having any other effect.
 /// </summary>
 (ILInstruction, BitSet) DoLift(ILInstruction inst)
 {
     if (MatchGetValueOrDefault(inst, out ILVariable inputVar))
     {
         // n.GetValueOrDefault() lifted => n.
         BitSet foundIndices = new BitSet(nullableVars.Count);
         for (int i = 0; i < nullableVars.Count; i++)
         {
             if (nullableVars[i] == inputVar)
             {
                 foundIndices[i] = true;
             }
         }
         if (foundIndices.Any())
         {
             return(new LdLoc(inputVar)
             {
                 ILRange = inst.ILRange
             }, foundIndices);
         }
         else
         {
             return(null, null);
         }
     }
     else if (inst is Conv conv)
     {
         var(arg, bits) = DoLift(conv.Argument);
         if (arg != null)
         {
             if (conv.HasFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count))
             {
                 // Cannot execute potentially-throwing instruction unless all
                 // the nullableVars are arguments to the instruction
                 // (thus causing it not to throw when any of them is null).
                 return(null, null);
             }
             var newInst = new Conv(arg, conv.InputType, conv.InputSign, conv.TargetType, conv.CheckForOverflow, isLifted: true)
             {
                 ILRange = conv.ILRange
             };
             return(newInst, bits);
         }
     }
     else if (inst is BinaryNumericInstruction binary)
     {
         var(left, leftBits)   = DoLift(binary.Left);
         var(right, rightBits) = DoLift(binary.Right);
         if (left != null && right == null && SemanticHelper.IsPure(binary.Right.Flags))
         {
             // Embed non-nullable pure expression in lifted expression.
             right = binary.Right.Clone();
         }
         if (left == null && right != null && SemanticHelper.IsPure(binary.Left.Flags))
         {
             // Embed non-nullable pure expression in lifted expression.
             left = binary.Left.Clone();
         }
         if (left != null && right != null)
         {
             var bits = leftBits ?? rightBits;
             if (rightBits != null)
             {
                 bits.UnionWith(rightBits);
             }
             if (binary.HasFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count))
             {
                 // Cannot execute potentially-throwing instruction unless all
                 // the nullableVars are arguments to the instruction
                 // (thus causing it not to throw when any of them is null).
                 return(null, null);
             }
             var newInst = new BinaryNumericInstruction(
                 binary.Operator, left, right,
                 binary.LeftInputType, binary.RightInputType,
                 binary.CheckForOverflow, binary.Sign,
                 isLifted: true
                 )
             {
                 ILRange = binary.ILRange
             };
             return(newInst, bits);
         }
     }
     return(null, null);
 }