Skip to content

Commit 7d6ea7c

Browse files
authored
clrinterp: Add missing must-expand intrinsics (dotnet#103326)
1 parent 069c156 commit 7d6ea7c

File tree

3 files changed

+250
-0
lines changed

3 files changed

+250
-0
lines changed

src/coreclr/vm/interpreter.cpp

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9257,13 +9257,34 @@ void Interpreter::DoCallWork(bool virtualCall, void* thisArg, CORINFO_RESOLVED_T
92579257
DoGetArrayDataReference();
92589258
didIntrinsic = true;
92599259
break;
9260+
92609261
#if INTERP_ILSTUBS
92619262
case NI_System_StubHelpers_GetStubContext:
92629263
OpStackSet<void*>(m_curStackHt, GetStubContext());
92639264
OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_NATIVEINT));
92649265
m_curStackHt++; didIntrinsic = true;
92659266
break;
92669267
#endif // INTERP_ILSTUBS
9268+
9269+
case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences:
9270+
DoIsReferenceOrContainsReferences(reinterpret_cast<CORINFO_METHOD_HANDLE>(methToCall));
9271+
didIntrinsic = true;
9272+
break;
9273+
9274+
case NI_System_Threading_Interlocked_CompareExchange:
9275+
// Here and in other Interlocked.* intrinsics we use sigInfo.retType to be able
9276+
// to detect small-integer overloads.
9277+
didIntrinsic = DoInterlockedCompareExchange(sigInfo.retType);
9278+
break;
9279+
9280+
case NI_System_Threading_Interlocked_Exchange:
9281+
didIntrinsic = DoInterlockedExchange(sigInfo.retType);
9282+
break;
9283+
9284+
case NI_System_Threading_Interlocked_ExchangeAdd:
9285+
didIntrinsic = DoInterlockedExchangeAdd(sigInfo.retType);
9286+
break;
9287+
92679288
default:
92689289
#if INTERP_TRACING
92699290
InterlockedIncrement(&s_totalInterpCallsToIntrinsicsUnhandled);
@@ -10903,6 +10924,197 @@ void Interpreter::DoGetArrayDataReference()
1090310924
OpStackTypeSet(ind, InterpreterType(CORINFO_TYPE_BYREF));
1090410925
}
1090510926

10927+
static bool HasByrefFields(MethodTable* pMT)
10928+
{
10929+
// Inspect all instance fields recursively
10930+
ApproxFieldDescIterator fieldIterator(pMT, ApproxFieldDescIterator::INSTANCE_FIELDS);
10931+
for (FieldDesc* pFD = fieldIterator.Next(); pFD != nullptr; pFD = fieldIterator.Next())
10932+
{
10933+
if (pFD->IsByRef())
10934+
{
10935+
return true;
10936+
}
10937+
if ((pFD->GetFieldType() == ELEMENT_TYPE_VALUETYPE &&
10938+
HasByrefFields(pFD->GetApproxFieldTypeHandleThrowing().AsMethodTable())))
10939+
{
10940+
return true;
10941+
}
10942+
}
10943+
return false;
10944+
}
10945+
10946+
void Interpreter::DoIsReferenceOrContainsReferences(CORINFO_METHOD_HANDLE method)
10947+
{
10948+
CONTRACTL{
10949+
THROWS;
10950+
GC_TRIGGERS;
10951+
MODE_COOPERATIVE;
10952+
} CONTRACTL_END;
10953+
10954+
CORINFO_SIG_INFO sigInfoFull;
10955+
{
10956+
GCX_PREEMP();
10957+
m_interpCeeInfo.getMethodSig(method, & sigInfoFull, nullptr);
10958+
}
10959+
10960+
MethodTable* typeArg = GetMethodTableFromClsHnd(sigInfoFull.sigInst.methInst[0]);
10961+
10962+
bool containsGcPtrs = typeArg->ContainsPointers();
10963+
10964+
// Return true for byref-like structs with ref fields (they might not have them)
10965+
if (!containsGcPtrs && typeArg->IsByRefLike())
10966+
{
10967+
containsGcPtrs |= HasByrefFields(typeArg);
10968+
}
10969+
10970+
OpStackSet<BOOL>(m_curStackHt, containsGcPtrs);
10971+
OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_INT));
10972+
m_curStackHt++;
10973+
}
10974+
10975+
bool Interpreter::DoInterlockedCompareExchange(CorInfoType retType)
10976+
{
10977+
CONTRACTL{
10978+
THROWS;
10979+
GC_TRIGGERS;
10980+
MODE_COOPERATIVE;
10981+
} CONTRACTL_END;
10982+
10983+
// These CompareExchange are must-expand:
10984+
//
10985+
// long CompareExchange(ref long location1, long value, long comparand)
10986+
// int CompareExchange(ref int location1, int value, int comparand)
10987+
// ushort CompareExchange(ref ushort location1, ushort value, ushort comparand)
10988+
// byte CompareExchange(ref byte location1, byte value, byte comparand)
10989+
//
10990+
// Detect these by retType (signature)
10991+
unsigned comparandInd = m_curStackHt - 1;
10992+
unsigned valueInd = m_curStackHt - 2;
10993+
unsigned locationInd = m_curStackHt - 3;
10994+
switch (retType)
10995+
{
10996+
case CORINFO_TYPE_LONG:
10997+
m_curStackHt -= 3;
10998+
OpStackSet<int64_t>(m_curStackHt, InterlockedCompareExchange64(
10999+
OpStackGet<int64_t*>(locationInd),
11000+
OpStackGet<int64_t>(valueInd),
11001+
OpStackGet<int64_t>(comparandInd)));
11002+
OpStackTypeSet(m_curStackHt, InterpreterType(retType));
11003+
m_curStackHt++;
11004+
return true;
11005+
11006+
case CORINFO_TYPE_INT:
11007+
m_curStackHt -= 3;
11008+
OpStackSet<LONG>(m_curStackHt, InterlockedCompareExchange(
11009+
OpStackGet<LONG*>(locationInd),
11010+
OpStackGet<LONG>(valueInd),
11011+
OpStackGet<LONG>(comparandInd)));
11012+
OpStackTypeSet(m_curStackHt, InterpreterType(retType));
11013+
m_curStackHt++;
11014+
return true;
11015+
11016+
case CORINFO_TYPE_SHORT:
11017+
case CORINFO_TYPE_BYTE:
11018+
NYI_INTERP("TODO: Implement must-expand atomics for small types.");
11019+
return false;
11020+
11021+
default:
11022+
// Non must-expand intrinsics
11023+
return false;
11024+
}
11025+
}
11026+
11027+
bool Interpreter::DoInterlockedExchange(CorInfoType retType)
11028+
{
11029+
CONTRACTL{
11030+
THROWS;
11031+
GC_TRIGGERS;
11032+
MODE_COOPERATIVE;
11033+
} CONTRACTL_END;
11034+
11035+
// These Exchange are must-expand:
11036+
//
11037+
// long Exchange(ref long location1, long value)
11038+
// int Exchange(ref int location1, int value)
11039+
// ushort Exchange(ref ushort location1, ushort value)
11040+
// byte Exchange(ref byte location1, byte value)
11041+
//
11042+
// Detect these by retType (signature)
11043+
unsigned valueInd = m_curStackHt - 1;
11044+
unsigned locationInd = m_curStackHt - 2;
11045+
switch (retType)
11046+
{
11047+
case CORINFO_TYPE_LONG:
11048+
m_curStackHt -= 2;
11049+
OpStackSet<int64_t>(m_curStackHt, InterlockedExchange64(
11050+
OpStackGet<int64_t*>(locationInd),
11051+
OpStackGet<int64_t>(valueInd)));
11052+
OpStackTypeSet(m_curStackHt, InterpreterType(retType));
11053+
m_curStackHt++;
11054+
return true;
11055+
11056+
case CORINFO_TYPE_INT:
11057+
m_curStackHt -= 2;
11058+
OpStackSet<LONG>(m_curStackHt, InterlockedExchange(
11059+
OpStackGet<LONG*>(locationInd),
11060+
OpStackGet<LONG>(valueInd)));
11061+
OpStackTypeSet(m_curStackHt, InterpreterType(retType));
11062+
m_curStackHt++;
11063+
return true;
11064+
11065+
case CORINFO_TYPE_SHORT:
11066+
case CORINFO_TYPE_BYTE:
11067+
NYI_INTERP("TODO: Implement must-expand Exchange for small types.");
11068+
return false;
11069+
11070+
default:
11071+
// Non must-expand intrinsics
11072+
return false;
11073+
}
11074+
}
11075+
11076+
bool Interpreter::DoInterlockedExchangeAdd(CorInfoType retType)
11077+
{
11078+
CONTRACTL{
11079+
THROWS;
11080+
GC_TRIGGERS;
11081+
MODE_COOPERATIVE;
11082+
} CONTRACTL_END;
11083+
11084+
// These ExchangeAdd are must-expand:
11085+
//
11086+
// long ExchangeAdd(ref long location1, long value)
11087+
// int ExchangeAdd(ref int location1, int value)
11088+
//
11089+
// Detect these by retType (signature)
11090+
unsigned valueInd = m_curStackHt - 1;
11091+
unsigned locationInd = m_curStackHt - 2;
11092+
switch (retType)
11093+
{
11094+
case CORINFO_TYPE_LONG:
11095+
m_curStackHt -= 2;
11096+
OpStackSet<int64_t>(m_curStackHt, InterlockedExchangeAdd64(
11097+
OpStackGet<int64_t*>(locationInd),
11098+
OpStackGet<int64_t>(valueInd)));
11099+
OpStackTypeSet(m_curStackHt, InterpreterType(retType));
11100+
m_curStackHt++;
11101+
return true;
11102+
11103+
case CORINFO_TYPE_INT:
11104+
m_curStackHt -= 2;
11105+
OpStackSet<LONG>(m_curStackHt, InterlockedExchangeAdd(
11106+
OpStackGet<LONG*>(locationInd),
11107+
OpStackGet<LONG>(valueInd)));
11108+
OpStackTypeSet(m_curStackHt, InterpreterType(retType));
11109+
m_curStackHt++;
11110+
return true;
11111+
11112+
default:
11113+
// Non must-expand intrinsics
11114+
return false;
11115+
}
11116+
}
11117+
1090611118
void Interpreter::RecordConstrainedCall()
1090711119
{
1090811120
CONTRACTL {
@@ -11762,6 +11974,34 @@ Interpreter::InterpreterNamedIntrinsics Interpreter::getNamedIntrinsicID(CEEInfo
1176211974
}
1176311975
}
1176411976
}
11977+
else if (strcmp(namespaceName, "CompilerServices") == 0)
11978+
{
11979+
if (strcmp(className, "RuntimeHelpers") == 0)
11980+
{
11981+
if (strcmp(methodName, "IsReferenceOrContainsReferences") == 0)
11982+
{
11983+
result = NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences;
11984+
}
11985+
}
11986+
}
11987+
}
11988+
else if (strncmp(namespaceName, "Threading", 8) == 0)
11989+
{
11990+
if (strcmp(className, "Interlocked") == 0)
11991+
{
11992+
if (strcmp(methodName, "CompareExchange") == 0)
11993+
{
11994+
result = NI_System_Threading_Interlocked_CompareExchange;
11995+
}
11996+
else if (strcmp(methodName, "Exchange") == 0)
11997+
{
11998+
result = NI_System_Threading_Interlocked_Exchange;
11999+
}
12000+
else if (strcmp(methodName, "ExchangeAdd") == 0)
12001+
{
12002+
result = NI_System_Threading_Interlocked_ExchangeAdd;
12003+
}
12004+
}
1176512005
}
1176612006
}
1176712007
}

src/coreclr/vm/interpreter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,10 @@ class Interpreter
920920
NI_Illegal = 0,
921921
NI_System_StubHelpers_GetStubContext,
922922
NI_System_Runtime_InteropService_MemoryMarshal_GetArrayDataReference,
923+
NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences,
924+
NI_System_Threading_Interlocked_CompareExchange,
925+
NI_System_Threading_Interlocked_Exchange,
926+
NI_System_Threading_Interlocked_ExchangeAdd,
923927
};
924928
static InterpreterNamedIntrinsics getNamedIntrinsicID(CEEInfo* info, CORINFO_METHOD_HANDLE methodHnd);
925929
static const char* getMethodName(CEEInfo* info, CORINFO_METHOD_HANDLE hnd, const char** className, const char** namespaceName = NULL, const char **enclosingClassName = NULL);
@@ -1790,6 +1794,10 @@ class Interpreter
17901794
void DoSIMDHwAccelerated();
17911795
void DoGetIsSupported();
17921796
void DoGetArrayDataReference();
1797+
void DoIsReferenceOrContainsReferences(CORINFO_METHOD_HANDLE method);
1798+
bool DoInterlockedCompareExchange(CorInfoType retType);
1799+
bool DoInterlockedExchange(CorInfoType retType);
1800+
bool DoInterlockedExchangeAdd(CorInfoType retType);
17931801

17941802
// Returns the proper generics context for use in resolving tokens ("precise" in the sense of including generic instantiation
17951803
// information).

src/coreclr/vm/threadstatics.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,8 @@ bool CanJITOptimizeTLSAccess()
811811
// Optimization is disabled for linux musl arm64
812812
#elif defined(TARGET_FREEBSD) && defined(TARGET_ARM64)
813813
// Optimization is disabled for FreeBSD/arm64
814+
#elif defined(FEATURE_INTERPRETER)
815+
// Optimization is disabled when interpreter may be used
814816
#else
815817
optimizeThreadStaticAccess = true;
816818
#if !defined(TARGET_OSX) && defined(TARGET_UNIX) && defined(TARGET_AMD64)

0 commit comments

Comments
 (0)