/// <summary> /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable. /// </summary> /// <param name="next">The next top-level expression</param> /// <param name="loadInst">The load within 'next'</param> /// <param name="inlinedExpression">The expression being inlined</param> static bool NonAggressiveInlineInto(ILInstruction next, ILInstruction loadInst, ILInstruction inlinedExpression, ILVariable v) { Debug.Assert(loadInst.IsDescendantOf(next)); // decide based on the source expression being inlined switch (inlinedExpression.OpCode) { case OpCode.DefaultValue: case OpCode.StObj: case OpCode.CompoundAssignmentInstruction: case OpCode.Await: return(true); case OpCode.LdLoc: if (v.StateMachineField == null && ((LdLoc)inlinedExpression).Variable.StateMachineField != null) { // Roslyn likes to put the result of fetching a state machine field into a temporary variable, // so inline more aggressively in such cases. return(true); } break; } var parent = loadInst.Parent; if (parent is ILiftableInstruction liftable && liftable.IsLifted) { return(true); // inline into lifted operators } if (parent is NullCoalescingInstruction && NullableType.IsNullable(v.Type)) { return(true); // inline nullables into ?? operator } // decide based on the target into which we are inlining switch (next.OpCode) { case OpCode.Leave: return(parent == next); case OpCode.IfInstruction: while (parent.MatchLogicNot(out _)) { parent = parent.Parent; } return(parent == next); case OpCode.SwitchInstruction: return(parent == next || (parent.MatchBinaryNumericInstruction(BinaryNumericOperator.Sub) && parent.Parent == next)); default: return(false); } }
/// <summary> /// Gets whether 'expressionBeingMoved' can be moved from somewhere before 'stmt' to become the replacement of 'targetLoad'. /// </summary> public static bool CanMoveInto(ILInstruction expressionBeingMoved, ILInstruction stmt, ILInstruction targetLoad) { Debug.Assert(targetLoad.IsDescendantOf(stmt)); for (ILInstruction inst = targetLoad; inst != stmt; inst = inst.Parent) { if (!inst.Parent.CanInlineIntoSlot(inst.ChildIndex, expressionBeingMoved)) { return(false); } // Check whether re-ordering with predecessors is valid: int childIndex = inst.ChildIndex; for (int i = 0; i < childIndex; ++i) { ILInstruction predecessor = inst.Parent.Children[i]; if (!IsSafeForInlineOver(predecessor, expressionBeingMoved)) { return(false); } } } return(true); }
/// <summary> /// Gets whether arg can be un-inlined out of stmt. /// </summary> internal static bool CanUninline(ILInstruction arg, ILInstruction stmt) { Debug.Assert(arg.IsDescendantOf(stmt)); for (ILInstruction inst = arg; inst != stmt; inst = inst.Parent) { if (!inst.SlotInfo.CanInlineInto) { return(false); } // Check whether re-ordering with predecessors is valid: int childIndex = inst.ChildIndex; for (int i = 0; i < childIndex; ++i) { ILInstruction predecessor = inst.Parent.Children[i]; if (!SemanticHelper.MayReorder(arg, predecessor)) { return(false); } } } return(true); }
/// <summary> /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable. /// </summary> /// <param name="next">The next top-level expression</param> /// <param name="loadInst">The load within 'next'</param> /// <param name="inlinedExpression">The expression being inlined</param> static bool NonAggressiveInlineInto(ILInstruction next, ILInstruction loadInst, ILInstruction inlinedExpression, ILVariable v) { Debug.Assert(loadInst.IsDescendantOf(next)); // decide based on the source expression being inlined switch (inlinedExpression.OpCode) { case OpCode.DefaultValue: case OpCode.StObj: case OpCode.NumericCompoundAssign: case OpCode.UserDefinedCompoundAssign: case OpCode.Await: return(true); case OpCode.LdLoc: if (v.StateMachineField == null && ((LdLoc)inlinedExpression).Variable.StateMachineField != null) { // Roslyn likes to put the result of fetching a state machine field into a temporary variable, // so inline more aggressively in such cases. return(true); } break; } var parent = loadInst.Parent; if (NullableLiftingTransform.MatchNullableCtor(parent, out _, out _)) { // inline into nullable ctor call in lifted operator parent = parent.Parent; } if (parent is ILiftableInstruction liftable && liftable.IsLifted) { return(true); // inline into lifted operators } switch (parent.OpCode) { case OpCode.NullCoalescingInstruction: if (NullableType.IsNullable(v.Type)) { return(true); // inline nullables into ?? operator } break; case OpCode.NullableUnwrap: return(true); // inline into ?. operator case OpCode.UserDefinedLogicOperator: case OpCode.DynamicLogicOperatorInstruction: return(true); // inline into (left slot of) user-defined && or || operator case OpCode.DynamicGetMemberInstruction: case OpCode.DynamicGetIndexInstruction: case OpCode.LdObj: if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign) { return(true); // inline into dynamic compound assignments } break; } // decide based on the target into which we are inlining switch (next.OpCode) { case OpCode.Leave: case OpCode.YieldReturn: return(parent == next); case OpCode.IfInstruction: while (parent.MatchLogicNot(out _)) { parent = parent.Parent; } return(parent == next); case OpCode.BlockContainer: if (((BlockContainer)next).EntryPoint.Instructions[0] is SwitchInstruction switchInst) { next = switchInst; goto case OpCode.SwitchInstruction; } else { return(false); } case OpCode.SwitchInstruction: return(parent == next || (parent.MatchBinaryNumericInstruction(BinaryNumericOperator.Sub) && parent.Parent == next)); default: return(false); } }