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 string.Empty as "" #64530

Merged
merged 10 commits into from
Feb 3, 2022
10 changes: 8 additions & 2 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
}
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
5 changes: 2 additions & 3 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 11 additions & 2 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>(asIndex->Index()->AsIntConCommon()->IconValue());
if (cnsIndex >= 0)
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 3 additions & 1 deletion src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3278,7 +3278,9 @@ private bool canGetVarArgsHandle(CORINFO_SIG_INFO* pSig)

private InfoAccessType emptyStringLiteral(ref void* ppValue)
{
return constructStringLiteral(_methodScope, (mdToken)CorTokenType.mdtString, ref ppValue);
ISymbolNode stringObject = _compilation.NodeFactory.SerializedStringObject("");
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
ppValue = (void*)ObjectToHandle(stringObject);
return stringObject.RepresentsIndirectionCell ? InfoAccessType.IAT_PVALUE : InfoAccessType.IAT_VALUE;
}

private uint getFieldThreadLocalStoreID(CORINFO_FIELD_STRUCT_* field, ref void* ppIndirection)
Expand Down