Add compile-time and run-time methods to obtain Vector<T> byte length#130023
Add compile-time and run-time methods to obtain Vector<T> byte length#130023snickolls-arm wants to merge 7 commits into
Conversation
Renames getVectorTByteLength to getCompileTimeVectorTByteLength, which evaluates the size of Vector<T> using available ISA features. Adds getRuntimeVectorTByteLength which queries the actual size of Vector<T> using a cached class handle discovered during import. This can be used for example when trying to evaluate the size of the Tier0 frame. On Arm64 with SVE available, ISA features alone are not enough to predict the size of Vector<T>, motivating this change. The JIT can use getRuntimeVectorTByteLength to discover the SVE vector length in scenarios where an exact size is required, such as when generating OSR patchpoints.
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
|
@dotnet/arm64-contrib Please could I have a review? |
| return TYP_UNDEF; | ||
| } | ||
|
|
||
| vectorTHandle = typeHnd; |
There was a problem hiding this comment.
So this can be one of many different handles (it is unique per <T>) and we've recently dropped all the other handle caching we had been doing for SIMD types...
Is there a reason we're doing it this way rather than doing it via an explicit JIT/EE query that can report the exact size and which the compiler can cache?
What is then the expected behavior for AOT/R2R scenarios? Are such compilations simply expected to never call getRuntimeVectorTByteLength? Do we need some kind of validation to help ensure it is only used in JIT scenarios where that information can be known to be correct/accurate?
There was a problem hiding this comment.
Is there a reason we're doing it this way rather than doing it via an explicit JIT/EE query that can report the exact size and which the compiler can cache?
No reason, I think this would be better. I'm not sure how to add a new function to the interface yet, but I'm happy to try and do this. On the VM side, I think it would trigger a load to Vector<T> through CorLibBinder and read the size from there?
What is then the expected behavior for AOT/R2R scenarios? Are such compilations simply expected to never call getRuntimeVectorTByteLength? Do we need some kind of validation to help ensure it is only used in JIT scenarios where that information can be known to be correct/accurate?
We want to avoid using this completely throughout the JIT. The only exceptions are JIT-only scenarios where we've deemed it most convenient to read VL, such as OSR where it will greatly simplify the implementation. We want to use it to compute an exact size of the tier0 stack frame rather than deal with an unknown sized chunk between the tier0 and tier1 frames.
Where it's been used in the implementation of intrinsics such as Sve.Load2xVectorAndUnzip, it's suggesting that the logic isn't VL-agnostic yet. So we'll need to make some changes there to make sure it works for AOT.
There was a problem hiding this comment.
A simple way to implement this would be to extend getBuiltinClass with an option for Vector<T>. Can Vector<T> be considered a 'builtin' class in this manner, or is there a special meaning behind it?
There was a problem hiding this comment.
There's not, we can use it for any of the corelib types that need it. That being said, you still have the open generic issue and that we'd prefer avoiding caching such handles.
What is the VM reporting for the size today if we query it via the normal getExactSize APIs? Is it 0? Should we maybe always have the VM report accurately and then we can override that in the simd name resolution we do here?
There was a problem hiding this comment.
As of #129852 I believe the VM reports the correct runtime size for all handles to Vector<T>, including the uninstantiated generic class.
I could make the cache private to the function using a static local, and I could cache either the handle from getBuiltinClass or the queried size from getClassSize, either is fine.
I could also just avoid caching until it becomes a performance concern. The only reason it's caching at the moment is because it can't find a handle to Vector<T> without it appearing somewhere in metadata.
For interest, why do we want to avoid caching generic handles? Can they change underneath you?
There was a problem hiding this comment.
It's rather just additional overhead and state growth for something we found was not a perf concern at all. It effectively regresses the 99% for the minority of hot functions that use vectorization themselves.
There was a problem hiding this comment.
2e997fb implements this suggestion using getBuiltinClass
We can use this to access a handle to Vector<T> without explicitly seeing one in program metadata.
Renames
getVectorTByteLengthtogetCompileTimeVectorTByteLength, which evaluates the size ofVector<T>using available ISA features.Adds
getRuntimeVectorTByteLengthwhich queries the actual size ofVector<T>using a cached class handle discovered during import.On Arm64 with SVE available, ISA features alone are not enough to predict the size of
Vector<T>, motivating this change. The JIT can usegetRuntimeVectorTByteLengthto discover the SVE vector length in scenarios where an exact size is required, such as when generating OSR patchpoints.