1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
.syntax unified
.thumb
#include <AsmOffsets.inc> // generated by the build from AsmOffsets.cpp
#include <unixasmmacros.inc>
#ifdef FEATURE_DYNAMIC_CODE
#ifdef _DEBUG
#define TRASH_SAVED_ARGUMENT_REGISTERS
#endif
#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)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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, _TEXT, NoHandler
// 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.
ldr r12, [sp, #-4] // Capture first argument from red zone into r12
PROLOG_PUSH "{r3}" // Push r3
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, =C_FUNC(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, =C_FUNC(RhpIntegerTrashValues)
ldr r2, [r3, #(2 * 4)]
ldr r3, [r3, #(3 * 4)]
#endif // TRASH_SAVED_ARGUMENT_REGISTERS
// Make the ReturnFromUniversalTransition alternate entry 4 byte aligned
.balign 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, _TEXT
.endm
// 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
#endif // FEATURE_DYNAMIC_CODE
|