Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ private static object ChkCastAny_NoCacheLookup(void* toTypeHnd, object obj)
return obj;
}

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void WriteBarrier(ref object? dst, object? obj);

// IsInstanceOf test used for unusual cases (naked type parameters, variant generic types)
// Unlike the IsInstanceOfInterface and IsInstanceOfClass functions,
// this test must deal with all kinds of type tests
Expand Down Expand Up @@ -454,7 +451,7 @@ private static void StelemRef(object?[] array, nint index, object? obj)
goto notExactMatch;

doWrite:
WriteBarrier(ref element, obj);
RuntimeHelpers.WriteBarrier(ref element, obj);
return;

assigningNull:
Expand All @@ -475,7 +472,7 @@ private static void StelemRef_Helper(ref object? element, void* elementType, obj
CastResult result = CastCache.TryGet(s_table!, (nuint)RuntimeHelpers.GetMethodTable(obj), (nuint)elementType);
if (result == CastResult.CanCast)
{
WriteBarrier(ref element, obj);
RuntimeHelpers.WriteBarrier(ref element, obj);
return;
}

Expand All @@ -493,7 +490,7 @@ private static void StelemRef_Helper_NoCacheLookup(ref object? element, void* el
ThrowArrayMismatchException();
}

WriteBarrier(ref element, obj2);
RuntimeHelpers.WriteBarrier(ref element, obj2);
}

[DebuggerHidden]
Expand Down
24 changes: 24 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5151,6 +5151,30 @@ class Compiler
GenTree* dereferencedAddress,
InlArgInfo* inlArgInfo);

typedef JitHashTable<CORINFO_METHOD_HANDLE, JitPtrKeyFuncs<struct CORINFO_METHOD_STRUCT_>, CORINFO_METHOD_HANDLE> HelperToManagedMap;
HelperToManagedMap* m_helperToManagedMap = nullptr;

public:
HelperToManagedMap* GetHelperToManagedMap()
{
if (m_helperToManagedMap == nullptr)
{
m_helperToManagedMap = new (getAllocator()) HelperToManagedMap(getAllocator());
}
return m_helperToManagedMap;
}
bool HelperToManagedMapLookup(CORINFO_METHOD_HANDLE helperCallHnd, CORINFO_METHOD_HANDLE* userCallHnd)
{
if (m_helperToManagedMap == nullptr)
{
return false;
}
bool found = m_helperToManagedMap->Lookup(helperCallHnd, userCallHnd);
return found;
}
private:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Map<helperMethodHnd, realManagedMethodHnd> mapping is done purely to simplify the existing logic that tries to match call->IsHelperCall(this, CORINFO_HELP_ARRADDR_ST) for e.g. an optimization.

I could just do it via call->IsHelperCall(this, CORINFO_HELP_ARRADDR_ST) || call->IsSpecialIntrinsic(this, NI_...) - but it's more changes + we might want to allow inlining for some other helper calls as well.


void impConvertToUserCallAndMarkForInlining(GenTreeCall* call);
void impMarkInlineCandidate(GenTree* call,
CORINFO_CONTEXT_HANDLE exactContextHnd,
bool exactContextNeedsRuntimeLookup,
Expand Down
31 changes: 31 additions & 0 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2383,6 +2383,34 @@ bool GenTreeCall::IsHelperCall(Compiler* compiler, unsigned helper) const
return IsHelperCall(compiler->eeFindHelper(helper));
}

//-------------------------------------------------------------------------
// IsHelperCallOrUserEquivalent: Determine if this GT_CALL node is a specific helper call
// or its CT_USER equivalent.
//
// Arguments:
// compiler - the compiler instance so that we can call eeFindHelper
//
// Return Value:
// Returns true if this GT_CALL node is a call to the specified helper.
//
bool GenTreeCall::IsHelperCallOrUserEquivalent(Compiler* compiler, unsigned helper) const
{
CORINFO_METHOD_HANDLE helperCallHnd = Compiler::eeFindHelper(helper);
if (IsHelperCall())
{
return helperCallHnd == gtCallMethHnd;
}

if (gtCallType == CT_USER_FUNC)
{
CORINFO_METHOD_HANDLE userCallHnd = NO_METHOD_HANDLE;
return compiler->impInlineRoot()->HelperToManagedMapLookup(helperCallHnd, &userCallHnd) &&
(userCallHnd == gtCallMethHnd);
}

return false;
}

//-------------------------------------------------------------------------
// IsRuntimeLookupHelperCall: Determine if this GT_CALL node represents a runtime lookup helper call.
//
Expand Down Expand Up @@ -12863,6 +12891,9 @@ void Compiler::gtDispTree(GenTree* tree,
case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant:
printf(" isKnownConst");
break;
case NI_System_Runtime_CompilerServices_RuntimeHelpers_WriteBarrier:
printf(" WriteBarrier");
break;
#if defined(FEATURE_SIMD)
case NI_SIMD_UpperRestore:
printf(" simdUpperRestore");
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -5784,6 +5784,8 @@ struct GenTreeCall final : public GenTree

bool IsHelperCall(Compiler* compiler, unsigned helper) const;

bool IsHelperCallOrUserEquivalent(Compiler* compiler, unsigned helper) const;

bool IsRuntimeLookupHelperCall(Compiler* compiler) const;

bool IsSpecialIntrinsic(Compiler* compiler, NamedIntrinsic ni) const;
Expand Down
7 changes: 6 additions & 1 deletion src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7362,7 +7362,12 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// The array helper takes a native int for array length.
// So if we have an int, explicitly extend it to be a native int.
index = impImplicitIorI4Cast(index, TYP_I_IMPL);
op1 = gtNewHelperCallNode(CORINFO_HELP_ARRADDR_ST, TYP_VOID, array, index, value);

GenTreeCall* call = gtNewHelperCallNode(CORINFO_HELP_ARRADDR_ST, TYP_VOID, array, index, value);
INDEBUG(call->gtRawILOffset = opcodeOffs);
impConvertToUserCallAndMarkForInlining(call);
op1 = call;

goto SPILL_APPEND;
}

Expand Down
61 changes: 61 additions & 0 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3360,6 +3360,8 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd,
// This one is just `return true/false`
case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant:

case NI_System_Runtime_CompilerServices_RuntimeHelpers_WriteBarrier:

// Not expanding this can lead to noticeable allocations in T0
case NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan:

Expand Down Expand Up @@ -3592,6 +3594,14 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd,
break;
}

case NI_System_Runtime_CompilerServices_RuntimeHelpers_WriteBarrier:
{
GenTree* val = impPopStack().val;
GenTree* dst = impPopStack().val;
retNode = gtNewStoreIndNode(TYP_REF, dst, val, GTF_IND_TGT_HEAP);
break;
}

case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant:
{
GenTree* op1 = impPopStack().val;
Expand Down Expand Up @@ -7890,6 +7900,53 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call,
call->AddGDVCandidateInfo(this, pInfo);
}

//------------------------------------------------------------------------
// impConvertToUserCallAndMarkForInlining: convert a helper call to a user call
// and mark it for inlining. This is used for helper calls that are
// known to be backed by a user method that can be inlined.
//
// Arguments:
// call - the helper call to convert
//
void Compiler::impConvertToUserCallAndMarkForInlining(GenTreeCall* call)
{
assert(call->IsHelperCall());

if (!opts.OptEnabled(CLFLG_INLINING))
{
return;
}

CORINFO_METHOD_HANDLE helperCallHnd = call->gtCallMethHnd;
CORINFO_METHOD_HANDLE managedCallHnd = NO_METHOD_HANDLE;
CORINFO_CONST_LOOKUP pNativeEntrypoint = {};
info.compCompHnd->getHelperFtn(eeGetHelperNum(helperCallHnd), &pNativeEntrypoint, &managedCallHnd);

if (managedCallHnd != NO_METHOD_HANDLE)
{
call->gtCallMethHnd = managedCallHnd;
call->gtCallType = CT_USER_FUNC;

CORINFO_CALL_INFO hCallInfo = {};
hCallInfo.hMethod = managedCallHnd;
hCallInfo.methodFlags = info.compCompHnd->getMethodAttribs(hCallInfo.hMethod);
impMarkInlineCandidate(call, nullptr, false, &hCallInfo, compInlineContext);

#if DEBUG
CORINFO_METHOD_HANDLE existingValue = NO_METHOD_HANDLE;
if (impInlineRoot()->HelperToManagedMapLookup(helperCallHnd, &existingValue))
{
// Let's make sure HelperToManagedMap::Overwrite behavior always overwrites the same value.
assert(existingValue == managedCallHnd);
}
#endif

impInlineRoot()->GetHelperToManagedMap()->Set(helperCallHnd, managedCallHnd, HelperToManagedMap::Overwrite);
JITDUMP("Converting helperCall [%06u] to user call [%s] and marking for inlining\n", dspTreeID(call),
eeGetMethodFullName(managedCallHnd));
}
}

//------------------------------------------------------------------------
// impMarkInlineCandidate: determine if this call can be subsequently inlined
//
Expand Down Expand Up @@ -10820,6 +10877,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
{
result = NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant;
}
else if (strcmp(methodName, "WriteBarrier") == 0)
{
result = NI_System_Runtime_CompilerServices_RuntimeHelpers_WriteBarrier;
}
else if (strcmp(methodName, "IsReferenceOrContainsReferences") == 0)
{
result =
Expand Down
11 changes: 10 additions & 1 deletion src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6503,13 +6503,22 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call)

// Morph stelem.ref helper call to store a null value, into a store into an array without the helper.
// This needs to be done after the arguments are morphed to ensure constant propagation has already taken place.
if (opts.OptimizationEnabled() && call->IsHelperCall(this, CORINFO_HELP_ARRADDR_ST))
if (opts.OptimizationEnabled() && call->IsHelperCallOrUserEquivalent(this, CORINFO_HELP_ARRADDR_ST))
{
assert(call->gtArgs.CountArgs() == 3);
GenTree* arr = call->gtArgs.GetArgByIndex(0)->GetNode();
GenTree* index = call->gtArgs.GetArgByIndex(1)->GetNode();
GenTree* value = call->gtArgs.GetArgByIndex(2)->GetNode();

if (!call->IsHelperCall())
{
// Convert back to helper call if it wasn't inlined.
// Currently, only helper calls are eligible to be direct calls if the target has reached
// its final tier. TODO: remove this workaround and convert this user call to direct as well.
call->gtCallMethHnd = eeFindHelper(CORINFO_HELP_ARRADDR_ST);
call->gtCallType = CT_HELPER;
}

if (gtCanSkipCovariantStoreCheck(value, arr))
{
// Either or both of the array and index arguments may have been spilled to temps by `fgMorphArgs`. Copy
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/namedintrinsiclist.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ enum NamedIntrinsic : unsigned short
NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant,
NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences,
NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable,
NI_System_Runtime_CompilerServices_RuntimeHelpers_WriteBarrier,

NI_System_Runtime_CompilerServices_AsyncHelpers_AsyncSuspend,
NI_System_Runtime_CompilerServices_AsyncHelpers_Await,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ public static int OffsetToStringData

[Intrinsic]
public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle);

[Intrinsic]
internal static void WriteBarrier(ref object? dst, object? obj) => dst = obj;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,6 @@ internal static class InternalCalls
internal static extern unsafe object RhpNewFastMisalign(MethodTable * pEEType);
#endif // FEATURE_64BIT_ALIGNMENT

[RuntimeImport(RuntimeLibrary, "RhpAssignRef")]
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe void RhpAssignRef(ref object? address, object? obj);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhpGcSafeZeroMemory")]
internal static extern unsafe ref byte RhpGcSafeZeroMemory(ref byte dmem, nuint size);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ public static unsafe void StelemRef(object?[] array, nint index, object? obj)
goto notExactMatch;

doWrite:
InternalCalls.RhpAssignRef(ref element, obj);
RuntimeHelpers.WriteBarrier(ref element, obj);
return;

assigningNull:
Expand All @@ -826,7 +826,7 @@ private static unsafe void StelemRef_Helper(ref object? element, MethodTable* el
CastResult result = s_castCache.TryGet((nuint)obj.GetMethodTable() + (int)AssignmentVariation.BoxedSource, (nuint)elementType);
if (result == CastResult.CanCast)
{
InternalCalls.RhpAssignRef(ref element, obj);
RuntimeHelpers.WriteBarrier(ref element, obj);
return;
}

Expand All @@ -843,7 +843,7 @@ private static unsafe void StelemRef_Helper_NoCacheLookup(ref object? element, M
throw elementType->GetClasslibException(ExceptionIDs.ArrayTypeMismatch);
}

InternalCalls.RhpAssignRef(ref element, obj);
RuntimeHelpers.WriteBarrier(ref element, obj);
}

private static unsafe object IsInstanceOfArray(MethodTable* pTargetType, object obj)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@ public static int OffsetToStringData
return string.FIRST_CHAR_OFFSET;
}
}

[Intrinsic]
internal static void WriteBarrier(ref object? dst, object? obj) => dst = obj;
}
}
5 changes: 0 additions & 5 deletions src/coreclr/vm/amd64/JitHelpers_Fast.asm
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,6 @@ Section segment para 'DATA'
JIT_WriteBarrier_Loc:
dq 0

LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT
; JIT_WriteBarrier(Object** dst, Object* src)
jmp QWORD PTR [JIT_WriteBarrier_Loc]
LEAF_END JIT_WriteBarrier_Callable, _TEXT

; There is an even more optimized version of these helpers possible which takes
; advantage of knowledge of which way the ephemeral heap is growing to only do 1/2
; that check (this is more significant in the JIT_WriteBarrier case).
Expand Down
8 changes: 0 additions & 8 deletions src/coreclr/vm/amd64/jithelpers_fast.S
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,6 @@ LEAF_END_MARKED JIT_ByRefWriteBarrier, _TEXT
.text
#endif

// ------------------------------------------------------------------
// __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val)
.balign 16
LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT
// JIT_WriteBarrier(Object** dst, Object* src)
jmp [rip + C_FUNC(JIT_WriteBarrier_Loc)]
LEAF_END JIT_WriteBarrier_Callable, _TEXT


// The following helper will access ("probe") a word on each page of the stack
// starting with the page right beneath rsp down to the one pointed to by r11.
Expand Down
15 changes: 0 additions & 15 deletions src/coreclr/vm/arm/asmhelpers.S
Original file line number Diff line number Diff line change
Expand Up @@ -675,21 +675,6 @@ g_rgWriteBarrierDescriptors:

.global g_rgWriteBarrierDescriptors

// ------------------------------------------------------------------
// __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val)
LEAF_ENTRY JIT_WriteBarrier_Callable

// Branch to the write barrier
#if defined(__clang__)
ldr r2, =JIT_WriteBarrier_Loc-(1f+4) // or R3? See targetarm.h
1:
add r2, pc
#else
ldr r2, =JIT_WriteBarrier_Loc
#endif
ldr pc, [r2]

LEAF_END JIT_WriteBarrier_Callable

#ifdef FEATURE_READYTORUN

Expand Down
14 changes: 0 additions & 14 deletions src/coreclr/vm/arm64/asmhelpers.S
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,6 @@ PATCH_LABEL ThePreStubPatchLabel
ret lr
LEAF_END ThePreStubPatch, _TEXT

// ------------------------// ------------------------------------------------------------------
// __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val)
LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT

// Setup args for JIT_WriteBarrier. x14 = dst ; x15 = val
mov x14, x0 // x14 = dst
mov x15, x1 // x15 = val

// Branch to the write barrier
PREPARE_EXTERNAL_VAR JIT_WriteBarrier_Loc, x17
ldr x17, [x17]
br x17
LEAF_END JIT_WriteBarrier_Callable, _TEXT

//
// x12 = UMEntryThunkData*
//
Expand Down
Loading
Loading