Skip to content
This repository has been archived by the owner on Nov 1, 2020. It is now read-only.

Commit

Permalink
ARM64: RhpGcPoll (#8343)
Browse files Browse the repository at this point in the history
- Initial port of GCProbe.S for ARM64
- Ensure RhpGcPoll & RhpGcPollRare are working
  • Loading branch information
RalfKornmannEnvision authored Sep 22, 2020
1 parent 529357b commit 64389da
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 169 deletions.
20 changes: 20 additions & 0 deletions src/Native/Runtime/arm64/GcProbe.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include <unixasmmacros.inc>
#include "AsmOffsets.inc"

.global RhpGcPoll2

LEAF_ENTRY RhpGcPoll
PREPARE_EXTERNAL_VAR_INDIRECT_W RhpTrapThreads, 0
cbnz w0, RhpGcPollRare // TrapThreadsFlags_None = 0
ret
LEAF_END RhpGcPoll

NESTED_ENTRY RhpGcPollRare, _TEXT, NoHandler
PUSH_COOP_PINVOKE_FRAME x0
bl RhpGcPoll2
POP_COOP_PINVOKE_FRAME
ret
NESTED_END RhpGcPollRare
168 changes: 0 additions & 168 deletions src/Native/Runtime/arm64/GcProbe.asm
Original file line number Diff line number Diff line change
Expand Up @@ -560,174 +560,6 @@ EXTRA_SAVE_SIZE equ (32*16)

NESTED_END RhpHijackForGcStressLeaf

#endif ;; FEATURE_GC_STRESS

#if 0 // used by the binder only
;;
;; The following functions are _jumped_ to when we need to transfer control from one method to another for EH
;; dispatch. These are needed to properly coordinate with the GC hijacking logic. We are essentially replacing
;; the return from the throwing method with a jump to the handler in the caller, but we need to be aware of
;; any return address hijack that may be in place for GC suspension. These routines use a quick test of the
;; return address against a specific GC hijack routine, and then fixup the stack pointer to what it would be
;; after a real return from the throwing method. Then, if we are not hijacked we can simply jump to the
;; handler in the caller.
;;
;; If we are hijacked, then we jump to a routine that will unhijack appropriately and wait for the GC to
;; complete. There are also variants for GC stress.
;;
;; Note that at this point we are either hijacked or we are not, and this will not change until we return to
;; managed code. It is an invariant of the system that a thread will only attempt to hijack or unhijack
;; another thread while the target thread is suspended in managed code, and this is _not_ managed code.
;;
MACRO
RTU_EH_JUMP_HELPER $funcName, $hijackFuncName, $isStress, $stressFuncName

LEAF_ENTRY $funcName
ldr x0, =$hijackFuncName
cmp x0, lr
beq RhpGCProbeForEHJump

IF $isStress
ldr x0, =$stressFuncName
cmp x0, lr
beq RhpGCStressProbeForEHJump
ENDIF

;; We are not hijacked, so we can return to the handler.
;; We return to keep the call/return prediction balanced.
mov lr, x2 ; Update the return address
ret
LEAF_END $funcName
MEND
;; We need an instance of the helper for each possible hijack function. The binder has enough
;; information to determine which one we need to use for any function.
RTU_EH_JUMP_HELPER RhpEHJumpScalar, RhpGcProbeHijack, {false}, 0
RTU_EH_JUMP_HELPER RhpEHJumpObject, RhpGcProbeHijack, {false}, 0
RTU_EH_JUMP_HELPER RhpEHJumpByref, RhpGcProbeHijack, {false}, 0
#ifdef FEATURE_GC_STRESS
RTU_EH_JUMP_HELPER RhpEHJumpScalarGCStress, RhpGcProbeHijack, {true}, RhpGcStressHijack
RTU_EH_JUMP_HELPER RhpEHJumpObjectGCStress, RhpGcProbeHijack, {true}, RhpGcStressHijack
RTU_EH_JUMP_HELPER RhpEHJumpByrefGCStress, RhpGcProbeHijack, {true}, RhpGcStressHijack
#endif

;;
;; Macro to setup our frame and adjust the location of the EH object reference for EH jump probe funcs.
;;
;; Register state on entry:
;; x0: scratch
;; x1: reference to the exception object.
;; x2: handler address we want to jump to.
;; Non-volatile registers are all already correct for return to the caller.
;; The stack is as if we are just about to returned from the call
;;
;; Register state on exit:
;; x0: reference to the exception object
;; x2: thread pointer
;;
MACRO
EHJumpProbeProlog

PROLOG_NOP mov x0, x1 ; move the ex object reference into x0 so we can report it
ALLOC_PROBE_FRAME 0x10, {true}
str x2, [sp, #PROBE_FRAME_SIZE]

;; x2 <- GetThread(), TRASHES x1
INLINE_GETTHREAD x2, x1

;; Recover the original return address and update the frame
ldr lr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress]
str lr, [sp, #OFFSETOF__PInvokeTransitionFrame__m_RIP]

;; ClearHijackState expects thread in x2
ClearHijackState

; TRASHES x1
INIT_PROBE_FRAME x2, x1, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0), PTFF_X0_IS_GCREF_HI, (PROBE_FRAME_SIZE + 8)
add x1, sp, xzr
str x1, [x2, #OFFSETOF__Thread__m_pHackPInvokeTunnel]
MEND

;;
;; Macro to re-adjust the location of the EH object reference, cleanup the frame, and make the
;; final jump to the handler for EH jump probe funcs.
;;
;; Register state on entry:
;; x0: reference to the exception object
;; x1-x3: scratch
;;
;; Register state on exit:
;; sp: correct for return to the caller
;; x1: reference to the exception object
;;
MACRO
EHJumpProbeEpilog

ldr x2, [sp, #PROBE_FRAME_SIZE]
FREE_PROBE_FRAME 0x10, {true} ; This restores exception object back into x0
EPILOG_NOP mov x1, x0 ; Move the Exception object back into x1 where the catch handler expects it
EPILOG_NOP br x2
MEND

;;
;; We are hijacked for a normal GC (not GC stress), so we need to unhijack and wait for the GC to complete.
;;
;; Register state on entry:
;; x0: reference to the exception object.
;; x2: thread
;; Non-volatile registers are all already correct for return to the caller.
;; The stack is as if we have tail called to this function (lr points to return address).
;;
;; Register state on exit:
;; x0: reference to the exception object
;;
NESTED_ENTRY RhpGCProbeForEHJump
brk 0xf000 ;; TODO: remove after debugging/testing stub
EHJumpProbeProlog

#ifdef _DEBUG
;;
;; If we get here, then we have been hijacked for a real GC, and our SyncState must
;; reflect that we've been requested to synchronize.

ldr x1, =RhpTrapThreads
ldr w1, [x1]
tbnz x1, #TrapThreadsFlags_TrapThreads_Bit, %0

bl RhDebugBreak
0
#endif ;; _DEBUG

mov x4, x2
WaitForGCCompletion

EHJumpProbeEpilog
NESTED_END RhpGCProbeForEHJump

#ifdef FEATURE_GC_STRESS
;;
;; We are hijacked for GC Stress (not a normal GC) so we need to invoke the GC stress helper.
;;
;; Register state on entry:
;; x1: reference to the exception object.
;; x2: thread
;; Non-volatile registers are all already correct for return to the caller.
;; The stack is as if we have tail called to this function (lr points to return address).
;;
;; Register state on exit:
;; x0: reference to the exception object
;;
NESTED_ENTRY RhpGCStressProbeForEHJump
brk 0xf000 ;; TODO: remove after debugging/testing stub
EHJumpProbeProlog

bl $REDHAWKGCINTERFACE__STRESSGC

EHJumpProbeEpilog
NESTED_END RhpGCStressProbeForEHJump
#endif ;; FEATURE_GC_STRESS
#endif ;; 0

#ifdef FEATURE_GC_STRESS
;;
;; INVARIANT: Don't trash the argument registers, the binder codegen depends on this.
;;
Expand Down
2 changes: 2 additions & 0 deletions src/Native/Runtime/portable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,9 @@ COOP_PINVOKE_HELPER(void *, RhGetCurrentThunkContext, ())

#endif

#if !(defined(TARGET_ARM64) && defined(TARGET_UNIX))
COOP_PINVOKE_HELPER(void, RhpGcPoll, ())
{
// TODO: implement
}
#endif
9 changes: 9 additions & 0 deletions src/Native/Runtime/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,15 @@ EXTERN_C NOINLINE void FASTCALL RhpWaitForGC2(PInvokeTransitionFrame * pFrame)
PalSetLastError(lastErrorOnEntry);
}

// Standard calling convention variant and actual implementation for RhpGcPoll
EXTERN_C NOINLINE void FASTCALL RhpGcPoll2(PInvokeTransitionFrame* pFrame)
{
Thread* pThread = ThreadStore::GetCurrentThread();
pFrame->m_pThread = pThread;

RhpWaitForGC2(pFrame);
}

void Thread::PushExInfo(ExInfo * pExInfo)
{
ValidateExInfoStack();
Expand Down
5 changes: 4 additions & 1 deletion src/Native/Runtime/unix/unixasmmacrosarm64.inc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
.macro NESTED_ENTRY Name, Section, Handler
LEAF_ENTRY \Name, \Section
.ifnc \Handler, NoHandler
.cfi_personality 0, C_FUNC(\Handler) // 0 == DW_EH_PE_absptr
.cfi_personality 0x1b, C_FUNC(\Handler) // 0x1b == DW_EH_PE_pcrel | DW_EH_PE_sdata4
// @TODO: Check if we have systems that use indirect personality 0x9b == DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4
.endif
.endm

Expand Down Expand Up @@ -71,6 +72,7 @@ C_FUNC(\Name):

.macro EPILOG_STACK_RESTORE
mov sp, fp
.cfi_restore sp
.endm

.macro PROLOG_SAVE_REG reg, ofs
Expand All @@ -84,6 +86,7 @@ C_FUNC(\Name):
.cfi_rel_offset \reg2, \ofs + 8
.ifc \reg1, fp
mov fp, sp
.cfi_def_cfa_register fp
.endif
.endm

Expand Down

0 comments on commit 64389da

Please sign in to comment.