@@ -5644,7 +5644,9 @@ void Compiler::lvaFixVirtualFrameOffsets()
56445644#endif
56455645
56465646 // The delta to be added to virtual offset to adjust it relative to frame pointer or SP
5647- int delta = 0 ;
5647+ int delta = 0 ;
5648+ int frameLocalsDelta = 0 ;
5649+ int frameBoundary = 0 ;
56485650
56495651#ifdef TARGET_XARCH
56505652 delta += REGSIZE_BYTES; // pushed PC (return address) for x86/x64
@@ -5669,7 +5671,25 @@ void Compiler::lvaFixVirtualFrameOffsets()
56695671 // We set FP to be after LR, FP
56705672 delta += 2 * REGSIZE_BYTES;
56715673 }
5672- #elif defined(TARGET_AMD64) || defined(TARGET_ARM64)
5674+ #elif defined(TARGET_ARM64)
5675+ else
5676+ {
5677+ // FP is used.
5678+ delta += codeGen->genTotalFrameSize () - codeGen->genSPtoFPdelta ();
5679+
5680+ // If we placed FP/LR at the bottom of the frame we need to shift all the variables
5681+ // on the new frame to account for it. See lvaAssignVirtualFrameOffsetsToLocals.
5682+ if (!codeGen->IsSaveFpLrWithAllCalleeSavedRegisters ())
5683+ {
5684+ // We set FP to be after LR, FP
5685+ frameLocalsDelta = 2 * REGSIZE_BYTES;
5686+ frameBoundary = opts.IsOSR () ? -info.compPatchpointInfo ->TotalFrameSize () : 0 ;
5687+ if (info.compIsVarArgs )
5688+ frameBoundary -= MAX_REG_ARG * REGSIZE_BYTES;
5689+ }
5690+ JITDUMP (" --- delta bump %d for FP frame, %d inside frame for FP/LR relocation\n " , delta, frameLocalsDelta);
5691+ }
5692+ #elif defined(TARGET_AMD64)
56735693 else
56745694 {
56755695 // FP is used.
@@ -5737,7 +5757,7 @@ void Compiler::lvaFixVirtualFrameOffsets()
57375757
57385758#if defined(TARGET_X86)
57395759 // On x86, we set the stack offset for a promoted field
5740- // to match a struct parameter in lvAssignFrameOffsetsToPromotedStructs .
5760+ // to match a struct parameter in lvaAssignFrameOffsetsToPromotedStructs .
57415761 if ((!varDsc->lvIsParam || parentvarDsc->lvIsParam ) && promotionType == PROMOTION_TYPE_DEPENDENT)
57425762#else
57435763 if (!varDsc->lvIsParam && promotionType == PROMOTION_TYPE_DEPENDENT)
@@ -5757,15 +5777,23 @@ void Compiler::lvaFixVirtualFrameOffsets()
57575777
57585778 if (doAssignStkOffs)
57595779 {
5760- JITDUMP (" -- V%02u was %d, now %d\n " , lclNum, varDsc->GetStackOffset (), varDsc->GetStackOffset () + delta);
5761- varDsc->SetStackOffset (varDsc->GetStackOffset () + delta);
5780+ int localDelta = delta;
5781+
5782+ if (frameLocalsDelta != 0 && varDsc->GetStackOffset () < frameBoundary)
5783+ {
5784+ localDelta += frameLocalsDelta;
5785+ }
5786+
5787+ JITDUMP (" -- V%02u was %d, now %d\n " , lclNum, varDsc->GetStackOffset (),
5788+ varDsc->GetStackOffset () + localDelta);
5789+ varDsc->SetStackOffset (varDsc->GetStackOffset () + localDelta);
57625790
57635791#if DOUBLE_ALIGN
57645792 if (genDoubleAlign () && !codeGen->isFramePointerUsed ())
57655793 {
57665794 if (varDsc->lvFramePointerBased )
57675795 {
5768- varDsc->SetStackOffset (varDsc->GetStackOffset () - delta );
5796+ varDsc->SetStackOffset (varDsc->GetStackOffset () - localDelta );
57695797
57705798 // We need to re-adjust the offsets of the parameters so they are EBP
57715799 // relative rather than stack/frame pointer relative
@@ -5787,9 +5815,13 @@ void Compiler::lvaFixVirtualFrameOffsets()
57875815 assert (codeGen->regSet .tmpAllFree ());
57885816 for (TempDsc* temp = codeGen->regSet .tmpListBeg (); temp != nullptr ; temp = codeGen->regSet .tmpListNxt (temp))
57895817 {
5790- temp->tdAdjustTempOffs (delta);
5818+ temp->tdAdjustTempOffs (delta + frameLocalsDelta );
57915819 }
57925820
5821+ if (lvaCachedGenericContextArgOffs < frameBoundary)
5822+ {
5823+ lvaCachedGenericContextArgOffs += frameLocalsDelta;
5824+ }
57935825 lvaCachedGenericContextArgOffs += delta;
57945826
57955827#if FEATURE_FIXED_OUT_ARGS
@@ -6045,30 +6077,6 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
60456077 codeGen->setFramePointerUsed (codeGen->isFramePointerRequired ());
60466078 }
60476079
6048- #ifdef TARGET_ARM64
6049- // Decide where to save FP and LR registers. We store FP/LR registers at the bottom of the frame if there is
6050- // a frame pointer used (so we get positive offsets from the frame pointer to access locals), but not if we
6051- // need a GS cookie AND localloc is used, since we need the GS cookie to protect the saved return value,
6052- // and also the saved frame pointer. See CodeGen::genPushCalleeSavedRegisters() for more details about the
6053- // frame types. Since saving FP/LR at high addresses is a relatively rare case, force using it during stress.
6054- // (It should be legal to use these frame types for every frame).
6055-
6056- if (opts.compJitSaveFpLrWithCalleeSavedRegisters == 0 )
6057- {
6058- // Default configuration
6059- codeGen->SetSaveFpLrWithAllCalleeSavedRegisters ((getNeedsGSSecurityCookie () && compLocallocUsed) ||
6060- opts.compDbgEnC || compStressCompile (STRESS_GENERIC_VARN, 20 ));
6061- }
6062- else if (opts.compJitSaveFpLrWithCalleeSavedRegisters == 1 )
6063- {
6064- codeGen->SetSaveFpLrWithAllCalleeSavedRegisters (false ); // Disable using new frames
6065- }
6066- else if ((opts.compJitSaveFpLrWithCalleeSavedRegisters == 2 ) || (opts.compJitSaveFpLrWithCalleeSavedRegisters == 3 ))
6067- {
6068- codeGen->SetSaveFpLrWithAllCalleeSavedRegisters (true ); // Force using new frames
6069- }
6070- #endif // TARGET_ARM64
6071-
60726080#ifdef TARGET_XARCH
60736081 // On x86/amd64, the return address has already been pushed by the call instruction in the caller.
60746082 stkOffs -= TARGET_POINTER_SIZE; // return address;
@@ -6117,9 +6125,13 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
61176125#endif // !TARGET_ARM
61186126
61196127#ifdef TARGET_ARM64
6120- // If the frame pointer is used, then we'll save FP/LR at the bottom of the stack.
6121- // Otherwise, we won't store FP, and we'll store LR at the top, with the other callee-save
6122- // registers (if any).
6128+ // If the frame pointer is used, then we'll save FP/LR either at the bottom of the stack
6129+ // or at the top of the stack depending on frame type. We make the decision after assigning
6130+ // the variables on the frame and then fix up the offsets in lvaFixVirtualFrameOffsets.
6131+ // For now, we proceed as if FP/LR were saved with the callee registers. If we later
6132+ // decide to move the FP/LR to the bottom of the frame it shifts all the assigned
6133+ // variables and temporaries by 16 bytes. The largest alignment we currently make is 16
6134+ // bytes for SIMD.
61236135
61246136 int initialStkOffs = 0 ;
61256137 if (info.compIsVarArgs )
@@ -6130,17 +6142,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
61306142 stkOffs -= initialStkOffs;
61316143 }
61326144
6133- if (codeGen->IsSaveFpLrWithAllCalleeSavedRegisters () || !isFramePointerUsed ()) // Note that currently we always have
6134- // a frame pointer
6135- {
6136- stkOffs -= compCalleeRegsPushed * REGSIZE_BYTES;
6137- }
6138- else
6139- {
6140- // Subtract off FP and LR.
6141- assert (compCalleeRegsPushed >= 2 );
6142- stkOffs -= (compCalleeRegsPushed - 2 ) * REGSIZE_BYTES;
6143- }
6145+ stkOffs -= compCalleeRegsPushed * REGSIZE_BYTES;
61446146
61456147#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
61466148
@@ -6810,15 +6812,6 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
68106812 }
68116813#endif // TARGET_AMD64
68126814
6813- #ifdef TARGET_ARM64
6814- if (!codeGen->IsSaveFpLrWithAllCalleeSavedRegisters () && isFramePointerUsed ()) // Note that currently we always have
6815- // a frame pointer
6816- {
6817- // Create space for saving FP and LR.
6818- stkOffs -= 2 * REGSIZE_BYTES;
6819- }
6820- #endif // TARGET_ARM64
6821-
68226815#if FEATURE_FIXED_OUT_ARGS
68236816 if (lvaOutgoingArgSpaceSize > 0 )
68246817 {
@@ -6856,6 +6849,44 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
68566849
68576850 noway_assert (compLclFrameSize + originalFrameSize ==
68586851 (unsigned )-(stkOffs + (pushedCount * (int )TARGET_POINTER_SIZE)));
6852+
6853+ #ifdef TARGET_ARM64
6854+ // Decide where to save FP and LR registers. We store FP/LR registers at the bottom of the frame if there is
6855+ // a frame pointer used (so we get positive offsets from the frame pointer to access locals), but not if we
6856+ // need a GS cookie AND localloc is used, since we need the GS cookie to protect the saved return value,
6857+ // and also the saved frame pointer. See CodeGen::genPushCalleeSavedRegisters() for more details about the
6858+ // frame types. Since saving FP/LR at high addresses is a relatively rare case, force using it during stress.
6859+ // (It should be legal to use these frame types for every frame).
6860+ //
6861+ // For Apple NativeAOT ABI we try to save the FP/LR registers on top to get canonical frame layout that can
6862+ // be represented with compact unwinding information. In order to maintain code quality we only do it when
6863+ // we can use SP-based addressing (!isFramePointerRequired) through lvaFrameAddress optimization, or if the
6864+ // whole frame is small enough that the negative FP-based addressing can address the whole frame.
6865+
6866+ if (opts.compJitSaveFpLrWithCalleeSavedRegisters == 0 )
6867+ {
6868+ if (IsTargetAbi (CORINFO_NATIVEAOT_ABI) && TargetOS::IsApplePlatform &&
6869+ (!codeGen->isFramePointerRequired () || codeGen->genTotalFrameSize () < 0x100 ))
6870+ {
6871+ codeGen->SetSaveFpLrWithAllCalleeSavedRegisters (true );
6872+ }
6873+ else
6874+ {
6875+ // Default configuration
6876+ codeGen->SetSaveFpLrWithAllCalleeSavedRegisters ((getNeedsGSSecurityCookie () && compLocallocUsed) ||
6877+ opts.compDbgEnC ||
6878+ compStressCompile (Compiler::STRESS_GENERIC_VARN, 20 ));
6879+ }
6880+ }
6881+ else if (opts.compJitSaveFpLrWithCalleeSavedRegisters == 1 )
6882+ {
6883+ codeGen->SetSaveFpLrWithAllCalleeSavedRegisters (false ); // Disable using new frames
6884+ }
6885+ else if ((opts.compJitSaveFpLrWithCalleeSavedRegisters == 2 ) || (opts.compJitSaveFpLrWithCalleeSavedRegisters == 3 ))
6886+ {
6887+ codeGen->SetSaveFpLrWithAllCalleeSavedRegisters (true ); // Force using new frames
6888+ }
6889+ #endif // TARGET_ARM64
68596890}
68606891
68616892// ------------------------------------------------------------------------
0 commit comments