diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index d3de1b50116c5..e04020abed554 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -9079,6 +9079,10 @@ void cTreeFlags(Compiler* comp, GenTree* tree) { chars += printf("[IND_INVARIANT]"); } + if (tree->gtFlags & GTF_IND_NONNULL) + { + chars += printf("[IND_NONNULL]"); + } break; case GT_CLS_VAR: diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 8f3a80c6dd063..022acf8801548 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -2608,6 +2608,7 @@ void Compiler::fgDebugCheckDispFlags(GenTree* tree, unsigned dispFlags, unsigned { printf("%c", (dispFlags & GTF_IND_INVARIANT) ? '#' : '-'); printf("%c", (dispFlags & GTF_IND_NONFAULTING) ? 'n' : '-'); + printf("%c", (dispFlags & GTF_IND_NONNULL) ? '@' : '-'); } GenTree::gtDispFlags(dispFlags, debugFlags); } diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index fa289cef25230..7039973a8e8eb 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -6107,6 +6107,12 @@ GenTree* Compiler::gtNewIndOfIconHandleNode(var_types indType, size_t addr, unsi // This indirection also is invariant. indNode->gtFlags |= GTF_IND_INVARIANT; + + if (iconFlags == GTF_ICON_STR_HDL) + { + // String literals are never null + indNode->gtFlags |= GTF_IND_NONNULL; + } } return indNode; } @@ -10252,6 +10258,12 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _ --msgLength; break; } + if (tree->gtFlags & GTF_IND_NONNULL) + { + printf("@"); + --msgLength; + break; + } } FALLTHROUGH; diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index e4ed857e1322e..1d4a2174056b9 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -866,10 +866,11 @@ struct GenTree // alignment of 1 byte) #define GTF_IND_INVARIANT 0x01000000 // GT_IND -- the target is invariant (a prejit indirection) #define GTF_IND_ARR_INDEX 0x00800000 // GT_IND -- the indirection represents an (SZ) array index +#define GTF_IND_NONNULL 0x00400000 // GT_IND -- the indirection never returns null (zero) #define GTF_IND_FLAGS \ (GTF_IND_VOLATILE | GTF_IND_TGTANYWHERE | GTF_IND_NONFAULTING | GTF_IND_TLS_REF | \ - GTF_IND_UNALIGNED | GTF_IND_INVARIANT | GTF_IND_ARR_INDEX | GTF_IND_TGT_NOT_HEAP) + GTF_IND_UNALIGNED | GTF_IND_INVARIANT | GTF_IND_NONNULL | GTF_IND_ARR_INDEX | GTF_IND_TGT_NOT_HEAP) #define GTF_CLS_VAR_VOLATILE 0x40000000 // GT_FIELD/GT_CLS_VAR -- same as GTF_IND_VOLATILE #define GTF_CLS_VAR_INITCLASS 0x20000000 // GT_FIELD/GT_CLS_VAR -- same as GTF_FLD_INITCLASS diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index de5f6d0bdd298..2dc100103915e 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6520,7 +6520,9 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) if (isStaticReadOnlyInited) { JITDUMP("Marking initialized static read-only field '%s' as invariant.\n", eeGetFieldName(symHnd)); - tree->gtFlags |= GTF_IND_INVARIANT; + + // Static readonly field is not null at this point (see getStaticFieldCurrentClass impl). + tree->gtFlags |= (GTF_IND_INVARIANT | GTF_IND_NONFAULTING | GTF_IND_NONNULL); tree->gtFlags &= ~GTF_ICON_INITCLASS; addr->gtFlags = GTF_ICON_CONST_PTR; } diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index b530c068b6c36..0525fde149209 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -8139,11 +8139,20 @@ void Compiler::fgValueNumberTree(GenTree* tree) { assert(!isVolatile); // We don't expect both volatile and invariant - // Is it a string literal? (it's always non-null) - if (addr->IsCnsIntOrI() && addr->IsIconHandle(GTF_ICON_STR_HDL)) + // Is this invariant indirect expected to always return a non-null value? + if ((tree->gtFlags & GTF_IND_NONNULL) != 0) { - tree->gtVNPair = vnStore->VNPairForFunc(tree->TypeGet(), VNF_StrCns, addrNvnp); - assert(addrXvnp.BothEqual() && (addrXvnp.GetLiberal() == ValueNumStore::VNForEmptyExcSet())); + assert(tree->gtFlags & GTF_IND_NONFAULTING); + tree->gtVNPair = vnStore->VNPairForFunc(tree->TypeGet(), VNF_NonNullIndirect, addrNvnp); + if (addr->IsCnsIntOrI()) + { + assert(addrXvnp.BothEqual() && (addrXvnp.GetLiberal() == ValueNumStore::VNForEmptyExcSet())); + } + else + { + assert(false && "it's not expected to be hit at the moment, but can be allowed."); + // tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp); + } } else { diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index 9297115c55752..b8f5f74e59955 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -144,7 +144,7 @@ ValueNumFuncDef(Box, 3, false, false, false) ValueNumFuncDef(BoxNullable, 3, false, false, false) ValueNumFuncDef(LazyStrCns, 2, false, true, false) // lazy-initialized string literal (helper) -ValueNumFuncDef(StrCns, 1, false, true, false) // indirect for a string literal +ValueNumFuncDef(NonNullIndirect, 1, false, true, false) // this indirect is expected to always return a non-null value ValueNumFuncDef(Unbox, 2, false, true, false) ValueNumFuncDef(LT_UN, 2, false, false, false) // unsigned or unordered comparisons