Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: import static readonly fields holding frozen objects as const handles #76112

Merged
merged 70 commits into from
Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
0fdefe2
handle static readonly for frozen objects in jit
EgorBo Sep 24, 2022
07fdafc
Address Jan's feedback
EgorBo Sep 24, 2022
ad69145
Address feedback
EgorBo Sep 24, 2022
f10b886
Mitigate regressions via getObjectType
EgorBo Sep 24, 2022
9bf46c6
Merge branch 'main' of github.com:dotnet/runtime into frozen-static-r…
EgorBo Sep 24, 2022
97c3cdf
use memcpy
EgorBo Sep 24, 2022
fd7e742
Address feedback
EgorBo Sep 24, 2022
75c40ae
make jit-format happy
EgorBo Sep 24, 2022
283b833
Fix FitsI32 assert
EgorBo Sep 24, 2022
00e3e69
clean up in methodcontext
EgorBo Sep 24, 2022
f7902d2
Update importer.cpp
EgorBo Sep 24, 2022
bf7947f
Implement getObjectType for ILC
EgorBo Sep 24, 2022
3f756d9
Merge branch 'frozen-static-readonly' of github.com:EgorBo/runtime-1 …
EgorBo Sep 24, 2022
5374bea
fix clang/gcc
EgorBo Sep 24, 2022
548dc9c
Update src/coreclr/vm/jitinterface.cpp
EgorBo Sep 24, 2022
1234e97
Address feedback
EgorBo Sep 24, 2022
1c584a1
Initial impl for NativeAOT
EgorBo Sep 25, 2022
1fc50c7
Use PreinitializationInfo
EgorBo Sep 25, 2022
600209f
Handle strings
EgorBo Sep 25, 2022
4cad20a
Update src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
EgorBo Sep 25, 2022
28f2749
Apply suggestions from code review
EgorBo Sep 25, 2022
594a6c2
Merge branch 'frozen-static-readonly' of github.com:EgorBo/runtime-1 …
EgorBo Sep 25, 2022
a07232d
clean up
EgorBo Sep 25, 2022
0c6fe27
Add a comment
EgorBo Sep 25, 2022
271f442
Fix build
EgorBo Sep 25, 2022
76f6cdc
Fix build
EgorBo Sep 25, 2022
133c268
Address Michal's feedback
EgorBo Sep 26, 2022
8880954
Address Michal's feedback
EgorBo Sep 26, 2022
63cd7cc
Simplify PM switch
EgorBo Sep 26, 2022
2656719
Implement getObjectType for FrozenObjectNode
EgorBo Sep 26, 2022
1006572
Address feedback
EgorBo Sep 26, 2022
543a5f5
Merge branch 'main' of github.com:dotnet/runtime into frozen-static-r…
EgorBo Sep 26, 2022
0d27470
Add isObjectImmutable
EgorBo Sep 26, 2022
48343ee
Update gcinfo.cpp
EgorBo Sep 26, 2022
7264f67
fix getRuntimeTypePointer on NativeAOT
EgorBo Sep 26, 2022
9aa1b6c
Merge branch 'frozen-static-readonly' of github.com:EgorBo/runtime-1 …
EgorBo Sep 26, 2022
8d4048a
fix getRuntimeTypePointer on NativeAOT
EgorBo Sep 26, 2022
edb4cd3
Merge branch 'main' of github.com:dotnet/runtime into frozen-static-r…
EgorBo Sep 26, 2022
0e75429
Merge branch 'main' of github.com:dotnet/runtime into frozen-static-r…
EgorBo Oct 6, 2022
44a8fc6
Fix compilation
EgorBo Oct 6, 2022
256bbd4
Apply suggestions from code review
EgorBo Oct 6, 2022
fefbbe1
ILC: use correct TargetPtrSize
EgorBo Oct 6, 2022
3d06a33
Address feedback
EgorBo Oct 6, 2022
afefe44
Add support for delegates, empty arrays and objects without fields
EgorBo Oct 6, 2022
eece561
Add some comments
EgorBo Oct 6, 2022
e2ae117
Update src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs
EgorBo Oct 6, 2022
34b39cb
Apply suggestions from code review
EgorBo Oct 6, 2022
22baa43
Ah, actually we need that condition, it can be a struct
EgorBo Oct 6, 2022
4752354
Address Jakob's feedback
EgorBo Oct 7, 2022
49ab023
fix compilation
EgorBo Oct 7, 2022
08d59f5
Use AssertMapExistsNoMessage
EgorBo Oct 7, 2022
45cd02b
Address feedback
EgorBo Oct 7, 2022
b578780
Update src/coreclr/vm/jitinterface.cpp
EgorBo Oct 7, 2022
84de4d2
Address feedback
EgorBo Oct 7, 2022
05e2639
fix compilation
EgorBo Oct 7, 2022
771fd6a
Update src/coreclr/vm/jitinterface.cpp
EgorBo Oct 7, 2022
1acf664
Update src/coreclr/vm/jitinterface.cpp
EgorBo Oct 7, 2022
806e985
Address feedback
EgorBo Oct 7, 2022
2a0fc88
add assert
EgorBo Oct 7, 2022
fe169c6
limit to primitives and objects on llc side
EgorBo Oct 7, 2022
30fbd21
fix check
EgorBo Oct 7, 2022
7c2d8dc
Apply suggestions from code review
EgorBo Oct 8, 2022
be7c16f
Fix indention since "if" was removed
EgorBo Oct 8, 2022
5fbe665
Apply suggestions from code review
EgorBo Oct 8, 2022
56908b0
Address feedback, align NativeAOT logic with CoreCLR
EgorBo Oct 8, 2022
cbe5cd0
Update src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoIm…
EgorBo Oct 8, 2022
76a72d8
Update src/coreclr/vm/jitinterface.cpp
jkotas Oct 8, 2022
aa5be51
Keep IsThreadStatic on vm side
EgorBo Oct 8, 2022
0f8b7cd
Merge branch 'main' of github.com:dotnet/runtime into frozen-static-r…
EgorBo Oct 9, 2022
e4ce64d
Merge branch 'main' of github.com:dotnet/runtime into frozen-static-r…
EgorBo Oct 10, 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
15 changes: 15 additions & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -2486,6 +2486,10 @@ class ICorStaticInfo
CORINFO_CLASS_HANDLE cls
) = 0;

virtual CORINFO_CLASS_HANDLE getObjectType(
void* typeObj
) = 0;

virtual bool getReadyToRunHelper(
CORINFO_RESOLVED_TOKEN * pResolvedToken,
CORINFO_LOOKUP_KIND * pGenericLookupKind,
Expand Down Expand Up @@ -3150,6 +3154,17 @@ class ICorDynamicInfo : public ICorStaticInfo
void **ppIndirection = NULL
) = 0;

// Returns true if the given field is "static readonly" and its value will never change
// also, returns its actual value via pValue argument. For now, it's either:
// * integer/floating point primitive
// * null
// * frozen object
virtual bool getReadonlyStaticFieldValue(
CORINFO_FIELD_HANDLE field,
uint8_t *buffer,
int bufferSize
) = 0;

// If pIsSpeculative is NULL, return the class handle for the value of ref-class typed
// static readonly fields, if there is a unique location for the static and the class
// is already initialized.
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/inc/icorjitinfoimpl_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ CorInfoHelpFunc getUnBoxHelper(
void* getRuntimeTypePointer(
CORINFO_CLASS_HANDLE cls) override;

CORINFO_CLASS_HANDLE getObjectType(
void* typeObj) override;

bool getReadyToRunHelper(
CORINFO_RESOLVED_TOKEN* pResolvedToken,
CORINFO_LOOKUP_KIND* pGenericLookupKind,
Expand Down Expand Up @@ -605,6 +608,11 @@ void* getFieldAddress(
CORINFO_FIELD_HANDLE field,
void** ppIndirection) override;

bool getReadonlyStaticFieldValue(
CORINFO_FIELD_HANDLE field,
uint8_t* buffer,
int bufferSize) override;

CORINFO_CLASS_HANDLE getStaticFieldCurrentClass(
CORINFO_FIELD_HANDLE field,
bool* pIsSpeculative) override;
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 = { /* 11b4ea58-c400-4c3d-995e-4e2f0676f6e8 */
0x11b4ea58,
0xc400,
0x4c3d,
{0x99, 0x5e, 0x4e, 0x2f, 0x6, 0x76, 0xf6, 0xe8}
constexpr GUID JITEEVersionIdentifier = { /* ebc78c0f-f765-41ea-9cf3-519e36dc6ce2 */
0xebc78c0f,
0xf765,
0x41ea,
{0x9c, 0xf3, 0x51, 0x9e, 0x36, 0xdc, 0x6c, 0xe2}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/ICorJitInfo_API_names.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ DEF_CLR_API(getTypeForBox)
DEF_CLR_API(getBoxHelper)
DEF_CLR_API(getUnBoxHelper)
DEF_CLR_API(getRuntimeTypePointer)
DEF_CLR_API(getObjectType)
DEF_CLR_API(getReadyToRunHelper)
DEF_CLR_API(getReadyToRunDelegateCtorHelper)
DEF_CLR_API(getHelperName)
Expand Down Expand Up @@ -152,6 +153,7 @@ DEF_CLR_API(canAccessFamily)
DEF_CLR_API(isRIDClassDomainID)
DEF_CLR_API(getClassDomainID)
DEF_CLR_API(getFieldAddress)
DEF_CLR_API(getReadonlyStaticFieldValue)
DEF_CLR_API(getStaticFieldCurrentClass)
DEF_CLR_API(getVarArgsHandle)
DEF_CLR_API(canGetVarArgsHandle)
Expand Down
20 changes: 20 additions & 0 deletions src/coreclr/jit/ICorJitInfo_API_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,15 @@ void* WrapICorJitInfo::getRuntimeTypePointer(
return temp;
}

CORINFO_CLASS_HANDLE WrapICorJitInfo::getObjectType(
void* typeObj)
{
API_ENTER(getObjectType);
CORINFO_CLASS_HANDLE temp = wrapHnd->getObjectType(typeObj);
API_LEAVE(getObjectType);
return temp;
}

bool WrapICorJitInfo::getReadyToRunHelper(
CORINFO_RESOLVED_TOKEN* pResolvedToken,
CORINFO_LOOKUP_KIND* pGenericLookupKind,
Expand Down Expand Up @@ -1450,6 +1459,17 @@ void* WrapICorJitInfo::getFieldAddress(
return temp;
}

bool WrapICorJitInfo::getReadonlyStaticFieldValue(
CORINFO_FIELD_HANDLE field,
uint8_t* buffer,
int bufferSize)
{
API_ENTER(getReadonlyStaticFieldValue);
bool temp = wrapHnd->getReadonlyStaticFieldValue(field, buffer, bufferSize);
API_LEAVE(getReadonlyStaticFieldValue);
return temp;
}

CORINFO_CLASS_HANDLE WrapICorJitInfo::getStaticFieldCurrentClass(
CORINFO_FIELD_HANDLE field,
bool* pIsSpeculative)
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3639,7 +3639,7 @@ class Compiler

GenTree* impInitClass(CORINFO_RESOLVED_TOKEN* pResolvedToken);

GenTree* impImportStaticReadOnlyField(void* fldAddr, var_types lclTyp);
GenTree* impImportStaticReadOnlyField(uint8_t* buffer, int bufferSize, var_types valueType);

GenTree* impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedToken,
CORINFO_ACCESS_FLAGS access,
Expand Down
14 changes: 14 additions & 0 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17574,6 +17574,20 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b
break;
}

case GT_CNS_INT:
{
if (tree->IsIconHandle(GTF_ICON_OBJ_HDL))
{
objClass = info.compCompHnd->getObjectType((void*)tree->AsIntCon()->IconValue());
if (objClass != NO_CLASS_HANDLE)
{
// if we managed to get a class handle it's definitely not null
*pIsNonNull = true;
}
}
break;
}

case GT_RET_EXPR:
{
// If we see a RET_EXPR, recurse through to examine the
Expand Down
201 changes: 73 additions & 128 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8116,133 +8116,92 @@ GenTree* Compiler::impInitClass(CORINFO_RESOLVED_TOKEN* pResolvedToken)
return node;
}

GenTree* Compiler::impImportStaticReadOnlyField(void* fldAddr, var_types lclTyp)
GenTree* Compiler::impImportStaticReadOnlyField(uint8_t* buffer, int bufferSize, var_types valueType)
{
GenTree* op1 = nullptr;

#if defined(DEBUG)
// If we're replaying under SuperPMI, we're going to read the data stored by SuperPMI and use it
// for optimization. Unfortunately, SuperPMI doesn't implement a guarantee on the alignment of
// this data, so for some platforms which don't allow unaligned access (e.g., Linux arm32),
// this can fault. We should fix SuperPMI to guarantee alignment, but that is a big change.
// Instead, simply fix up the data here for future use.

// This variable should be the largest size element, with the largest alignment requirement,
// and the native C++ compiler should guarantee sufficient alignment.
double aligned_data = 0.0;
void* p_aligned_data = &aligned_data;
if (info.compMethodSuperPMIIndex != -1)
{
switch (lclTyp)
{
case TYP_BOOL:
case TYP_BYTE:
case TYP_UBYTE:
static_assert_no_msg(sizeof(unsigned __int8) == sizeof(bool));
static_assert_no_msg(sizeof(unsigned __int8) == sizeof(signed char));
static_assert_no_msg(sizeof(unsigned __int8) == sizeof(unsigned char));
// No alignment necessary for byte.
break;

case TYP_SHORT:
case TYP_USHORT:
static_assert_no_msg(sizeof(unsigned __int16) == sizeof(short));
static_assert_no_msg(sizeof(unsigned __int16) == sizeof(unsigned short));
if ((size_t)fldAddr % sizeof(unsigned __int16) != 0)
{
*(unsigned __int16*)p_aligned_data = GET_UNALIGNED_16(fldAddr);
fldAddr = p_aligned_data;
}
break;

case TYP_INT:
case TYP_UINT:
case TYP_FLOAT:
static_assert_no_msg(sizeof(unsigned __int32) == sizeof(int));
static_assert_no_msg(sizeof(unsigned __int32) == sizeof(unsigned int));
static_assert_no_msg(sizeof(unsigned __int32) == sizeof(float));
if ((size_t)fldAddr % sizeof(unsigned __int32) != 0)
{
*(unsigned __int32*)p_aligned_data = GET_UNALIGNED_32(fldAddr);
fldAddr = p_aligned_data;
}
break;

case TYP_LONG:
case TYP_ULONG:
case TYP_DOUBLE:
static_assert_no_msg(sizeof(unsigned __int64) == sizeof(__int64));
static_assert_no_msg(sizeof(unsigned __int64) == sizeof(double));
if ((size_t)fldAddr % sizeof(unsigned __int64) != 0)
{
*(unsigned __int64*)p_aligned_data = GET_UNALIGNED_64(fldAddr);
fldAddr = p_aligned_data;
}
break;

default:
assert(!"Unexpected lclTyp");
break;
}
}
#endif // DEBUG
// We plan to support larger values (for structs), for now let's keep it 64 bit
assert(bufferSize == sizeof(INT64));

switch (lclTyp)
GenTree* tree = nullptr;
switch (valueType)
{
int ival;
__int64 lval;
double dval;
// Use memcpy to read from the buffer and create an Icon/Dcon tree
#define CreateTreeFromBuffer(type, treeFactory) \
type v##type; \
memcpy(&v##type, buffer, sizeof(type)); \
tree = treeFactory(v##type);

case TYP_BOOL:
ival = *((bool*)fldAddr);
goto IVAL_COMMON;

{
CreateTreeFromBuffer(bool, gtNewIconNode);
break;
}
case TYP_BYTE:
ival = *((signed char*)fldAddr);
goto IVAL_COMMON;

{
CreateTreeFromBuffer(int8_t, gtNewIconNode);
break;
}
case TYP_UBYTE:
ival = *((unsigned char*)fldAddr);
goto IVAL_COMMON;

{
CreateTreeFromBuffer(uint8_t, gtNewIconNode);
break;
}
case TYP_SHORT:
ival = *((short*)fldAddr);
goto IVAL_COMMON;

{
CreateTreeFromBuffer(int16_t, gtNewIconNode);
break;
}
case TYP_USHORT:
ival = *((unsigned short*)fldAddr);
goto IVAL_COMMON;

{
CreateTreeFromBuffer(uint16_t, gtNewIconNode);
break;
}
case TYP_UINT:
case TYP_INT:
ival = *((int*)fldAddr);
IVAL_COMMON:
op1 = gtNewIconNode(ival);
{
CreateTreeFromBuffer(int32_t, gtNewIconNode);
break;

}
case TYP_LONG:
case TYP_ULONG:
lval = *((__int64*)fldAddr);
op1 = gtNewLconNode(lval);
{
CreateTreeFromBuffer(int64_t, gtNewLconNode);
break;

}
case TYP_FLOAT:
dval = *((float*)fldAddr);
op1 = gtNewDconNode(dval);
op1->gtType = TYP_FLOAT;
{
CreateTreeFromBuffer(float, gtNewDconNode);
break;

}
case TYP_DOUBLE:
dval = *((double*)fldAddr);
op1 = gtNewDconNode(dval);
{
CreateTreeFromBuffer(double, gtNewDconNode);
break;
}
case TYP_REF:
{
ssize_t ptr;
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
memcpy(&ptr, buffer, sizeof(ssize_t));

default:
assert(!"Unexpected lclTyp");
if (ptr == 0)
{
tree = gtNewNull();
}
else
{
setMethodHasFrozenObjects();
tree = gtNewIconEmbHndNode((void*)ptr, nullptr, GTF_ICON_OBJ_HDL, nullptr);
tree->gtType = TYP_REF;
INDEBUG(tree->AsIntCon()->gtTargetHandle = (size_t)ptr);
}
break;
}
default:
return nullptr;
}

return op1;
assert(tree != nullptr);
tree->gtType = genActualType(valueType);
return tree;
}

GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedToken,
Expand Down Expand Up @@ -15322,34 +15281,20 @@ void Compiler::impImportBlockCode(BasicBlock* block)
case CORINFO_FIELD_STATIC_ADDRESS:
// Replace static read-only fields with constant if possible
if ((aflags & CORINFO_ACCESS_GET) && (fieldInfo.fieldFlags & CORINFO_FLG_FIELD_FINAL) &&
!(fieldInfo.fieldFlags & CORINFO_FLG_FIELD_STATIC_IN_HEAP) &&
(varTypeIsIntegral(lclTyp) || varTypeIsFloating(lclTyp)))
!(fieldInfo.fieldFlags & CORINFO_FLG_FIELD_STATIC_IN_HEAP))
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
{
CorInfoInitClassResult initClassResult =
info.compCompHnd->initClass(resolvedToken.hField, info.compMethodHnd,
impTokenLookupContextHandle);

if (initClassResult & CORINFO_INITCLASS_INITIALIZED)
const int bufferSize = sizeof(uint64_t);
uint8_t buffer[bufferSize] = {0};
if (info.compCompHnd->getReadonlyStaticFieldValue(resolvedToken.hField, buffer, bufferSize))
{
void** pFldAddr = nullptr;
void* fldAddr =
info.compCompHnd->getFieldAddress(resolvedToken.hField, (void**)&pFldAddr);

// We should always be able to access this static's address directly
//
assert(pFldAddr == nullptr);

op1 = impImportStaticReadOnlyField(fldAddr, lclTyp);

// Widen small types since we're propagating the value
// instead of producing an indir.
//
op1->gtType = genActualType(lclTyp);

goto FIELD_DONE;
GenTree* cnsValue = impImportStaticReadOnlyField(buffer, bufferSize, lclTyp);
if (cnsValue != nullptr)
{
op1 = cnsValue;
goto FIELD_DONE;
}
}
}

FALLTHROUGH;

case CORINFO_FIELD_STATIC_RVA_ADDRESS:
Expand Down
Loading