JIT: optimize more array covariant store checks in the importer#189
JIT: optimize more array covariant store checks in the importer#189AndyAyersMS merged 7 commits intodotnet:masterfrom AndyAyersMS:CovariantStoreImporterOpts
Conversation
The importer was already optimizing away some array covariant store checks, for cases where the value being stored was null, or the value being stored came from the same array. Change this to only optimize array covariant store checks in the importer when optimization is enabled. For minopts, invoking the store helper produces smaller code. Update `gtGetClassHandle` to obtain the array handle from array newobjs, and use this to also optimize cases where the destination array is exactly `object[]` or is `T[]` where `T` is final and not itself subject to special casting logic. In particular this gets the common case where `T` is `string`. Closes dotnet/coreclr#6537.
|
Code size impact when optimizing (from dotnet/coreclr#6537). Large regressions are all cases where loop cloning now kicks in, as this change makes more array accesses explicit in the JIT's IR, and that's what triggers cloning. Likely these are poor decisions by the cloner. Fixing that is a separate issue; see dotnet/coreclr#2634 and related. Will update with Tier0 code size impact when I have it. |
|
@dotnet/jit-contrib PTAL |
erozenfeld
left a comment
There was a problem hiding this comment.
LGTM with a minor suggestion.
src/coreclr/src/jit/importer.cpp
Outdated
| { | ||
| DWORD elementAttribs = info.compCompHnd->getClassAttribs(arrayElementHandle); | ||
| DWORD flagsMask = CORINFO_FLG_FINAL | CORINFO_FLG_MARSHAL_BYREF | | ||
| CORINFO_FLG_CONTEXTFUL | CORINFO_FLG_VARIANCE | CORINFO_FLG_ARRAY; |
There was a problem hiding this comment.
This is the third place in the code where we have CORINFO_FLG_FINAL | CORINFO_FLG_MARSHAL_BYREF | CORINFO_FLG_CONTEXTFUL | CORINFO_FLG_VARIANCE as a mask (or part of a mask). Perhaps we should factor this into a helper.
|
Tier0 diffs show a few regressions. The store null optimization happens in morph too, and we get slightly worse codegen in Tier0 if we transform the array store there as opposed to in the importer. So if we now defer in the importer at Tier0, it seems like we should defer in morph, too. |
|
Updated Tier0 diffs, no regressions. |
src/coreclr/src/jit/importer.cpp
Outdated
| if (indexFromOp1->OperGet() == GT_LCL_VAR) | ||
| // Check for assignment to same array, ie. arrLcl[i] = arrLcl[j] | ||
| // This does not need CORINFO_HELP_ARRADDR_ST | ||
| if (arrayNodeFrom->OperGet() == GT_INDEX && arrayNodeTo->gtOper == GT_LCL_VAR) |
There was a problem hiding this comment.
Factor all this logic into a separate method? E.g. impCanSkipCovariantStoreCheck ?
src/coreclr/src/jit/importer.cpp
Outdated
| // | ||
| bool Compiler::impCanSkipCovariantStoreCheck(GenTree* arrayNodeFrom, GenTree* arrayNodeTo) | ||
| { | ||
| if (!opts.OptimizationEnabled()) |
There was a problem hiding this comment.
Nit: Micro-optimize for fast JIT and move this check to be at the callsite of this method?
I have looked at other similar places in the JIT that call OptimizationEnabled(). Some of them have at the callsite and some of them have as the early out like this.
There was a problem hiding this comment.
In this case I think it reads better having it at the call site, and there's just one call site, so I'll move it there.
|
Build breaks... |
src/coreclr/src/jit/importer.cpp
Outdated
| return true; | ||
| } | ||
|
|
||
| // Check if destination array is exactly object[]. |
There was a problem hiding this comment.
This comment is now misplaced.
src/coreclr/src/jit/importer.cpp
Outdated
| // | ||
| // Note we are conservative on array of arrays here. It might be worth checking | ||
| // for element types like int[]. | ||
| const bool elementTypeIsExact = impIsClassExact(arrayElementHandle); |
There was a problem hiding this comment.
The local variable seems unnecessary since it's used just one.
src/coreclr/src/jit/importer.cpp
Outdated
| DWORD flags = info.compCompHnd->getClassAttribs(pResolvedToken->hClass); | ||
|
|
||
| // If the class is final and is not marshal byref, variant or | ||
| // contextful, the jit can expand the IsInst check inline. |
There was a problem hiding this comment.
I would remove or rephrase this comment since it can easily get out of sync with the implementation of impIsClassExact.
src/coreclr/src/jit/importer.cpp
Outdated
| // can skip the array store covariance check. | ||
| // | ||
| // Arguments: | ||
| // arrayNodeFrom -- tree producing the value to store |
There was a problem hiding this comment.
This name is slightly confusing since the value to store isn't normally an array. Maybe simply nodeFrom?
There was a problem hiding this comment.
How about just value and array.
|
@erozenfeld last commit should address your feedback |
|
LGTM, thanks |
Removes Object and UIntPtr as valid cast from pointer. Fixes the following code which returned true and now returns false: ``` typeof(object).IsAssignableFrom(typeof(byte*)) typeof(UIntPtr).IsAssignableFrom(typeof(byte*)) ``` Fixes dotnet#189
The importer was already optimizing away some array covariant store checks,
for cases where the value being stored was null, or the value being stored
came from the same array.
Change this to only optimize array covariant store checks in the importer
when optimization is enabled. For minopts, invoking the store helper produces
smaller code.
Update
gtGetClassHandleto obtain the array handle from array newobjs,and use this to also optimize cases where the destination array is exactly
object[]or isT[]whereTis final and not itself subject to specialcasting logic. In particular this gets the common case where
Tisstring.Closes dotnet/coreclr#6537.