Merge pull request #2884 from wtgodbe/pathFix
[platform/upstream/coreclr.git] / src / vm / amd64 / jithelpers_fast.S
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 .intel_syntax noprefix
6 #include "unixasmmacros.inc"
7
8 // Mark start of the code region that we patch at runtime
9 LEAF_ENTRY JIT_PatchedCodeStart, _TEXT
10         ret
11 LEAF_END JIT_PatchedCodeStart, _TEXT
12
13 // This is used by the mechanism to hold either the JIT_WriteBarrier_PreGrow 
14 // or JIT_WriteBarrier_PostGrow code (depending on the state of the GC). It _WILL_
15 // change at runtime as the GC changes. Initially it should simply be a copy of the 
16 // larger of the two functions (JIT_WriteBarrier_PostGrow) to ensure we have created
17 // enough space to copy that code in.
18 .balign 16
19 LEAF_ENTRY JIT_WriteBarrier, _TEXT
20 #ifdef _DEBUG
21         // In debug builds, this just contains jump to the debug version of the write barrier by default
22         jmp C_FUNC(JIT_WriteBarrier_Debug)
23 #endif
24
25         // Do the move into the GC .  It is correct to take an AV here, the EH code
26         // figures out that this came from a WriteBarrier and correctly maps it back
27         // to the managed method which called the WriteBarrier (see setup in
28         // InitializeExceptionHandling, vm\exceptionhandling.cpp).
29         mov     [rdi], rsi
30
31         NOP_3_BYTE // padding for alignment of constant
32
33         // Can't compare a 64 bit immediate, so we have to move them into a
34         // register.  Values of these immediates will be patched at runtime.
35         // By using two registers we can pipeline better.  Should we decide to use
36         // a special non-volatile calling convention, this should be changed to
37         // just one.
38
39         movabs  rax, 0xF0F0F0F0F0F0F0F0
40
41         // Check the lower and upper ephemeral region bounds
42         cmp     rsi, rax
43         // jb      Exit
44         .byte 0x72, 0x36
45
46         nop // padding for alignment of constant
47
48         movabs  r8, 0xF0F0F0F0F0F0F0F0
49
50         cmp     rsi, r8
51         // jae     Exit
52         .byte 0x73, 0x26
53
54         nop // padding for alignment of constant
55
56         movabs  rax, 0xF0F0F0F0F0F0F0F0
57
58         // Touch the card table entry, if not already dirty.
59         shr     rdi, 0Bh
60         cmp     byte ptr [rdi + rax], 0FFh
61         // jne     UpdateCardTable
62         .byte 0x75, 0x02
63         REPRET
64
65     UpdateCardTable:
66         mov     byte ptr [rdi + rax], 0FFh
67         ret
68
69     .balign 16
70     Exit:
71         REPRET
72     // make sure this guy is bigger than any of the other guys
73     .balign 16
74         nop
75 LEAF_END_MARKED JIT_WriteBarrier, _TEXT
76
77 // Mark start of the code region that we patch at runtime
78 LEAF_ENTRY JIT_PatchedCodeLast, _TEXT
79         ret
80 LEAF_END JIT_PatchedCodeLast, _TEXT
81
82 // There is an even more optimized version of these helpers possible which takes
83 // advantage of knowledge of which way the ephemeral heap is growing to only do 1/2
84 // that check (this is more significant in the JIT_WriteBarrier case).
85 //
86 // Additionally we can look into providing helpers which will take the src/dest from
87 // specific registers (like x86) which _could_ (??) make for easier register allocation
88 // for the JIT64, however it might lead to having to have some nasty code that treats
89 // these guys really special like... :(.
90 //
91 // Version that does the move, checks whether or not it's in the GC and whether or not
92 // it needs to have it's card updated
93 //
94 // void JIT_CheckedWriteBarrier(Object** dst, Object* src)
95 LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT
96
97         // When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference
98         // but if it isn't then it will just return.
99         //
100         // See if this is in GCHeap
101         PREPARE_EXTERNAL_VAR g_lowest_address, rax
102         cmp     rdi, [rax]
103         // jb      NotInHeap
104         .byte 0x72, 0x0e
105         PREPARE_EXTERNAL_VAR g_highest_address, rax
106         cmp     rdi, [rax]
107         // jnb     NotInHeap
108         .byte 0x73, 0x02
109         
110         // call C_FUNC(JIT_WriteBarrier)
111         .byte 0xeb, 0x84
112
113     NotInHeap:
114         // See comment above about possible AV
115         mov     [rdi], rsi
116         ret
117 LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT
118
119 // JIT_ByRefWriteBarrier has weird symantics, see usage in StubLinkerX86.cpp
120 //
121 // Entry:
122 //   RDI - address of ref-field (assigned to)
123 //   RSI - address of the data  (source)
124 //
125 //   Note: RyuJIT assumes that all volatile registers can be trashed by
126 //   the CORINFO_HELP_ASSIGN_BYREF helper (i.e. JIT_ByRefWriteBarrier).
127 //   The precise set is defined by RBM_CALLEE_TRASH.
128 //
129 //   RCX is trashed
130 //   RAX is trashed
131 //   R10 is trashed on Debug build
132 //   R11 is trashed on Debug build
133 // Exit:
134 //   RDI, RSI are incremented by SIZEOF(LPVOID)
135 LEAF_ENTRY JIT_ByRefWriteBarrier, _TEXT
136         mov     rcx, [rsi]
137
138 // If !WRITE_BARRIER_CHECK do the write first, otherwise we might have to do some ShadowGC stuff
139 #ifndef WRITE_BARRIER_CHECK
140         // rcx is [rsi]
141         mov     [rdi], rcx
142 #endif
143
144         // When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference
145         // but if it isn't then it will just return.
146         //
147         // See if this is in GCHeap
148         PREPARE_EXTERNAL_VAR g_lowest_address, rax
149         cmp     rdi, [rax]
150         jb      NotInHeap_ByRefWriteBarrier
151         PREPARE_EXTERNAL_VAR g_highest_address, rax
152         cmp     rdi, [rax]
153         jnb     NotInHeap_ByRefWriteBarrier
154
155 #ifdef WRITE_BARRIER_CHECK
156         // **ALSO update the shadow GC heap if that is enabled**
157         // Do not perform the work if g_GCShadow is 0
158         PREPARE_EXTERNAL_VAR g_GCShadow, rax
159         cmp     qword ptr [rax], 0
160         je      NoShadow_ByRefWriteBarrier
161
162         // If we end up outside of the heap don't corrupt random memory
163         mov     r10, rdi
164         PREPARE_EXTERNAL_VAR g_lowest_address, rax
165         sub     r10, [rax]
166         jb      NoShadow_ByRefWriteBarrier
167
168         // Check that our adjusted destination is somewhere in the shadow gc
169         PREPARE_EXTERNAL_VAR g_GCShadow, rax
170         add     r10, [rax]
171         PREPARE_EXTERNAL_VAR g_GCShadowEnd, rax
172         cmp     r10, [rax]
173         ja      NoShadow_ByRefWriteBarrier
174
175         // Write ref into real GC
176         mov     [rdi], rcx
177         // Write ref into shadow GC
178         mov     [r10], rcx
179
180         // Ensure that the write to the shadow heap occurs before the read from
181         // the GC heap so that race conditions are caught by INVALIDGCVALUE
182         mfence
183
184         // Check that GC/ShadowGC values match
185         mov     r11, [rdi]
186         mov     rax, [r10]
187         cmp     rax, r11
188         je      DoneShadow_ByRefWriteBarrier
189         mov     r11, INVALIDGCVALUE
190         mov     [r10], r11
191
192         jmp     DoneShadow_ByRefWriteBarrier
193
194     // If we don't have a shadow GC we won't have done the write yet
195     NoShadow_ByRefWriteBarrier:
196         mov     [rdi], rcx
197
198     // If we had a shadow GC then we already wrote to the real GC at the same time
199     // as the shadow GC so we want to jump over the real write immediately above.
200     // Additionally we know for sure that we are inside the heap and therefore don't
201     // need to replicate the above checks.
202     DoneShadow_ByRefWriteBarrier:
203 #endif
204
205         // See if we can just quick out
206         PREPARE_EXTERNAL_VAR g_ephemeral_low, rax
207         cmp     rcx, [rax]
208         jb      Exit_ByRefWriteBarrier
209         PREPARE_EXTERNAL_VAR g_ephemeral_high, rax
210         cmp     rcx, [rax]
211         jnb     Exit_ByRefWriteBarrier
212
213         // move current rdi value into rcx and then increment the pointers
214         mov     rcx, rdi
215         add     rsi, 8h
216         add     rdi, 8h
217
218         // Check if we need to update the card table
219         // Calc pCardByte
220         shr     rcx, 0Bh
221         PREPARE_EXTERNAL_VAR g_card_table, rax
222         add     rcx, [rax]
223
224         // Check if this card is dirty
225         cmp     byte ptr [rcx], 0FFh
226         jne     UpdateCardTable_ByRefWriteBarrier
227         REPRET
228
229     UpdateCardTable_ByRefWriteBarrier:
230         mov     byte ptr [rcx], 0FFh
231         ret
232
233     .balign 16
234     NotInHeap_ByRefWriteBarrier:
235 // If WRITE_BARRIER_CHECK then we won't have already done the mov and should do it here
236 // If !WRITE_BARRIER_CHECK we want _NotInHeap and _Leave to be the same and have both
237 // 16 byte aligned.
238 #ifdef WRITE_BARRIER_CHECK
239         // rcx is [rsi]
240         mov     [rdi], rcx
241 #endif
242     Exit_ByRefWriteBarrier:
243         // Increment the pointers before leaving
244         add     rdi, 8h
245         add     rsi, 8h
246         ret
247 LEAF_END JIT_ByRefWriteBarrier, _TEXT