diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index e465cd68058ef4..d84ba8760dca00 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -659,7 +659,6 @@ class CodeGen final : public CodeGenInterface virtual bool IsSaveFpLrWithAllCalleeSavedRegisters() const; bool genSaveFpLrWithAllCalleeSavedRegisters; bool genForceFuncletFrameType5; - bool genReverseAndPairCalleeSavedRegisters; #endif // TARGET_ARM64 //------------------------------------------------------------------------- diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 50b947bc5bce14..6e4db910847df7 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -845,19 +845,12 @@ void CodeGen::genSaveCalleeSavedRegisterGroup(regMaskTP regsMask, int spDelta, i for (int i = 0; i < regStack.Height(); ++i) { - RegPair regPair = genReverseAndPairCalleeSavedRegisters ? regStack.Top(i) : regStack.Bottom(i); + RegPair regPair = regStack.Bottom(i); if (regPair.reg2 != REG_NA) { // We can use a STP instruction. - if (genReverseAndPairCalleeSavedRegisters) - { - genPrologSaveRegPair(regPair.reg2, regPair.reg1, spOffset, spDelta, false, REG_IP0, nullptr); - } - else - { - genPrologSaveRegPair(regPair.reg1, regPair.reg2, spOffset, spDelta, regPair.useSaveNextPair, REG_IP0, - nullptr); - } + genPrologSaveRegPair(regPair.reg1, regPair.reg2, spOffset, spDelta, regPair.useSaveNextPair, REG_IP0, + nullptr); spOffset += 2 * slotSize; } @@ -933,9 +926,8 @@ void CodeGen::genSaveCalleeSavedRegistersHelp(regMaskTP regsToSaveMask, int lowe // Save integer registers at higher addresses than floating-point registers. - regMaskTP maskSaveRegsFrame = regsToSaveMask & (RBM_FP | RBM_LR); regMaskTP maskSaveRegsFloat = regsToSaveMask & RBM_ALLFLOAT; - regMaskTP maskSaveRegsInt = regsToSaveMask & ~maskSaveRegsFloat & ~maskSaveRegsFrame; + regMaskTP maskSaveRegsInt = regsToSaveMask & ~maskSaveRegsFloat; if (maskSaveRegsFloat != RBM_NONE) { @@ -947,13 +939,6 @@ void CodeGen::genSaveCalleeSavedRegistersHelp(regMaskTP regsToSaveMask, int lowe if (maskSaveRegsInt != RBM_NONE) { genSaveCalleeSavedRegisterGroup(maskSaveRegsInt, spDelta, lowestCalleeSavedOffset); - spDelta = 0; - lowestCalleeSavedOffset += genCountBits(maskSaveRegsInt) * FPSAVE_REGSIZE_BYTES; - } - - if (maskSaveRegsFrame != RBM_NONE) - { - genPrologSaveRegPair(REG_FP, REG_LR, lowestCalleeSavedOffset, spDelta, false, REG_IP0, nullptr); // No need to update spDelta, lowestCalleeSavedOffset since they're not used after this. } } @@ -985,20 +970,13 @@ void CodeGen::genRestoreCalleeSavedRegisterGroup(regMaskTP regsMask, int spDelta stackDelta = spDelta; } - RegPair regPair = genReverseAndPairCalleeSavedRegisters ? regStack.Bottom(i) : regStack.Top(i); + RegPair regPair = regStack.Top(i); if (regPair.reg2 != REG_NA) { spOffset -= 2 * slotSize; - if (genReverseAndPairCalleeSavedRegisters) - { - genEpilogRestoreRegPair(regPair.reg2, regPair.reg1, spOffset, stackDelta, false, REG_IP1, nullptr); - } - else - { - genEpilogRestoreRegPair(regPair.reg1, regPair.reg2, spOffset, stackDelta, regPair.useSaveNextPair, - REG_IP1, nullptr); - } + genEpilogRestoreRegPair(regPair.reg1, regPair.reg2, spOffset, stackDelta, regPair.useSaveNextPair, REG_IP1, + nullptr); } else { @@ -1065,19 +1043,11 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in // Save integer registers at higher addresses than floating-point registers. - regMaskTP maskRestoreRegsFrame = regsToRestoreMask & (RBM_FP | RBM_LR); regMaskTP maskRestoreRegsFloat = regsToRestoreMask & RBM_ALLFLOAT; - regMaskTP maskRestoreRegsInt = regsToRestoreMask & ~maskRestoreRegsFloat & ~maskRestoreRegsFrame; + regMaskTP maskRestoreRegsInt = regsToRestoreMask & ~maskRestoreRegsFloat; // Restore in the opposite order of saving. - if (maskRestoreRegsFrame != RBM_NONE) - { - int spFrameDelta = (maskRestoreRegsFloat != RBM_NONE || maskRestoreRegsInt != RBM_NONE) ? 0 : spDelta; - spOffset -= 2 * REGSIZE_BYTES; - genEpilogRestoreRegPair(REG_FP, REG_LR, spOffset, spFrameDelta, false, REG_IP1, nullptr); - } - if (maskRestoreRegsInt != RBM_NONE) { int spIntDelta = (maskRestoreRegsFloat != RBM_NONE) ? 0 : spDelta; // should we delay the SP adjustment? diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index dec27882bb2285..b42afb963a0f86 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -255,7 +255,6 @@ CodeGen::CodeGen(Compiler* theCompiler) #ifdef TARGET_ARM64 genSaveFpLrWithAllCalleeSavedRegisters = false; genForceFuncletFrameType5 = false; - genReverseAndPairCalleeSavedRegisters = false; #endif // TARGET_ARM64 } @@ -4813,29 +4812,6 @@ void CodeGen::genFinalizeFrame() } #endif // TARGET_ARM -#ifdef TARGET_ARM64 - if (compiler->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && TargetOS::IsApplePlatform) - { - JITDUMP("Setting genReverseAndPairCalleeSavedRegisters = true"); - - genReverseAndPairCalleeSavedRegisters = true; - - // Make sure we push the registers in pairs if possible. If we only allocate a contiguous - // block of registers this should add at most one integer and at most one floating point - // register to the list. The stack has to be 16-byte aligned, so in worst case it results - // in allocating 16 bytes more space on stack if odd number of integer and odd number of - // FP registers were occupied. Same number of instructions will be generated, just the - // STR instructions are replaced with STP (store pair). - regMaskTP maskModifiedRegs = regSet.rsGetModifiedRegsMask(); - regMaskTP maskPairRegs = ((maskModifiedRegs & (RBM_V8 | RBM_V10 | RBM_V12 | RBM_V14)).getLow() << 1) | - ((maskModifiedRegs & (RBM_R19 | RBM_R21 | RBM_R23 | RBM_R25 | RBM_R27)).getLow() << 1); - if (maskPairRegs != RBM_NONE) - { - regSet.rsSetRegsModified(maskPairRegs); - } - } -#endif - #ifdef DEBUG if (verbose) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 956e05e9b71f00..076187e1bfb56a 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4324,8 +4324,6 @@ class Compiler #ifdef TARGET_ARM int lvaFrameAddress(int varNum, bool mustBeFPBased, regNumber* pBaseReg, int addrModeOffset, bool isFloatUsage); -#elif TARGET_ARM64 - int lvaFrameAddress(int varNum, bool* pFPbased, bool suppressFPtoSPRewrite = false); #else int lvaFrameAddress(int varNum, bool* pFPbased); #endif diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index ccb2584ac92550..1494bd39aee2a4 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -2639,9 +2639,6 @@ inline int Compiler::lvaFrameAddress( int varNum, bool mustBeFPBased, regNumber* pBaseReg, int addrModeOffset, bool isFloatUsage) -#elif TARGET_ARM64 - int - Compiler::lvaFrameAddress(int varNum, bool* pFPbased, bool suppressFPtoSPRewrite) #else int Compiler::lvaFrameAddress(int varNum, bool* pFPbased) @@ -2811,16 +2808,6 @@ inline { *pBaseReg = REG_SPBASE; } -#elif defined(TARGET_ARM64) - if (FPbased && !suppressFPtoSPRewrite && !codeGen->isFramePointerRequired() && varOffset < 0 && !opts.IsOSR() && - lvaDoneFrameLayout == Compiler::FINAL_FRAME_LAYOUT && codeGen->IsSaveFpLrWithAllCalleeSavedRegisters()) - { - int spVarOffset = varOffset + codeGen->genSPtoFPdelta(); - JITDUMP("lvaFrameAddress optimization for V%02u: [FP-%d] -> [SP+%d]\n", varNum, -varOffset, spVarOffset); - FPbased = false; - varOffset = spVarOffset; - } - *pFPbased = FPbased; #else *pFPbased = FPbased; #endif diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 3fd34318d9ec11..f0d00b5e020954 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -12270,7 +12270,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) int varNum = id->idAddr()->iiaLclVar.lvaVarNum(); unsigned ofs = AlignDown(id->idAddr()->iiaLclVar.lvaOffset(), TARGET_POINTER_SIZE); bool FPbased; - int adr = emitComp->lvaFrameAddress(varNum, &FPbased, true); + int adr = emitComp->lvaFrameAddress(varNum, &FPbased); if (id->idGCref() != GCT_NONE) { emitGCvarLiveUpd(adr + ofs, varNum, id->idGCref(), dst DEBUG_ARG(varNum)); @@ -12311,11 +12311,15 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) // If there are 2 GC vars in this instrDesc, get the 2nd variable // that should be tracked. - adr2 = emitComp->lvaFrameAddress(varNum2, &FPbased2, true); + adr2 = emitComp->lvaFrameAddress(varNum2, &FPbased2); ofs2Dist = EA_SIZE_IN_BYTES(size); #ifdef DEBUG assert(FPbased == FPbased2); - if (!FPbased) + if (FPbased) + { + assert(id->idReg3() == REG_FP); + } + else { assert(encodingZRtoSP(id->idReg3()) == REG_SP); } diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index ad9b203b454e3b..8de5ff872cae21 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -5641,9 +5641,7 @@ void Compiler::lvaFixVirtualFrameOffsets() #endif // The delta to be added to virtual offset to adjust it relative to frame pointer or SP - int delta = 0; - int frameLocalsDelta = 0; - int frameBoundary = 0; + int delta = 0; #ifdef TARGET_XARCH delta += REGSIZE_BYTES; // pushed PC (return address) for x86/x64 @@ -5668,25 +5666,7 @@ void Compiler::lvaFixVirtualFrameOffsets() // We set FP to be after LR, FP delta += 2 * REGSIZE_BYTES; } -#elif defined(TARGET_ARM64) - else - { - // FP is used. - delta += codeGen->genTotalFrameSize() - codeGen->genSPtoFPdelta(); - - // If we placed FP/LR at the bottom of the frame we need to shift all the variables - // on the new frame to account for it. See lvaAssignVirtualFrameOffsetsToLocals. - if (!codeGen->IsSaveFpLrWithAllCalleeSavedRegisters()) - { - // We set FP to be after LR, FP - frameLocalsDelta = 2 * REGSIZE_BYTES; - frameBoundary = opts.IsOSR() ? -info.compPatchpointInfo->TotalFrameSize() : 0; - if (info.compIsVarArgs) - frameBoundary -= MAX_REG_ARG * REGSIZE_BYTES; - } - JITDUMP("--- delta bump %d for FP frame, %d inside frame for FP/LR relocation\n", delta, frameLocalsDelta); - } -#elif defined(TARGET_AMD64) +#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) else { // FP is used. @@ -5754,7 +5734,7 @@ void Compiler::lvaFixVirtualFrameOffsets() #if defined(TARGET_X86) // On x86, we set the stack offset for a promoted field - // to match a struct parameter in lvaAssignFrameOffsetsToPromotedStructs. + // to match a struct parameter in lvAssignFrameOffsetsToPromotedStructs. if ((!varDsc->lvIsParam || parentvarDsc->lvIsParam) && promotionType == PROMOTION_TYPE_DEPENDENT) #else if (!varDsc->lvIsParam && promotionType == PROMOTION_TYPE_DEPENDENT) @@ -5774,23 +5754,15 @@ void Compiler::lvaFixVirtualFrameOffsets() if (doAssignStkOffs) { - int localDelta = delta; - - if (frameLocalsDelta != 0 && varDsc->GetStackOffset() < frameBoundary) - { - localDelta += frameLocalsDelta; - } - - JITDUMP("-- V%02u was %d, now %d\n", lclNum, varDsc->GetStackOffset(), - varDsc->GetStackOffset() + localDelta); - varDsc->SetStackOffset(varDsc->GetStackOffset() + localDelta); + JITDUMP("-- V%02u was %d, now %d\n", lclNum, varDsc->GetStackOffset(), varDsc->GetStackOffset() + delta); + varDsc->SetStackOffset(varDsc->GetStackOffset() + delta); #if DOUBLE_ALIGN if (genDoubleAlign() && !codeGen->isFramePointerUsed()) { if (varDsc->lvFramePointerBased) { - varDsc->SetStackOffset(varDsc->GetStackOffset() - localDelta); + varDsc->SetStackOffset(varDsc->GetStackOffset() - delta); // We need to re-adjust the offsets of the parameters so they are EBP // relative rather than stack/frame pointer relative @@ -5812,13 +5784,9 @@ void Compiler::lvaFixVirtualFrameOffsets() assert(codeGen->regSet.tmpAllFree()); for (TempDsc* temp = codeGen->regSet.tmpListBeg(); temp != nullptr; temp = codeGen->regSet.tmpListNxt(temp)) { - temp->tdAdjustTempOffs(delta + frameLocalsDelta); + temp->tdAdjustTempOffs(delta); } - if (lvaCachedGenericContextArgOffs < frameBoundary) - { - lvaCachedGenericContextArgOffs += frameLocalsDelta; - } lvaCachedGenericContextArgOffs += delta; #if FEATURE_FIXED_OUT_ARGS @@ -6074,6 +6042,30 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() codeGen->setFramePointerUsed(codeGen->isFramePointerRequired()); } +#ifdef TARGET_ARM64 + // Decide where to save FP and LR registers. We store FP/LR registers at the bottom of the frame if there is + // a frame pointer used (so we get positive offsets from the frame pointer to access locals), but not if we + // need a GS cookie AND localloc is used, since we need the GS cookie to protect the saved return value, + // and also the saved frame pointer. See CodeGen::genPushCalleeSavedRegisters() for more details about the + // frame types. Since saving FP/LR at high addresses is a relatively rare case, force using it during stress. + // (It should be legal to use these frame types for every frame). + + if (opts.compJitSaveFpLrWithCalleeSavedRegisters == 0) + { + // Default configuration + codeGen->SetSaveFpLrWithAllCalleeSavedRegisters((getNeedsGSSecurityCookie() && compLocallocUsed) || + opts.compDbgEnC || compStressCompile(STRESS_GENERIC_VARN, 20)); + } + else if (opts.compJitSaveFpLrWithCalleeSavedRegisters == 1) + { + codeGen->SetSaveFpLrWithAllCalleeSavedRegisters(false); // Disable using new frames + } + else if ((opts.compJitSaveFpLrWithCalleeSavedRegisters == 2) || (opts.compJitSaveFpLrWithCalleeSavedRegisters == 3)) + { + codeGen->SetSaveFpLrWithAllCalleeSavedRegisters(true); // Force using new frames + } +#endif // TARGET_ARM64 + #ifdef TARGET_XARCH // On x86/amd64, the return address has already been pushed by the call instruction in the caller. stkOffs -= TARGET_POINTER_SIZE; // return address; @@ -6122,13 +6114,9 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() #endif // !TARGET_ARM #ifdef TARGET_ARM64 - // If the frame pointer is used, then we'll save FP/LR either at the bottom of the stack - // or at the top of the stack depending on frame type. We make the decision after assigning - // the variables on the frame and then fix up the offsets in lvaFixVirtualFrameOffsets. - // For now, we proceed as if FP/LR were saved with the callee registers. If we later - // decide to move the FP/LR to the bottom of the frame it shifts all the assigned - // variables and temporaries by 16 bytes. The largest alignment we currently make is 16 - // bytes for SIMD. + // If the frame pointer is used, then we'll save FP/LR at the bottom of the stack. + // Otherwise, we won't store FP, and we'll store LR at the top, with the other callee-save + // registers (if any). int initialStkOffs = 0; if (info.compIsVarArgs) @@ -6139,7 +6127,17 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() stkOffs -= initialStkOffs; } - stkOffs -= compCalleeRegsPushed * REGSIZE_BYTES; + if (codeGen->IsSaveFpLrWithAllCalleeSavedRegisters() || !isFramePointerUsed()) // Note that currently we always have + // a frame pointer + { + stkOffs -= compCalleeRegsPushed * REGSIZE_BYTES; + } + else + { + // Subtract off FP and LR. + assert(compCalleeRegsPushed >= 2); + stkOffs -= (compCalleeRegsPushed - 2) * REGSIZE_BYTES; + } #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) @@ -6809,6 +6807,15 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() } #endif // TARGET_AMD64 +#ifdef TARGET_ARM64 + if (!codeGen->IsSaveFpLrWithAllCalleeSavedRegisters() && isFramePointerUsed()) // Note that currently we always have + // a frame pointer + { + // Create space for saving FP and LR. + stkOffs -= 2 * REGSIZE_BYTES; + } +#endif // TARGET_ARM64 + #if FEATURE_FIXED_OUT_ARGS if (lvaOutgoingArgSpaceSize > 0) { @@ -6846,44 +6853,6 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() noway_assert(compLclFrameSize + originalFrameSize == (unsigned)-(stkOffs + (pushedCount * (int)TARGET_POINTER_SIZE))); - -#ifdef TARGET_ARM64 - // Decide where to save FP and LR registers. We store FP/LR registers at the bottom of the frame if there is - // a frame pointer used (so we get positive offsets from the frame pointer to access locals), but not if we - // need a GS cookie AND localloc is used, since we need the GS cookie to protect the saved return value, - // and also the saved frame pointer. See CodeGen::genPushCalleeSavedRegisters() for more details about the - // frame types. Since saving FP/LR at high addresses is a relatively rare case, force using it during stress. - // (It should be legal to use these frame types for every frame). - // - // For Apple NativeAOT ABI we try to save the FP/LR registers on top to get canonical frame layout that can - // be represented with compact unwinding information. In order to maintain code quality we only do it when - // we can use SP-based addressing (!isFramePointerRequired) through lvaFrameAddress optimization, or if the - // whole frame is small enough that the negative FP-based addressing can address the whole frame. - - if (opts.compJitSaveFpLrWithCalleeSavedRegisters == 0) - { - if (IsTargetAbi(CORINFO_NATIVEAOT_ABI) && TargetOS::IsApplePlatform && - (!codeGen->isFramePointerRequired() || codeGen->genTotalFrameSize() < 0x100)) - { - codeGen->SetSaveFpLrWithAllCalleeSavedRegisters(true); - } - else - { - // Default configuration - codeGen->SetSaveFpLrWithAllCalleeSavedRegisters((getNeedsGSSecurityCookie() && compLocallocUsed) || - opts.compDbgEnC || - compStressCompile(Compiler::STRESS_GENERIC_VARN, 20)); - } - } - else if (opts.compJitSaveFpLrWithCalleeSavedRegisters == 1) - { - codeGen->SetSaveFpLrWithAllCalleeSavedRegisters(false); // Disable using new frames - } - else if ((opts.compJitSaveFpLrWithCalleeSavedRegisters == 2) || (opts.compJitSaveFpLrWithCalleeSavedRegisters == 3)) - { - codeGen->SetSaveFpLrWithAllCalleeSavedRegisters(true); // Force using new frames - } -#endif // TARGET_ARM64 } //------------------------------------------------------------------------ diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachNative.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachNative.cs index 8b3af9392a7b1c..14db96a9354747 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachNative.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachNative.cs @@ -120,18 +120,5 @@ internal static class MachNative public const uint PLATFORM_TVOSSIMULATOR = 8; public const uint PLATFORM_WATCHOSSIMULATOR = 9; public const uint PLATFORM_DRIVERKIT = 10; - - public const uint UNWIND_ARM64_MODE_FRAMELESS = 0x02000000; - public const uint UNWIND_ARM64_MODE_DWARF = 0x03000000; - public const uint UNWIND_ARM64_MODE_FRAME = 0x04000000; - public const uint UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001; - public const uint UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002; - public const uint UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004; - public const uint UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008; - public const uint UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010; - public const uint UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100; - public const uint UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200; - public const uint UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400; - public const uint UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs index 4a1742c36b8f58..2424ec434126c2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs @@ -752,164 +752,26 @@ void EmitCompactUnwindSymbol(string symbolName) private protected override string ExternCName(string name) => "_" + name; - private static uint GetArm64CompactUnwindCode(byte[] blobData) + // This represents the following DWARF code: + // DW_CFA_advance_loc: 4 + // DW_CFA_def_cfa_offset: +16 + // DW_CFA_offset: W29 -16 + // DW_CFA_offset: W30 -8 + // DW_CFA_advance_loc: 4 + // DW_CFA_def_cfa_register: W29 + // which is generated for the following frame prolog/epilog: + // stp fp, lr, [sp, #-10]! + // mov fp, sp + // ... + // ldp fp, lr, [sp], #0x10 + // ret + private static ReadOnlySpan DwarfArm64EmptyFrame => new byte[] { - if (blobData == null || blobData.Length == 0) - { - return UNWIND_ARM64_MODE_FRAMELESS; - } - - Debug.Assert(blobData.Length % 8 == 0); - - short spReg = -1; - - int codeOffset = 0; - short cfaRegister = spReg; - int cfaOffset = 0; - int spOffset = 0; - - const int REG_DWARF_X19 = 19; - const int REG_DWARF_X30 = 30; - const int REG_DWARF_FP = 29; - const int REG_DWARF_D8 = 72; - const int REG_DWARF_D15 = 79; - const int REG_IDX_X19 = 0; - const int REG_IDX_X28 = 9; - const int REG_IDX_FP = 10; - const int REG_IDX_LR = 11; - const int REG_IDX_D8 = 12; - const int REG_IDX_D15 = 19; - Span registerOffset = stackalloc int[20]; - - registerOffset.Fill(int.MinValue); - - // First process all the CFI codes to figure out the layout of X19-X28, FP, LR, and - // D8-D15 on the stack. - int offset = 0; - while (offset < blobData.Length) - { - codeOffset = Math.Max(codeOffset, blobData[offset++]); - CFI_OPCODE opcode = (CFI_OPCODE)blobData[offset++]; - short dwarfReg = BinaryPrimitives.ReadInt16LittleEndian(blobData.AsSpan(offset)); - offset += sizeof(short); - int cfiOffset = BinaryPrimitives.ReadInt32LittleEndian(blobData.AsSpan(offset)); - offset += sizeof(int); - - switch (opcode) - { - case CFI_OPCODE.CFI_DEF_CFA_REGISTER: - cfaRegister = dwarfReg; - - if (spOffset != 0) - { - for (int i = 0; i < registerOffset.Length; i++) - if (registerOffset[i] != int.MinValue) - registerOffset[i] -= spOffset; - - cfaOffset += spOffset; - spOffset = 0; - } - - break; - - case CFI_OPCODE.CFI_REL_OFFSET: - Debug.Assert(cfaRegister == spReg); - if (dwarfReg >= REG_DWARF_X19 && dwarfReg <= REG_DWARF_X30) // X19 - X28, FP, LR - { - registerOffset[dwarfReg - REG_DWARF_X19 + REG_IDX_X19] = cfiOffset; - } - else if (dwarfReg >= REG_DWARF_D8 && dwarfReg <= REG_DWARF_D15) // D8 - D15 - { - registerOffset[dwarfReg - REG_DWARF_D8 + REG_IDX_D8] = cfiOffset; - } - else - { - // We cannot represent this register in the compact unwinding format, - // fallback to DWARF immediately. - return UNWIND_ARM64_MODE_DWARF; - } - break; - - case CFI_OPCODE.CFI_ADJUST_CFA_OFFSET: - if (cfaRegister != spReg) - { - cfaOffset += cfiOffset; - } - else - { - spOffset += cfiOffset; - - for (int i = 0; i < registerOffset.Length; i++) - if (registerOffset[i] != int.MinValue) - registerOffset[i] += cfiOffset; - } - break; - } - } - - uint unwindCode; - int nextOffset; - - if (cfaRegister == REG_DWARF_FP && - cfaOffset == 16 && - registerOffset[REG_IDX_FP] == -16 && - registerOffset[REG_IDX_LR] == -8) - { - // Frame format - FP/LR are saved on the top. SP is restored to FP+16 - unwindCode = UNWIND_ARM64_MODE_FRAME; - nextOffset = -24; - } - else if (cfaRegister == -1 && spOffset <= 65520 && - registerOffset[REG_IDX_FP] == int.MinValue && registerOffset[REG_IDX_LR] == int.MinValue) - { - // Frameless format - FP/LR are not saved, SP must fit within the representable range - uint encodedSpOffset = (uint)(spOffset / 16) << 12; - unwindCode = UNWIND_ARM64_MODE_FRAMELESS | encodedSpOffset; - nextOffset = spOffset - 8; - } - else - { - return UNWIND_ARM64_MODE_DWARF; - } - - // Check that the integer register pairs are in the right order and mark - // a flag for each successive pair that is present. - for (int i = REG_IDX_X19; i < REG_IDX_X28; i += 2) - { - if (registerOffset[i] == int.MinValue) - { - if (registerOffset[i + 1] != int.MinValue) - return UNWIND_ARM64_MODE_DWARF; - } - else if (registerOffset[i] == nextOffset) - { - if (registerOffset[i + 1] != nextOffset - 8) - return UNWIND_ARM64_MODE_DWARF; - nextOffset -= 16; - unwindCode |= UNWIND_ARM64_FRAME_X19_X20_PAIR << (i >> 1); - } - } - - // Check that the floating point register pairs are in the right order and mark - // a flag for each successive pair that is present. - for (int i = REG_IDX_D8; i < REG_IDX_D15; i += 2) - { - if (registerOffset[i] == int.MinValue) - { - if (registerOffset[i + 1] != int.MinValue) - return UNWIND_ARM64_MODE_DWARF; - } - else if (registerOffset[i] == nextOffset) - { - if (registerOffset[i + 1] != nextOffset - 8) - return UNWIND_ARM64_MODE_DWARF; - nextOffset -= 16; - unwindCode |= UNWIND_ARM64_FRAME_D8_D9_PAIR << (i >> 1); - } - } - - return unwindCode; - } + 0x04, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x02, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x02, 0x1E, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x08, 0x01, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00 + }; private protected override bool EmitCompactUnwinding(string startSymbolName, ulong length, string lsdaSymbolName, byte[] blob) { @@ -917,7 +779,11 @@ private protected override bool EmitCompactUnwinding(string startSymbolName, ulo if (_cpuType == CPU_TYPE_ARM64) { - encoding = GetArm64CompactUnwindCode(blob); + if (blob.AsSpan().SequenceEqual(DwarfArm64EmptyFrame)) + { + // Frame-based encoding, no saved registers + encoding = 0x04000000; + } } _compactUnwindCodes.Add(new CompactUnwindCode(