-
Notifications
You must be signed in to change notification settings - Fork 2.6k
[x86/Linux] Fix ResumeEsp before resume #10749
Conversation
|
\CC @YongseopKim |
|
@parjong yes, there is some issue with network connection speed to the OSX machines. I believe people are looking into it. |
|
Thanks you for reply 👍 |
| } | ||
|
|
||
| const size_t curEBP = *pRD->GetEbpLocation(); | ||
| return GetOutermostBaseFP(curEBP, info); |
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.
Does this work correctly for the cases where we have added stack alignment before pushing the arguments?
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.
As I understand jitter records stack changes (including stack alignment padding) for ESP frame, and thus this approach works for ESP frames. For EBP frame, jitter reports frame size, and thus this approach seems to work as we could derive resume SP from ebp and frame size.
I am currently not sure about funclets in ebp framed function.
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.
Can you verify that it works for the funclets too? I have looked for a good candidate of a fcall that throws and can be easily forced to throw and has at least one stack argument and found ArrayNative::ArrayClear. It throws if the passed iIndex is below the lower bound of the array or if the iIndex + iLength is over the end of the array.
It looks like this FCall is invoked when you call Array.Clear static method.
So if you write a little test app and placed the following piece of code into a funclet, you should be able to put a breakpoint inside the if (codeInfo.IsFunclet()) in the EECodeManager::GetResumeSp and then step through your code to see if it works as expected.
byte[] b = new byte[4];
Array.Clear(b, 0, 8);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.
Sorry for unclear comment.
I already found a case that GetPushedArgSize becomes incorrect while working on #10685. I tried to set baseSP as curESP + GetPushedArgSize(...) instead of pContext->pCurrentContext->ResumeEsp, but JIT.Methodical.flowgraph.dev10_bug679008.fgloop fails with that approach.
It seems that we need to emit full arg info for all managed methods in order to fix this case. I'm not sure about the amount of work for this task.
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 have a quick idea. Maybe we can solve it in an easy way, based on the fact that we know that a funclet is called from native code and we know what the prolog of a funclet looks like. Since funclet cannot allocate any locals, we know that the ESP we are looking for is the ESP after the prolog of the funclet, which is ESP at which the asm helper called the funclet minus the return address size and that aligned to 16 bytes.
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'm not sure whether we could get ESP at which the asm helper called correctly.
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 submitted #10782 to address this issue. Could you please take a look?
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 have quickly looked at the #10782, but my first impression is that it should be our last resort if there is no other way. I feel uneasy about adding the full argument info even for EBP frames. But I'll think about it more.
But as for my idea how to solve the funclet issue differently, the CallEHFunclet and CallEHFilterFunclet records the funclet caller SP in ExceptionTracker::m_EHClauseInfo.GetCallerStackFrameForEHClauseReference(). So in ExceptionTracker::ResumeExecution, we could read that value from the right exception tracker and pass it to the EECodeManager::GetResumeSp. If there are no issues that I don't see at this late time at night, I think that it would work.
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 guess that the suggested approach works for resuming, but I'm not sure whether this approach could address incorrect unwinding during GC and 1st pass EH.
Could you let me know your opinion?
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.
One approach to optimize #10782 is to emit full arg info only for functions with funclet. Is it possible to add some flag on GCInfo header (to indicate arg info type)?
src/vm/i386/cgenx86.cpp
Outdated
|
|
||
| pRD->pCurrentContext->Eip = pRD->ControlPC = m_MachState.GetRetAddr(); | ||
| pRD->pCurrentContext->Esp = pRD->pCurrentContext->ResumeEsp = pRD->SP = (DWORD) m_MachState.esp(); | ||
| pRD->pCurrentContext->Esp = pRD->SP = (DWORD) m_MachState.esp(); |
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 we make the same change in TailCallFrame::UpdateRegDisplay and HijackFrame::UpdateRegDisplay? They set the Esp and ResumeEsp the same way 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.
I think so, but I would like to isolate those changes from this change.
src/vm/exceptionhandling.cpp
Outdated
| UPDATEREG(Edi); | ||
| UPDATEREG(Ebp); | ||
|
|
||
| UPDATEREG_VAL(ResumeEsp); |
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 am not excited about the updating of ResumeEsp at this place since it is a not a non-volatile register and the function is called UpdateNonvolatileRegisters. Unfortunately the only other place where we have the necessary data to update the ResumeESP is the caller and that's called FixNonvolatileRegisters, so it would not be any better. And I don't really want to rename the functions.
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 may revise GetResumeSp to take CONTEXT instead REGDISPLAY.
Then, we could fix ResumeEsp inside ProcessCLRException just before calling ExceptionTracker::ResumeExecution.
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.
Great, that sounds much better to me! And maybe we can just change the method to set the ResumeEsp internally in the context instead of returning it back. And maybe rename the method to something like FixResumeEsp then.
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.
It seems that the correctness of GetPushedArgSize should be guaranteed before implementing this change. I'll take a look.
src/vm/i386/cgenx86.cpp
Outdated
| // | ||
| // Fix up ResumeSp | ||
| // | ||
| pRD->pCurrentContext->ResumeEsp = EECodeManager::GetResumeSp(pRD); |
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 assumes that the FCall is always called from JITed code. It is not always the case. For example, the FCall can be called by CallDescrWorker.
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.
It is possible, but does not matter as we use ResumeEsp only when resuming from managed code.
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 think it will be more obvious after you make the change that you've suggested.
|
@janvorli Revised PR as I mentioned, but it introduces baseservices.compilerservices.RuntimeWrappedException regression. I'll take a look into this issue. |
a586896 to
21db4ce
Compare
|
955fbc2 resolves baseservices.compilerservices.RuntimeWrappedException regression. |
But I was thinking about it and it seems we have two more options to solve the issue:
@parjong can you please tell me your opinions on these options? |
Full arg info is required only for functions with funclets. FCALL itself does not matter with the current approach. All the function with funclet are ebp-framed, but we need to treat its funclets as esp-framed. Actually, #10782 already introduced
I once considered this approach, but concluded that it will not work. Recently, it turns out that x86 JIT emits nested calls. The presence of nested calls makes it impossible to estimate stack alignment padding without additional meta data such as GCInfo.
I totally agree that enabling |
|
@parjong it seems I got confused by the title of this PR. So the problem you are solving is not just FCALLs in a funclet, but all calls made from a funclet? That when we unwind from any such call back to the funclet, we cannot get the correct resume ESP without a fix? |
|
@dotnet-bot test OSX10.12 x64 Checked Build and Test (network) |
|
@janvorli Sorry for the confusion. Yes, this PR attempts to address general incorrect resume esp issue due to stack alignment padding (via using GCInfo). This issue currently appears only for FCALL as managed unwinder uses a heuristic to adjust resume esp (which may be incorrect in the presence of nested calls). I guess that we will suffer from incorrect resume esp issue when we unwind from any nested call even for managed functions without a fix like this. This PR is based on the assumption that #10782 attempts to fix this issue (makes |
This commit fixes RseumeEsp just before resuming, which resolves 4 test failures: