From 9a49a284a2fd539f84e8f8840eb279182bfb60c8 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 16 Oct 2022 20:20:58 +0200 Subject: [PATCH 1/7] Fold 'static readonly STRUCT' to constants --- src/coreclr/jit/importer.cpp | 98 ++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index f2ac7b70511bad..31045fedfd8a58 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -9505,7 +9505,105 @@ void Compiler::impImportBlockCode(BasicBlock* block) } } } + else if ((lclTyp == TYP_STRUCT) && opts.OptimizationEnabled() && !opts.IsReadyToRun()) + { + // TODO: implement for NativeAOT as part of this PR + CORINFO_CLASS_HANDLE fieldClsHnd; + var_types fieldElementType = + JITtype2varType(info.compCompHnd->getFieldType(resolvedToken.hField, &fieldClsHnd, + resolvedToken.hClass)); + assert(fieldElementType == TYP_STRUCT); + unsigned fieldsCount = info.compCompHnd->getClassNumInstanceFields(fieldClsHnd); + + // CanPromoteStructType is responsible for various legality checks such as + // overlapping fields, etc. + if (structPromotionHelper->CanPromoteStructType(fieldClsHnd)) + { + // TODO-CQ: Fold some non-promotable structs such as Guid (11 fields) + assert(fieldsCount <= MAX_NumOfFieldsInPromotableStruct); + + const int bufferSize = MAX_NumOfFieldsInPromotableStruct * sizeof(int64_t); + uint8_t buffer[bufferSize] = {0}; + unsigned totalSize = info.compCompHnd->getClassSize(fieldClsHnd); + if ((totalSize <= bufferSize) && + info.compCompHnd->getReadonlyStaticFieldValue(resolvedToken.hField, buffer, + totalSize)) + { + struct InnerFieldInfo + { + var_types type; + unsigned offset; + }; + + // Do a quick validation loop over fields before we start producing trees + // we want to make sure we only work with integral primitives + InnerFieldInfo fields[MAX_NumOfFieldsInPromotableStruct] = {}; + for (unsigned fldIndex = 0; fldIndex < fieldsCount; fldIndex++) + { + CORINFO_FIELD_HANDLE innerField = + info.compCompHnd->getFieldInClass(fieldClsHnd, fldIndex); + + if (innerField == NULL) // is it needed? + { + goto NONFOLDABLE_FIELD; + } + + CORINFO_CLASS_HANDLE innerFieldClsHnd; + CorInfoType innerFieldType = + info.compCompHnd->getFieldType(innerField, &innerFieldClsHnd, + fieldClsHnd); + var_types filedVarType = JITtype2varType(innerFieldType); + + // Technically, we can support frozen gc refs here + // and maybe floating point in future + if (!varTypeIsIntegral(filedVarType)) + { + goto NONFOLDABLE_FIELD; + } + + fields[fldIndex].type = filedVarType; + fields[fldIndex].offset = info.compCompHnd->getFieldOffset(innerField); + } + + unsigned structTempNum = + lvaGrabTemp(true DEBUGARG("folding static ro fld struct")); + lvaSetStruct(structTempNum, fieldClsHnd, false); + + for (unsigned fldIndex = 0; fldIndex < fieldsCount; fldIndex++) + { + var_types fldType = fields[fldIndex].type; + GenTreeLclFld* fieldTree = + gtNewLclFldNode(structTempNum, fldType, fields[fldIndex].offset); + GenTree* constValTree; + + if (varTypeIsLong(fldType)) + { + int64_t value; + memcpy(&value, buffer + fields[fldIndex].offset, genTypeSize(fldType)); + constValTree = gtNewLconNode(value); + } + else + { + assert(varTypeIsIntegral(fldType)); + + ssize_t value; + memcpy(&value, buffer + fields[fldIndex].offset, genTypeSize(fldType)); + constValTree = gtNewIconNode(value, fldType); + } + + GenTree* fieldAsgTree = gtNewAssignNode(fieldTree, constValTree); + impAppendTree(fieldAsgTree, CHECK_SPILL_NONE, impCurStmtDI); + } + + JITDUMP("Folding 'static readonly %s' field to a set of %d ASG nodes\n", + eeGetClassName(fieldClsHnd), fieldsCount); + op1 = impCreateLocalNode(structTempNum DEBUGARG(0)); + goto FIELD_DONE; + } + } + } } + NONFOLDABLE_FIELD: FALLTHROUGH; case CORINFO_FIELD_STATIC_RVA_ADDRESS: From 432cf04e95b7af2f5f89ee749e999ea98cf0c9a0 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 16 Oct 2022 21:32:47 +0200 Subject: [PATCH 2/7] Clean up --- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/importer.cpp | 26 ++++++-------------------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index dc3e3c4171e798..8922342212f24e 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3631,7 +3631,7 @@ class Compiler GenTree* impInitClass(CORINFO_RESOLVED_TOKEN* pResolvedToken); - GenTree* impImportStaticReadOnlyField(uint8_t* buffer, int bufferSize, var_types valueType); + GenTree* impImportStaticReadOnlyField(uint8_t* buffer, var_types valueType); GenTree* impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_ACCESS_FLAGS access, diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 31045fedfd8a58..a4927f95d78312 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -4135,11 +4135,8 @@ GenTree* Compiler::impInitClass(CORINFO_RESOLVED_TOKEN* pResolvedToken) return node; } -GenTree* Compiler::impImportStaticReadOnlyField(uint8_t* buffer, int bufferSize, var_types valueType) +GenTree* Compiler::impImportStaticReadOnlyField(uint8_t* buffer, var_types valueType) { - // We plan to support larger values (for structs), for now let's keep it 64 bit - assert(bufferSize == sizeof(INT64)); - GenTree* tree = nullptr; switch (valueType) { @@ -9497,7 +9494,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (info.compCompHnd->getReadonlyStaticFieldValue(resolvedToken.hField, buffer, genTypeSize(lclTyp))) { - GenTree* cnsValue = impImportStaticReadOnlyField(buffer, bufferSize, lclTyp); + GenTree* cnsValue = impImportStaticReadOnlyField(buffer, lclTyp); if (cnsValue != nullptr) { op1 = cnsValue; @@ -9574,22 +9571,11 @@ void Compiler::impImportBlockCode(BasicBlock* block) var_types fldType = fields[fldIndex].type; GenTreeLclFld* fieldTree = gtNewLclFldNode(structTempNum, fldType, fields[fldIndex].offset); - GenTree* constValTree; - - if (varTypeIsLong(fldType)) - { - int64_t value; - memcpy(&value, buffer + fields[fldIndex].offset, genTypeSize(fldType)); - constValTree = gtNewLconNode(value); - } - else - { - assert(varTypeIsIntegral(fldType)); - ssize_t value; - memcpy(&value, buffer + fields[fldIndex].offset, genTypeSize(fldType)); - constValTree = gtNewIconNode(value, fldType); - } + // Read corresponding chunk from the buffer for the given sub-field + GenTree* constValTree = + impImportStaticReadOnlyField(buffer + fields[fldIndex].offset, fldType); + assert(constValTree != nullptr); GenTree* fieldAsgTree = gtNewAssignNode(fieldTree, constValTree); impAppendTree(fieldAsgTree, CHECK_SPILL_NONE, impCurStmtDI); From cd8d02fc0867274f720f4d66e31bcc9289e4e147 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Mon, 17 Oct 2022 00:52:05 +0200 Subject: [PATCH 3/7] Simplify to single-field --- src/coreclr/jit/importer.cpp | 124 +++++++++++++---------------------- 1 file changed, 45 insertions(+), 79 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index a4927f95d78312..dd4a65dcc71b3c 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -9506,87 +9506,53 @@ void Compiler::impImportBlockCode(BasicBlock* block) { // TODO: implement for NativeAOT as part of this PR CORINFO_CLASS_HANDLE fieldClsHnd; - var_types fieldElementType = - JITtype2varType(info.compCompHnd->getFieldType(resolvedToken.hField, &fieldClsHnd, - resolvedToken.hClass)); - assert(fieldElementType == TYP_STRUCT); - unsigned fieldsCount = info.compCompHnd->getClassNumInstanceFields(fieldClsHnd); - - // CanPromoteStructType is responsible for various legality checks such as - // overlapping fields, etc. - if (structPromotionHelper->CanPromoteStructType(fieldClsHnd)) + info.compCompHnd->getFieldType(resolvedToken.hField, &fieldClsHnd, + resolvedToken.hClass); + + if (info.compCompHnd->getClassNumInstanceFields(fieldClsHnd) != 1) { - // TODO-CQ: Fold some non-promotable structs such as Guid (11 fields) - assert(fieldsCount <= MAX_NumOfFieldsInPromotableStruct); - - const int bufferSize = MAX_NumOfFieldsInPromotableStruct * sizeof(int64_t); - uint8_t buffer[bufferSize] = {0}; - unsigned totalSize = info.compCompHnd->getClassSize(fieldClsHnd); - if ((totalSize <= bufferSize) && - info.compCompHnd->getReadonlyStaticFieldValue(resolvedToken.hField, buffer, - totalSize)) - { - struct InnerFieldInfo - { - var_types type; - unsigned offset; - }; - - // Do a quick validation loop over fields before we start producing trees - // we want to make sure we only work with integral primitives - InnerFieldInfo fields[MAX_NumOfFieldsInPromotableStruct] = {}; - for (unsigned fldIndex = 0; fldIndex < fieldsCount; fldIndex++) - { - CORINFO_FIELD_HANDLE innerField = - info.compCompHnd->getFieldInClass(fieldClsHnd, fldIndex); - - if (innerField == NULL) // is it needed? - { - goto NONFOLDABLE_FIELD; - } - - CORINFO_CLASS_HANDLE innerFieldClsHnd; - CorInfoType innerFieldType = - info.compCompHnd->getFieldType(innerField, &innerFieldClsHnd, - fieldClsHnd); - var_types filedVarType = JITtype2varType(innerFieldType); - - // Technically, we can support frozen gc refs here - // and maybe floating point in future - if (!varTypeIsIntegral(filedVarType)) - { - goto NONFOLDABLE_FIELD; - } - - fields[fldIndex].type = filedVarType; - fields[fldIndex].offset = info.compCompHnd->getFieldOffset(innerField); - } - - unsigned structTempNum = - lvaGrabTemp(true DEBUGARG("folding static ro fld struct")); - lvaSetStruct(structTempNum, fieldClsHnd, false); - - for (unsigned fldIndex = 0; fldIndex < fieldsCount; fldIndex++) - { - var_types fldType = fields[fldIndex].type; - GenTreeLclFld* fieldTree = - gtNewLclFldNode(structTempNum, fldType, fields[fldIndex].offset); - - // Read corresponding chunk from the buffer for the given sub-field - GenTree* constValTree = - impImportStaticReadOnlyField(buffer + fields[fldIndex].offset, fldType); - assert(constValTree != nullptr); - - GenTree* fieldAsgTree = gtNewAssignNode(fieldTree, constValTree); - impAppendTree(fieldAsgTree, CHECK_SPILL_NONE, impCurStmtDI); - } - - JITDUMP("Folding 'static readonly %s' field to a set of %d ASG nodes\n", - eeGetClassName(fieldClsHnd), fieldsCount); - op1 = impCreateLocalNode(structTempNum DEBUGARG(0)); - goto FIELD_DONE; - } + // Only single-field structs are supported here to avoid potential regressions where + // Metadata-driven struct promotion leads to regressions. + goto NONFOLDABLE_FIELD; + } + + const int bufferSize = TARGET_POINTER_SIZE; + uint8_t buffer[bufferSize] = {0}; + unsigned totalSize = info.compCompHnd->getClassSize(fieldClsHnd); + + if ((totalSize == 0) || (totalSize > bufferSize) || + !info.compCompHnd->getReadonlyStaticFieldValue(resolvedToken.hField, buffer, + totalSize)) + { + goto NONFOLDABLE_FIELD; } + + CORINFO_FIELD_HANDLE innerField = info.compCompHnd->getFieldInClass(fieldClsHnd, 0); + CORINFO_CLASS_HANDLE innerFieldClsHnd; + var_types filedVarType = JITtype2varType( + info.compCompHnd->getFieldType(innerField, &innerFieldClsHnd, fieldClsHnd)); + + // Technically, we can support frozen gc refs here and maybe floating point in future + if (!varTypeIsIntegral(filedVarType)) + { + goto NONFOLDABLE_FIELD; + } + + unsigned fldOffset = info.compCompHnd->getFieldOffset(innerField); + unsigned structTempNum = lvaGrabTemp(true DEBUGARG("folding static ro fld struct")); + lvaSetStruct(structTempNum, fieldClsHnd, false); + + GenTree* constValTree = impImportStaticReadOnlyField(buffer, filedVarType); + assert(constValTree != nullptr); + + GenTreeLclFld* fieldTree = gtNewLclFldNode(structTempNum, filedVarType, fldOffset); + GenTree* fieldAsgTree = gtNewAssignNode(fieldTree, constValTree); + impAppendTree(fieldAsgTree, CHECK_SPILL_NONE, impCurStmtDI); + + JITDUMP("Folding 'static readonly %s' field to an ASG(LCL, CNS) node\n", + eeGetClassName(fieldClsHnd)); + op1 = impCreateLocalNode(structTempNum DEBUGARG(0)); + goto FIELD_DONE; } } NONFOLDABLE_FIELD: From 500f1191046a90dd7d439251e8e86936314efbf1 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Mon, 17 Oct 2022 01:05:41 +0200 Subject: [PATCH 4/7] Remove IsReadyToRun check --- src/coreclr/jit/importer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index dd4a65dcc71b3c..2a603a05d788cb 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -9484,7 +9484,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) case CORINFO_FIELD_STATIC_SHARED_STATIC_HELPER: 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)) + if ((aflags & CORINFO_ACCESS_GET) && (fieldInfo.fieldFlags & CORINFO_FLG_FIELD_FINAL) && + opts.OptimizationEnabled()) { const int bufferSize = sizeof(uint64_t); uint8_t buffer[bufferSize] = {0}; @@ -9502,9 +9503,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) } } } - else if ((lclTyp == TYP_STRUCT) && opts.OptimizationEnabled() && !opts.IsReadyToRun()) + else if (lclTyp == TYP_STRUCT) { - // TODO: implement for NativeAOT as part of this PR CORINFO_CLASS_HANDLE fieldClsHnd; info.compCompHnd->getFieldType(resolvedToken.hField, &fieldClsHnd, resolvedToken.hClass); From 061ec809b0e903c67060ef80682816be78e96390 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Tue, 18 Oct 2022 17:20:37 +0200 Subject: [PATCH 5/7] Update src/coreclr/jit/importer.cpp Co-authored-by: Andy Ayers --- src/coreclr/jit/importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 2a603a05d788cb..af599e2e2c6081 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -9529,7 +9529,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) CORINFO_FIELD_HANDLE innerField = info.compCompHnd->getFieldInClass(fieldClsHnd, 0); CORINFO_CLASS_HANDLE innerFieldClsHnd; - var_types filedVarType = JITtype2varType( + var_types fieldVarType = JITtype2varType( info.compCompHnd->getFieldType(innerField, &innerFieldClsHnd, fieldClsHnd)); // Technically, we can support frozen gc refs here and maybe floating point in future From 0bf8917ab73b600d9429e660411203694ba05443 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 18 Oct 2022 17:40:18 +0200 Subject: [PATCH 6/7] check inner field size to match parent struct --- src/coreclr/jit/importer.cpp | 38 ++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index af599e2e2c6081..d9234f687da36d 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -9516,36 +9516,44 @@ void Compiler::impImportBlockCode(BasicBlock* block) goto NONFOLDABLE_FIELD; } - const int bufferSize = TARGET_POINTER_SIZE; - uint8_t buffer[bufferSize] = {0}; - unsigned totalSize = info.compCompHnd->getClassSize(fieldClsHnd); - - if ((totalSize == 0) || (totalSize > bufferSize) || - !info.compCompHnd->getReadonlyStaticFieldValue(resolvedToken.hField, buffer, - totalSize)) - { - goto NONFOLDABLE_FIELD; - } - CORINFO_FIELD_HANDLE innerField = info.compCompHnd->getFieldInClass(fieldClsHnd, 0); CORINFO_CLASS_HANDLE innerFieldClsHnd; var_types fieldVarType = JITtype2varType( info.compCompHnd->getFieldType(innerField, &innerFieldClsHnd, fieldClsHnd)); // Technically, we can support frozen gc refs here and maybe floating point in future - if (!varTypeIsIntegral(filedVarType)) + if (!varTypeIsIntegral(fieldVarType)) + { + goto NONFOLDABLE_FIELD; + } + + unsigned totalSize = info.compCompHnd->getClassSize(fieldClsHnd); + unsigned fldOffset = info.compCompHnd->getFieldOffset(innerField); + + if ((fldOffset != 0) || (totalSize != info.compCompHnd->getClassSize(fieldClsHnd)) || + (totalSize == 0)) + { + // The field is expected to be of the exact size as the struct with 0 offset + goto NONFOLDABLE_FIELD; + } + + const int bufferSize = TARGET_POINTER_SIZE; + uint8_t buffer[bufferSize] = {0}; + + if ((totalSize > bufferSize) || + !info.compCompHnd->getReadonlyStaticFieldValue(resolvedToken.hField, buffer, + totalSize)) { goto NONFOLDABLE_FIELD; } - unsigned fldOffset = info.compCompHnd->getFieldOffset(innerField); unsigned structTempNum = lvaGrabTemp(true DEBUGARG("folding static ro fld struct")); lvaSetStruct(structTempNum, fieldClsHnd, false); - GenTree* constValTree = impImportStaticReadOnlyField(buffer, filedVarType); + GenTree* constValTree = impImportStaticReadOnlyField(buffer, fieldVarType); assert(constValTree != nullptr); - GenTreeLclFld* fieldTree = gtNewLclFldNode(structTempNum, filedVarType, fldOffset); + GenTreeLclFld* fieldTree = gtNewLclFldNode(structTempNum, fieldVarType, fldOffset); GenTree* fieldAsgTree = gtNewAssignNode(fieldTree, constValTree); impAppendTree(fieldAsgTree, CHECK_SPILL_NONE, impCurStmtDI); From 7cb6e336081f18a47ef635023a13c352e92ce7a7 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 18 Oct 2022 19:57:25 +0200 Subject: [PATCH 7/7] Move to a separate function --- src/coreclr/jit/compiler.h | 3 +- src/coreclr/jit/importer.cpp | 184 ++++++++++++++++++++--------------- 2 files changed, 110 insertions(+), 77 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 8922342212f24e..6d925b6d04f6e6 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3631,7 +3631,8 @@ class Compiler GenTree* impInitClass(CORINFO_RESOLVED_TOKEN* pResolvedToken); - GenTree* impImportStaticReadOnlyField(uint8_t* buffer, var_types valueType); + GenTree* impImportStaticReadOnlyField(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE ownerCls); + GenTree* impImportCnsTreeFromBuffer(uint8_t* buffer, var_types valueType); GenTree* impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_ACCESS_FLAGS access, diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index d9234f687da36d..b71f28979ad87b 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -4135,7 +4135,109 @@ GenTree* Compiler::impInitClass(CORINFO_RESOLVED_TOKEN* pResolvedToken) return node; } -GenTree* Compiler::impImportStaticReadOnlyField(uint8_t* buffer, var_types valueType) +//------------------------------------------------------------------------ +// impImportStaticReadOnlyField: Tries to import 'static readonly' field +// as a constant if the host type is statically initialized. +// +// Arguments: +// field - 'static readonly' field +// ownerCls - class handle of the type the given field defined in +// +// Return Value: +// The tree representing the constant value of the statically initialized +// readonly tree. +// +GenTree* Compiler::impImportStaticReadOnlyField(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE ownerCls) +{ + if (!opts.OptimizationEnabled()) + { + return nullptr; + } + + CORINFO_CLASS_HANDLE fieldClsHnd; + var_types fieldType = JITtype2varType(info.compCompHnd->getFieldType(field, &fieldClsHnd, ownerCls)); + + const int bufferSize = sizeof(uint64_t); + uint8_t buffer[bufferSize] = {0}; + if (varTypeIsIntegral(fieldType) || varTypeIsFloating(fieldType) || (fieldType == TYP_REF)) + { + assert(bufferSize >= genTypeSize(fieldType)); + if (info.compCompHnd->getReadonlyStaticFieldValue(field, buffer, genTypeSize(fieldType))) + { + GenTree* cnsValue = impImportCnsTreeFromBuffer(buffer, fieldType); + if (cnsValue != nullptr) + { + return cnsValue; + } + } + } + else if (fieldType == TYP_STRUCT) + { + + if (info.compCompHnd->getClassNumInstanceFields(fieldClsHnd) != 1) + { + // Only single-field structs are supported here to avoid potential regressions where + // Metadata-driven struct promotion leads to regressions. + return nullptr; + } + + CORINFO_FIELD_HANDLE innerField = info.compCompHnd->getFieldInClass(fieldClsHnd, 0); + CORINFO_CLASS_HANDLE innerFieldClsHnd; + var_types fieldVarType = + JITtype2varType(info.compCompHnd->getFieldType(innerField, &innerFieldClsHnd, fieldClsHnd)); + + // Technically, we can support frozen gc refs here and maybe floating point in future + if (!varTypeIsIntegral(fieldVarType)) + { + return nullptr; + } + + unsigned totalSize = info.compCompHnd->getClassSize(fieldClsHnd); + unsigned fldOffset = info.compCompHnd->getFieldOffset(innerField); + + if ((fldOffset != 0) || (totalSize != info.compCompHnd->getClassSize(fieldClsHnd)) || (totalSize == 0)) + { + // The field is expected to be of the exact size as the struct with 0 offset + return nullptr; + } + + const int bufferSize = TARGET_POINTER_SIZE; + uint8_t buffer[bufferSize] = {0}; + + if ((totalSize > bufferSize) || !info.compCompHnd->getReadonlyStaticFieldValue(field, buffer, totalSize)) + { + return nullptr; + } + + unsigned structTempNum = lvaGrabTemp(true DEBUGARG("folding static ro fld struct")); + lvaSetStruct(structTempNum, fieldClsHnd, false); + + GenTree* constValTree = impImportCnsTreeFromBuffer(buffer, fieldVarType); + assert(constValTree != nullptr); + + GenTreeLclFld* fieldTree = gtNewLclFldNode(structTempNum, fieldVarType, fldOffset); + GenTree* fieldAsgTree = gtNewAssignNode(fieldTree, constValTree); + impAppendTree(fieldAsgTree, CHECK_SPILL_NONE, impCurStmtDI); + + JITDUMP("Folding 'static readonly %s' field to an ASG(LCL, CNS) node\n", eeGetClassName(fieldClsHnd)); + + return impCreateLocalNode(structTempNum DEBUGARG(0)); + } + return nullptr; +} + +//------------------------------------------------------------------------ +// impImportCnsTreeFromBuffer: read value of the given type from the +// given buffer to create a tree representing that constant. +// +// Arguments: +// buffer - array of bytes representing the value +// valueType - type of the value +// +// Return Value: +// The tree representing the constant from the given buffer +// +GenTree* Compiler::impImportCnsTreeFromBuffer(uint8_t* buffer, var_types valueType) { GenTree* tree = nullptr; switch (valueType) @@ -9484,86 +9586,16 @@ void Compiler::impImportBlockCode(BasicBlock* block) case CORINFO_FIELD_STATIC_SHARED_STATIC_HELPER: 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) && - opts.OptimizationEnabled()) + if ((aflags & CORINFO_ACCESS_GET) && (fieldInfo.fieldFlags & CORINFO_FLG_FIELD_FINAL)) { - const int bufferSize = sizeof(uint64_t); - uint8_t buffer[bufferSize] = {0}; - if (varTypeIsIntegral(lclTyp) || varTypeIsFloating(lclTyp) || (lclTyp == TYP_REF)) - { - assert(bufferSize >= genTypeSize(lclTyp)); - if (info.compCompHnd->getReadonlyStaticFieldValue(resolvedToken.hField, buffer, - genTypeSize(lclTyp))) - { - GenTree* cnsValue = impImportStaticReadOnlyField(buffer, lclTyp); - if (cnsValue != nullptr) - { - op1 = cnsValue; - goto FIELD_DONE; - } - } - } - else if (lclTyp == TYP_STRUCT) - { - CORINFO_CLASS_HANDLE fieldClsHnd; - info.compCompHnd->getFieldType(resolvedToken.hField, &fieldClsHnd, - resolvedToken.hClass); + GenTree* newTree = impImportStaticReadOnlyField(resolvedToken.hField, resolvedToken.hClass); - if (info.compCompHnd->getClassNumInstanceFields(fieldClsHnd) != 1) - { - // Only single-field structs are supported here to avoid potential regressions where - // Metadata-driven struct promotion leads to regressions. - goto NONFOLDABLE_FIELD; - } - - CORINFO_FIELD_HANDLE innerField = info.compCompHnd->getFieldInClass(fieldClsHnd, 0); - CORINFO_CLASS_HANDLE innerFieldClsHnd; - var_types fieldVarType = JITtype2varType( - info.compCompHnd->getFieldType(innerField, &innerFieldClsHnd, fieldClsHnd)); - - // Technically, we can support frozen gc refs here and maybe floating point in future - if (!varTypeIsIntegral(fieldVarType)) - { - goto NONFOLDABLE_FIELD; - } - - unsigned totalSize = info.compCompHnd->getClassSize(fieldClsHnd); - unsigned fldOffset = info.compCompHnd->getFieldOffset(innerField); - - if ((fldOffset != 0) || (totalSize != info.compCompHnd->getClassSize(fieldClsHnd)) || - (totalSize == 0)) - { - // The field is expected to be of the exact size as the struct with 0 offset - goto NONFOLDABLE_FIELD; - } - - const int bufferSize = TARGET_POINTER_SIZE; - uint8_t buffer[bufferSize] = {0}; - - if ((totalSize > bufferSize) || - !info.compCompHnd->getReadonlyStaticFieldValue(resolvedToken.hField, buffer, - totalSize)) - { - goto NONFOLDABLE_FIELD; - } - - unsigned structTempNum = lvaGrabTemp(true DEBUGARG("folding static ro fld struct")); - lvaSetStruct(structTempNum, fieldClsHnd, false); - - GenTree* constValTree = impImportStaticReadOnlyField(buffer, fieldVarType); - assert(constValTree != nullptr); - - GenTreeLclFld* fieldTree = gtNewLclFldNode(structTempNum, fieldVarType, fldOffset); - GenTree* fieldAsgTree = gtNewAssignNode(fieldTree, constValTree); - impAppendTree(fieldAsgTree, CHECK_SPILL_NONE, impCurStmtDI); - - JITDUMP("Folding 'static readonly %s' field to an ASG(LCL, CNS) node\n", - eeGetClassName(fieldClsHnd)); - op1 = impCreateLocalNode(structTempNum DEBUGARG(0)); + if (newTree != nullptr) + { + op1 = newTree; goto FIELD_DONE; } } - NONFOLDABLE_FIELD: FALLTHROUGH; case CORINFO_FIELD_STATIC_RVA_ADDRESS: