diff options
author | Bruce Forstall <brucefo@microsoft.com> | 2021-06-19 02:51:50 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-19 02:51:50 +0300 |
commit | f5da499194580958cbaca9abdaf209572ec3b748 (patch) | |
tree | 45e88eaec87b2b9546f058d60899c75b94893b46 /src/coreclr/jit | |
parent | 27fece253494ac80f9f13843cbaa0e99a5d502ae (diff) |
Fix AMD64 epilog ABI (#54357)
* Fix AMD64 epilog ABI
The Windows AMD64 epilog ABI states that an `lea rsp,[rbp+constant]` instruction
may only be used if a frame pointer has been reported to the OS in the prolog
unwind info, otherwise an `add rsp, constant` instruction must be used.
There were a number of cases where the JIT used the `lea` form simply because
a frame pointer had been established and was available, even though it had not
been reported to the OS (and, thus, the frame was effectively an `rsp` frame).
Fix this by using the same condition in the epilog for determining which form
to use, `lea` or `add`, that was used in the prolog to determine whether or not
to report the frame pointer in the unwind info.
Fixes #54320
* Formatting
* Fix OSR
Diffstat (limited to 'src/coreclr/jit')
-rw-r--r-- | src/coreclr/jit/codegencommon.cpp | 37 |
1 files changed, 35 insertions, 2 deletions
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index f977ddfe6f3..e99f047c061 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -7454,7 +7454,7 @@ void CodeGen::genFnProlog() // Establish the AMD64 frame pointer after the OS-reported prolog. if (doubleAlignOrFramePointerUsed()) { - bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC; + const bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC; genEstablishFramePointer(compiler->codeGen->genSPtoFPdelta(), reportUnwindData); } #endif // TARGET_AMD64 @@ -8147,7 +8147,31 @@ void CodeGen::genFnEpilog(BasicBlock* block) /* Compute the size in bytes we've pushed/popped */ - if (!doubleAlignOrFramePointerUsed()) + bool removeEbpFrame = doubleAlignOrFramePointerUsed(); + +#ifdef TARGET_AMD64 + // We only remove the EBP frame using the frame pointer (using `lea rsp, [rbp + const]`) + // if we reported the frame pointer in the prolog. The Windows x64 unwinding ABI specifically + // disallows this `lea` form: + // + // See https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog?view=msvc-160#epilog-code + // + // "When a frame pointer is not used, the epilog must use add RSP,constant to deallocate the fixed part of the + // stack. It may not use lea RSP,constant[RSP] instead. This restriction exists so the unwind code has fewer + // patterns to recognize when searching for epilogs." + // + // Otherwise, we must use `add RSP, constant`, as stated. So, we need to use the same condition + // as genFnProlog() used in determining whether to report the frame pointer in the unwind data. + // This is a subset of the `doubleAlignOrFramePointerUsed()` cases. + // + if (removeEbpFrame) + { + const bool reportUnwindData = compiler->compLocallocUsed || compiler->opts.compDbgEnC; + removeEbpFrame = removeEbpFrame && reportUnwindData; + } +#endif // TARGET_AMD64 + + if (!removeEbpFrame) { // We have an ESP frame */ @@ -8177,6 +8201,15 @@ void CodeGen::genFnEpilog(BasicBlock* block) genPopCalleeSavedRegisters(); +#ifdef TARGET_AMD64 + // In the case where we have an RSP frame, and no frame pointer reported in the OS unwind info, + // but we do have a pushed frame pointer and established frame chain, we do need to pop RBP. + if (doubleAlignOrFramePointerUsed()) + { + inst_RV(INS_pop, REG_EBP, TYP_I_IMPL); + } +#endif // TARGET_AMD64 + // Extra OSR adjust to get to where RBP was saved by the original frame, and // restore RBP. // |