/// <summary>
        /// if (comp(ldloc v == ldnull)) {
        ///     stloc v(DelegateConstruction)
        /// }
        /// =>
        /// stloc v(DelegateConstruction)
        /// </summary>
        bool CachedDelegateInitializationWithLocal(IfInstruction inst)
        {
            Block trueInst = inst.TrueInst as Block;

            if (trueInst == null || (trueInst.Instructions.Count != 1) || !inst.FalseInst.MatchNop())
            {
                return(false);
            }
            if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdLoc(out ILVariable v) || !right.MatchLdNull())
            {
                return(false);
            }
            var storeInst = trueInst.Instructions.Last();

            if (!storeInst.MatchStLoc(v, out ILInstruction value))
            {
                return(false);
            }
            if (!DelegateConstruction.IsDelegateConstruction(value as NewObj, true))
            {
                return(false);
            }
            // do not transform if there are other stores/loads of this variable
            if (v.StoreCount != 2 || v.StoreInstructions.Count != 2 || v.LoadCount != 2 || v.AddressCount != 0)
            {
                return(false);
            }
            // do not transform if the first assignment is not assigning null:
            var otherStore = v.StoreInstructions.OfType <StLoc>().SingleOrDefault(store => store != storeInst);

            if (otherStore == null || !otherStore.Value.MatchLdNull() || !(otherStore.Parent is Block))
            {
                return(false);
            }
            // do not transform if there is no usage directly afterwards
            var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1);

            if (nextInstruction == null)
            {
                return(false);
            }
            var usages = nextInstruction.Descendants.Where(i => i.MatchLdLoc(v)).ToArray();

            if (usages.Length != 1)
            {
                return(false);
            }
            context.Step("CachedDelegateInitializationWithLocal", inst);
            ((Block)otherStore.Parent).Instructions.Remove(otherStore);
            inst.ReplaceWith(storeInst);
            return(true);
        }
Exemplo n.º 2
0
        public virtual void VisitIf(IfInstruction x)
        {
            x.Condition.VisitMe(this);

            x.IfBranch.VisitMe(this);

            if (x.ElseBranch != null)
            {
                x.ElseBranch.VisitMe(this);
            }

            VisitInstruction(x);
        }
Exemplo n.º 3
0
        protected internal override void VisitIfInstruction(IfInstruction inst)
        {
            DebugStartPoint(inst);
            inst.Condition.AcceptVisitor(this);
            State branchState = state.Clone();

            inst.TrueInst.AcceptVisitor(this);
            State afterTrueState = state;

            state = branchState;
            inst.FalseInst.AcceptVisitor(this);
            state.JoinWith(afterTrueState);
            DebugEndPoint(inst);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Builds structured control flow for the block associated with the control flow node.
        /// </summary>
        /// <remarks>
        /// After a block was processed, it should use structured control flow
        /// and have just a single 'regular' exit point (last branch instruction in the block)
        /// </remarks>
        public void Run(Block block, BlockTransformContext context)
        {
            this.context          = context;
            this.currentContainer = (BlockContainer)block.Parent;

            // We only embed blocks into this block if they aren't referenced anywhere else,
            // so those blocks are dominated by this block.
            // BlockILTransform thus guarantees that the blocks being embedded are already
            // fully processed.

            var cfgNode = context.ControlFlowNode;

            Debug.Assert(cfgNode.UserData == block);

            // Because this transform runs at the beginning of the block transforms,
            // we know that `block` is still a (non-extended) basic block.

            // Last instruction is one with unreachable endpoint
            // (guaranteed by combination of BlockContainer and Block invariants)
            Debug.Assert(block.Instructions.Last().HasFlag(InstructionFlags.EndPointUnreachable));
            ILInstruction exitInst = block.Instructions.Last();

            // Previous-to-last instruction might have conditional control flow,
            // usually an IfInstruction with a branch:
            IfInstruction ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction;

            if (ifInst != null && ifInst.FalseInst.OpCode == OpCode.Nop)
            {
                HandleIfInstruction(cfgNode, block, ifInst, ref exitInst);
            }
            else
            {
                SwitchInstruction switchInst = block.Instructions.SecondToLastOrDefault() as SwitchInstruction;
                if (switchInst != null)
                {
                    HandleSwitchInstruction(cfgNode, block, switchInst, ref exitInst);
                }
            }
            if (IsUsableBranchToChild(cfgNode, exitInst))
            {
                // "...; goto usableblock;"
                // -> embed target block in this block
                context.Step("Inline target block of unconditional branch", exitInst);
                var targetBlock = ((Branch)exitInst).TargetBlock;
                Debug.Assert(exitInst == block.Instructions.Last());
                block.Instructions.RemoveAt(block.Instructions.Count - 1);
                block.Instructions.AddRange(targetBlock.Instructions);
                targetBlock.Remove();
            }
        }
Exemplo n.º 5
0
        public void TestValues()
        {
            VirtualMachine vm = new VirtualMachine();

            List <byte> actual = new IfInstruction()
                                 .Condition(_condition)
                                 .Body(b =>
            {
                b.Literal(_val);
            })
                                 .ToInstructions();

            TestHelper.AssertResultsEqual(_expected, actual);
        }
Exemplo n.º 6
0
        public void TestExpressions()
        {
            VirtualMachine vm = new VirtualMachine();

            List <byte> actual = new IfInstruction()
                                 .Condition(new LiteralInstruction((byte)(_condition ? 1 : 0)))
                                 .Body(b =>
            {
                b.Literal(_val);
            })
                                 .ToInstructions();

            TestHelper.AssertResultsEqual(_expected, actual);
        }
Exemplo n.º 7
0
 /// <summary>
 /// if (comp.i4(comp.o(ldobj delegateType(ldsflda CachedAnonMethodDelegate) != ldnull) == ldc.i4 0)) Block {
 ///     stloc s(stobj(ldflda(CachedAnonMethodDelegate), DelegateConstruction))
 /// } else Block {
 ///     stloc s(ldobj System.Action(ldsflda $I4-1))
 /// }
 /// =>
 ///	stloc s(DelegateConstruction)
 /// </summary>
 bool CachedDelegateInitializationVB(IfInstruction inst)
 {
     if (!(inst.TrueInst is Block trueInst && inst.FalseInst is Block falseInst))
     {
         return(false);
     }
     if (trueInst.Instructions.Count != 1 || falseInst.Instructions.Count != 1)
     {
         return(false);
     }
     if (!(trueInst.Instructions[0].MatchStLoc(out var s, out var trueInitValue) &&
           falseInst.Instructions[0].MatchStLoc(s, out var falseInitValue)))
     {
         return(false);
     }
     if (s.Kind != VariableKind.StackSlot || s.StoreCount != 2 || s.LoadCount != 1)
     {
         return(false);
     }
     if (!(trueInitValue is StObj stobj) || !(falseInitValue is LdObj ldobj))
     {
         return(false);
     }
     if (!(stobj.Value is NewObj delegateConstruction))
     {
         return(false);
     }
     if (!stobj.Target.MatchLdsFlda(out var field1) ||
         !ldobj.Target.MatchLdsFlda(out var field2) ||
         !field1.Equals(field2))
     {
         return(false);
     }
     if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !right.MatchLdNull())
     {
         return(false);
     }
     if (!ldobj.Match(left).Success)
     {
         return(false);
     }
     if (!DelegateConstruction.MatchDelegateConstruction(delegateConstruction, out _, out _, out _, true))
     {
         return(false);
     }
     context.Step("CachedDelegateInitializationVB", inst);
     inst.ReplaceWith(new StLoc(s, delegateConstruction));
     return(true);
 }
Exemplo n.º 8
0
        public void TestNoConditionOrBody()
        {
            VirtualMachine vm = new VirtualMachine();

            _expected = new List <byte>()
            {
                (byte)InstructionsEnum.If,
                (byte)InstructionsEnum.EndIf
            };

            List <byte> actual = new IfInstruction()
                                 .ToInstructions();

            TestHelper.AssertResultsEqual(_expected, actual);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Main entry point into the normal code path of this transform.
        /// Called by expression transform.
        /// </summary>
        public bool Run(IfInstruction ifInst)
        {
            if (!context.Settings.LiftNullables)
            {
                return(false);
            }
            var lifted = Lift(ifInst, ifInst.TrueInst, ifInst.FalseInst);

            if (lifted != null)
            {
                ifInst.ReplaceWith(lifted);
                return(true);
            }
            return(false);
        }
Exemplo n.º 10
0
        void VisitLogicNot(Comp inst, ILInstruction arg)
        {
            ILInstruction lhs, rhs;

            if (arg is Comp comp)
            {
                if ((!comp.InputType.IsFloatType() && !comp.IsLifted) || comp.Kind.IsEqualityOrInequality())
                {
                    context.Step("push negation into comparison", inst);
                    comp.Kind = comp.Kind.Negate();
                    comp.AddILRange(inst);
                    inst.ReplaceWith(comp);
                }
                comp.AcceptVisitor(this);
            }
            else if (arg.MatchLogicAnd(out lhs, out rhs))
            {
                // logic.not(if (lhs) rhs else ldc.i4 0)
                // ==> if (logic.not(lhs)) ldc.i4 1 else logic.not(rhs)
                context.Step("push negation into logic.and", inst);
                IfInstruction ifInst = (IfInstruction)arg;
                var           ldc0   = ifInst.FalseInst;
                Debug.Assert(ldc0.MatchLdcI4(0));
                ifInst.Condition = Comp.LogicNot(lhs).WithILRange(inst);
                ifInst.TrueInst  = new LdcI4(1).WithILRange(ldc0);
                ifInst.FalseInst = Comp.LogicNot(rhs).WithILRange(inst);
                inst.ReplaceWith(ifInst);
                ifInst.AcceptVisitor(this);
            }
            else if (arg.MatchLogicOr(out lhs, out rhs))
            {
                // logic.not(if (lhs) ldc.i4 1 else rhs)
                // ==> if (logic.not(lhs)) logic.not(rhs) else ldc.i4 0)
                context.Step("push negation into logic.or", inst);
                IfInstruction ifInst = (IfInstruction)arg;
                var           ldc1   = ifInst.TrueInst;
                Debug.Assert(ldc1.MatchLdcI4(1));
                ifInst.Condition = Comp.LogicNot(lhs).WithILRange(inst);
                ifInst.TrueInst  = Comp.LogicNot(rhs).WithILRange(inst);
                ifInst.FalseInst = new LdcI4(0).WithILRange(ldc1);
                inst.ReplaceWith(ifInst);
                ifInst.AcceptVisitor(this);
            }
            else
            {
                arg.AcceptVisitor(this);
            }
        }
Exemplo n.º 11
0
        ILInstruction Lift(IfInstruction ifInst, ILInstruction trueInst, ILInstruction falseInst)
        {
            if (!MatchNullableCtor(trueInst, out var utype, out var exprToLift))
            {
                return(null);
            }
            if (!MatchNull(falseInst, utype))
            {
                return(null);
            }
            ILInstruction lifted;

            if (nullableVars.Count == 1 && MatchGetValueOrDefault(exprToLift, nullableVars[0]))
            {
                // v != null ? call GetValueOrDefault(ldloca v) : null
                // => conv.nop.lifted(ldloc v)
                // This case is handled separately from DoLift() because
                // that doesn't introduce nop-conversions.
                context.Step("if => conv.nop.lifted", ifInst);
                var inputUType = NullableType.GetUnderlyingType(nullableVars[0].Type);
                lifted = new Conv(
                    new LdLoc(nullableVars[0]),
                    inputUType.GetStackType(), inputUType.GetSign(), utype.ToPrimitiveType(),
                    checkForOverflow: false,
                    isLifted: true
                    )
                {
                    ILRange = ifInst.ILRange
                };
            }
            else
            {
                context.Step("NullableLiftingTransform.DoLift", ifInst);
                BitSet bits;
                (lifted, bits) = DoLift(exprToLift);
                if (lifted != null && !bits.All(0, nullableVars.Count))
                {
                    // don't lift if a nullableVar doesn't contribute to the result
                    lifted = null;
                }
            }
            if (lifted != null)
            {
                Debug.Assert(lifted is ILiftableInstruction liftable && liftable.IsLifted &&
                             liftable.UnderlyingResultType == exprToLift.ResultType);
            }
            return(lifted);
        }
Exemplo n.º 12
0
        protected internal override void VisitIfInstruction(IfInstruction inst)
        {
            inst.TrueInst.AcceptVisitor(this);
            inst.FalseInst.AcceptVisitor(this);
            inst = HandleConditionalOperator(inst);

            // Bring LogicAnd/LogicOr into their canonical forms:
            // if (cond) ldc.i4 0 else RHS --> if (!cond) RHS else ldc.i4 0
            // if (cond) RHS else ldc.i4 1 --> if (!cond) ldc.i4 1 else RHS
            // Be careful: when both LHS and RHS are the constant 1, we must not
            // swap the arguments as it would lead to an infinite transform loop.
            if (inst.TrueInst.MatchLdcI4(0) && !inst.FalseInst.MatchLdcI4(0) ||
                inst.FalseInst.MatchLdcI4(1) && !inst.TrueInst.MatchLdcI4(1))
            {
                context.Step("canonicalize logic and/or", inst);
                var t = inst.TrueInst;
                inst.TrueInst  = inst.FalseInst;
                inst.FalseInst = t;
                inst.Condition = Comp.LogicNot(inst.Condition);
            }
            // Process condition after our potential modifications.
            inst.Condition.AcceptVisitor(this);

            if (new NullableLiftingTransform(context).Run(inst))
            {
                return;
            }

            if (TransformDynamicAddAssignOrRemoveAssign(inst))
            {
                return;
            }
            if (inst.MatchIfInstructionPositiveCondition(out var condition, out var trueInst, out var falseInst))
            {
                ILInstruction transformed = UserDefinedLogicTransform.Transform(condition, trueInst, falseInst);
                if (transformed == null)
                {
                    transformed = UserDefinedLogicTransform.TransformDynamic(condition, trueInst, falseInst);
                }
                if (transformed != null)
                {
                    context.Step("User-defined short-circuiting logic operator (roslyn pattern)", condition);
                    transformed.AddILRange(inst);
                    inst.ReplaceWith(transformed);
                    return;
                }
            }
        }
Exemplo n.º 13
0
        /// <summary>
        ///   if (...) br trueBlock;
        /// ->
        ///   if (...) { trueBlock... }
        ///
        /// Only inlines branches that are strictly dominated by this block (incoming edge count == 1)
        /// </summary>
        private bool InlineTrueBranch(IfInstruction ifInst)
        {
            if (!CanInline(ifInst.TrueInst))
            {
                return(false);
            }

            context.Step("Inline block as then-branch", ifInst.TrueInst);
            // The targetBlock was already processed, and is ready to embed
            var targetBlock = ((Branch)ifInst.TrueInst).TargetBlock;

            targetBlock.Remove();
            ifInst.TrueInst = targetBlock;

            return(true);
        }
Exemplo n.º 14
0
        private ILInstruction CombineConditions(ILInstruction[] conditions)
        {
            ILInstruction condition = null;

            foreach (var c in conditions)
            {
                if (condition == null)
                {
                    condition = Comp.LogicNot(c);
                }
                else
                {
                    condition = IfInstruction.LogicAnd(Comp.LogicNot(c), condition);
                }
            }
            return(condition);
        }
Exemplo n.º 15
0
        protected internal override void VisitIfInstruction(IfInstruction inst)
        {
            // Bring LogicAnd/LogicOr into their canonical forms:
            // if (cond) ldc.i4 0 else RHS --> if (!cond) RHS else ldc.i4 0
            // if (cond) RHS else ldc.i4 1 --> if (!cond) ldc.i4 1 else RHS
            // Be careful: when both LHS and RHS are the constant 1, we must not
            // swap the arguments as it would lead to an infinite transform loop.
            if (inst.TrueInst.MatchLdcI4(0) && !inst.FalseInst.MatchLdcI4(0) ||
                inst.FalseInst.MatchLdcI4(1) && !inst.TrueInst.MatchLdcI4(1))
            {
                context.Step("canonicalize logic and/or", inst);
                var t = inst.TrueInst;
                inst.TrueInst  = inst.FalseInst;
                inst.FalseInst = t;
                inst.Condition = new LogicNot(inst.Condition);
            }

            base.VisitIfInstruction(inst);

            // if (cond) stloc (A, V1) else stloc (A, V2) --> stloc (A, if (cond) V1 else V2)
            Block trueInst = inst.TrueInst as Block;

            if (trueInst == null || trueInst.Instructions.Count != 1)
            {
                return;
            }
            Block falseInst = inst.FalseInst as Block;

            if (falseInst == null || falseInst.Instructions.Count != 1)
            {
                return;
            }
            ILVariable    v;
            ILInstruction value1, value2;

            if (trueInst.Instructions[0].MatchStLoc(out v, out value1) && falseInst.Instructions[0].MatchStLoc(v, out value2))
            {
                context.Step("conditional operator", inst);
                inst.ReplaceWith(new StLoc(v, new IfInstruction(new LogicNot(inst.Condition), value2, value1)));
            }
        }
        /// <summary>
        /// stloc s(ldobj(ldflda(CachedAnonMethodDelegate))
        /// if (comp(ldloc s == null)) {
        ///		stloc s(stobj(ldflda(CachedAnonMethodDelegate), DelegateConstruction))
        ///	}
        ///	=>
        ///	stloc s(DelegateConstruction)
        /// </summary>
        bool CachedDelegateInitializationRoslynWithLocal(IfInstruction inst)
        {
            Block trueInst = inst.TrueInst as Block;

            if (trueInst == null || (trueInst.Instructions.Count != 1) || !inst.FalseInst.MatchNop())
            {
                return(false);
            }
            if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdLoc(out ILVariable s) || !right.MatchLdNull())
            {
                return(false);
            }
            var storeInst     = trueInst.Instructions.Last() as StLoc;
            var storeBeforeIf = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex - 1) as StLoc;

            if (storeBeforeIf == null || storeInst == null || storeBeforeIf.Variable != s || storeInst.Variable != s)
            {
                return(false);
            }
            if (!(storeInst.Value is StObj stobj) || !(storeBeforeIf.Value is LdObj ldobj))
            {
                return(false);
            }
            if (!(stobj.Value is NewObj))
            {
                return(false);
            }
            if (!stobj.Target.MatchLdFlda(out var _, out var field1) || !ldobj.Target.MatchLdFlda(out var __, out var field2) || !field1.Equals(field2))
            {
                return(false);
            }
            if (!DelegateConstruction.IsDelegateConstruction((NewObj)stobj.Value, true))
            {
                return(false);
            }
            context.Step("CachedDelegateInitializationRoslynWithLocal", inst);
            storeBeforeIf.Value = stobj.Value;
            return(true);
        }
Exemplo n.º 17
0
        public override void VisitIf(IfInstruction x)
        {
            var condition = getValue(x.Condition);

            //TODO needs to be compiled as satement
            var tmpVariable = _compiler.GetTemporaryVariable(x.ReturnType);
            var ifBranch    = compileBranch(x.IfBranch, tmpVariable);
            var elseBranch  = compileBranch(x.ElseBranch, tmpVariable);

            Expression ifStatement;

            if (elseBranch == null)
            {
                ifStatement = Expression.IfThen(condition, ifBranch);
            }
            else
            {
                ifStatement = Expression.IfThenElse(condition, ifBranch, elseBranch);
            }

            emit(Expression.Block(ifStatement, tmpVariable));
        }
Exemplo n.º 18
0
        protected internal override void VisitIfInstruction(IfInstruction inst)
        {
            // Bring LogicAnd/LogicOr into their canonical forms:
            // if (cond) ldc.i4 0 else RHS --> if (!cond) RHS else ldc.i4 0
            // if (cond) RHS else ldc.i4 1 --> if (!cond) ldc.i4 1 else RHS
            // Be careful: when both LHS and RHS are the constant 1, we must not
            // swap the arguments as it would lead to an infinite transform loop.
            if (inst.TrueInst.MatchLdcI4(0) && !inst.FalseInst.MatchLdcI4(0) ||
                inst.FalseInst.MatchLdcI4(1) && !inst.TrueInst.MatchLdcI4(1))
            {
                context.Step("canonicalize logic and/or", inst);
                var t = inst.TrueInst;
                inst.TrueInst  = inst.FalseInst;
                inst.FalseInst = t;
                inst.Condition = new LogicNot(inst.Condition);
            }

            base.VisitIfInstruction(inst);

            inst = HandleConditionalOperator(inst);
            new NullableLiftingTransform(context).Run(inst);
        }
        /// <summary>
        /// if (comp(ldsfld CachedAnonMethodDelegate == ldnull)) {
        ///     stsfld CachedAnonMethodDelegate(DelegateConstruction)
        /// }
        /// ... one usage of CachedAnonMethodDelegate ...
        /// =>
        /// ... one usage of DelegateConstruction ...
        /// </summary>
        bool CachedDelegateInitializationWithField(IfInstruction inst)
        {
            Block trueInst = inst.TrueInst as Block;

            if (trueInst == null || trueInst.Instructions.Count != 1 || !inst.FalseInst.MatchNop())
            {
                return(false);
            }
            var storeInst = trueInst.Instructions[0];

            if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdsFld(out IField field) || !right.MatchLdNull())
            {
                return(false);
            }
            if (!storeInst.MatchStsFld(out IField field2, out ILInstruction value) || !field.Equals(field2) || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
            {
                return(false);
            }
            if (!DelegateConstruction.IsDelegateConstruction(value as NewObj, true))
            {
                return(false);
            }
            var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1);

            if (nextInstruction == null)
            {
                return(false);
            }
            var usages = nextInstruction.Descendants.Where(i => i.MatchLdsFld(field)).ToArray();

            if (usages.Length != 1)
            {
                return(false);
            }
            context.Step("CachedDelegateInitializationWithField", inst);
            usages[0].ReplaceWith(value);
            return(true);
        }
Exemplo n.º 20
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);
        }
Exemplo n.º 21
0
        protected internal override void VisitComp(Comp inst)
        {
            // "logic.not(arg)" is sugar for "comp(arg != ldc.i4 0)"
            if (inst.MatchLogicNot(out var arg))
            {
                VisitLogicNot(inst, arg);
                return;
            }
            else if (inst.Kind == ComparisonKind.Inequality && inst.LiftingKind == ComparisonLiftingKind.None &&
                     inst.Right.MatchLdcI4(0) && (IfInstruction.IsInConditionSlot(inst) || inst.Left is Comp)
                     )
            {
                // if (comp(x != 0)) ==> if (x)
                // comp(comp(...) != 0) => comp(...)
                context.Step("Remove redundant comp(... != 0)", inst);
                inst.Left.AddILRange(inst);
                inst.ReplaceWith(inst.Left);
                inst.Left.AcceptVisitor(this);
                return;
            }

            base.VisitComp(inst);
            if (inst.IsLifted)
            {
                return;
            }
            if (inst.Right.MatchLdNull())
            {
                if (inst.Kind == ComparisonKind.GreaterThan)
                {
                    context.Step("comp(left > ldnull)  => comp(left != ldnull)", inst);
                    inst.Kind = ComparisonKind.Inequality;
                }
                else if (inst.Kind == ComparisonKind.LessThanOrEqual)
                {
                    context.Step("comp(left <= ldnull) => comp(left == ldnull)", inst);
                    inst.Kind = ComparisonKind.Equality;
                }
            }
            else if (inst.Left.MatchLdNull())
            {
                if (inst.Kind == ComparisonKind.LessThan)
                {
                    context.Step("comp(ldnull < right)  => comp(ldnull != right)", inst);
                    inst.Kind = ComparisonKind.Inequality;
                }
                else if (inst.Kind == ComparisonKind.GreaterThanOrEqual)
                {
                    context.Step("comp(ldnull >= right) => comp(ldnull == right)", inst);
                    inst.Kind = ComparisonKind.Equality;
                }
            }

            var rightWithoutConv = inst.Right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend);

            if (rightWithoutConv.MatchLdcI4(0) &&
                inst.Sign == Sign.Unsigned &&
                (inst.Kind == ComparisonKind.GreaterThan || inst.Kind == ComparisonKind.LessThanOrEqual))
            {
                if (inst.Kind == ComparisonKind.GreaterThan)
                {
                    context.Step("comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)", inst);
                    inst.Kind = ComparisonKind.Inequality;
                    VisitComp(inst);
                    return;
                }
                else if (inst.Kind == ComparisonKind.LessThanOrEqual)
                {
                    context.Step("comp.unsigned(left <= ldc.i4 0) => comp(left == ldc.i4 0)", inst);
                    inst.Kind = ComparisonKind.Equality;
                    VisitComp(inst);
                    return;
                }
            }
            else if (rightWithoutConv.MatchLdcI4(0) && inst.Kind.IsEqualityOrInequality())
            {
                if (inst.Left.MatchLdLen(StackType.I, out ILInstruction array))
                {
                    // comp.unsigned(ldlen array == conv i4->i(ldc.i4 0))
                    // => comp(ldlen.i4 array == ldc.i4 0)
                    // This is a special case where the C# compiler doesn't generate conv.i4 after ldlen.
                    context.Step("comp(ldlen.i4 array == ldc.i4 0)", inst);
                    inst.InputType = StackType.I4;
                    inst.Left.ReplaceWith(new LdLen(StackType.I4, array).WithILRange(inst.Left));
                    inst.Right = rightWithoutConv;
                }
                else if (inst.Left is Conv conv && conv.TargetType == PrimitiveType.I && conv.Argument.ResultType == StackType.O)
                {
                    // C++/CLI sometimes uses this weird comparison with null:
                    context.Step("comp(conv o->i (ldloc obj) == conv i4->i <sign extend>(ldc.i4 0))", inst);
                    // -> comp(ldloc obj == ldnull)
                    inst.InputType = StackType.O;
                    inst.Left      = conv.Argument;
                    inst.Right     = new LdNull().WithILRange(inst.Right);
                    inst.Right.AddILRange(rightWithoutConv);
                }
            }

            if (inst.Right.MatchLdNull() && inst.Left.MatchBox(out arg, out var type) && type.Kind == TypeKind.TypeParameter)
            {
                if (inst.Kind == ComparisonKind.Equality)
                {
                    context.Step("comp(box T(..) == ldnull) -> comp(.. == ldnull)", inst);
                    inst.Left = arg;
                }
                if (inst.Kind == ComparisonKind.Inequality)
                {
                    context.Step("comp(box T(..) != ldnull) -> comp(.. != ldnull)", inst);
                    inst.Left = arg;
                }
            }
        }
Exemplo n.º 22
0
        /// <summary>
        /// op is either add or remove/subtract:
        /// if (dynamic.isevent (target)) {
        ///     dynamic.invokemember.invokespecial.discard op_Name(target, value)
        /// } else {
        ///     dynamic.compound.op (dynamic.getmember Name(target), value)
        /// }
        /// =>
        /// dynamic.compound.op (dynamic.getmember Name(target), value)
        /// </summary>
        bool TransformDynamicAddAssignOrRemoveAssign(IfInstruction inst)
        {
            if (!inst.MatchIfInstructionPositiveCondition(out var condition, out var trueInst, out var falseInst))
            {
                return(false);
            }
            if (!(condition is DynamicIsEventInstruction isEvent))
            {
                return(false);
            }
            trueInst  = Block.Unwrap(trueInst);
            falseInst = Block.Unwrap(falseInst);
            if (!(falseInst is DynamicCompoundAssign dynamicCompoundAssign))
            {
                return(false);
            }
            if (!(dynamicCompoundAssign.Target is DynamicGetMemberInstruction getMember))
            {
                return(false);
            }
            if (!SemanticHelper.IsPure(isEvent.Argument.Flags))
            {
                return(false);
            }
            if (!isEvent.Argument.Match(getMember.Target).Success)
            {
                return(false);
            }
            if (!(trueInst is DynamicInvokeMemberInstruction invokeMember))
            {
                return(false);
            }
            if (!(invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSpecialName) && invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.ResultDiscarded)))
            {
                return(false);
            }
            switch (dynamicCompoundAssign.Operation)
            {
            case ExpressionType.AddAssign:
                if (invokeMember.Name != "add_" + getMember.Name)
                {
                    return(false);
                }
                break;

            case ExpressionType.SubtractAssign:
                if (invokeMember.Name != "remove_" + getMember.Name)
                {
                    return(false);
                }
                break;

            default:
                return(false);
            }
            if (!dynamicCompoundAssign.Value.Match(invokeMember.Arguments[1]).Success)
            {
                return(false);
            }
            if (!invokeMember.Arguments[0].Match(getMember.Target).Success)
            {
                return(false);
            }
            context.Step("+= / -= dynamic.isevent pattern -> dynamic.compound.op", inst);
            inst.ReplaceWith(dynamicCompoundAssign);
            return(true);
        }
Exemplo n.º 23
0
 protected override void SetMyBlockInternalArg()
 {
     myBlockInternalArg = new IfInstruction(this);
 }
Exemplo n.º 24
0
        protected internal override void VisitLogicNot(LogicNot inst)
        {
            ILInstruction arg, lhs, rhs;

            if (inst.Argument.MatchLogicNot(out arg))
            {
                context.Step("logic.not(logic.not(arg)) => arg", inst);
                Debug.Assert(arg.ResultType == StackType.I4);
                arg.AddILRange(inst.ILRange);
                arg.AddILRange(inst.Argument.ILRange);
                inst.ReplaceWith(arg);
                arg.AcceptVisitor(this);
            }
            else if (inst.Argument is Comp comp)
            {
                if ((comp.InputType != StackType.F && !comp.IsLifted) || comp.Kind.IsEqualityOrInequality())
                {
                    context.Step("push negation into comparison", inst);
                    comp.Kind = comp.Kind.Negate();
                    comp.AddILRange(inst.ILRange);
                    inst.ReplaceWith(comp);
                }
                comp.AcceptVisitor(this);
            }
            else if (inst.Argument.MatchLogicAnd(out lhs, out rhs))
            {
                // logic.not(if (lhs) rhs else ldc.i4 0)
                // ==> if (logic.not(lhs)) ldc.i4 1 else logic.not(rhs)
                context.Step("push negation into logic.and", inst);
                IfInstruction ifInst = (IfInstruction)inst.Argument;
                var           ldc0   = ifInst.FalseInst;
                Debug.Assert(ldc0.MatchLdcI4(0));
                ifInst.Condition = new LogicNot(lhs)
                {
                    ILRange = inst.ILRange
                };
                ifInst.TrueInst = new LdcI4(1)
                {
                    ILRange = ldc0.ILRange
                };
                ifInst.FalseInst = new LogicNot(rhs)
                {
                    ILRange = inst.ILRange
                };
                inst.ReplaceWith(ifInst);
                ifInst.AcceptVisitor(this);
            }
            else if (inst.Argument.MatchLogicOr(out lhs, out rhs))
            {
                // logic.not(if (lhs) ldc.i4 1 else rhs)
                // ==> if (logic.not(lhs)) logic.not(rhs) else ldc.i4 0)
                context.Step("push negation into logic.or", inst);
                IfInstruction ifInst = (IfInstruction)inst.Argument;
                var           ldc1   = ifInst.TrueInst;
                Debug.Assert(ldc1.MatchLdcI4(1));
                ifInst.Condition = new LogicNot(lhs)
                {
                    ILRange = inst.ILRange
                };
                ifInst.TrueInst = new LogicNot(rhs)
                {
                    ILRange = inst.ILRange
                };
                ifInst.FalseInst = new LdcI4(0)
                {
                    ILRange = ldc1.ILRange
                };
                inst.ReplaceWith(ifInst);
                ifInst.AcceptVisitor(this);
            }
            else
            {
                inst.Argument.AcceptVisitor(this);
            }
        }
Exemplo n.º 25
0
        bool MatchWhileLoop(BlockContainer loop, out IfInstruction condition, out Block loopBody)
        {
            // ConditionDetection favours leave inside if and branch at end of block
            // while-loop:
            // if (!loop-condition) leave loop-container
            // ...
            condition = null;
            loopBody  = null;
            if (!(loop.EntryPoint.Instructions[0] is IfInstruction ifInstruction))
            {
                return(false);
            }
            if (!ifInstruction.FalseInst.MatchNop())
            {
                return(false);
            }
            if (UsesVariableCapturedInLoop(loop, ifInstruction.Condition))
            {
                return(false);
            }

            condition = ifInstruction;
            if (!ifInstruction.TrueInst.MatchLeave(loop))
            {
                return(false);
            }

            context.Step("Transform to while (condition) loop", loop);
            loop.Kind = ContainerKind.While;
            //invert comparison
            ifInstruction.Condition = Comp.LogicNot(ifInstruction.Condition);
            ifInstruction.FalseInst = ifInstruction.TrueInst;
            //move the rest of the body into a new block
            loopBody = ConditionDetection.ExtractBlock(loop.EntryPoint, 1, loop.EntryPoint.Instructions.Count);
            loop.Blocks.Insert(1, loopBody);
            if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable))
            {
                loopBody.Instructions.Add(new Leave(loop));
            }

            ifInstruction.TrueInst = new Branch(loopBody);
            ExpressionTransforms.RunOnSingleStatement(ifInstruction, context);

            // Analyze conditions and decide whether to move some of them out of the condition block:

            /*var conditions = new List<ILInstruction>();
             * SplitConditions(condition.Condition, conditions);
             * // Break apart conditions that could be a MoveNext call followed by a Current accessor call:
             * if (MightBeHeaderOfForEach(loop, conditions)) {
             *      ifInstruction.Condition = conditions[0];
             *      foreach (var cond in conditions.Skip(1).Reverse()) {
             *              IfInstruction inst;
             *              loopBody.Instructions.Insert(0, inst = new IfInstruction(Comp.LogicNot(cond), new Leave(loop)));
             *              ExpressionTransforms.RunOnSingleStatment(inst, context);
             *      }
             * }*/

            // Invert condition and unwrap nested block, if loop ends in a break or return statement preceeded by an IfInstruction.

            /*while (loopBody.Instructions.Last() is Leave leave && loopBody.Instructions.SecondToLastOrDefault() is IfInstruction nestedIf && nestedIf.FalseInst.MatchNop()) {
             *      switch (nestedIf.TrueInst) {
             *              case Block nestedBlock:
             *                      loopBody.Instructions.RemoveAt(leave.ChildIndex);
             *                      loopBody.Instructions.AddRange(nestedBlock.Instructions);
             *                      break;
             *              case Branch br:
             *                      leave.ReplaceWith(nestedIf.TrueInst);
             *                      break;
             *              default:
             *                      return true;
             *      }
             *      nestedIf.Condition = Comp.LogicNot(nestedIf.Condition);
             *      nestedIf.TrueInst = leave;
             *      ExpressionTransforms.RunOnSingleStatment(nestedIf, context);
             *      if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable))
             *              loopBody.Instructions.Add(new Leave(loop));
             * }*/
            return(true);
        }
Exemplo n.º 26
0
        bool MatchForLoop(BlockContainer loop, IfInstruction whileCondition, Block whileLoopBody)
        {
            // for loops have exactly two incoming edges at the entry point.
            if (loop.EntryPoint.IncomingEdgeCount != 2)
            {
                return(false);
            }
            // try to find an increment block:
            // consists of simple statements only.
            var incrementBlock = GetIncrementBlock(loop, whileLoopBody);

            if (incrementBlock != null)
            {
                // we found a possible increment block, just make sure, that there are at least three blocks:
                // - condition block
                // - loop body
                // - increment block
                if (incrementBlock.Instructions.Count <= 1 || loop.Blocks.Count < 3)
                {
                    return(false);
                }
                context.Step("Transform to for loop", loop);
                // move the block to the end of the loop:
                loop.Blocks.MoveElementToEnd(incrementBlock);
                loop.Kind = ContainerKind.For;
            }
            else
            {
                // we need to move the increment statements into its own block:
                // last must be a branch entry-point
                var last         = whileLoopBody.Instructions.LastOrDefault();
                var secondToLast = whileLoopBody.Instructions.SecondToLastOrDefault();
                if (last == null || secondToLast == null)
                {
                    return(false);
                }
                if (!last.MatchBranch(loop.EntryPoint))
                {
                    return(false);
                }
                // we only deal with 'numeric' increments
                if (!MatchIncrement(secondToLast, out var incrementVariable))
                {
                    return(false);
                }
                // the increment variable must be local/stack variable
                if (incrementVariable.Kind == VariableKind.Parameter)
                {
                    return(false);
                }
                // split conditions:
                var conditions = new List <ILInstruction>();
                SplitConditions(whileCondition.Condition, conditions);
                IfInstruction forCondition       = null;
                int           numberOfConditions = 0;
                foreach (var condition in conditions)
                {
                    // the increment variable must be used in the condition
                    if (!condition.Descendants.Any(inst => inst.MatchLdLoc(incrementVariable)))
                    {
                        break;
                    }
                    // condition should not contain an assignment
                    if (condition.Descendants.Any(IsAssignment))
                    {
                        break;
                    }
                    if (forCondition == null)
                    {
                        forCondition = new IfInstruction(condition, whileCondition.TrueInst, whileCondition.FalseInst);
                    }
                    else
                    {
                        forCondition.Condition = IfInstruction.LogicAnd(forCondition.Condition, condition);
                    }
                    numberOfConditions++;
                }
                if (numberOfConditions == 0)
                {
                    return(false);
                }
                context.Step("Transform to for loop", loop);
                // split condition block:
                whileCondition.ReplaceWith(forCondition);
                ExpressionTransforms.RunOnSingleStatement(forCondition, context);
                for (int i = conditions.Count - 1; i >= numberOfConditions; i--)
                {
                    IfInstruction inst;
                    whileLoopBody.Instructions.Insert(0, inst = new IfInstruction(Comp.LogicNot(conditions[i]), new Leave(loop)));
                    ExpressionTransforms.RunOnSingleStatement(inst, context);
                }
                // create a new increment block and add it at the end:
                int secondToLastIndex = secondToLast.ChildIndex;
                var newIncremenBlock  = new Block();
                loop.Blocks.Add(newIncremenBlock);
                // move the increment instruction:
                newIncremenBlock.Instructions.Add(secondToLast);
                newIncremenBlock.Instructions.Add(last);
                newIncremenBlock.AddILRange(secondToLast.ILRange);
                whileLoopBody.Instructions.RemoveRange(secondToLastIndex, 2);
                whileLoopBody.Instructions.Add(new Branch(newIncremenBlock));
                // complete transform.
                loop.Kind = ContainerKind.For;
            }
            return(true);
        }
Exemplo n.º 27
0
        /// <summary>
        /// Matches a do-while loop and performs the following transformations:
        /// - combine all compatible conditions into one IfInstruction.
        /// - extract conditions into a condition block, or move the existing condition block to the end.
        /// </summary>
        bool MatchDoWhileLoop(BlockContainer loop)
        {
            (List <IfInstruction> conditions, ILInstruction exit, bool swap, bool split, bool unwrap) = AnalyzeDoWhileConditions(loop);
            // not a do-while loop, exit.
            if (conditions == null || conditions.Count == 0)
            {
                return(false);
            }
            context.Step("Transform to do-while loop", loop);
            Block conditionBlock;
            // first we remove all extracted instructions from the original block.
            var originalBlock = (Block)exit.Parent;

            if (unwrap)
            {
                // we found a condition block nested in a condition that is followed by a return statement:
                // we flip the condition and swap the blocks
                Debug.Assert(originalBlock.Parent is IfInstruction);
                var returnCondition = (IfInstruction)originalBlock.Parent;
                var topLevelBlock   = (Block)returnCondition.Parent;
                Debug.Assert(topLevelBlock.Parent == loop);
                var leaveFunction = topLevelBlock.Instructions[returnCondition.ChildIndex + 1];
                Debug.Assert(leaveFunction.MatchReturn(out _));
                returnCondition.Condition = Comp.LogicNot(returnCondition.Condition);
                returnCondition.TrueInst  = leaveFunction;
                // simplify the condition:
                ExpressionTransforms.RunOnSingleStatement(returnCondition, context);
                topLevelBlock.Instructions.RemoveAt(returnCondition.ChildIndex + 1);
                topLevelBlock.Instructions.AddRange(originalBlock.Instructions);
                originalBlock = topLevelBlock;
                split         = true;
            }
            originalBlock.Instructions.RemoveRange(originalBlock.Instructions.Count - conditions.Count - 1, conditions.Count + 1);
            // we need to split the block:
            if (split)
            {
                // add a new block at the end and add a branch to the new block.
                conditionBlock = new Block();
                loop.Blocks.Add(conditionBlock);
                originalBlock.Instructions.Add(new Branch(conditionBlock));
            }
            else
            {
                // move the condition block to the end.
                conditionBlock = originalBlock;
                loop.Blocks.MoveElementToEnd(originalBlock);
            }
            // combine all conditions and the exit instruction into one IfInstruction:
            IfInstruction condition = null;

            conditionBlock.AddILRange(exit.ILRange);
            foreach (var inst in conditions)
            {
                conditionBlock.AddILRange(inst.ILRange);
                if (condition == null)
                {
                    condition = inst;
                    if (swap)
                    {
                        // branches must be swapped and condition negated:
                        condition.Condition = Comp.LogicNot(condition.Condition);
                        condition.FalseInst = condition.TrueInst;
                        condition.TrueInst  = exit;
                    }
                    else
                    {
                        condition.FalseInst = exit;
                    }
                }
                else
                {
                    if (swap)
                    {
                        condition.Condition = IfInstruction.LogicAnd(Comp.LogicNot(inst.Condition), condition.Condition);
                    }
                    else
                    {
                        condition.Condition = IfInstruction.LogicAnd(inst.Condition, condition.Condition);
                    }
                }
            }
            // insert the combined conditions into the condition block:
            conditionBlock.Instructions.Add(condition);
            // simplify the condition:
            ExpressionTransforms.RunOnSingleStatement(condition, context);
            // transform complete
            loop.Kind = ContainerKind.DoWhile;
            return(true);
        }
Exemplo n.º 28
0
        private void HandleIfInstruction(ControlFlowNode cfgNode, Block block, IfInstruction ifInst, ref ILInstruction exitInst)
        {
            if (ShouldSwapIfTargets(ifInst.TrueInst, exitInst))
            {
                // "if (c) goto lateBlock; goto earlierBlock;"
                // -> "if (!c)" goto earlierBlock; goto lateBlock;
                // This reordering should make the if structure correspond more closely to the original C# source code
                context.Step("Negate if", ifInst);
                block.Instructions[block.Instructions.Count - 1] = ifInst.TrueInst;
                ifInst.TrueInst  = exitInst;
                exitInst         = block.Instructions.Last();
                ifInst.Condition = Comp.LogicNot(ifInst.Condition);
            }

            ILInstruction trueExitInst;

            if (IsUsableBranchToChild(cfgNode, ifInst.TrueInst))
            {
                // "if (...) goto targetBlock; exitInst;"
                // -> "if (...) { targetBlock } exitInst;"
                context.Step("Inline block as then-branch", ifInst);
                var targetBlock = ((Branch)ifInst.TrueInst).TargetBlock;
                // The targetBlock was already processed, we can embed it into the if statement:
                targetBlock.Remove();
                ifInst.TrueInst = targetBlock;
                ILInstruction nestedCondition, nestedTrueInst;
                while (targetBlock.Instructions.Count > 0 &&
                       targetBlock.Instructions[0].MatchIfInstruction(out nestedCondition, out nestedTrueInst))
                {
                    nestedTrueInst = UnpackBlockContainingOnlyBranch(nestedTrueInst);
                    if (DetectExitPoints.CompatibleExitInstruction(exitInst, nestedTrueInst))
                    {
                        // "if (...) { if (nestedCondition) goto exitPoint; ... } goto exitPoint;"
                        // -> "if (... && !nestedCondition) { ... } goto exitPoint;"
                        context.Step("Combine 'if (cond1 && !cond2)' in then-branch", ifInst);
                        ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, Comp.LogicNot(nestedCondition));
                        targetBlock.Instructions.RemoveAt(0);
                        // Update targetBlock label now that we've removed the first instruction
                        if (targetBlock.Instructions.FirstOrDefault()?.ILRange.IsEmpty == false)
                        {
                            int offset = targetBlock.Instructions[0].ILRange.Start;
                            targetBlock.ILRange = new Interval(offset, offset);
                        }
                        continue;                         // try to find more nested conditions
                    }
                    if (nestedTrueInst is Block nestedTrueBlock &&
                        DetectExitPoints.CompatibleExitInstruction(exitInst, nestedTrueBlock.Instructions.Last()) &&
                        targetBlock.HasFlag(InstructionFlags.EndPointUnreachable))
                    {
                        // "if (...) { if (nestedCondition) { trueInst...; goto exitPoint; } falseInst...; } goto exitPoint;"
                        // -> "if (...) { if (!nestedCondition) { falseInst...; } trueInst... } goto exitPoint;"
                        // (only if end-point of 'falseInst...' is unreachable)
                        context.Step("Invert nested condition to reduce number of gotos", ifInst);
                        var nestedIfInst = (IfInstruction)targetBlock.Instructions[0];
                        nestedIfInst.Condition = Comp.LogicNot(nestedCondition);
                        nestedTrueBlock.Instructions.RemoveAt(nestedTrueBlock.Instructions.Count - 1);                         // remove nested goto exitPoint;
                        // remove falseInsts from outer block
                        var falseInsts = targetBlock.Instructions.Skip(1).ToArray();
                        targetBlock.Instructions.RemoveRange(1, targetBlock.Instructions.Count - 1);
                        // add trueInsts to outer block
                        targetBlock.Instructions.AddRange(nestedTrueBlock.Instructions);
                        // add falseInsts to inner block
                        nestedTrueBlock.Instructions.ReplaceList(falseInsts);
                        nestedIfInst.Condition.AcceptVisitor(new ExpressionTransforms {
                            context = new StatementTransformContext(context)
                        });
                    }
                    break;
                }

                trueExitInst = targetBlock.Instructions.LastOrDefault();
                if (DetectExitPoints.CompatibleExitInstruction(exitInst, trueExitInst))
                {
                    // "if (...) { ...; goto exitPoint } goto exitPoint;"
                    // -> "if (...) { ... } goto exitPoint;"
                    context.Step("Remove redundant 'goto exitPoint;' in then-branch", ifInst);
                    targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1);
                    trueExitInst = null;
                    if (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0].MatchIfInstruction(out nestedCondition, out nestedTrueInst))
                    {
                        // "if (...) { if (nestedCondition) nestedTrueInst; } exitInst;"
                        // --> "if (... && nestedCondition) nestedTrueInst; } exitInst"
                        context.Step("Combine if conditions into logic.and (in then-branch)", ifInst);
                        ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, nestedCondition);
                        ifInst.TrueInst  = nestedTrueInst;
                        trueExitInst     = (nestedTrueInst as Block)?.Instructions.LastOrDefault();
                    }
                }
            }
            else
            {
                trueExitInst = ifInst.TrueInst;
            }
            if (IsUsableBranchToChild(cfgNode, exitInst))
            {
                var targetBlock   = ((Branch)exitInst).TargetBlock;
                var falseExitInst = targetBlock.Instructions.LastOrDefault();
                if (DetectExitPoints.CompatibleExitInstruction(trueExitInst, falseExitInst))
                {
                    // if (...) { ...; goto exitPoint; } goto nextBlock; nextBlock: ...; goto exitPoint;
                    // -> if (...) { ... } else { ... } goto exitPoint;
                    context.Step("Inline block as else-branch", ifInst);
                    targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1);
                    targetBlock.Remove();
                    ifInst.FalseInst = targetBlock;
                    exitInst         = block.Instructions[block.Instructions.Count - 1] = falseExitInst;
                    Block trueBlock = ifInst.TrueInst as Block;
                    if (trueBlock != null)
                    {
                        Debug.Assert(trueExitInst == trueBlock.Instructions.Last());
                        trueBlock.Instructions.RemoveAt(trueBlock.Instructions.Count - 1);
                    }
                    else
                    {
                        Debug.Assert(trueExitInst == ifInst.TrueInst);
                        ifInst.TrueInst = new Nop {
                            ILRange = ifInst.TrueInst.ILRange
                        };
                    }
                }
            }
            if (IsEmpty(ifInst.TrueInst))
            {
                // prefer empty true-branch to empty-else branch
                context.Step("Swap empty then-branch with else-branch", ifInst);
                var oldTrue = ifInst.TrueInst;
                ifInst.TrueInst  = ifInst.FalseInst;
                ifInst.FalseInst = new Nop {
                    ILRange = oldTrue.ILRange
                };
                ifInst.Condition = Comp.LogicNot(ifInst.Condition);

                // After swapping, it's possible that we can introduce a short-circuit operator:
                Block         trueBlock = ifInst.TrueInst as Block;
                ILInstruction nestedCondition, nestedTrueInst;
                if (trueBlock != null && trueBlock.Instructions.Count == 1 &&
                    trueBlock.FinalInstruction is Nop &&
                    trueBlock.Instructions[0].MatchIfInstruction(out nestedCondition, out nestedTrueInst))
                {
                    // if (cond) if (nestedCond) nestedTrueInst
                    // ==> if (cond && nestedCond) nestedTrueInst
                    context.Step("Combine if conditions into logic.and (after branch swapping)", ifInst);
                    ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, nestedCondition);
                    ifInst.TrueInst  = nestedTrueInst;
                }
            }
            else if (ifInst.FalseInst.OpCode != OpCode.Nop && ifInst.FalseInst.ILRange.Start < ifInst.TrueInst.ILRange.Start)
            {
                // swap true and false branches of if/else construct,
                // to bring them in the same order as the IL code
                context.Step("Swap then-branch with else-branch", ifInst);
                var oldTrue = ifInst.TrueInst;
                ifInst.TrueInst  = ifInst.FalseInst;
                ifInst.FalseInst = oldTrue;
                ifInst.Condition = Comp.LogicNot(ifInst.Condition);
            }
        }
Exemplo n.º 29
0
        /// <summary>
        /// Looks for common exits in the inlined then and else branches of an if instruction
        /// and performs inversions and simplifications to merge them provided they don't
        /// isolate a higher priority block exit
        /// </summary>
        private void MergeCommonBranches(Block block, IfInstruction ifInst)
        {
            var thenExits = new List <ILInstruction>();

            AddExits(ifInst.TrueInst, 0, thenExits);
            if (thenExits.Count == 0)
            {
                return;
            }

            // if there are any exits from the then branch, then the else is redundant and shouldn't exist
            Debug.Assert(IsEmpty(ifInst.FalseInst));
            Debug.Assert(ifInst.Parent == block);
            var elseExits      = new List <ILInstruction>();
            int falseInstIndex = block.Instructions.IndexOf(ifInst) + 1;

            AddExits(block, falseInstIndex, elseExits);

            var commonExits = elseExits.Where(e1 => thenExits.Any(e2 => DetectExitPoints.CompatibleExitInstruction(e1, e2)));

            // find the common exit with the highest block exit priority
            ILInstruction commonExit = null;

            foreach (var exit in commonExits)
            {
                if (commonExit == null || CompareBlockExitPriority(exit, commonExit) > 0)
                {
                    commonExit = exit;
                }
            }

            if (commonExit == null)
            {
                return;
            }

            // if the current block exit has higher priority than the exits to merge,
            // determine if this merge will isolate the current block exit
            // that is, no sequence of inversions can restore it to the block exit position
            var blockExit = block.Instructions.Last();

            if (CompareBlockExitPriority(blockExit, commonExit, true) > 0 && !WillShortCircuit(block, ifInst, commonExit))
            {
                return;
            }

            // could improve performance by directly implementing the || short-circuit when WillShortCircuit
            // currently the same general sequence of transformations introduces both operators

            context.StepStartGroup("Merge common branches " + commonExit, ifInst);
            ProduceExit(ifInst.TrueInst, 0, commonExit);
            ProduceExit(block, falseInstIndex, commonExit);

            // if (...) { ...; blockExit; } ...; blockExit;
            // -> if (...) { ...; blockExit; } else { ... } blockExit;
            if (ifInst != block.Instructions.SecondToLastOrDefault())
            {
                context.Step("Embed else-block for goto removal", ifInst);
                Debug.Assert(IsEmpty(ifInst.FalseInst));
                ifInst.FalseInst = ExtractBlock(block, block.Instructions.IndexOf(ifInst) + 1, block.Instructions.Count - 1);
            }

            // if (...) { ...; goto blockExit; } blockExit;
            // -> if (...) { ... } blockExit;
            // OR
            // if (...) { ...; goto blockExit; } else { ... } blockExit;
            // -> if (...) { ... } else { ... } blockExit;
            context.Step("Remove redundant 'goto blockExit;' in then-branch", ifInst);
            if (!(ifInst.TrueInst is Block trueBlock) || trueBlock.Instructions.Count == 1)
            {
                ifInst.TrueInst = new Nop().WithILRange(ifInst.TrueInst);
            }
Exemplo n.º 30
0
        bool MatchWhileLoop(BlockContainer loop, out IfInstruction condition, out Block loopBody)
        {
            // ConditionDetection favours leave inside if and branch at end of block
            // while-loop:
            // if (!loop-condition) leave loop-container
            // ...
            condition = null;
            loopBody  = loop.EntryPoint;
            if (!(loopBody.Instructions[0] is IfInstruction ifInstruction))
            {
                return(false);
            }

            if (!ifInstruction.FalseInst.MatchNop())
            {
                return(false);
            }

            if (UsesVariableCapturedInLoop(loop, ifInstruction.Condition))
            {
                return(false);
            }

            condition = ifInstruction;
            if (!ifInstruction.TrueInst.MatchLeave(loop))
            {
                // sometimes the loop-body is nested within the if
                // if (loop-condition) { loop-body }
                // leave loop-container

                if (loopBody.Instructions.Count != 2 || !loop.EntryPoint.Instructions.Last().MatchLeave(loop))
                {
                    return(false);
                }

                if (!ifInstruction.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable))
                {
                    ((Block)ifInstruction.TrueInst).Instructions.Add(new Leave(loop));
                }

                ConditionDetection.InvertIf(loopBody, ifInstruction, context);
            }

            context.Step("Transform to while (condition) loop", loop);
            loop.Kind = ContainerKind.While;
            //invert comparison
            ifInstruction.Condition = Comp.LogicNot(ifInstruction.Condition);
            ifInstruction.FalseInst = ifInstruction.TrueInst;
            //move the rest of the body into a new block
            loopBody = ConditionDetection.ExtractBlock(loop.EntryPoint, 1, loop.EntryPoint.Instructions.Count);
            loop.Blocks.Insert(1, loopBody);
            if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable))
            {
                loopBody.Instructions.Add(new Leave(loop));
            }

            ifInstruction.TrueInst = new Branch(loopBody);
            ExpressionTransforms.RunOnSingleStatement(ifInstruction, context);

            // Analyze conditions and decide whether to move some of them out of the condition block:

            /*var conditions = new List<ILInstruction>();
             * SplitConditions(condition.Condition, conditions);
             * // Break apart conditions that could be a MoveNext call followed by a Current accessor call:
             * if (MightBeHeaderOfForEach(loop, conditions)) {
             *      ifInstruction.Condition = conditions[0];
             *      foreach (var cond in conditions.Skip(1).Reverse()) {
             *              IfInstruction inst;
             *              loopBody.Instructions.Insert(0, inst = new IfInstruction(Comp.LogicNot(cond), new Leave(loop)));
             *              ExpressionTransforms.RunOnSingleStatment(inst, context);
             *      }
             * }*/

            return(true);
        }