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