Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f335448
Fold const RVA access
EgorBo Nov 23, 2022
0282bfe
fix build
EgorBo Nov 23, 2022
9780a9a
Address feedback
EgorBo Nov 23, 2022
ae56d28
Clean up
EgorBo Nov 23, 2022
e35ac7c
fix compilation
EgorBo Nov 24, 2022
b8e2dd5
NativeAOT support (jit side)
EgorBo Nov 24, 2022
aaf129e
Update src/coreclr/vm/jitinterface.cpp
EgorBo Nov 24, 2022
f7b0d4e
Implement NativeAOT side
EgorBo Nov 24, 2022
1a6400a
Merge branch 'fold-const-rva' of github.com:EgorBo/runtime-1 into fol…
EgorBo Nov 24, 2022
29f4e9d
Update assertionprop.cpp
EgorBo Nov 24, 2022
8d471c1
Add tests, clean up
EgorBo Nov 24, 2022
7f402fc
Update ConstIndexRVA.cs
EgorBo Nov 24, 2022
14743b6
fix test
EgorBo Nov 24, 2022
784d02b
Merge branch 'fold-const-rva' of github.com:EgorBo/runtime-1 into fol…
EgorBo Nov 24, 2022
0dbc4c2
Merge branch 'main' of github.com:dotnet/runtime into fold-const-rva
EgorBo Nov 24, 2022
677c55d
Merge branch 'main' of github.com:dotnet/runtime into fold-const-rva
EgorBo Nov 25, 2022
142ad61
Address feedback
EgorBo Nov 26, 2022
66e481c
fix assert
EgorBo Nov 26, 2022
c4aa231
Update src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorIn…
EgorBo Nov 26, 2022
8164730
Fix AOT
EgorBo Nov 26, 2022
7c55cea
Merge branch 'main' of github.com:dotnet/runtime into fold-const-rva
EgorBo Nov 28, 2022
d718816
Address feedback
EgorBo Nov 28, 2022
f8051a8
make tryReadRvaFieldData static
EgorBo Nov 28, 2022
f45a76e
address feedback
EgorBo Nov 28, 2022
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
1 change: 1 addition & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -3212,6 +3212,7 @@ class ICorDynamicInfo : public ICorStaticInfo
CORINFO_FIELD_HANDLE field,
uint8_t *buffer,
int bufferSize,
int valueOffset = 0,
bool ignoreMovableObjects = true
) = 0;

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/inc/icorjitinfoimpl_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ bool getReadonlyStaticFieldValue(
CORINFO_FIELD_HANDLE field,
uint8_t* buffer,
int bufferSize,
int valueOffset,
bool ignoreMovableObjects) override;

CORINFO_CLASS_HANDLE getStaticFieldCurrentClass(
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
#define GUID_DEFINED
#endif // !GUID_DEFINED

constexpr GUID JITEEVersionIdentifier = { /* da097b39-7f43-458a-990a-0b65406d5ff3 */
0xda097b39,
0x7f43,
0x458a,
{0x99, 0xa, 0xb, 0x65, 0x40, 0x6d, 0x5f, 0xf3}
constexpr GUID JITEEVersionIdentifier = { /* 0330a175-dd05-4760-840f-a1a4c47284d3 */
0x330a175,
0xdd05,
0x4760,
{0x84, 0xf, 0xa1, 0xa4, 0xc4, 0x72, 0x84, 0xd3}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1488,10 +1488,11 @@ bool WrapICorJitInfo::getReadonlyStaticFieldValue(
CORINFO_FIELD_HANDLE field,
uint8_t* buffer,
int bufferSize,
int valueOffset,
bool ignoreMovableObjects)
{
API_ENTER(getReadonlyStaticFieldValue);
bool temp = wrapHnd->getReadonlyStaticFieldValue(field, buffer, bufferSize, ignoreMovableObjects);
bool temp = wrapHnd->getReadonlyStaticFieldValue(field, buffer, bufferSize, valueOffset, ignoreMovableObjects);
API_LEAVE(getReadonlyStaticFieldValue);
return temp;
}
Expand Down
159 changes: 157 additions & 2 deletions src/coreclr/jit/valuenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2113,7 +2113,7 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN)
{
uint8_t buffer[TARGET_POINTER_SIZE] = {0};
if (m_pComp->info.compCompHnd->getReadonlyStaticFieldValue(field, buffer,
TARGET_POINTER_SIZE, false))
TARGET_POINTER_SIZE, 0, false))
{
// In case of 64bit jit emitting 32bit codegen this handle will be 64bit
// value holding 32bit handle with upper half zeroed (hence, "= NULL").
Expand Down Expand Up @@ -8494,6 +8494,65 @@ void Compiler::fgValueNumberSsaVarDef(GenTreeLclVarCommon* lcl)
}
}

//----------------------------------------------------------------------------------
// fgGetFieldSeqAndAddress: Try to obtain a constant address with a FieldSeq from the
// given tree. It can be either INT_CNS or e.g. ADD(INT_CNS, ADD(INT_CNS, INT_CNS))
// tree where only one of the constants is expected to have a field sequence.
//
// Arguments:
// tree - tree node to inspect
// pAddress - [Out] resulting address with all offsets combined
// pFseq - [Out] field sequence
//
// Return Value:
// true if the pattern was recognized and a new VN is assigned
//
static bool fgGetFieldSeqAndAddress(GenTree* tree, ssize_t* pAddress, FieldSeq** pFseq)
{
if (tree->IsCnsIntOrI())
{
ssize_t val = tree->AsIntCon()->IconValue();
FieldSeq* fseq = tree->AsIntCon()->gtFieldSeq;
if (fseq != nullptr)
{
*pFseq = fseq;
*pAddress = val;
return true;
}
return false;
}

ssize_t val = 0;
while (tree->OperIs(GT_ADD))
{
GenTree* op1 = tree->gtGetOp1();
GenTree* op2 = tree->gtGetOp2();
if (op1->IsCnsIntOrI() && (op1->AsIntCon()->gtFieldSeq == nullptr))
{
val += op1->AsIntCon()->IconValue();
tree = op2;
}
else if (op2->IsCnsIntOrI() && (op2->AsIntCon()->gtFieldSeq == nullptr))
{
val += op2->AsIntCon()->IconValue();
tree = op1;
}
else
{
// Unsupported tree
return false;
}
}

if (tree->IsCnsIntOrI() && (tree->AsIntCon()->gtFieldSeq != nullptr))
{
*pFseq = tree->AsIntCon()->gtFieldSeq;
*pAddress = tree->AsIntCon()->IconValue() + val;
return true;
}
return false;
}

//----------------------------------------------------------------------------------
// fgValueNumberConstLoad: Try to detect const_immutable_array[cns_index] tree
// and apply a constant VN representing given element at cns_index in that array.
Expand All @@ -8506,9 +8565,105 @@ void Compiler::fgValueNumberSsaVarDef(GenTreeLclVarCommon* lcl)
//
bool Compiler::fgValueNumberConstLoad(GenTreeIndir* tree)
{
if (!tree->gtVNPair.BothEqual())
{
return false;
}

// First, let's check if we can detect RVA[const_index] pattern to fold, e.g.:
//
// static ReadOnlySpan<sbyte> RVA => new sbyte[] { -100, 100 }
//
// sbyte GetVal() => RVA[1]; // fold to '100'
//
ssize_t address = 0;
FieldSeq* fieldSeq = nullptr;
if (fgGetFieldSeqAndAddress(tree->gtGetOp1(), &address, &fieldSeq) &&
(fieldSeq->GetKind() == FieldSeq::FieldKind::SimpleStaticKnownAddress))
{
CORINFO_FIELD_HANDLE fieldHandle = fieldSeq->GetFieldHandle();
assert(fieldSeq->GetKind() == FieldSeq::FieldKind::SimpleStaticKnownAddress);

ssize_t byteOffset = address - fieldSeq->GetOffset();
int size = (int)genTypeSize(tree->TypeGet());
const int maxElementSize = sizeof(int64_t);
if ((size > 0) && (size <= maxElementSize) && (byteOffset >= 0) && (byteOffset < INT_MAX))
{
uint8_t buffer[maxElementSize] = {0};
if (info.compCompHnd->getReadonlyStaticFieldValue(fieldHandle, (uint8_t*)&buffer, size, (int)byteOffset))
{
// For now we only support these primitives, we can extend this list to FP, SIMD and structs in future.
switch (tree->TypeGet())
{
#define READ_VALUE(typ) \
typ val = 0; \
memcpy(&val, buffer, sizeof(typ));

case TYP_BOOL:
case TYP_UBYTE:
{
READ_VALUE(uint8_t);
tree->gtVNPair.SetBoth(vnStore->VNForIntCon(val));
return true;
}
case TYP_BYTE:
{
READ_VALUE(int8_t);
tree->gtVNPair.SetBoth(vnStore->VNForIntCon(val));
return true;
}
case TYP_SHORT:
{
READ_VALUE(int16_t);
tree->gtVNPair.SetBoth(vnStore->VNForIntCon(val));
return true;
}
case TYP_USHORT:
{
READ_VALUE(uint16_t);
tree->gtVNPair.SetBoth(vnStore->VNForIntCon(val));
return true;
}
case TYP_INT:
{
READ_VALUE(int32_t);
tree->gtVNPair.SetBoth(vnStore->VNForIntCon(val));
return true;
}
case TYP_UINT:
{
READ_VALUE(uint32_t);
tree->gtVNPair.SetBoth(vnStore->VNForIntCon(val));
return true;
}
case TYP_LONG:
{
READ_VALUE(int64_t);
tree->gtVNPair.SetBoth(vnStore->VNForLongCon(val));
return true;
}
case TYP_ULONG:
{
READ_VALUE(uint64_t);
tree->gtVNPair.SetBoth(vnStore->VNForLongCon(val));
return true;
}
default:
break;
}
}
}
}

// Throughput check, the logic below is only for USHORT (char)
if (!tree->TypeIs(TYP_USHORT))
{
return false;
}

ValueNum addrVN = tree->gtGetOp1()->gtVNPair.GetLiberal();
VNFuncApp funcApp;
if (!tree->TypeIs(TYP_USHORT) || !tree->gtVNPair.BothEqual() || !vnStore->GetVNFunc(addrVN, &funcApp))
if (!vnStore->GetVNFunc(addrVN, &funcApp))
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2244,12 +2244,12 @@ private static uint _getClassDomainID(IntPtr thisHandle, IntPtr* ppException, CO
}

[UnmanagedCallersOnly]
private static byte _getReadonlyStaticFieldValue(IntPtr thisHandle, IntPtr* ppException, CORINFO_FIELD_STRUCT_* field, byte* buffer, int bufferSize, byte ignoreMovableObjects)
private static byte _getReadonlyStaticFieldValue(IntPtr thisHandle, IntPtr* ppException, CORINFO_FIELD_STRUCT_* field, byte* buffer, int bufferSize, int valueOffset, byte ignoreMovableObjects)
{
var _this = GetThis(thisHandle);
try
{
return _this.getReadonlyStaticFieldValue(field, buffer, bufferSize, ignoreMovableObjects != 0) ? (byte)1 : (byte)0;
return _this.getReadonlyStaticFieldValue(field, buffer, bufferSize, valueOffset, ignoreMovableObjects != 0) ? (byte)1 : (byte)0;
}
catch (Exception ex)
{
Expand Down Expand Up @@ -2838,7 +2838,7 @@ private static IntPtr GetUnmanagedCallbacks()
callbacks[148] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte>)&_isRIDClassDomainID;
callbacks[149] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void**, uint>)&_getClassDomainID;
callbacks[150] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, void*>)&_getFieldAddress;
callbacks[151] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, int, byte, byte>)&_getReadonlyStaticFieldValue;
callbacks[151] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, int, int, byte, byte>)&_getReadonlyStaticFieldValue;
callbacks[152] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, CORINFO_CLASS_STRUCT_*>)&_getStaticFieldCurrentClass;
callbacks[153] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, IntPtr>)&_getVarArgsHandle;
callbacks[154] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetVarArgsHandle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ FUNCTIONS
bool isRIDClassDomainID(CORINFO_CLASS_HANDLE cls);
unsigned getClassDomainID (CORINFO_CLASS_HANDLE cls, void **ppIndirection);
void* getFieldAddress(CORINFO_FIELD_HANDLE field, VOIDSTARSTAR ppIndirection);
bool getReadonlyStaticFieldValue(CORINFO_FIELD_HANDLE field, uint8_t *buffer, int bufferSize, bool ignoreMovableObjects);
bool getReadonlyStaticFieldValue(CORINFO_FIELD_HANDLE field, uint8_t *buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects);
CORINFO_CLASS_HANDLE getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, BoolStar pIsSpeculative);
CORINFO_VARARGS_HANDLE getVarArgsHandle(CORINFO_SIG_INFO *pSig, void **ppIndirection);
bool canGetVarArgsHandle(CORINFO_SIG_INFO *pSig);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2998,8 +2998,25 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses
return 0;
}

private bool getReadonlyStaticFieldValue(CORINFO_FIELD_STRUCT_* fieldHandle, byte* buffer, int bufferSize, bool ignoreMovableObjects)
private bool getReadonlyStaticFieldValue(CORINFO_FIELD_STRUCT_* fieldHandle, byte* buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects)
{
Debug.Assert(fieldHandle != null);
Debug.Assert(buffer != null);
Debug.Assert(bufferSize > 0);
Debug.Assert(valueOffset >= 0);

FieldDesc field = HandleToObject(fieldHandle);
Debug.Assert(field.IsStatic);

if (!field.IsThreadStatic && field.IsInitOnly && field.HasRva && field is EcmaField ecmaField)
{
ReadOnlySpan<byte> rvaData = ecmaField.GetFieldRvaData();
if (rvaData.Length >= bufferSize && valueOffset <= rvaData.Length - bufferSize)
{
rvaData.Slice(valueOffset, bufferSize).CopyTo(new Span<byte>(buffer, bufferSize));
return true;
}
}
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

using ILCompiler;
using ILCompiler.DependencyAnalysis;
using Internal.TypeSystem.Ecma;

#if SUPPORT_JIT
using MethodCodeNode = Internal.Runtime.JitSupport.JitMethodCodeNode;
Expand Down Expand Up @@ -2213,17 +2214,33 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses
return index;
}

private bool getReadonlyStaticFieldValue(CORINFO_FIELD_STRUCT_* fieldHandle, byte* buffer, int bufferSize, bool ignoreMovableObjects)
private bool getReadonlyStaticFieldValue(CORINFO_FIELD_STRUCT_* fieldHandle, byte* buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects)
{
Debug.Assert(fieldHandle != null);
Debug.Assert(buffer != null);
Debug.Assert(bufferSize > 0);
Debug.Assert(valueOffset >= 0);

FieldDesc field = HandleToObject(fieldHandle);
Debug.Assert(field.IsStatic);


if (!field.IsThreadStatic && field.IsInitOnly && field.OwningType is MetadataType owningType)
{
if (field.HasRva)
{
if (field is EcmaField ecmaField)
{
ReadOnlySpan<byte> rvaData = ecmaField.GetFieldRvaData();
if (rvaData.Length >= bufferSize && valueOffset <= rvaData.Length - bufferSize)
{
rvaData.Slice(valueOffset, bufferSize).CopyTo(new Span<byte>(buffer, bufferSize));
return true;
}
}
return false;
}

PreinitializationManager preinitManager = _compilation.NodeFactory.PreinitializationManager;
if (preinitManager.IsPreinitialized(owningType))
{
Expand All @@ -2234,6 +2251,7 @@ private bool getReadonlyStaticFieldValue(CORINFO_FIELD_STRUCT_* fieldHandle, byt

if (value == null)
{
Debug.Assert(valueOffset == 0);
Debug.Assert(bufferSize == targetPtrSize);

// Write "null" to buffer
Expand All @@ -2246,13 +2264,15 @@ private bool getReadonlyStaticFieldValue(CORINFO_FIELD_STRUCT_* fieldHandle, byt
switch (data)
{
case byte[] bytes:
Debug.Assert(bufferSize == bytes.Length);

// Ensure we have enough room in the buffer, it can be a large struct
bytes.AsSpan().CopyTo(new Span<byte>(buffer, bufferSize));
return true;
if (bytes.Length >= bufferSize && valueOffset <= bytes.Length - bufferSize)
{
bytes.AsSpan(valueOffset, bufferSize).CopyTo(new Span<byte>(buffer, bufferSize));
return true;
}
return false;

case FrozenObjectNode or FrozenStringNode:
Debug.Assert(valueOffset == 0);
Debug.Assert(bufferSize == targetPtrSize);

// save handle's value to buffer
Expand Down
5 changes: 3 additions & 2 deletions src/coreclr/tools/aot/jitinterface/jitinterface_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ struct JitInterfaceCallbacks
bool (* isRIDClassDomainID)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls);
unsigned (* getClassDomainID)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, void** ppIndirection);
void* (* getFieldAddress)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE field, void** ppIndirection);
bool (* getReadonlyStaticFieldValue)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE field, uint8_t* buffer, int bufferSize, bool ignoreMovableObjects);
bool (* getReadonlyStaticFieldValue)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE field, uint8_t* buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects);
CORINFO_CLASS_HANDLE (* getStaticFieldCurrentClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE field, bool* pIsSpeculative);
CORINFO_VARARGS_HANDLE (* getVarArgsHandle)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_SIG_INFO* pSig, void** ppIndirection);
bool (* canGetVarArgsHandle)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_SIG_INFO* pSig);
Expand Down Expand Up @@ -1665,10 +1665,11 @@ class JitInterfaceWrapper : public ICorJitInfo
CORINFO_FIELD_HANDLE field,
uint8_t* buffer,
int bufferSize,
int valueOffset,
bool ignoreMovableObjects)
{
CorInfoExceptionClass* pException = nullptr;
bool temp = _callbacks->getReadonlyStaticFieldValue(_thisHandle, &pException, field, buffer, bufferSize, ignoreMovableObjects);
bool temp = _callbacks->getReadonlyStaticFieldValue(_thisHandle, &pException, field, buffer, bufferSize, valueOffset, ignoreMovableObjects);
if (pException != nullptr) throw pException;
return temp;
}
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ LWM(GetDelegateCtor, Agnostic_GetDelegateCtorIn, Agnostic_GetDelegateCtorOut)
LWM(GetEEInfo, DWORD, Agnostic_CORINFO_EE_INFO)
LWM(GetEHinfo, DLD, Agnostic_CORINFO_EH_CLAUSE)
LWM(GetFieldAddress, DWORDLONG, Agnostic_GetFieldAddress)
LWM(GetReadonlyStaticFieldValue, DLDD, DD)
LWM(GetReadonlyStaticFieldValue, DLDDD, DD)
LWM(GetStaticFieldCurrentClass, DWORDLONG, Agnostic_GetStaticFieldCurrentClass)
LWM(GetFieldClass, DWORDLONG, DWORDLONG)
LWM(GetFieldInClass, DLD, DWORDLONG)
Expand Down
Loading