-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Fold IND(frozenObj, CNS) #85127
Fold IND(frozenObj, CNS) #85127
Conversation
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch, @kunalspathak Issue DetailsNeeded for #85014 (to fold CQ improvement example: static int Case1()
{
// Read 4 bytes from a frozen object
return Unsafe.As<char, int>(
ref MemoryMarshal.GetReference("hello world".AsSpan()));
}
static bool Case2()
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
} Current codegen (Main): ; Assembly listing for method Program:Case1():int
mov rax, 0x13200309C10 ; 'hello world'
add rax, 12
mov eax, dword ptr [rax]
ret
; Total bytes of code 17
; Assembly listing for method Program:Case2():bool
mov rax, 0x1F480209C4C
mov rdx, 0x20002000200020
or rdx, qword ptr [rax]
mov rax, 0x64006E00690077
xor rax, rdx
mov rdx, 0x1F480209C52
mov rcx, 0x20002000200020
or rcx, qword ptr [rdx]
mov rdx, 0x730077006F0064
xor rdx, rcx
or rax, rdx
sete al
movzx rax, al
ret
; Total bytes of code 82 New codegen (PR): ; Assembly listing for method Program:Case1():int
mov eax, 0x650068
ret
; Total bytes of code 6
; Assembly listing for method Program:Case2():bool
mov eax, 1
ret
; Total bytes of code 6
|
@jkotas can you please take a look at the VM side? I targeted two cases here:
static readonly Func<int,int> MyFunc = (x,y) => ...; and with help of PGO we should be able to inline it at its invocation without GDV checks. For that we'd need to teach JIT to emit FOH allocators in static constructors so it's not happening today yet. So overall no strong motivation for this PR, just decided to give my prototype a try and can close if you think there are not much value here 🙂 Jit-diffs:
|
Do you have any thoughts on how this could apply to AOT? If we want to be able to eventually read the delegate method pointer, that's doable, but we would need to communicate a reloc and the proposed interface doesn't allow that. If we had some extra argument where the EE side could say the result is actually a reloc and this is the handle to use, it would be workable. We would just check on the EE side that the offset points to a relocatable part and the attempted read is pointer sized. |
I've just added support for string literals for NAOT. For delegates I guess we need a real use-case first to make sure logic is tested so I'll extend once we find it, ok? For JIT the use case can be DynamicPGO emitting a GDV check but we currently never allocate delegates on FOH yet |
@jakobbotsch @SingleAccretion can you please review jit side since you reviewed previous similiar changes. |
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs
Outdated
Show resolved
Hide resolved
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs
Outdated
Show resolved
Hide resolved
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs
Outdated
Show resolved
Hide resolved
/azp list |
This comment was marked as resolved.
This comment was marked as resolved.
/azp run runtime-extra-platforms |
Azure Pipelines successfully started running 1 pipeline(s). |
src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
Show resolved
Hide resolved
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs
Outdated
Show resolved
Hide resolved
src/coreclr/jit/valuenum.cpp
Outdated
if (info.compCompHnd->readObject(obj, (uint8_t*)&buffer, size, (int)byteOffset)) | ||
{ | ||
ValueNum vn = vnStore->VNForGenericCon(tree->TypeGet(), buffer); | ||
assert(!vnStore->IsVNObjHandle(vn)); | ||
tree->gtVNPair.SetBoth(vn); | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
readObject
makes getArrayOrStringLength
as well as getStringChar
redundant.
In general, the special-cased folding for GT_ARR_LENGTH
is out-of-sync with this code, which is odd. ARR_LENGTH
code tries to deduce the value even out of non-frozen objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I also had that in mind, in theory, more specialized helpers abstract us from knowing exact object layout on JIT side but JIT already does that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, the special-cased folding for GT_ARR_LENGTH is out-of-sync with this code, which is odd. ARR_LENGTH code tries to deduce the value even out of non-frozen objects.
I'll think how to remove getArrayOrStringLength
as well as getStringChar
separately if you don't mind. Currently, getArrayOrStringLength
is the only API that can work with object handles which are not frozen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added comments
src/coreclr/vm/jitinterface.cpp
Outdated
OBJECTREF objRef = getObjectFromJitHandle(handle); | ||
_ASSERTE(objRef != NULL); | ||
|
||
// Require that the object is immutable, we can relax this later if needed for partially immutable objects |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is nothing wrong with the JIT reading mutable object if it uses the information correctly (e.g. as a guard).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is nothing wrong with the JIT reading mutable object if it uses the information correctly (e.g. as a guard).
Ah, I should revert the change and move this check back to JIT side since JIT should not fold accesses to mutubale fields in frozen objects on NativeAOT too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jkotas does VM side look good otherwise (reverted the change)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API looks very similar to getReadonlyStaticFieldValue
, the difference is in how they specify "this".
- Should
readObject
wrap object references asCORINFO_OBJECT_HANDLE
, same asgetReadonlyStaticFieldValue
? - Can the names be unified (
read
vs.get
) to make it more obvious that these APIs are very similar?
(I understand that you do not need these for current PR. I am ok if you say that these are TODOs.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should readObject wrap object references as CORINFO_OBJECT_HANDLE, same as getReadonlyStaticFieldValue?
Not sure I understand this part - isn't already wrapped? the signature is:
bool readObject(CORINFO_OBJECT_HANDLE handle, uint8_t* buffer, int bufferSize, int valueOffset)
Can the names be unified (read vs. get) to make it more obvious that these APIs are very similar?
Yeah I can rename, to, say getObjectData
I guess
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or you mean when we take an object and read its GC field? I assume that will never happen now but possible in futute
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or you mean when we take an object and read its GC field? I assume that will never happen now but possible in futute
Yes, this is what I meant.
With this PR + if we allocate (presumably, should work on NativeAOT with a static profile). Profile is needed to devirtualize the delegate call and then the GDV check is folded by this PR PS: the redundant load seems to be some left-over side effect |
src/coreclr/inc/corinfo.h
Outdated
@@ -3244,6 +3244,13 @@ class ICorDynamicInfo : public ICorStaticInfo | |||
bool ignoreMovableObjects = true | |||
) = 0; | |||
|
|||
virtual bool getObjectData( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Both methods return the same thing, but one calls it Value and the other calls Data. Can we use the same name for the thing that the method returns?
Some ideas:
readStaticField
+readObject
readStaticFieldData
+readObjectData
getStaticFieldContent
+getObjectContent
(getReadonlyStaticFieldValue
method does not seem to be strictly limited to readonly static fields, so the name can be relaxed).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Decided to go with:
getObjectType -> getObjectContent;
getReadonlyStaticFieldValue -> getStaticFieldContent
Put a condition to getObjectContent
to give up on types with GC pointers - want to have a real use case to test properly if you don't mind.
… -> getStaticFieldContent
# Conflicts: # src/coreclr/inc/jiteeversionguid.h
The new
This seems to be a test case problem, these casts assume little-endian:
|
Oops sorry for breaking that, yes, the test assumes LE, do you want me to file a fix/disable it for BE or you want to do it yourself? |
1 similar comment
Oops sorry for breaking that, yes, the test assumes LE, do you want me to file a fix/disable it for BE or you want to do it yourself? |
If you could fix this, I'd appreciate it - thanks! I think a simple fix could be to just use different expected results depending on |
Needed for #85014 (to fold
_methodPtr
field access in a frozen delegate)CQ improvement example:
Current codegen (Main):
New codegen (PR):