Delete Windows ARM32 support (#97686)

Continuation of #86065.
This commit is contained in:
Michal Strehovský authored and GitHub committed 2024-01-30 10:23:56 +01:00
1 parent d47f040521
commit 3a71efaf36
12 files changed
-2837

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -1,283 +0,0 @@
;; Licensed to the.NET Foundation under one or more agreements.
;; The.NET Foundation licenses this file to you under the MIT license.
;; OS provided macros
#include <ksarm.h>
;; generated by the build from AsmOffsets.cpp
#include "AsmOffsets.inc"
;;
;; CONSTANTS -- INTEGER
;;
TSF_Attached equ 0x01
TSF_SuppressGcStress equ 0x08
TSF_DoNotTriggerGc equ 0x10
TSF_SuppressGcStress__OR__TSF_DoNotTriggerGC equ 0x18
;; GC type flags
GC_ALLOC_FINALIZE equ 1
GC_ALLOC_ALIGN8_BIAS equ 4
GC_ALLOC_ALIGN8 equ 8
;; GC minimal sized object. We use this to switch between 4 and 8 byte alignment in the GC heap (see AllocFast.asm).
SIZEOF__MinObject equ 12
ASSERT (SIZEOF__MinObject :MOD: 8) == 4
;; Note: these must match the defs in PInvokeTransitionFrameFlags
PTFF_SAVE_R4 equ 0x00000001
PTFF_SAVE_R5 equ 0x00000002
PTFF_SAVE_R6 equ 0x00000004
PTFF_SAVE_R7 equ 0x00000008
PTFF_SAVE_R8 equ 0x00000010
PTFF_SAVE_R9 equ 0x00000020
PTFF_SAVE_R10 equ 0x00000040
PTFF_SAVE_ALL_PRESERVED equ 0x00000077 ;; NOTE: FP is not included in this set!
PTFF_SAVE_SP equ 0x00000100
PTFF_SAVE_R0 equ 0x00000200 ;; R0 is saved if it contains a GC ref and we're in hijack handler
PTFF_SAVE_ALL_SCRATCH equ 0x00003e00 ;; R0-R3,LR (R12 is trashed by the helpers anyway, but LR is relevant for loop hijacking
PTFF_R0_IS_GCREF equ 0x00004000 ;; iff PTFF_SAVE_R0: set -> r0 is Object, clear -> r0 is scalar
PTFF_R0_IS_BYREF equ 0x00008000 ;; iff PTFF_SAVE_R0: set -> r0 is ByRef, clear -> r0 is Object or scalar
PTFF_THREAD_ABORT equ 0x00010000 ;; indicates that ThreadAbortException should be thrown when returning from the transition
;; These must match the TrapThreadsFlags enum
TrapThreadsFlags_None equ 0
TrapThreadsFlags_AbortInProgress equ 1
TrapThreadsFlags_TrapThreads equ 2
;; This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT
STATUS_REDHAWK_THREAD_ABORT equ 0x43
;;
;; Rename fields of nested structs
;;
OFFSETOF__Thread__m_alloc_context__alloc_ptr equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr
OFFSETOF__Thread__m_alloc_context__alloc_limit equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit
__tls_array equ 0x2C ;; offsetof(TEB, ThreadLocalStoragePointer)
;;
;; MACROS
;;
GBLS __SECTIONREL_tls_CurrentThread
__SECTIONREL_tls_CurrentThread SETS "SECTIONREL_tls_CurrentThread"
MACRO
INLINE_GETTHREAD $destReg, $trashReg
EXTERN _tls_index
ldr $destReg, =_tls_index
ldr $destReg, [$destReg]
mrc p15, 0, $trashReg, c13, c0, 2
ldr $trashReg, [$trashReg, #__tls_array]
ldr $destReg, [$trashReg, $destReg, lsl #2]
ldr $trashReg, $__SECTIONREL_tls_CurrentThread
add $destReg, $trashReg
MEND
;; INLINE_GETTHREAD_CONSTANT_POOL macro has to be used after the last function in the .asm file that used
;; INLINE_GETTHREAD. Optionally, it can be also used after any function that used INLINE_GETTHREAD
;; to improve density, or to reduce distance between the constant pool and its use.
MACRO
INLINE_GETTHREAD_CONSTANT_POOL
EXTERN tls_CurrentThread
$__SECTIONREL_tls_CurrentThread
DCD tls_CurrentThread
RELOC 15 ;; SECREL
__SECTIONREL_tls_CurrentThread SETS "$__SECTIONREL_tls_CurrentThread":CC:"_"
MEND
MACRO
INLINE_THREAD_UNHIJACK $threadReg, $trashReg1, $trashReg2
;;
;; Thread::Unhijack()
;;
ldr $trashReg1, [$threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress]
cbz $trashReg1, %ft0
ldr $trashReg2, [$threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation]
str $trashReg1, [$trashReg2]
mov $trashReg1, #0
str $trashReg1, [$threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation]
str $trashReg1, [$threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress]
0
MEND
DEFAULT_FRAME_SAVE_FLAGS equ PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_SP
;;
;; Macro used from unmanaged helpers called from managed code where the helper does not transition immediately
;; into pre-emptive mode but may cause a GC and thus requires the stack is crawlable. This is typically the
;; case for helpers that meddle in GC state (e.g. allocation helpers) where the code must remain in
;; cooperative mode since it handles object references and internal GC state directly but a garbage collection
;; may be inevitable. In these cases we need to be able to transition to pre-meptive mode deep within the
;; unmanaged code but still be able to initialize the stack iterator at the first stack frame which may hold
;; interesting GC references. In all our helper cases this corresponds to the most recent managed frame (e.g.
;; the helper's caller).
;;
;; This macro builds a frame describing the current state of managed code.
;;
;; INVARIANTS
;; - The macro assumes it defines the method prolog, it should typically be the first code in a method and
;; certainly appear before any attempt to alter the stack pointer.
;; - This macro uses trashReg (after its initial value has been saved in the frame) and upon exit trashReg
;; will contain the address of transition frame.
;;
MACRO
PUSH_COOP_PINVOKE_FRAME $trashReg
PROLOG_STACK_ALLOC 4 ; Save space for caller's SP
PROLOG_PUSH {r4-r6,r8-r10} ; Save preserved registers
PROLOG_STACK_ALLOC 8 ; Save space for flags and Thread*
PROLOG_PUSH {r7} ; Save caller's FP
PROLOG_PUSH {r11,lr} ; Save caller's frame-chain pointer and PC
; Compute SP value at entry to this method and save it in the last slot of the frame (slot #11).
add $trashReg, sp, #(12 * 4)
str $trashReg, [sp, #(11 * 4)]
; Record the bitmask of saved registers in the frame (slot #4).
mov $trashReg, #DEFAULT_FRAME_SAVE_FLAGS
str $trashReg, [sp, #(4 * 4)]
mov $trashReg, sp
MEND
;; Pop the frame and restore register state preserved by PUSH_COOP_PINVOKE_FRAME
MACRO
POP_COOP_PINVOKE_FRAME
EPILOG_POP {r11,lr} ; Restore caller's frame-chain pointer and PC (return address)
EPILOG_POP {r7} ; Restore caller's FP
EPILOG_STACK_FREE 8 ; Discard flags and Thread*
EPILOG_POP {r4-r6,r8-r10} ; Restore preserved registers
EPILOG_STACK_FREE 4 ; Discard caller's SP
MEND
; Macro used to assign an alternate name to a symbol containing characters normally disallowed in a symbol
; name (e.g. C++ decorated names).
MACRO
SETALIAS $name, $symbol
GBLS $name
$name SETS "|$symbol|"
MEND
;
; Helper macro: create a global label for the given name,
; decorate it, and export it for external consumption.
;
MACRO
__ExportLabelName $FuncName
LCLS Name
Name SETS "|$FuncName|"
EXPORT $Name
$Name
MEND
;
; Macro for indicating an alternate entry point into a function.
;
MACRO
LABELED_RETURN_ADDRESS $ReturnAddressName
; export the return address name, but do not perturb the code by forcing alignment
__ExportLabelName $ReturnAddressName
; flush any pending literal pool stuff
ROUT
MEND
MACRO
EXPORT_POINTER_TO_ADDRESS $Name
1
AREA |.rdata|, ALIGN=4, DATA, READONLY
$Name
DCD %BT1
EXPORT $Name
TEXTAREA
ROUT
MEND
;-----------------------------------------------------------------------------
; Macro used to check (in debug builds only) whether the stack is 64-bit aligned (a requirement before calling
; out into C++/OS code). Invoke this directly after your prolog (if the stack frame size is fixed) or directly
; before a call (if you have a frame pointer and a dynamic stack). A breakpoint will be invoked if the stack
; is misaligned.
;
MACRO
CHECK_STACK_ALIGNMENT
#ifdef _DEBUG
push {r0}
add r0, sp, #4
tst r0, #7
pop {r0}
beq %F0
EMIT_BREAKPOINT
0
#endif
MEND
;; Loads a 32bit constant into destination register
MACRO
MOV32 $destReg, $constant
movw $destReg, #(($constant) & 0xFFFF)
movt $destReg, #(($constant) >> 16)
MEND
;;
;; CONSTANTS -- SYMBOLS
;;
SETALIAS G_LOWEST_ADDRESS, g_lowest_address
SETALIAS G_HIGHEST_ADDRESS, g_highest_address
SETALIAS G_EPHEMERAL_LOW, g_ephemeral_low
SETALIAS G_EPHEMERAL_HIGH, g_ephemeral_high
SETALIAS G_CARD_TABLE, g_card_table
SETALIAS G_FREE_OBJECT_EETYPE, ?g_pFreeObjectEEType@@3PAVEEType@@A
#ifdef FEATURE_GC_STRESS
SETALIAS THREAD__HIJACKFORGCSTRESS, ?HijackForGcStress@Thread@@SAXPAUPAL_LIMITED_CONTEXT@@@Z
#endif ;; FEATURE_GC_STRESS
;;
;; IMPORTS
;;
EXTERN RhpGcAlloc
EXTERN RhDebugBreak
EXTERN RhpWaitForGC2
EXTERN RhExceptionHandling_FailedAllocation
EXTERN $G_LOWEST_ADDRESS
EXTERN $G_HIGHEST_ADDRESS
EXTERN $G_EPHEMERAL_LOW
EXTERN $G_EPHEMERAL_HIGH
EXTERN $G_CARD_TABLE
EXTERN RhpTrapThreads
EXTERN $G_FREE_OBJECT_EETYPE
EXTERN RhThrowHwEx
EXTERN RhThrowEx
EXTERN RhRethrow
#ifdef FEATURE_GC_STRESS
EXTERN RhpStressGc
EXTERN $THREAD__HIJACKFORGCSTRESS
#endif ;; FEATURE_GC_STRESS
File diff suppressed because it is too large. Load diff
@@ -1,361 +0,0 @@
;; Licensed to the .NET Foundation under one or more agreements.
;; The .NET Foundation licenses this file to you under the MIT license.
#include "AsmMacros.h"
TEXTAREA
;; ARM64TODO: do same fix here as on Arm64?
SETALIAS g_fGcStressStarted, ?g_GCShadow@@3PAEA
EXTERN $g_fGcStressStarted
PROBE_SAVE_FLAGS_EVERYTHING equ DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_ALL_SCRATCH
PROBE_SAVE_FLAGS_R0_IS_GCREF equ DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0 + PTFF_R0_IS_GCREF
;; Build a map of symbols representing offsets into a transition frame (see PInvokeTransitionFrame in
;; rhbinder.h and keep these two in sync.
map 0
m_ChainPointer field 4 ; r11 - OS frame chain used for quick stackwalks
m_RIP field 4 ; lr
m_FramePointer field 4 ; r7
m_pThread field 4
m_Flags field 4 ; bitmask of saved registers
m_PreservedRegs field (4 * 6) ; r4-r6,r8-r10
m_CallersSP field 4 ; sp at routine entry
m_SavedR0 field 4 ; r0
m_VolatileRegs field (4 * 4) ; r1-r3,lr
m_ReturnVfpRegs field (8 * 4) ; d0-d3, not really part of the struct
m_SavedAPSR field 4 ; saved condition codes
PROBE_FRAME_SIZE field 0
;; Support for setting up a transition frame when performing a GC probe. In many respects this is very
;; similar to the logic in PUSH_COOP_PINVOKE_FRAME in AsmMacros.h. In most cases setting up the
;; transition frame comprises the entirety of the caller's prolog (and initial non-prolog code) and
;; similarly for the epilog. Those cases can be dealt with using PROLOG_PROBE_FRAME and EPILOG_PROBE_FRAME
;; defined below. For the special cases where additional work has to be done in the prolog we also provide
;; the lower level macros ALLOC_PROBE_FRAME, FREE_PROBE_FRAME and INIT_PROBE_FRAME that allow more control
;; to be asserted.
;;
;; Note that we currently employ a significant simplification of frame setup: we always allocate a
;; maximally-sized PInvokeTransitionFrame and save all of the registers. Depending on the caller this can
;; lead to upto five additional register saves (r0-r3,r12) or 20 bytes of stack space. I have done no
;; analysis to see whether any of the worst cases occur on performance sensitive paths and whether the
;; additional saves will show any measurable degradation.
;; Perform the parts of setting up a probe frame that can occur during the prolog (and indeed this macro
;; can only be called from within the prolog).
MACRO
ALLOC_PROBE_FRAME
PROLOG_STACK_ALLOC 4 ; Space for saved APSR
PROLOG_VPUSH {d0-d3} ; Save floating point return registers
PROLOG_PUSH {r0-r3,lr} ; Save volatile registers
PROLOG_STACK_ALLOC 4 ; Space for caller's SP
PROLOG_PUSH {r4-r6,r8-r10} ; Save non-volatile registers
PROLOG_STACK_ALLOC 8 ; Space for flags and Thread*
PROLOG_PUSH {r7} ; Save caller's frame pointer
PROLOG_PUSH {r11,lr} ; Save frame-chain pointer and return address
MEND
;; Undo the effects of an ALLOC_PROBE_FRAME. This may only be called within an epilog. Note that all
;; registers are restored (apart for sp and pc), even volatiles.
MACRO
FREE_PROBE_FRAME
EPILOG_POP {r11,lr} ; Restore frame-chain pointer and return address
EPILOG_POP {r7} ; Restore caller's frame pointer
EPILOG_STACK_FREE 8 ; Discard flags and Thread*
EPILOG_POP {r4-r6,r8-r10} ; Restore non-volatile registers
EPILOG_STACK_FREE 4 ; Discard caller's SP
EPILOG_POP {r0-r3,lr} ; Restore volatile registers
EPILOG_VPOP {d0-d3} ; Restore floating point return registers
EPILOG_STACK_FREE 4 ; Space for saved APSR
MEND
;; Complete the setup of a probe frame allocated with ALLOC_PROBE_FRAME with the initialization that can
;; occur only outside the prolog (includes linking the frame to the current Thread). This macro assumes SP
;; is invariant outside of the prolog.
;;
;; $threadReg : register containing the Thread* (this will be preserved)
;; $trashReg : register that can be trashed by this macro
;; $BITMASK : value to initialize m_Flags field with (register or #constant)
;; $frameSize : total size of the method's stack frame (including probe frame size)
MACRO
INIT_PROBE_FRAME $threadReg, $trashReg, $BITMASK, $frameSize
str $threadReg, [sp, #m_pThread] ; Thread *
mov $trashReg, $BITMASK ; Bitmask of preserved registers
str $trashReg, [sp, #m_Flags]
add $trashReg, sp, #$frameSize
str $trashReg, [sp, #m_CallersSP]
MEND
;; Simple macro to use when setting up the probe frame can comprise the entire prolog. Call this macro
;; first in the method (no further prolog instructions can be added after this).
;;
;; $threadReg : register containing the Thread* (this will be preserved). If defaulted (specify |) then
;; the current thread will be calculated inline into r2 ($trashReg must not equal r2 in
;; this case)
;; $trashReg : register that can be trashed by this macro
;; $BITMASK : value to initialize m_Flags field with (register or #constant)
MACRO
PROLOG_PROBE_FRAME $threadReg, $trashReg, $BITMASK
; Local string tracking the name of the register in which the Thread* is kept. Defaults to the value
; of $threadReg.
LCLS __PPF_ThreadReg
__PPF_ThreadReg SETS "$threadReg"
; Define the method prolog, allocating enough stack space for the PInvokeTransitionFrame and saving
; incoming register values into it.
ALLOC_PROBE_FRAME
; If the caller didn't provide a value for $threadReg then generate code to fetch the Thread* into r2.
; Record that r2 holds the Thread* in our local variable.
IF "$threadReg" == ""
ASSERT "$trashReg" != "r2"
__PPF_ThreadReg SETS "r2"
INLINE_GETTHREAD $__PPF_ThreadReg, $trashReg
ENDIF
; Perform the rest of the PInvokeTransitionFrame initialization.
INIT_PROBE_FRAME $__PPF_ThreadReg, $trashReg, $BITMASK, PROBE_FRAME_SIZE
str sp, [$__PPF_ThreadReg, #OFFSETOF__Thread__m_pDeferredTransitionFrame]
MEND
; Simple macro to use when PROLOG_PROBE_FRAME was used to set up and initialize the prolog and
; PInvokeTransitionFrame. This will define the epilog including a return via the restored LR.
MACRO
EPILOG_PROBE_FRAME
FREE_PROBE_FRAME
EPILOG_RETURN
MEND
;;
;; The prolog for all GC suspension hijacks (normal and stress). Fixes up the hijacked return address, and
;; clears the hijack state.
;;
;; Register state on entry:
;; All registers correct for return to the original return address.
;;
;; Register state on exit:
;; r2: thread pointer
;; r3: trashed
;; r12: trashed
;;
MACRO
FixupHijackedCallstack
;; r2 <- GetThread(), TRASHES r3
INLINE_GETTHREAD r2, r3
;;
;; Fix the stack by restoring the original return address
;;
ldr lr, [r2, #OFFSETOF__Thread__m_pvHijackedReturnAddress]
;; Clear hijack state
mov r12, #0
str r12, [r2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation]
str r12, [r2, #OFFSETOF__Thread__m_pvHijackedReturnAddress]
MEND
MACRO
HijackTargetFakeProlog
;; This is a fake entrypoint for the method that 'tricks' the OS into calling our personality routine.
;; The code here should never be executed, and the unwind info is bogus, but we don't mind since the
;; stack is broken by the hijack anyway until after we fix it below.
PROLOG_PUSH {lr}
nop ; We also need a nop here to simulate the implied bl instruction. Without
; this, an OS-applied -2 will back up into the method prolog and the unwind
; will not be applied as desired.
MEND
;;
;;
;;
;; GC Probe Hijack targets
;;
;;
EXTERN RhpPInvokeExceptionGuard
NESTED_ENTRY RhpGcProbeHijackWrapper, .text, RhpPInvokeExceptionGuard
HijackTargetFakeProlog
LABELED_RETURN_ADDRESS RhpGcProbeHijack
FixupHijackedCallstack
ldr r3, =RhpTrapThreads
ldr r3, [r3]
tst r3, #TrapThreadsFlags_TrapThreads
bne %0
bx lr
0
mov r12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0)
b RhpWaitForGC
NESTED_END RhpGcProbeHijackWrapper
#ifdef FEATURE_GC_STRESS
;;
;;
;; GC Stress Hijack targets
;;
;;
LEAF_ENTRY RhpGcStressHijack
FixupHijackedCallstack
mov r12, #DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0
b RhpGcStressProbe
LEAF_END RhpGcStressHijack
;;
;; Worker for our GC stress probes. Do not call directly!!
;; Instead, go through RhpGcStressHijack.
;; This worker performs the GC Stress work and returns to the original return address.
;;
;; Register state on entry:
;; r0: hijacked function return value
;; r1: hijacked function return value
;; r2: thread pointer
;; r12: register bitmask
;;
;; Register state on exit:
;; Scratch registers, except for r0, have been trashed
;; All other registers restored as they were when the hijack was first reached.
;;
NESTED_ENTRY RhpGcStressProbe
PROLOG_PROBE_FRAME r2, r3, r12
bl RhpStressGc
EPILOG_PROBE_FRAME
NESTED_END RhpGcStressProbe
#endif ;; FEATURE_GC_STRESS
EXTERN RhpThrowHwEx
NESTED_ENTRY RhpWaitForGC
PROLOG_PROBE_FRAME r2, r3, r12
ldr r0, [r2, #OFFSETOF__Thread__m_pDeferredTransitionFrame]
bl RhpWaitForGC2
ldr r2, [sp, #OFFSETOF__PInvokeTransitionFrame__m_Flags]
tst r2, #PTFF_THREAD_ABORT
bne %1
EPILOG_PROBE_FRAME
1
FREE_PROBE_FRAME
EPILOG_NOP mov r0, #STATUS_REDHAWK_THREAD_ABORT
EPILOG_NOP mov r1, lr ;; return address as exception PC
EPILOG_BRANCH RhpThrowHwEx
NESTED_END RhpWaitForGC
LEAF_ENTRY RhpGcPoll
ldr r0, =RhpTrapThreads
ldr r0, [r0]
tst r0, #TrapThreadsFlags_TrapThreads
bne RhpGcPollRare
bx lr
LEAF_END RhpGcPoll
NESTED_ENTRY RhpGcPollRare
PROLOG_PROBE_FRAME |, r3, #PROBE_SAVE_FLAGS_EVERYTHING
; Unhijack this thread, if necessary.
INLINE_THREAD_UNHIJACK r2, r0, r1 ;; trashes r0, r1
ldr r0, [r2, #OFFSETOF__Thread__m_pDeferredTransitionFrame]
bl RhpWaitForGC2
EPILOG_PROBE_FRAME
NESTED_END RhpGcPollRare
LEAF_ENTRY RhpGcPollStress
;
; loop hijacking is used instead
;
__debugbreak
LEAF_END RhpGcPollStress
#ifdef FEATURE_GC_STRESS
NESTED_ENTRY RhpHijackForGcStress
PROLOG_PUSH {r0,r1} ; Save return value
PROLOG_VPUSH {d0-d3} ; Save VFP return value
;;
;; Setup a PAL_LIMITED_CONTEXT that looks like what you'd get if you had suspended this thread at the
;; IP after the call to this helper.
;;
;; This is very likely overkill since the calculation of the return address should only need SP and
;; LR, but this is test code, so I'm not too worried about efficiency.
;;
;; Setup a PAL_LIMITED_CONTEXT on the stack {
;; we'll need to reserve the size of the D registers in the context
;; compute in the funny way below to include any padding between LR and D
DREG_SZ equ (SIZEOF__PAL_LIMITED_CONTEXT - (OFFSETOF__PAL_LIMITED_CONTEXT__LR + 4))
PROLOG_STACK_ALLOC DREG_SZ ;; Reserve space for d8-d15
PROLOG_PUSH {r0,lr} ;; Reserve space for SP and store LR
PROLOG_PUSH {r0,r4-r11,lr}
;; } end PAL_LIMITED_CONTEXT
;; Compute and save SP at callsite.
add r0, sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x20 + 8) ;; +0x20 for vpush {d0-d3}, +8 for push {r0,r1}
str r0, [sp, #OFFSETOF__PAL_LIMITED_CONTEXT__SP]
mov r0, sp ; Address of PAL_LIMITED_CONTEXT
bl $THREAD__HIJACKFORGCSTRESS
;; epilog
EPILOG_POP {r0,r4-r11,lr}
EPILOG_STACK_FREE DREG_SZ + 8 ; Discard saved SP and LR and space for d8-d15
EPILOG_VPOP {d0-d3} ; Restore VFP return value
EPILOG_POP {r0,r1} ; Restore return value
bx lr
NESTED_END RhpHijackForGcStress
#endif ;; FEATURE_GC_STRESS
#ifdef FEATURE_GC_STRESS
;;
;; INVARIANT: Don't trash the argument registers, the binder codegen depends on this.
;;
LEAF_ENTRY RhpSuppressGcStress
push {r0-r2}
INLINE_GETTHREAD r0, r1
Retry
ldrex r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags]
orr r1, #TSF_SuppressGcStress
strex r2, r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags]
cbz r2, Success
b Retry
Success
pop {r0-r2}
bx lr
LEAF_END RhpSuppressGcStress
#endif ;; FEATURE_GC_STRESS
INLINE_GETTHREAD_CONSTANT_POOL
end
@@ -1,83 +0,0 @@
;; Licensed to the .NET Foundation under one or more agreements.
;; The .NET Foundation licenses this file to you under the MIT license.
#include "kxarm.h"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
__tls_array equ 0x2C ;; offsetof(TEB, ThreadLocalStoragePointer)
POINTER_SIZE equ 0x04
;; TLS variables
AREA |.tls$|, DATA
ThunkParamSlot % 0x4
TEXTAREA
EXTERN _tls_index
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interop Thunks Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; RhCommonStub
;;
NESTED_ENTRY RhCommonStub
;; Custom calling convention:
;; red zone has pointer to the current thunk's data block (data contains 2 pointer values: context + target pointers)
;; Copy red zone value into r12 so that the PROLOG_PUSH doesn't destroy it
PROLOG_NOP ldr r12, [sp, #-4]
PROLOG_PUSH {r0-r3}
;; Save context data into the ThunkParamSlot thread-local variable
;; A pointer to the delegate and function pointer for open static delegate should have been saved in the thunk's context cell during thunk allocation
ldr r3, =_tls_index
ldr r2, [r3]
mrc p15, #0, r3, c13, c0, #2
ldr r3, [r3, #__tls_array]
ldr r2, [r3, r2, lsl #2] ;; r2 <- our TLS base
;; r2 = base address of TLS data
;; r12 = address of context cell in thunk's data
;; store thunk address in thread static
ldr r1, [r12]
ldr r3, =ThunkParamSlot
str r1, [r2, r3] ;; ThunkParamSlot <- context slot data
;; Now load the target address and jump to it.
ldr r12, [r12, #POINTER_SIZE]
EPILOG_POP {r0-r3}
bx r12
NESTED_END RhCommonStub
;;
;; IntPtr RhGetCommonStubAddress()
;;
LEAF_ENTRY RhGetCommonStubAddress
ldr r0, =RhCommonStub
bx lr
LEAF_END RhGetCommonStubAddress
;;
;; IntPtr RhGetCurrentThunkContext()
;;
LEAF_ENTRY RhGetCurrentThunkContext
ldr r3, =_tls_index
ldr r2, [r3]
mrc p15, #0, r3, c13, c0, #2
ldr r3, [r3, #__tls_array]
ldr r2, [r3, r2, lsl #2] ;; r2 <- our TLS base
ldr r3, =ThunkParamSlot
ldr r0, [r2, r3] ;; r0 <- ThunkParamSlot
bx lr
LEAF_END RhGetCurrentThunkContext
END
@@ -1,10 +0,0 @@
;; Licensed to the .NET Foundation under one or more agreements.
;; The .NET Foundation licenses this file to you under the MIT license.
#include "AsmMacros.h"
TEXTAREA
LEAF_END
end
@@ -1,8 +0,0 @@
;; Licensed to the .NET Foundation under one or more agreements.
;; The .NET Foundation licenses this file to you under the MIT license.
#include "AsmMacros.h"
TEXTAREA
INLINE_GETTHREAD_CONSTANT_POOL
@@ -1,141 +0,0 @@
;; Licensed to the .NET Foundation under one or more agreements.
;; The .NET Foundation licenses this file to you under the MIT license.
#include "AsmMacros.h"
TEXTAREA
#ifdef FEATURE_CACHED_INTERFACE_DISPATCH
EXTERN RhpCidResolve
EXTERN RhpUniversalTransition_DebugStepTailCall
;; Macro that generates code to check a single cache entry.
MACRO
CHECK_CACHE_ENTRY $entry
;; Check a single entry in the cache.
;; R1 : Instance MethodTable*
;; R2: Cache data structure
;; R12 : Trashed. On successful check, set to the target address to jump to.
ldr r12, [r2, #(OFFSETOF__InterfaceDispatchCache__m_rgEntries + ($entry * 8))]
cmp r1, r12
bne %ft0
ldr r12, [r2, #(OFFSETOF__InterfaceDispatchCache__m_rgEntries + ($entry * 8) + 4)]
b %fa99
0
MEND
;; Macro that generates a stub consuming a cache with the given number of entries.
GBLS StubName
MACRO
DEFINE_INTERFACE_DISPATCH_STUB $entries
StubName SETS "RhpInterfaceDispatch$entries"
NESTED_ENTRY $StubName
;; On input we have the indirection cell data structure in r12. But we need more scratch registers and
;; we may A/V on a null this. Both of these suggest we need a real prolog and epilog.
PROLOG_PUSH {r1-r2}
;; r12 currently holds the indirection cell address. We need to get the cache structure instead.
ldr r2, [r12, #OFFSETOF__InterfaceDispatchCell__m_pCache]
;; Load the MethodTable from the object instance in r0.
ldr r1, [r0]
GBLA CurrentEntry
CurrentEntry SETA 0
WHILE CurrentEntry < $entries
CHECK_CACHE_ENTRY CurrentEntry
CurrentEntry SETA CurrentEntry + 1
WEND
;; Point r12 to the indirection cell using the back pointer in the cache block
ldr r12, [r2, #OFFSETOF__InterfaceDispatchCache__m_pCell]
EPILOG_POP {r1-r2}
EPILOG_BRANCH RhpInterfaceDispatchSlow
;; Common epilog for cache hits. Have to out of line it here due to limitation on the number of
;; epilogs imposed by the unwind code macros.
99
;; R2 contains address of the cache block. We store it in the red zone in case the target we jump
;; to needs it.
;; R12 contains the target address to jump to
EPILOG_POP r1
;; The red zone is only 8 bytes long, so we have to store r2 into it between the pops.
EPILOG_NOP str r2, [sp, #-4]
EPILOG_POP r2
EPILOG_BRANCH_REG r12
NESTED_END $StubName
MEND
;; Define all the stub routines we currently need.
DEFINE_INTERFACE_DISPATCH_STUB 1
DEFINE_INTERFACE_DISPATCH_STUB 2
DEFINE_INTERFACE_DISPATCH_STUB 4
DEFINE_INTERFACE_DISPATCH_STUB 8
DEFINE_INTERFACE_DISPATCH_STUB 16
DEFINE_INTERFACE_DISPATCH_STUB 32
DEFINE_INTERFACE_DISPATCH_STUB 64
;; Initial dispatch on an interface when we don't have a cache yet.
LEAF_ENTRY RhpInitialInterfaceDispatch
;; The stub that jumped here pushed r12, which contains the interface dispatch cell
;; we need to pop it here
pop { r12 }
;; Simply tail call the slow dispatch helper.
b RhpInterfaceDispatchSlow
LEAF_END RhpInitialInterfaceDispatch
LEAF_ENTRY RhpVTableOffsetDispatch
;; On input we have the indirection cell data structure in r12. But we need more scratch registers and
;; we may A/V on a null this. Both of these suggest we need a real prolog and epilog.
PROLOG_PUSH {r1}
;; r12 currently holds the indirection cell address. We need to update it to point to the vtable
;; offset instead.
ldr r12, [r12, #OFFSETOF__InterfaceDispatchCell__m_pCache]
;; Load the MethodTable from the object instance in r0.
ldr r1, [r0]
;; add the vtable offset to the MethodTable pointer
add r12, r1, r12
;; Load the target address of the vtable into r12
ldr r12, [r12]
EPILOG_POP {r1}
EPILOG_BRANCH_REG r12
LEAF_END RhpVTableOffsetDispatch
;; Cache miss case, call the runtime to resolve the target and update the cache.
LEAF_ENTRY RhpInterfaceDispatchSlow
ALTERNATE_ENTRY RhpInitialDynamicInterfaceDispatch
;; r12 has the interface dispatch cell address in it.
;; The calling convention of the universal thunk is that the parameter
;; for the universal thunk target is to be placed in sp-8
;; and the universal thunk target address is to be placed in sp-4
str r12, [sp, #-8]
ldr r12, =RhpCidResolve
str r12, [sp, #-4]
;; jump to universal transition thunk
b RhpUniversalTransition_DebugStepTailCall
LEAF_END RhpInterfaceDispatchSlow
#endif // FEATURE_CACHED_INTERFACE_DISPATCH
end
@@ -1,273 +0,0 @@
;; Licensed to the .NET Foundation under one or more agreements.
;; The .NET Foundation licenses this file to you under the MIT license.
#include "kxarm.h"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; STUBS & DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
THUNK_CODESIZE equ 0x10 ;; 4-byte mov, 2-byte add, 4-byte str, 4-byte ldr, 2-byte branch
THUNK_DATASIZE equ 0x08 ;; 2 dwords
THUNK_POOL_NUM_THUNKS_PER_PAGE equ 0xFA ;; 250 thunks per page
PAGE_SIZE equ 0x1000 ;; 4K
POINTER_SIZE equ 0x04
MACRO
NAMED_READONLY_DATA_SECTION $name, $areaAlias
AREA $areaAlias,DATA,READONLY
RO$name % 4
MEND
MACRO
NAMED_READWRITE_DATA_SECTION $name, $areaAlias
AREA $areaAlias,DATA
RW$name % 4
MEND
MACRO
LOAD_DATA_ADDRESS $groupIndex, $index
ALIGN 0x10 ;; make sure we align to 16-byte boundary for CFG table
;; set r12 to beginning of data page : r12 <- pc - (THUNK_CODESIZE * current thunk's index - sizeof(mov+add instructions)) + PAGE_SIZE
;; fix offset of the data : r12 <- r12 + (THUNK_DATASIZE * current thunk's index)
mov.w r12, PAGE_SIZE + ($groupIndex * THUNK_DATASIZE * 10 + THUNK_DATASIZE * $index) - (8 + $groupIndex * THUNK_CODESIZE * 10 + THUNK_CODESIZE * $index)
add.n r12, r12, pc
MEND
MACRO
JUMP_TO_COMMON $groupIndex, $index
;; start : r12 points to the current thunks first data cell in the data page
;; put r12 into the red zone : r12 isn't changed
;; set r12 to beginning of data page : r12 <- r12 - (THUNK_DATASIZE * current thunk's index)
;; fix offset to point to last DWROD in page : r12 <- r11 + PAGE_SIZE - POINTER_SIZE
;; jump to the location pointed at by the last dword in the data page
str.w r12, [sp, #-4]
ldr.w r12, [r12, #(PAGE_SIZE - POINTER_SIZE - ($groupIndex * THUNK_DATASIZE * 10 + THUNK_DATASIZE * $index))]
bx.n r12
MEND
MACRO
TenThunks $groupIndex
;; Each thunk will load the address of its corresponding data (from the page that immediately follows)
;; and call a common stub. The address of the common stub is setup by the caller (last dword
;; in the thunks data section) depending on the 'kind' of thunks needed (interop, fat function pointers, etc...)
;; Each data block used by a thunk consists of two dword values:
;; - Context: some value given to the thunk as context (passed in eax). Example for fat-fptrs: context = generic dictionary
;; - Target : target code that the thunk eventually jumps to.
LOAD_DATA_ADDRESS $groupIndex,0
JUMP_TO_COMMON $groupIndex,0
LOAD_DATA_ADDRESS $groupIndex,1
JUMP_TO_COMMON $groupIndex,1
LOAD_DATA_ADDRESS $groupIndex,2
JUMP_TO_COMMON $groupIndex,2
LOAD_DATA_ADDRESS $groupIndex,3
JUMP_TO_COMMON $groupIndex,3
LOAD_DATA_ADDRESS $groupIndex,4
JUMP_TO_COMMON $groupIndex,4
LOAD_DATA_ADDRESS $groupIndex,5
JUMP_TO_COMMON $groupIndex,5
LOAD_DATA_ADDRESS $groupIndex,6
JUMP_TO_COMMON $groupIndex,6
LOAD_DATA_ADDRESS $groupIndex,7
JUMP_TO_COMMON $groupIndex,7
LOAD_DATA_ADDRESS $groupIndex,8
JUMP_TO_COMMON $groupIndex,8
LOAD_DATA_ADDRESS $groupIndex,9
JUMP_TO_COMMON $groupIndex,9
MEND
MACRO
THUNKS_PAGE_BLOCK
TenThunks 0
TenThunks 1
TenThunks 2
TenThunks 3
TenThunks 4
TenThunks 5
TenThunks 6
TenThunks 7
TenThunks 8
TenThunks 9
TenThunks 10
TenThunks 11
TenThunks 12
TenThunks 13
TenThunks 14
TenThunks 15
TenThunks 16
TenThunks 17
TenThunks 18
TenThunks 19
TenThunks 20
TenThunks 21
TenThunks 22
TenThunks 23
TenThunks 24
MEND
;;
;; The first thunks section should be 64K aligned because it can get
;; mapped multiple times in memory, and mapping works on allocation
;; granularity boundaries (we don't want to map more than what we need)
;;
;; The easiest way to do so is by having the thunks section at the
;; first 64K aligned virtual address in the binary. We provide a section
;; layout file to the linker to tell it how to layout the thunks sections
;; that we care about. (ndp\rh\src\runtime\DLLs\app\mrt100_app_sectionlayout.txt)
;;
;; The PE spec says images cannot have gaps between sections (other
;; than what is required by the section alignment value in the header),
;; therefore we need a couple of padding data sections (otherwise the
;; OS will not load the image).
;;
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment0, "|.pad0|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment1, "|.pad1|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment2, "|.pad2|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment3, "|.pad3|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment4, "|.pad4|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment5, "|.pad5|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment6, "|.pad6|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment7, "|.pad7|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment8, "|.pad8|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment9, "|.pad9|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment10, "|.pad10|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment11, "|.pad11|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment12, "|.pad12|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment13, "|.pad13|"
NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment14, "|.pad14|"
;;
;; Thunk Stubs
;; NOTE: Keep number of blocks in sync with macro/constant named 'NUM_THUNK_BLOCKS' in:
;; - ndp\FxCore\src\System.Private.CoreLib\System\Runtime\InteropServices\ThunkPool.cs
;; - ndp\rh\src\tools\rhbind\zapimage.h
;;
LEAF_ENTRY ThunkPool, "|.tks0|"
THUNKS_PAGE_BLOCK
LEAF_END ThunkPool
NAMED_READWRITE_DATA_SECTION ThunkData0, "|.tkd0|"
LEAF_ENTRY ThunkPool1, "|.tks1|"
THUNKS_PAGE_BLOCK
LEAF_END ThunkPool1
NAMED_READWRITE_DATA_SECTION ThunkData1, "|.tkd1|"
LEAF_ENTRY ThunkPool2, "|.tks2|"
THUNKS_PAGE_BLOCK
LEAF_END ThunkPool2
NAMED_READWRITE_DATA_SECTION ThunkData2, "|.tkd2|"
LEAF_ENTRY ThunkPool3, "|.tks3|"
THUNKS_PAGE_BLOCK
LEAF_END ThunkPool3
NAMED_READWRITE_DATA_SECTION ThunkData3, "|.tkd3|"
LEAF_ENTRY ThunkPool4, "|.tks4|"
THUNKS_PAGE_BLOCK
LEAF_END ThunkPool4
NAMED_READWRITE_DATA_SECTION ThunkData4, "|.tkd4|"
LEAF_ENTRY ThunkPool5, "|.tks5|"
THUNKS_PAGE_BLOCK
LEAF_END ThunkPool5
NAMED_READWRITE_DATA_SECTION ThunkData5, "|.tkd5|"
LEAF_ENTRY ThunkPool6, "|.tks6|"
THUNKS_PAGE_BLOCK
LEAF_END ThunkPool6
NAMED_READWRITE_DATA_SECTION ThunkData6, "|.tkd6|"
LEAF_ENTRY ThunkPool7, "|.tks7|"
THUNKS_PAGE_BLOCK
LEAF_END ThunkPool7
NAMED_READWRITE_DATA_SECTION ThunkData7, "|.tkd7|"
;;
;; IntPtr RhpGetThunksBase()
;;
LEAF_ENTRY RhpGetThunksBase
;; Return the address of the first thunk pool to the caller (this is really the base address)
ldr r0, =ThunkPool
sub r0, r0, #1
bx lr
LEAF_END RhpGetThunksBase
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; General Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; int RhpGetNumThunksPerBlock()
;;
LEAF_ENTRY RhpGetNumThunksPerBlock
mov r0, THUNK_POOL_NUM_THUNKS_PER_PAGE
bx lr
LEAF_END RhpGetNumThunksPerBlock
;;
;; int RhpGetThunkSize()
;;
LEAF_ENTRY RhpGetThunkSize
mov r0, THUNK_CODESIZE
bx lr
LEAF_END RhpGetThunkSize
;;
;; int RhpGetNumThunkBlocksPerMapping()
;;
LEAF_ENTRY RhpGetNumThunkBlocksPerMapping
mov r0, 8
bx lr
LEAF_END RhpGetNumThunkBlocksPerMapping
;;
;; int RhpGetThunkBlockSize
;;
LEAF_ENTRY RhpGetThunkBlockSize
mov r0, PAGE_SIZE * 2
bx lr
LEAF_END RhpGetThunkBlockSize
;;
;; IntPtr RhpGetThunkDataBlockAddress(IntPtr thunkStubAddress)
;;
LEAF_ENTRY RhpGetThunkDataBlockAddress
mov r12, PAGE_SIZE - 1
bic r0, r0, r12
add r0, PAGE_SIZE
bx lr
LEAF_END RhpGetThunkDataBlockAddress
;;
;; IntPtr RhpGetThunkStubsBlockAddress(IntPtr thunkDataAddress)
;;
LEAF_ENTRY RhpGetThunkStubsBlockAddress
mov r12, PAGE_SIZE - 1
bic r0, r0, r12
sub r0, PAGE_SIZE
bx lr
LEAF_END RhpGetThunkStubsBlockAddress
END
@@ -1,157 +0,0 @@
;; Licensed to the .NET Foundation under one or more agreements.
;; The .NET Foundation licenses this file to you under the MIT license.
#include "AsmMacros.h"
#ifdef _DEBUG
#define TRASH_SAVED_ARGUMENT_REGISTERS
#endif
#ifdef TRASH_SAVED_ARGUMENT_REGISTERS
EXTERN RhpIntegerTrashValues
EXTERN RhpFpTrashValues
#endif ;; TRASH_SAVED_ARGUMENT_REGISTERS
#define COUNT_ARG_REGISTERS (4)
#define INTEGER_REGISTER_SIZE (4)
#define ARGUMENT_REGISTERS_SIZE (COUNT_ARG_REGISTERS * INTEGER_REGISTER_SIZE)
;; Largest return block is 4 doubles
#define RETURN_BLOCK_SIZE (32)
#define COUNT_FLOAT_ARG_REGISTERS (8)
#define FLOAT_REGISTER_SIZE (8)
#define FLOAT_ARG_REGISTERS_SIZE (COUNT_FLOAT_ARG_REGISTERS * FLOAT_REGISTER_SIZE)
#define PUSHED_LR_SIZE (4)
#define PUSHED_R11_SIZE (4)
;;
;; From CallerSP to ChildSP, the stack frame is composed of the following adjacent regions:
;;
;; ARGUMENT_REGISTERS_SIZE
;; RETURN_BLOCK_SIZE
;; FLOAT_ARG_REGISTERS_SIZE
;; PUSHED_LR
;; PUSHED_R11
;;
#define DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK (PUSHED_R11_SIZE + PUSHED_LR_SIZE + FLOAT_ARG_REGISTERS_SIZE)
TEXTAREA
;;
;; RhpUniversalTransition
;;
;; At input to this function, r0-3, d0-7 and the stack may contain any number of arguments.
;;
;; In addition, there are 2 extra arguments passed in the RED ZONE (8 byte negative space
;; off of sp).
;; sp-4 will contain the managed function that is to be called by this transition function
;; sp-8 will contain the pointer sized extra argument to the managed function
;;
;; When invoking the callee:
;;
;; r0 shall contain a pointer to the TransitionBlock
;; r1 shall contain the value that was in sp-8 at entry to this function
;;
;; Frame layout is:
;;
;; {StackPassedArgs} ChildSP+078 CallerSP+000
;; {IntArgRegs (r0-r3) (0x10 bytes)} ChildSP+068 CallerSP-010
;; {ReturnBlock (0x20 bytes)} ChildSP+048 CallerSP-030
;; -- The base address of the Return block is the TransitionBlock pointer, the floating point args are
;; in the neg space of the TransitionBlock pointer. Note that the callee has knowledge of the exact
;; layout of all pieces of the frame that lie at or above the pushed floating point registers.
;; {FpArgRegs (d0-d7) (0x40 bytes)} ChildSP+008 CallerSP-070
;; {PushedLR} ChildSP+004 CallerSP-074
;; {PushedR11} ChildSP+000 CallerSP-078
;;
;; NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure
;; must be updated as well.
;;
;; NOTE: The callee receives a pointer to the base of the ReturnBlock, and the callee has
;; knowledge of the exact layout of all pieces of the frame that lie at or above the pushed
;; FpArgRegs.
;;
;; NOTE: The stack walker guarantees that conservative GC reporting will be applied to
;; everything between the base of the ReturnBlock and the top of the StackPassedArgs.
;;
MACRO
UNIVERSAL_TRANSITION $FunctionName
NESTED_ENTRY Rhp$FunctionName
;; Save argument registers (including floating point) and the return address.
;; NOTE: While we do that, capture the two arguments in the red zone into r12 and r3.
PROLOG_NOP ldr r12, [sp, #-4] ; Capture first argument from red zone into r12
PROLOG_PUSH {r3} ; Push r3
PROLOG_NOP ldr r3, [sp, #-4] ; Capture second argument from red zone into r3
PROLOG_PUSH {r0-r2} ; Push the rest of the registers
PROLOG_STACK_ALLOC RETURN_BLOCK_SIZE ; Save space a buffer to be used to hold return buffer data.
PROLOG_VPUSH {d0-d7} ; Capture the floating point argument registers
PROLOG_PUSH {r11,lr} ; Save caller's frame chain pointer and PC
;; Setup the arguments to the transition thunk.
mov r1, r3
#ifdef TRASH_SAVED_ARGUMENT_REGISTERS
;; Before calling out, trash all of the argument registers except the ones (r0, r1) that
;; hold outgoing arguments. All of these registers have been saved to the transition
;; frame, and the code at the call target is required to use only the transition frame
;; copies when dispatching this call to the eventual callee.
ldr r3, =RhpFpTrashValues
vldr d0, [r3, #(0 * 8)]
vldr d1, [r3, #(1 * 8)]
vldr d2, [r3, #(2 * 8)]
vldr d3, [r3, #(3 * 8)]
vldr d4, [r3, #(4 * 8)]
vldr d5, [r3, #(5 * 8)]
vldr d6, [r3, #(6 * 8)]
vldr d7, [r3, #(7 * 8)]
ldr r3, =RhpIntegerTrashValues
ldr r2, [r3, #(2 * 4)]
ldr r3, [r3, #(3 * 4)]
#endif // TRASH_SAVED_ARGUMENT_REGISTERS
;; Make the ReturnFromUniversalTransition alternate entry 4 byte aligned
ALIGN 4
add r0, sp, #DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK ;; First parameter to target function is a pointer to the return block
blx r12
EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom$FunctionName
; We cannot make the label public as that tricks DIA stackwalker into thinking
; it's the beginning of a method. For this reason we export an auxiliary variable
; holding the address instead.
;; Move the result (the target address) to r12 so it doesn't get overridden when we restore the
;; argument registers. Additionally make sure the thumb2 bit is set.
orr r12, r0, #1
;; Restore caller's frame chain pointer and PC.
EPILOG_POP {r11,lr}
;; Restore the argument registers.
EPILOG_VPOP {d0-d7}
EPILOG_STACK_FREE RETURN_BLOCK_SIZE ; pop return block conservatively reported area
EPILOG_POP {r0-r3}
;; Tailcall to the target address.
EPILOG_BRANCH_REG r12
NESTED_END Rhp$FunctionName
MEND
; To enable proper step-in behavior in the debugger, we need to have two instances
; of the thunk. For the first one, the debugger steps into the call in the function,
; for the other, it steps over it.
UNIVERSAL_TRANSITION UniversalTransition
UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall
END
@@ -1,421 +0,0 @@
;; Licensed to the .NET Foundation under one or more agreements.
;; The .NET Foundation licenses this file to you under the MIT license.
;;
;; Define the helpers used to implement the write barrier required when writing an object reference into a
;; location residing on the GC heap. Such write barriers allow the GC to optimize which objects in
;; non-ephemeral generations need to be scanned for references to ephemeral objects during an ephemeral
;; collection.
;;
#include "AsmMacros.h"
TEXTAREA
;; Macro used to copy contents of newly updated GC heap locations to a shadow copy of the heap. This is used
;; during garbage collections to verify that object references where never written to the heap without using a
;; write barrier. Note that we're potentially racing to update the shadow heap while other threads are writing
;; new references to the real heap. Since this can't be solved perfectly without critical sections around the
;; entire update process, we instead update the shadow location and then re-check the real location (as two
;; ordered operations) and if there is a disparity we'll re-write the shadow location with a special value
;; (INVALIDGCVALUE) which disables the check for that location. Since the shadow heap is only validated at GC
;; time and these write barrier operations are atomic wrt to GCs this is sufficient to guarantee that the
;; shadow heap contains only valid copies of real heap values or INVALIDGCVALUE.
#ifdef WRITE_BARRIER_CHECK
SETALIAS g_GCShadow, ?g_GCShadow@@3PAEA
SETALIAS g_GCShadowEnd, ?g_GCShadowEnd@@3PAEA
EXTERN $g_GCShadow
EXTERN $g_GCShadowEnd
MACRO
;; On entry:
;; $DESTREG: location to be updated
;; $REFREG: objectref to be stored
;;
;; On exit:
;; r12: trashed
;; other registers are preserved
;;
UPDATE_GC_SHADOW $DESTREG, $REFREG
;; If g_GCShadow is 0, don't perform the check.
ldr r12, =$g_GCShadow
ldr r12, [r12]
cmp r12, 0
beq %ft1
;; Save $DESTREG since we're about to modify it (and we need the original value both within the macro and
;; once we exit the macro). Note that this is naughty since we're altering the stack pointer outside of
;; the prolog inside a method without a frame. But given that this is only debug code and generally we
;; shouldn't be walking the stack at this point it seems preferable to recoding the all the barrier
;; variants to set up frames. The compiler knows exactly which registers are trashed in the simple write
;; barrier case, so we don't have any more scratch registers to play with (and doing so would only make
;; things harder if at a later stage we want to allow multiple barrier versions based on the input
;; registers).
push $DESTREG
;; Transform $DESTREG into the equivalent address in the shadow heap.
ldr r12, =$G_LOWEST_ADDRESS
ldr r12, [r12]
subs $DESTREG, r12
blo %ft0
ldr r12, =$g_GCShadow
ldr r12, [r12]
add $DESTREG, r12
ldr r12, =$g_GCShadowEnd
ldr r12, [r12]
cmp $DESTREG, r12
bhs %ft0
;; Update the shadow heap.
str $REFREG, [$DESTREG]
;; The following read must be strongly ordered wrt to the write we've just performed in order to
;; prevent race conditions.
dmb
;; Now check that the real heap location still contains the value we just wrote into the shadow heap.
ldr r12, [sp]
ldr r12, [r12]
cmp r12, $REFREG
beq %ft0
;; Someone went and updated the real heap. We need to invalidate the shadow location since we can't
;; guarantee whose shadow update won.
movw r12, #0xcccd
movt r12, #0xcccc
str r12, [$DESTREG]
0
;; Restore original $DESTREG value from the stack.
pop $DESTREG
1
MEND
#else // WRITE_BARRIER_CHECK
MACRO
UPDATE_GC_SHADOW $DESTREG, $REFREG
MEND
#endif // WRITE_BARRIER_CHECK
;; There are several different helpers used depending on which register holds the object reference. Since all
;; the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the
;; name of the register that points to the location to be updated and the name of the register that holds the
;; object reference (this should be in upper case as it's used in the definition of the name of the helper).
;; Define a sub-macro first that expands to the majority of the barrier implementation. This is used below for
;; some interlocked helpers that need an inline barrier.
MACRO
;; On entry:
;; $DESTREG: location to be updated
;; $REFREG: objectref to be stored
;;
;; On exit:
;; $DESTREG, r12: trashed
;; other registers are preserved
;;
INSERT_CHECKED_WRITE_BARRIER_CORE $DESTREG, $REFREG
;; The location being updated might not even lie in the GC heap (a handle or stack location for
;; instance), in which case no write barrier is required.
ldr r12, =$G_LOWEST_ADDRESS
ldr r12, [r12]
cmp $DESTREG, r12
blo %ft0
ldr r12, =$G_HIGHEST_ADDRESS
ldr r12, [r12]
cmp $DESTREG, r12
bhs %ft0
;; Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless
;; we're in a debug build and write barrier checking has been enabled).
UPDATE_GC_SHADOW $DESTREG, $REFREG
;; If the reference is to an object that's not in an ephemeral generation we have no need to track it
;; (since the object won't be collected or moved by an ephemeral collection).
ldr r12, =$G_EPHEMERAL_LOW
ldr r12, [r12]
cmp $REFREG, r12
blo %ft0
ldr r12, =$G_EPHEMERAL_HIGH
ldr r12, [r12]
cmp $REFREG, r12
bhs %ft0
;; All tests pass, so update the card table.
ldr r12, =$G_CARD_TABLE
ldr r12, [r12]
add r12, r12, $DESTREG, lsr #10
;; Check that this card hasn't already been written. Avoiding useless writes is a big win on
;; multi-proc systems since it avoids cache thrashing.
ldrb $DESTREG, [r12]
cmp $DESTREG, #0xFF
beq %ft0
mov $DESTREG, #0xFF
strb $DESTREG, [r12]
0
MEND
;; There are several different helpers used depending on which register holds the object reference. Since all
;; the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the
;; name of the register that points to the location to be updated and the name of the register that holds the
;; object reference (this should be in upper case as it's used in the definition of the name of the helper).
;; Define a sub-macro first that expands to the majority of the barrier implementation. This is used below for
;; some interlocked helpers that need an inline barrier.
MACRO
;; On entry:
;; $DESTREG: location to be updated
;; $REFREG: objectref to be stored
;;
;; On exit:
;; $DESTREG, r12: trashed
;; other registers are preserved
;;
INSERT_UNCHECKED_WRITE_BARRIER_CORE $DESTREG, $REFREG
;; Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless
;; we're in a debug build and write barrier checking has been enabled).
UPDATE_GC_SHADOW $DESTREG, $REFREG
;; If the reference is to an object that's not in an ephemeral generation we have no need to track it
;; (since the object won't be collected or moved by an ephemeral collection).
ldr r12, =$G_EPHEMERAL_LOW
ldr r12, [r12]
cmp $REFREG, r12
blo %ft0
ldr r12, =$G_EPHEMERAL_HIGH
ldr r12, [r12]
cmp $REFREG, r12
bhs %ft0
;; All tests pass, so update the card table.
ldr r12, =$G_CARD_TABLE
ldr r12, [r12]
add r12, r12, $DESTREG, lsr #10
;; Check that this card hasn't already been written. Avoiding useless writes is a big win on
;; multi-proc systems since it avoids cache thrashing.
ldrb $DESTREG, [r12]
cmp $DESTREG, #0xFF
beq %ft0
mov $DESTREG, #0xFF
strb $DESTREG, [r12]
0
MEND
MACRO
;; Define a helper with a name of the form RhpCheckedAssignRefR0 etc. The location to be updated is in
;; $DESTREG. The object reference that will be assigned into that location is in one of the other
;; general registers determined by the value of $REFREG. R12 is used as a scratch register.
;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular:
;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at WriteBarrierFunctionAvLocation
;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address
DEFINE_CHECKED_WRITE_BARRIER $DESTREG, $REFREG
gbls WriteBarrierFunction
gbls WriteBarrierFunctionAvLocation
WriteBarrierFunction SETS "RhpCheckedAssignRef":cc:"$REFREG"
WriteBarrierFunctionAvLocation SETS "RhpCheckedAssignRefAvLocation":cc:"$REFREG"
EXPORT $WriteBarrierFunction
$WriteBarrierFunction
;; Export the canonical write barrier under unqualified name as well
IF "$REFREG" == "R1"
ALTERNATE_ENTRY RhpCheckedAssignRef
ENDIF
;; Use the GC write barrier as a convenient place to implement the managed memory model for ARM. The
;; intent is that writes to the target object ($REFREG) will be visible across all CPUs before the
;; write to the destination ($DESTREG). This covers most of the common scenarios where the programmer
;; might assume strongly ordered accessess, namely where the preceding writes are used to initialize
;; the object and the final write, made by this barrier in the instruction following the DMB,
;; publishes that object for other threads/cpus to see.
;;
;; Note that none of this is relevant for single cpu machines. We may choose to implement a
;; uniprocessor specific version of this barrier if uni-proc becomes a significant scenario again.
dmb
;; Write the reference into the location. Note that we rely on the fact that no GC can occur between
;; here and the card table update we may perform below.
ALTERNATE_ENTRY $WriteBarrierFunctionAvLocation
IF "$REFREG" == "R1"
ALTERNATE_ENTRY RhpCheckedAssignRefAVLocation
ENDIF
str $REFREG, [$DESTREG]
INSERT_CHECKED_WRITE_BARRIER_CORE $DESTREG, $REFREG
bx lr
MEND
MACRO
;; Define a helper with a name of the form RhpAssignRefR0 etc. The location to be updated is in
;; $DESTREG. The object reference that will be assigned into that location is in one of the other
;; general registers determined by the value of $REFREG. R12 is used as a scratch register.
;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular:
;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at WriteBarrierFunctionAvLocation
;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address
DEFINE_UNCHECKED_WRITE_BARRIER $DESTREG, $REFREG
gbls WriteBarrierFunction
gbls WriteBarrierFunctionAvLocation
WriteBarrierFunction SETS "RhpAssignRef":cc:"$REFREG"
WriteBarrierFunctionAvLocation SETS "RhpAssignRefAvLocation":cc:"$REFREG"
;; Export the canonical write barrier under unqualified name as well
IF "$REFREG" == "R1"
ALTERNATE_ENTRY RhpAssignRef
ENDIF
EXPORT $WriteBarrierFunction
$WriteBarrierFunction
;; Use the GC write barrier as a convenient place to implement the managed memory model for ARM. The
;; intent is that writes to the target object ($REFREG) will be visible across all CPUs before the
;; write to the destination ($DESTREG). This covers most of the common scenarios where the programmer
;; might assume strongly ordered accessess, namely where the preceding writes are used to initialize
;; the object and the final write, made by this barrier in the instruction following the DMB,
;; publishes that object for other threads/cpus to see.
;;
;; Note that none of this is relevant for single cpu machines. We may choose to implement a
;; uniprocessor specific version of this barrier if uni-proc becomes a significant scenario again.
dmb
;; Write the reference into the location. Note that we rely on the fact that no GC can occur between
;; here and the card table update we may perform below.
ALTERNATE_ENTRY $WriteBarrierFunctionAvLocation
IF "$REFREG" == "R1"
ALTERNATE_ENTRY RhpAssignRefAVLocation
ENDIF
str $REFREG, [$DESTREG]
INSERT_UNCHECKED_WRITE_BARRIER_CORE $DESTREG, $REFREG
bx lr
MEND
;; One day we might have write barriers for all the possible argument registers but for now we have
;; just one write barrier that assumes the input register is R1.
DEFINE_CHECKED_WRITE_BARRIER R0, R1
DEFINE_UNCHECKED_WRITE_BARRIER R0, R1
;; Interlocked operation helpers where the location is an objectref, thus requiring a GC write barrier upon
;; successful updates.
;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular:
;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedLockCmpXchgAVLocation
;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address
;; Interlocked compare exchange on objectref.
;;
;; On entry:
;; r0: pointer to objectref
;; r1: exchange value
;; r2: comparand
;;
;; On exit:
;; r0: original value of objectref
;; r1,r2,r3,r12: trashed
;;
LEAF_ENTRY RhpCheckedLockCmpXchg
;; To implement our chosen memory model for ARM we insert a memory barrier at GC write barriers. This
;; barrier must occur before the object reference update, so we have to do it unconditionally even
;; though the update may fail below.
dmb
CX_Retry
;; Check location value is what we expect.
ALTERNATE_ENTRY RhpCheckedLockCmpXchgAVLocation
ldrex r3, [r0]
cmp r2, r3
bne CX_NoUpdate
;; Current value matches comparand, attempt to update with the new value.
strex r3, r1, [r0]
cmp r3, #0
bne CX_Retry ; Retry the operation if another write beat us there
;; We've successfully updated the value of the objectref so now we need a GC write barrier.
;; The following barrier code takes the destination in r0 and the value in r1 so the arguments are
;; already correctly set up.
INSERT_CHECKED_WRITE_BARRIER_CORE r0, r1
;; The original value was equal to the comparand which is still in r2 so we can return that.
mov r0, r2
bx lr
CX_NoUpdate
;; Location value didn't match comparand, return that value.
mov r0, r3
bx lr
LEAF_END RhpCheckedLockCmpXchg
;; Interlocked exchange on objectref.
;;
;; On entry:
;; r0: pointer to objectref
;; r1: exchange value
;;
;; On exit:
;; r0: original value of objectref
;; r1,r2,r3,r12: trashed
;;
;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular:
;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen within at RhpCheckedXchgAVLocation
;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address
LEAF_ENTRY RhpCheckedXchg
;; To implement our chosen memory model for ARM we insert a memory barrier at GC write barriers. This
;; barrier must occur before the object reference update.
dmb
X_Retry
ALTERNATE_ENTRY RhpCheckedXchgAVLocation
;; Read the original contents of the location.
ldrex r2, [r0]
;; Attempt to update with the new value.
strex r3, r1, [r0]
cmp r3, #0
bne X_Retry ; Retry the operation if another write beat us there
;; We've successfully updated the value of the objectref so now we need a GC write barrier.
;; The following barrier code takes the destination in r0 and the value in r1 so the arguments are
;; already correctly set up.
INSERT_CHECKED_WRITE_BARRIER_CORE r0, r1
;; The original value is currently in r2. We need to return it in r0.
mov r0, r2
bx lr
LEAF_END RhpCheckedXchg
end
@@ -125,20 +125,6 @@ EXTERN_C void __faststorefence();
#pragma intrinsic(__faststorefence)
#define PalMemoryBarrier() __faststorefence()
#elif defined(HOST_ARM)
EXTERN_C void __yield(void);
#pragma intrinsic(__yield)
EXTERN_C void __dmb(unsigned int _Type);
#pragma intrinsic(__dmb)
FORCEINLINE void PalYieldProcessor()
{
__dmb(_ARM_BARRIER_ISHST);
__yield();
}
#define PalMemoryBarrier() __dmb(_ARM_BARRIER_ISH)
#elif defined(HOST_ARM64)
EXTERN_C void __yield(void);