2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
7 #include "unixasmmacros.inc"
9 // Mark start of the code region that we patch at runtime
10 LEAF_ENTRY JIT_PatchedCodeStart, _TEXT
12 LEAF_END JIT_PatchedCodeStart, _TEXT
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.
20 LEAF_ENTRY JIT_WriteBarrier, _TEXT
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)
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).
32 NOP_3_BYTE // padding for alignment of constant
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
40 movabs rax, 0xF0F0F0F0F0F0F0F0
42 // Check the lower and upper ephemeral region bounds
47 nop // padding for alignment of constant
49 movabs r8, 0xF0F0F0F0F0F0F0F0
55 nop // padding for alignment of constant
57 movabs rax, 0xF0F0F0F0F0F0F0F0
59 // Touch the card table entry, if not already dirty.
61 cmp byte ptr [rdi + rax], 0FFh
62 // jne UpdateCardTable
67 mov byte ptr [rdi + rax], 0FFh
73 // make sure this guy is bigger than any of the other guys
76 LEAF_END_MARKED JIT_WriteBarrier, _TEXT
78 // Mark start of the code region that we patch at runtime
79 LEAF_ENTRY JIT_PatchedCodeLast, _TEXT
81 LEAF_END JIT_PatchedCodeLast, _TEXT
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).
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... :(.
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
95 // void JIT_CheckedWriteBarrier(Object** dst, Object* src)
96 LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT
98 // When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference
99 // but if it isn't then it will just return.
101 // See if this is in GCHeap
102 PREPARE_EXTERNAL_VAR g_lowest_address, rax
106 PREPARE_EXTERNAL_VAR g_highest_address, rax
111 // call C_FUNC(JIT_WriteBarrier)
115 // See comment above about possible AV
118 LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT
120 // JIT_ByRefWriteBarrier has weird symantics, see usage in StubLinkerX86.cpp
123 // RDI - address of ref-field (assigned to)
124 // RSI - address of the data (source)
125 // RCX can be trashed
127 // RDI, RSI are incremented by SIZEOF(LPVOID)
128 LEAF_ENTRY JIT_ByRefWriteBarrier, _TEXT
132 // If !WRITE_BARRIER_CHECK do the write first, otherwise we might have to do some ShadowGC stuff
133 #ifndef WRITE_BARRIER_CHECK
138 // When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference
139 // but if it isn't then it will just return.
141 // See if this is in GCHeap
142 PREPARE_EXTERNAL_VAR g_lowest_address, rax
144 jb NotInHeap_ByRefWriteBarrier
145 PREPARE_EXTERNAL_VAR g_highest_address, rax
147 jnb NotInHeap_ByRefWriteBarrier
149 #ifdef WRITE_BARRIER_CHECK
150 // we can only trash rcx in this function so in _DEBUG we need to save
151 // some scratch registers.
155 // **ALSO update the shadow GC heap if that is enabled**
156 // Do not perform the work if g_GCShadow is 0
157 PREPARE_EXTERNAL_VAR g_GCShadow, rax
158 cmp qword ptr [rax], 0
159 je NoShadow_ByRefWriteBarrier
161 // If we end up outside of the heap don't corrupt random memory
163 PREPARE_EXTERNAL_VAR g_lowest_address, rax
165 jb NoShadow_ByRefWriteBarrier
167 // Check that our adjusted destination is somewhere in the shadow gc
168 PREPARE_EXTERNAL_VAR g_GCShadow, rax
170 PREPARE_EXTERNAL_VAR g_GCShadowEnd, rax
172 ja NoShadow_ByRefWriteBarrier
174 // Write ref into real GC
176 // Write ref into shadow GC
179 // Ensure that the write to the shadow heap occurs before the read from
180 // the GC heap so that race conditions are caught by INVALIDGCVALUE
183 // Check that GC/ShadowGC values match
187 je DoneShadow_ByRefWriteBarrier
188 mov r11, INVALIDGCVALUE
191 jmp DoneShadow_ByRefWriteBarrier
193 // If we don't have a shadow GC we won't have done the write yet
194 NoShadow_ByRefWriteBarrier:
197 // If we had a shadow GC then we already wrote to the real GC at the same time
198 // as the shadow GC so we want to jump over the real write immediately above.
199 // Additionally we know for sure that we are inside the heap and therefore don't
200 // need to replicate the above checks.
201 DoneShadow_ByRefWriteBarrier:
206 // See if we can just quick out
207 PREPARE_EXTERNAL_VAR g_ephemeral_low, rax
209 jb Exit_ByRefWriteBarrier
210 PREPARE_EXTERNAL_VAR g_ephemeral_high, rax
212 jnb Exit_ByRefWriteBarrier
214 // move current rdi value into rcx and then increment the pointers
219 // Check if we need to update the card table
222 PREPARE_EXTERNAL_VAR g_card_table, rax
227 // Check if this card is dirty
228 cmp byte ptr [rcx], 0FFh
229 jne UpdateCardTable_ByRefWriteBarrier
232 UpdateCardTable_ByRefWriteBarrier:
233 mov byte ptr [rcx], 0FFh
237 NotInHeap_ByRefWriteBarrier:
238 // If WRITE_BARRIER_CHECK then we won't have already done the mov and should do it here
239 // If !WRITE_BARRIER_CHECK we want _NotInHeap and _Leave to be the same and have both
241 #ifdef WRITE_BARRIER_CHECK
245 Exit_ByRefWriteBarrier:
246 // Increment the pointers before leaving
251 LEAF_END JIT_ByRefWriteBarrier, _TEXT