-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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: Fix too wide loads on arm64 for small structs #76341
Conversation
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch Issue DetailsFixes #76194 We've had similar issues in the past e.g. #64683 (reproduces even on .NET Framework)
|
11e36e3
to
64e80d3
Compare
Co-authored-by: SingleAccretion <[email protected]>
src/coreclr/jit/lower.cpp
Outdated
LIR::Use retValUse(BlockRange(), &ret->gtOp1, ret); | ||
unsigned tmpNum = BAD_VAR_NUM; | ||
if (structCls != NO_CLASS_HANDLE) | ||
{ | ||
tmpNum = comp->lvaGrabTemp(true DEBUGARG("mis-sized struct return")); | ||
comp->genReturnLocal = tmpNum; | ||
comp->lvaSetStruct(tmpNum, structCls, 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.
LIR::Use retValUse(BlockRange(), &ret->gtOp1, ret); | |
unsigned tmpNum = BAD_VAR_NUM; | |
if (structCls != NO_CLASS_HANDLE) | |
{ | |
tmpNum = comp->lvaGrabTemp(true DEBUGARG("mis-sized struct return")); | |
comp->genReturnLocal = tmpNum; | |
comp->lvaSetStruct(tmpNum, structCls, true); | |
} | |
LIR::Use retValUse(BlockRange(), &ret->gtOp1, ret); | |
unsigned tmpNum = comp->lvaGrabTemp(true DEBUGARG("mis-sized struct return")); | |
comp->lvaSetStruct(tmpNum, structCls, false); |
genReturnLocal
has a very specific purpose, no reason to set it here.- No unsafe value class checks needed (in principle, in practice it doesn't matter) since the address of this local won't escape.
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.
genReturnLocal has a very specific purpose, no reason to set it here.
Tests assert after this patch: Assertion failed 'tmp == genReturnLocal'
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.
I reverted some suggestions if you don't mind because we hit the path for non-structs as well (e.g. small type primitives) so now it's cleaner that we have a special case for that rare case with IND<struct>
.
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.
We still need to handle the case with handle-less BLK
nodes though.
Reproduction:
struct Sx3
{
public byte A, B, C;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static Sx3 Problem(void* s)
{
Sx3 a;
Unsafe.CopyBlock(&a, s, 3);
return a;
}
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.
thanks, reverted the revert. @SingleAccretion does it look good to you? I assume it's just your change written with my hands 😄
Co-authored-by: SingleAccretion <[email protected]>
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.
Yep, looks good!
@jakobbotsch could you please take a look and sign it off? CI failure is known: #78290 |
if (realSize == 0) | ||
{ | ||
// TODO-ADDR: delete once "IND<struct>" nodes are no more | ||
realSize = comp->info.compCompHnd->getClassSize(structCls); |
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.
I assume it is guaranteed that structCls != NO_CLASS_HANDLE
here or we would have hit some BADCODE
previously? (e.g. returning struct in method declared to return int)
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.
Not that the current behavior tries to be careful in such cases, I've just tried and got an AccessViolationException in Main for it when I changed return type to int while was trying to return a small struct.
LLVMAOT failure was known and already fixed in Main |
_ptr = mmap(null, 2 * PageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | ||
if (_ptr != null && mprotect(_ptr + PageSize, PageSize, PROT_NONE) != 0) | ||
{ | ||
munmap(_ptr, 2 * PageSize); |
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.
This looks like it will result in a double free
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.
(But I doubt that's the cause of #78758)
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.
Ah, I see the reason now:
On success, mmap() returns a pointer to the mapped area. On
error, the value MAP_FAILED (that is, (void *) -1) is returned,
and [errno](https://man7.org/linux/man-pages/man3/errno.3.html) is set to indicate the error.
So this does not correctly check that we failed to allocate the memory.
Fixes #76194
using @SingleAccretion's snippet since mine was uglier.
We've had similar issues in the past e.g. #64683 (reproduces even on .NET Framework x64)
it's just that normally it's an extremely rare case, perhaps, still worth backporting to .NET 7.0