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.
6 #include "unixasmmacros.inc"
8 // Mark start of the code region that we patch at runtime
9 LEAF_ENTRY JIT_PatchedCodeStart, _TEXT
11 LEAF_END JIT_PatchedCodeStart, _TEXT
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.
19 LEAF_ENTRY JIT_WriteBarrier, _TEXT
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)
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).
31 NOP_3_BYTE // padding for alignment of constant
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
39 movabs rax, 0xF0F0F0F0F0F0F0F0
41 // Check the lower and upper ephemeral region bounds
46 nop // padding for alignment of constant
48 movabs r8, 0xF0F0F0F0F0F0F0F0
54 nop // padding for alignment of constant
56 movabs rax, 0xF0F0F0F0F0F0F0F0
58 // Touch the card table entry, if not already dirty.
60 cmp byte ptr [rdi + rax], 0FFh
61 // jne UpdateCardTable
66 mov byte ptr [rdi + rax], 0FFh
72 // make sure this guy is bigger than any of the other guys
75 LEAF_END_MARKED JIT_WriteBarrier, _TEXT
77 // Mark start of the code region that we patch at runtime
78 LEAF_ENTRY JIT_PatchedCodeLast, _TEXT
80 LEAF_END JIT_PatchedCodeLast, _TEXT
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).
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... :(.
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
94 // void JIT_CheckedWriteBarrier(Object** dst, Object* src)
95 LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT
97 // When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference
98 // but if it isn't then it will just return.
100 // See if this is in GCHeap
101 PREPARE_EXTERNAL_VAR g_lowest_address, rax
105 PREPARE_EXTERNAL_VAR g_highest_address, rax
110 // call C_FUNC(JIT_WriteBarrier)
114 // See comment above about possible AV
117 LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT
119 // JIT_ByRefWriteBarrier has weird symantics, see usage in StubLinkerX86.cpp
122 // RDI - address of ref-field (assigned to)
123 // RSI - address of the data (source)
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.
131 // R10 is trashed on Debug build
132 // R11 is trashed on Debug build
134 // RDI, RSI are incremented by SIZEOF(LPVOID)
135 LEAF_ENTRY JIT_ByRefWriteBarrier, _TEXT
138 // If !WRITE_BARRIER_CHECK do the write first, otherwise we might have to do some ShadowGC stuff
139 #ifndef WRITE_BARRIER_CHECK
144 // When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference
145 // but if it isn't then it will just return.
147 // See if this is in GCHeap
148 PREPARE_EXTERNAL_VAR g_lowest_address, rax
150 jb NotInHeap_ByRefWriteBarrier
151 PREPARE_EXTERNAL_VAR g_highest_address, rax
153 jnb NotInHeap_ByRefWriteBarrier
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
162 // If we end up outside of the heap don't corrupt random memory
164 PREPARE_EXTERNAL_VAR g_lowest_address, rax
166 jb NoShadow_ByRefWriteBarrier
168 // Check that our adjusted destination is somewhere in the shadow gc
169 PREPARE_EXTERNAL_VAR g_GCShadow, rax
171 PREPARE_EXTERNAL_VAR g_GCShadowEnd, rax
173 ja NoShadow_ByRefWriteBarrier
175 // Write ref into real GC
177 // Write ref into shadow GC
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
184 // Check that GC/ShadowGC values match
188 je DoneShadow_ByRefWriteBarrier
189 mov r11, INVALIDGCVALUE
192 jmp DoneShadow_ByRefWriteBarrier
194 // If we don't have a shadow GC we won't have done the write yet
195 NoShadow_ByRefWriteBarrier:
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:
205 // See if we can just quick out
206 PREPARE_EXTERNAL_VAR g_ephemeral_low, rax
208 jb Exit_ByRefWriteBarrier
209 PREPARE_EXTERNAL_VAR g_ephemeral_high, rax
211 jnb Exit_ByRefWriteBarrier
213 // move current rdi value into rcx and then increment the pointers
218 // Check if we need to update the card table
221 PREPARE_EXTERNAL_VAR g_card_table, rax
224 // Check if this card is dirty
225 cmp byte ptr [rcx], 0FFh
226 jne UpdateCardTable_ByRefWriteBarrier
229 UpdateCardTable_ByRefWriteBarrier:
230 mov byte ptr [rcx], 0FFh
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
238 #ifdef WRITE_BARRIER_CHECK
242 Exit_ByRefWriteBarrier:
243 // Increment the pointers before leaving
247 LEAF_END JIT_ByRefWriteBarrier, _TEXT