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