break;
}
+ case NI_System_SpanHelpers_SequenceEqual:
+ case NI_System_Buffer_Memmove:
+ {
+ if (FgStack::IsConstArgument(pushedStack.Top(), impInlineInfo))
+ {
+ // Constant (at its call-site) argument feeds the Memmove/Memcmp length argument.
+ // We most likely will be able to unroll it.
+ // It is important to only raise this hint for constant arguments, if it's just a
+ // constant in the inlinee itself then we don't need to inline it for unrolling.
+ compInlineResult->Note(InlineObservation::CALLSITE_UNROLLABLE_MEMOP);
+ }
+ break;
+ }
+
case NI_System_Span_get_Item:
case NI_System_ReadOnlySpan_get_Item:
{
case NI_System_SpanHelpers_SequenceEqual:
case NI_System_Buffer_Memmove:
{
- // We'll try to unroll this in lower for constant input.
- isSpecial = true;
+ if (sig->sigInst.methInstCount == 0)
+ {
+ // We'll try to unroll this in lower for constant input.
+ isSpecial = true;
+ }
+ // The generic version is also marked as [Intrinsic] just as a hint for the inliner
break;
}
INLINE_OBSERVATION(FOLDABLE_EXPR_UN, int, "foldable unary expression", INFORMATION, CALLSITE)
INLINE_OBSERVATION(FOLDABLE_BRANCH, int, "foldable branch", INFORMATION, CALLSITE)
INLINE_OBSERVATION(FOLDABLE_SWITCH, int, "foldable switch", INFORMATION, CALLSITE)
+INLINE_OBSERVATION(UNROLLABLE_MEMOP, int, "unrollable memmove/memcmp", INFORMATION, CALLSITE)
INLINE_OBSERVATION(DIV_BY_CNS, int, "dividy by const", INFORMATION, CALLSITE)
INLINE_OBSERVATION(CONSTANT_ARG_FEEDS_TEST, bool, "constant argument feeds test", INFORMATION, CALLSITE)
INLINE_OBSERVATION(DEPTH, int, "depth", INFORMATION, CALLSITE)
m_FoldableSwitch++;
break;
+ case InlineObservation::CALLSITE_UNROLLABLE_MEMOP:
+ m_UnrollableMemop++;
+ break;
+
case InlineObservation::CALLEE_HAS_SWITCH:
m_Switch++;
break;
// in prejit-root mode.
bbLimit += 5 + m_Switch * 10;
}
- bbLimit += m_FoldableBranch + m_FoldableSwitch * 10;
+ bbLimit += m_FoldableBranch + m_FoldableSwitch * 10 + m_UnrollableMemop * 2;
if ((unsigned)value > bbLimit)
{
break;
}
+ if (m_UnrollableMemop > 0)
+ {
+ multiplier += m_UnrollableMemop;
+ JITDUMP("\nInline candidate has %d unrollable memory operations. Multiplier increased to %g.",
+ m_UnrollableMemop, multiplier);
+ }
+
if (m_FoldableSwitch > 0)
{
multiplier += 6.0;
XATTR_I4(m_FoldableExprUn)
XATTR_I4(m_FoldableBranch)
XATTR_I4(m_FoldableSwitch)
+ XATTR_I4(m_UnrollableMemop)
XATTR_I4(m_Switch)
XATTR_I4(m_DivByCns)
XATTR_B(m_ReturnsStructByValue)
, m_FoldableExprUn(0)
, m_FoldableBranch(0)
, m_FoldableSwitch(0)
+ , m_UnrollableMemop(0)
, m_Switch(0)
, m_DivByCns(0)
, m_ReturnsStructByValue(false)
unsigned m_FoldableExprUn;
unsigned m_FoldableBranch;
unsigned m_FoldableSwitch;
+ unsigned m_UnrollableMemop;
unsigned m_Switch;
unsigned m_DivByCns;
bool m_ReturnsStructByValue : 1;
#if !MONO // Mono BulkMoveWithWriteBarrier is in terms of elements (not bytes) and takes a type handle.
+ [Intrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void Memmove<T>(ref T destination, ref T source, nuint elementCount)
{
{
if (value is not null)
{
- Append(valueCount: value.Length, value: ref value.GetRawStringData());
+ Append(ref value.GetRawStringData(), value.Length);
}
return this;
/// <summary>Appends a specified number of chars starting from the specified reference.</summary>
private void Append(ref char value, int valueCount)
{
- Debug.Assert(valueCount >= 0, $"Invalid length; should have been validated by caller.");
+ Debug.Assert(valueCount >= 0, "Invalid length; should have been validated by caller.");
if (valueCount != 0)
{
char[] chunkChars = m_ChunkChars;