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: Fold const RVA access (via const index) #78783

Merged
merged 24 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
19 changes: 16 additions & 3 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3335,7 +3335,8 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion,
return nullptr;
}

GenTree* newTree = tree;
GenTree* newTree = tree;
bool propagateType = false;

// Update 'newTree' with the new value from our table
// Typically newTree == tree and we are updating the node in place
Expand Down Expand Up @@ -3364,9 +3365,16 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion,
case O2K_CONST_INT:

// Don't propagate handles if we need to report relocs.
if (opts.compReloc && curAssertion->op2.HasIconFlag())
if (opts.compReloc && curAssertion->op2.HasIconFlag() && curAssertion->op2.u1.iconVal != 0)
{
return nullptr;
if (curAssertion->op2.GetIconFlag() == GTF_ICON_STATIC_HDL)
{
propagateType = true;
}
else
{
return nullptr;
}
}

// We assume that we do not try to do assertion prop on mismatched
Expand Down Expand Up @@ -3394,6 +3402,11 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion,
// Conservatively don't allow propagation of ICON TYP_REF into BYREF
return nullptr;
}
propagateType = true;
}

if (propagateType)
{
newTree->ChangeType(tree->TypeGet());
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,11 @@ bool CodeGen::genCreateAddrMode(
if (op2->IsIntCnsFitsInI32() && (op2->gtType != TYP_REF) && FitsIn<INT32>(cns + op2->AsIntConCommon()->IconValue()))
{
// We should not be building address modes out of non-foldable constants
assert(op2->AsIntConCommon()->ImmedValCanBeFolded(compiler, addr->OperGet()));
if (!op2->AsIntConCommon()->ImmedValCanBeFolded(compiler, addr->OperGet()))
{
assert(compiler->opts.compReloc);
return false;
}

/* We're adding a constant */

Expand Down
148 changes: 146 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,55 @@ void Compiler::fgValueNumberSsaVarDef(GenTreeLclVarCommon* lcl)
}
}

//----------------------------------------------------------------------------------
// fgGetStaticFieldSeqAndAddress: 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 given tree is a static field address
//
static bool fgGetStaticFieldSeqAndAddress(GenTree* tree, ssize_t* pAddress, FieldSeq** pFseq)
{
ssize_t val = 0;
// Accumulate final offset
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
{
// We only inspect constants and additions
return false;
}
}

// Base address is expected to be static field's address
if ((tree->IsCnsIntOrI()) && (tree->AsIntCon()->gtFieldSeq != nullptr) &&
(tree->AsIntCon()->gtFieldSeq->GetKind() == FieldSeq::FieldKind::SimpleStaticKnownAddress))
{
*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 +8555,104 @@ 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 (fgGetStaticFieldSeqAndAddress(tree->gtGetOp1(), &address, &fieldSeq))
{
assert(fieldSeq->GetKind() == FieldSeq::FieldKind::SimpleStaticKnownAddress);
CORINFO_FIELD_HANDLE fieldHandle = fieldSeq->GetFieldHandle();

ssize_t byteOffset = address - fieldSeq->GetOffset();
int size = (int)genTypeSize(tree->TypeGet());
const int maxElementSize = sizeof(int64_t);
if ((fieldHandle != nullptr) && (size > 0) && (size <= maxElementSize) && ((size_t)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
20 changes: 20 additions & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4079,5 +4079,25 @@ private bool notifyInstructionSetUsage(InstructionSet instructionSet, bool suppo
return supportEnabled ? _compilation.InstructionSetSupport.IsInstructionSetSupported(instructionSet) : false;
}
#endif

private static bool TryReadRvaFieldData(FieldDesc field, byte* buffer, int bufferSize, int valueOffset)
{
Debug.Assert(buffer != null);
Debug.Assert(bufferSize > 0);
Debug.Assert(valueOffset >= 0);
Debug.Assert(field.IsStatic);
Debug.Assert(field.HasRva);

if (!field.IsThreadStatic && field.IsInitOnly && 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;
}
}
}
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,16 @@ 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);
FieldDesc field = HandleToObject(fieldHandle);

EgorBo marked this conversation as resolved.
Show resolved Hide resolved
// For crossgen2 we only support RVA fields
if (_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(field.OwningType) && field.HasRva)
{
return TryReadRvaFieldData(field, buffer, bufferSize, valueOffset);
}
return false;
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
}

Expand Down
Loading