Skip to content

Commit 7b3e409

Browse files
authored
[NativeAOT/ARM] Save R9 (REG_SAVED_LOCALLOC_SP) in PInvoke frames (#97919)
* Save R9 (REG_SAVED_LOCALLOC_SP) in PInvoke frames * Handle 'mov r9, sp' as part of prolog * Remove m_ChainPointer from PInvokeTransitionFrame and update comments
1 parent e1be9a7 commit 7b3e409

File tree

7 files changed

+37
-37
lines changed

7 files changed

+37
-37
lines changed

src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ PLAT_ASM_OFFSET(10, ExInfo, m_idxCurClause)
1717
PLAT_ASM_OFFSET(18, ExInfo, m_frameIter)
1818
PLAT_ASM_OFFSET(130, ExInfo, m_notifyDebuggerSP)
1919

20+
PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_FramePointer)
2021
PLAT_ASM_OFFSET(4, PInvokeTransitionFrame, m_RIP)
21-
PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer)
22-
PLAT_ASM_OFFSET(0c, PInvokeTransitionFrame, m_pThread)
23-
PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_Flags)
24-
PLAT_ASM_OFFSET(14, PInvokeTransitionFrame, m_PreservedRegs)
22+
PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_pThread)
23+
PLAT_ASM_OFFSET(c, PInvokeTransitionFrame, m_Flags)
24+
PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_PreservedRegs)
2525

2626
PLAT_ASM_SIZEOF(118, StackFrameIterator)
2727
PLAT_ASM_OFFSET(08, StackFrameIterator, m_FramePointer)

src/coreclr/nativeaot/Runtime/arm/GcProbe.S

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,19 @@
2121
// Define the method prolog, allocating enough stack space for the PInvokeTransitionFrame and saving
2222
// incoming register values into it.
2323
PROLOG_VPUSH "{d0-d3}" // Save d0-d3 which can have the floating point return value
24-
PROLOG_STACK_ALLOC 4 // Padding for 8-byte alignment
2524
PROLOG_PUSH "{r0,r1}" // Save return registers
2625
PROLOG_STACK_ALLOC 4 // Space for caller's SP
2726
PROLOG_PUSH "{r4-r10}" // Save non-volatile registers
2827
PROLOG_STACK_ALLOC 8 // Space for flags and Thread*
29-
PROLOG_PUSH "{r11}" // Save caller's frame pointer
30-
PROLOG_PUSH "{r11,lr}" // Save frame-chain pointer and return address
28+
PROLOG_PUSH "{r11,lr}" // Save caller's frame pointer and return address
3129

3230
str \threadReg, [sp, #OFFSETOF__PInvokeTransitionFrame__m_pThread]
3331
mov \trashReg, \BITMASK
3432
str \trashReg, [sp, #OFFSETOF__PInvokeTransitionFrame__m_Flags]
3533

3634
// Compute SP value at entry to this method and save it in slot of the frame.
37-
add \trashReg, sp, #(16 * 4 + 4 * 8)
38-
str \trashReg, [sp, #(12 * 4)]
35+
add \trashReg, sp, #(14 * 4 + 4 * 8)
36+
str \trashReg, [sp, #(11 * 4)]
3937

4038
// Link the frame into the Thread
4139
str sp, [\threadReg, #OFFSETOF__Thread__m_pDeferredTransitionFrame]
@@ -47,13 +45,11 @@
4745
// object refs or byrefs).
4846
//
4947
.macro POP_PROBE_FRAME
50-
EPILOG_POP "{r11,lr}" // Restore frame-chain pointer and return address
51-
EPILOG_POP "{r11}" // Restore caller's frame pointer
48+
EPILOG_POP "{r11,lr}" // Restore caller's frame pointer and return address
5249
EPILOG_STACK_FREE 8 // Discard flags and Thread*
5350
EPILOG_POP "{r4-r10}" // Restore non-volatile registers
5451
EPILOG_STACK_FREE 4 // Discard caller's SP
5552
EPILOG_POP "{r0,r1}" // Restore return registers
56-
EPILOG_STACK_FREE 4 // Discard padding for 8-byte alignment
5753
EPILOG_VPOP "{d0-d3}" // Restore d0-d3 which can have the floating point return value
5854
.endm
5955

src/coreclr/nativeaot/Runtime/arm/PInvoke.S

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
NESTED_ENTRY RhpPInvoke, _TEXT, NoHandler
2121
str lr, [r0, #OFFSETOF__PInvokeTransitionFrame__m_RIP]
2222
str r11, [r0, #OFFSETOF__PInvokeTransitionFrame__m_FramePointer]
23-
str sp, [r0, #OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs]
24-
mov r3, #PTFF_SAVE_SP
23+
// We need to save R9 which could be frame pointer if the caller method uses stackalloc (REG_SAVED_LOCALLOC_SP)
24+
str r9, [r0, #OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs]
25+
str sp, [r0, #OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs + 4]
26+
mov r3, #(PTFF_SAVE_R9 + PTFF_SAVE_SP)
2527
str r3, [r0, #OFFSETOF__PInvokeTransitionFrame__m_Flags]
2628

2729
PROLOG_PUSH "{r5,lr}"

src/coreclr/nativeaot/Runtime/inc/rhbinder.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -400,11 +400,8 @@ struct PInvokeTransitionFrame
400400
#else // USE_PORTABLE_HELPERS
401401
struct PInvokeTransitionFrame
402402
{
403-
#ifdef TARGET_ARM
404-
TgtPTR_Void m_ChainPointer; // R11, used by OS to walk stack quickly
405-
#endif
406-
#ifdef TARGET_ARM64
407-
// On arm64, the FP and LR registers are pushed in that order when setting up frames
403+
#if defined(TARGET_ARM64) || defined(TARGET_ARM)
404+
// On arm32/arm64, the FP and LR registers are pushed in that order when setting up frames
408405
TgtPTR_Void m_FramePointer;
409406
TgtPTR_Void m_RIP;
410407
#else

src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,11 +532,15 @@ int UnixNativeCodeManager::IsInProlog(MethodInfo * pMethodInfo, PTR_VOID pvAddre
532532
// MOV SP, R4
533533
#define MOV_SP_R4 0x46A5
534534

535+
// MOV R9, SP
536+
#define MOV_R9_SP 0x46E9
537+
535538
uint16_t* pInstr = (uint16_t*)pvAddress;
536539
uint32_t instr = *pInstr;
537540

538541
if ((instr & SUB_SP_IMM_MASK) == SUB_SP_IMM_BITS ||
539-
(instr & PUSH_MASK) == PUSH_BITS)
542+
(instr & PUSH_MASK) == PUSH_BITS ||
543+
instr == MOV_R9_SP)
540544
{
541545
return 1;
542546
}

src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define TSF_DoNotTriggerGc 0x10
1616

1717
#define PTFF_SAVE_ALL_PRESERVED 0x0000007F // NOTE: R11 is not included in this set!
18+
#define PTFF_SAVE_R9 0x00000020
1819
#define PTFF_SAVE_SP 0x00000100
1920
#define PTFF_SAVE_R0 0x00000200
2021
#define PTFF_THREAD_ABORT 0x00100000
@@ -244,30 +245,28 @@ C_FUNC(\Name):
244245
//
245246
.macro PUSH_COOP_PINVOKE_FRAME trashReg
246247

247-
PROLOG_STACK_ALLOC 8 // Save space for caller's SP and 8-byte alignment padding
248+
PROLOG_STACK_ALLOC 4 // Save space for caller's SP
248249
PROLOG_PUSH "{r4-r10}" // Save preserved registers
249250
PROLOG_STACK_ALLOC 8 // Save space for flags and Thread*
250-
PROLOG_PUSH "{r11}" // Save caller's FP
251-
PROLOG_PUSH "{r11,lr}" // Save caller's frame-chain pointer and PC
251+
PROLOG_PUSH "{r11,lr}" // Save caller's frame pointer and PC
252252

253253
// Compute SP value at entry to this method and save it in the last slot of the frame (slot #12).
254-
add \trashReg, sp, #(14 * 4)
255-
str \trashReg, [sp, #(12 * 4)]
254+
add \trashReg, sp, #(12 * 4)
255+
str \trashReg, [sp, #(11 * 4)]
256256

257-
// Record the bitmask of saved registers in the frame (slot #4).
257+
// Record the bitmask of saved registers in the frame (slot #3).
258258
mov \trashReg, #DEFAULT_FRAME_SAVE_FLAGS
259-
str \trashReg, [sp, #(4 * 4)]
259+
str \trashReg, [sp, #(3 * 4)]
260260

261261
mov \trashReg, sp
262262
.endm
263263

264264
// Pop the frame and restore register state preserved by PUSH_COOP_PINVOKE_FRAME
265265
.macro POP_COOP_PINVOKE_FRAME
266-
EPILOG_POP "{r11,lr}" // Restore caller's frame-chain pointer and PC (return address)
267-
EPILOG_POP "{r11}" // Restore caller's FP
266+
EPILOG_POP "{r11,lr}" // Restore caller's frame pointer and PC (return address)
268267
EPILOG_STACK_FREE 8 // Discard flags and Thread*
269268
EPILOG_POP "{r4-r10}" // Restore preserved registers
270-
EPILOG_STACK_FREE 8 // Discard caller's SP and 8-byte alignment padding
269+
EPILOG_STACK_FREE 4 // Discard caller's SP
271270
.endm
272271

273272
// thumb with PIC version

src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2006,20 +2006,22 @@ private int SizeOfPInvokeTransitionFrame
20062006
get
20072007
{
20082008
// struct PInvokeTransitionFrame:
2009-
// #ifdef _TARGET_ARM_
2010-
// m_ChainPointer
2011-
// #endif
2012-
// m_RIP
2013-
// m_FramePointer
2009+
// m_RIP (1)
2010+
// m_FramePointer (1)
20142011
// m_pThread
20152012
// m_Flags + align (no align for ARM64 that has 64 bit m_Flags)
2016-
// m_PreserverRegs - RSP
2013+
// m_PreservedRegs - RSP / R9 (2)
20172014
// No need to save other preserved regs because of the JIT ensures that there are
20182015
// no live GC references in callee saved registers around the PInvoke callsite.
2016+
//
2017+
// (1) On ARM32/ARM64 the order of m_RIP and m_FramePointer is reverse
2018+
// (2) R9 is saved for ARM32 because it needs to be preserved for methods with stackalloc
20192019
int size = 5 * this.PointerSize;
20202020

20212021
if (_compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.ARM)
2022-
size += this.PointerSize; // m_ChainPointer
2022+
{
2023+
size += this.PointerSize; // R9 (REG_SAVED_LOCALLOC_SP)
2024+
}
20232025

20242026
return size;
20252027
}

0 commit comments

Comments
 (0)