diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 7a2909373104cc..61dda8f4ca86bf 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -5793,6 +5793,12 @@ GenTree* Compiler::gtNewStringLiteralNode(InfoAccessType iat, void* pValue) // GenTreeIntCon* Compiler::gtNewStringLiteralLength(GenTreeStrCon* node) { + if (node->IsStringEmptyField()) + { + JITDUMP("Folded String.Empty.Length to 0\n"); + return gtNewIconNode(0); + } + int length = -1; const char16_t* str = info.compCompHnd->getStringLiteral(node->gtScpHnd, node->gtSconCPX, &length); if (length >= 0) @@ -5802,11 +5808,11 @@ GenTreeIntCon* Compiler::gtNewStringLiteralLength(GenTreeStrCon* node) // str can be NULL for dynamic context if (str != nullptr) { - JITDUMP("String '\"%ws\".Length' is '%d'\n", str, length) + JITDUMP("Folded '\"%ws\".Length' to '%d'\n", str, length) } else { - JITDUMP("String 'CNS_STR.Length' is '%d'\n", length) + JITDUMP("Folded 'CNS_STR.Length' to '%d'\n", length) } return iconNode; } diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 085bc17a1e5107..05cb769cc262e6 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -3240,11 +3240,19 @@ struct GenTreeDblCon : public GenTree /* gtStrCon -- string constant (GT_CNS_STR) */ +#define EMPTY_STRING_SCON (unsigned)-1 + struct GenTreeStrCon : public GenTree { unsigned gtSconCPX; CORINFO_MODULE_HANDLE gtScpHnd; + // Returns true if this GT_CNS_STR was imported for String.Empty field + bool IsStringEmptyField() + { + return gtSconCPX == EMPTY_STRING_SCON && gtScpHnd == nullptr; + } + // Because this node can come from an inlined method we need to // have the scope handle, since it will become a helper call. GenTreeStrCon(unsigned sconCPX, CORINFO_MODULE_HANDLE mod DEBUGARG(bool largeNode = false)) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index ffc99f1efa57af..ac361b08e8eb5f 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -15073,9 +15073,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) { assert(aflags & CORINFO_ACCESS_GET); - LPVOID pValue; - InfoAccessType iat = info.compCompHnd->emptyStringLiteral(&pValue); - op1 = gtNewStringLiteralNode(iat, pValue); + // Import String.Empty as "" (GT_CNS_STR with a fake SconCPX = 0) + op1 = gtNewSconNode(EMPTY_STRING_SCON, nullptr); goto FIELD_DONE; } break; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 4e458d5ad80323..557955dc4ee223 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -5184,7 +5184,9 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) noway_assert(elemTyp != TYP_STRUCT || elemStructType != nullptr); // Fold "cns_str"[cns_index] to ushort constant - if (opts.OptimizationEnabled() && asIndex->Arr()->OperIs(GT_CNS_STR) && asIndex->Index()->IsIntCnsFitsInI32()) + // NOTE: don't do it for empty string, the operation will fail anyway + if (opts.OptimizationEnabled() && asIndex->Arr()->OperIs(GT_CNS_STR) && + !asIndex->Arr()->AsStrCon()->IsStringEmptyField() && asIndex->Index()->IsIntCnsFitsInI32()) { const int cnsIndex = static_cast(asIndex->Index()->AsIntConCommon()->IconValue()); if (cnsIndex >= 0) @@ -9433,11 +9435,18 @@ GenTree* Compiler::fgMorphConst(GenTree* tree) tree->gtFlags &= ~(GTF_ALL_EFFECT | GTF_REVERSE_OPS); - if (tree->OperGet() != GT_CNS_STR) + if (!tree->OperIs(GT_CNS_STR)) { return tree; } + if (tree->AsStrCon()->IsStringEmptyField()) + { + LPVOID pValue; + InfoAccessType iat = info.compCompHnd->emptyStringLiteral(&pValue); + return fgMorphTree(gtNewStringLiteralNode(iat, pValue)); + } + // TODO-CQ: Do this for compCurBB->isRunRarely(). Doing that currently will // guarantee slow performance for that block. Instead cache the return value // of CORINFO_HELP_STRCNS and go to cache first giving reasonable perf. diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 1b1fbdf550777d..4706aaa7f7901c 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -748,8 +748,17 @@ private bool canTailCall(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHOD_STRUC private InfoAccessType constructStringLiteral(CORINFO_MODULE_STRUCT_* module, mdToken metaTok, ref void* ppValue) { MethodIL methodIL = (MethodIL)HandleToObject((IntPtr)module); - object literal = methodIL.GetObject((int)metaTok); - ISymbolNode stringObject = _compilation.NodeFactory.SerializedStringObject((string)literal); + + ISymbolNode stringObject; + if (metaTok == (mdToken)CorConstants.CorTokenType.mdtString) + { + stringObject = _compilation.NodeFactory.SerializedStringObject(""); + } + else + { + object literal = methodIL.GetObject((int)metaTok); + stringObject = _compilation.NodeFactory.SerializedStringObject((string)literal); + } ppValue = (void*)ObjectToHandle(stringObject); return stringObject.RepresentsIndirectionCell ? InfoAccessType.IAT_PVALUE : InfoAccessType.IAT_VALUE; }