1 /*
2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 #include "precompiled.hpp"
26 #include "asm/macroAssembler.inline.hpp"
27 #include "gc/g1/g1BarrierSet.hpp"
28 #include "gc/g1/g1BarrierSetAssembler.hpp"
29 #include "gc/g1/g1BarrierSetRuntime.hpp"
30 #include "gc/g1/g1CardTable.hpp"
31 #include "gc/g1/g1ThreadLocalData.hpp"
32 #include "gc/g1/heapRegion.hpp"
33 #include "interpreter/interp_masm.hpp"
34 #include "runtime/sharedRuntime.hpp"
35 #include "utilities/macros.hpp"
36 #ifdef COMPILER1
37 #include "c1/c1_LIRAssembler.hpp"
38 #include "c1/c1_MacroAssembler.hpp"
39 #include "gc/g1/c1/g1BarrierSetC1.hpp"
40 #endif
41
42 #define __ masm->
43
gen_write_ref_array_pre_barrier(MacroAssembler * masm,DecoratorSet decorators,Register addr,Register count)44 void G1BarrierSetAssembler::gen_write_ref_array_pre_barrier(MacroAssembler* masm, DecoratorSet decorators,
45 Register addr, Register count) {
46 bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0;
47
48 if (!dest_uninitialized) {
49 Register thread = NOT_LP64(rax) LP64_ONLY(r15_thread);
50 #ifndef _LP64
51 __ push(thread);
52 __ get_thread(thread);
53 #endif
54
55 Label filtered;
56 Address in_progress(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()));
57 // Is marking active?
58 if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) {
59 __ cmpl(in_progress, 0);
60 } else {
61 assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption");
62 __ cmpb(in_progress, 0);
63 }
64
65 NOT_LP64(__ pop(thread);)
66
67 __ jcc(Assembler::equal, filtered);
68
69 __ pusha(); // push registers
70 #ifdef _LP64
71 if (count == c_rarg0) {
72 if (addr == c_rarg1) {
73 // exactly backwards!!
74 __ xchgptr(c_rarg1, c_rarg0);
75 } else {
76 __ movptr(c_rarg1, count);
77 __ movptr(c_rarg0, addr);
78 }
79 } else {
80 __ movptr(c_rarg0, addr);
81 __ movptr(c_rarg1, count);
82 }
83 if (UseCompressedOops) {
84 __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_array_pre_narrow_oop_entry), 2);
85 } else {
86 __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_array_pre_oop_entry), 2);
87 }
88 #else
89 __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_array_pre_oop_entry),
90 addr, count);
91 #endif
92 __ popa();
93
94 __ bind(filtered);
95 }
96 }
97
gen_write_ref_array_post_barrier(MacroAssembler * masm,DecoratorSet decorators,Register addr,Register count,Register tmp)98 void G1BarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators,
99 Register addr, Register count, Register tmp) {
100 __ pusha(); // push registers (overkill)
101 #ifdef _LP64
102 if (c_rarg0 == count) { // On win64 c_rarg0 == rcx
103 assert_different_registers(c_rarg1, addr);
104 __ mov(c_rarg1, count);
105 __ mov(c_rarg0, addr);
106 } else {
107 assert_different_registers(c_rarg0, count);
108 __ mov(c_rarg0, addr);
109 __ mov(c_rarg1, count);
110 }
111 __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_array_post_entry), 2);
112 #else
113 __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_array_post_entry),
114 addr, count);
115 #endif
116 __ popa();
117 }
118
load_at(MacroAssembler * masm,DecoratorSet decorators,BasicType type,Register dst,Address src,Register tmp1,Register tmp_thread)119 void G1BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
120 Register dst, Address src, Register tmp1, Register tmp_thread) {
121 bool on_oop = type == T_OBJECT || type == T_ARRAY;
122 bool on_weak = (decorators & ON_WEAK_OOP_REF) != 0;
123 bool on_phantom = (decorators & ON_PHANTOM_OOP_REF) != 0;
124 bool on_reference = on_weak || on_phantom;
125 ModRefBarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp_thread);
126 if (on_oop && on_reference) {
127 const Register thread = NOT_LP64(tmp_thread) LP64_ONLY(r15_thread);
128 NOT_LP64(__ get_thread(thread));
129
130 // Generate the G1 pre-barrier code to log the value of
131 // the referent field in an SATB buffer.
132 g1_write_barrier_pre(masm /* masm */,
133 noreg /* obj */,
134 dst /* pre_val */,
135 thread /* thread */,
136 tmp1 /* tmp */,
137 true /* tosca_live */,
138 true /* expand_call */);
139 }
140 }
141
g1_write_barrier_pre(MacroAssembler * masm,Register obj,Register pre_val,Register thread,Register tmp,bool tosca_live,bool expand_call)142 void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm,
143 Register obj,
144 Register pre_val,
145 Register thread,
146 Register tmp,
147 bool tosca_live,
148 bool expand_call) {
149 // If expand_call is true then we expand the call_VM_leaf macro
150 // directly to skip generating the check by
151 // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp.
152
153 #ifdef _LP64
154 assert(thread == r15_thread, "must be");
155 #endif // _LP64
156
157 Label done;
158 Label runtime;
159
160 assert(pre_val != noreg, "check this code");
161
162 if (obj != noreg) {
163 assert_different_registers(obj, pre_val, tmp);
164 assert(pre_val != rax, "check this code");
165 }
166
167 Address in_progress(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()));
168 Address index(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_index_offset()));
169 Address buffer(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset()));
170
171 // Is marking active?
172 if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) {
173 __ cmpl(in_progress, 0);
174 } else {
175 assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption");
176 __ cmpb(in_progress, 0);
177 }
178 __ jcc(Assembler::equal, done);
179
180 // Do we need to load the previous value?
181 if (obj != noreg) {
182 __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW);
183 }
184
185 // Is the previous value null?
186 __ cmpptr(pre_val, (int32_t) NULL_WORD);
187 __ jcc(Assembler::equal, done);
188
189 // Can we store original value in the thread's buffer?
190 // Is index == 0?
191 // (The index field is typed as size_t.)
192
193 __ movptr(tmp, index); // tmp := *index_adr
194 __ cmpptr(tmp, 0); // tmp == 0?
195 __ jcc(Assembler::equal, runtime); // If yes, goto runtime
196
197 __ subptr(tmp, wordSize); // tmp := tmp - wordSize
198 __ movptr(index, tmp); // *index_adr := tmp
199 __ addptr(tmp, buffer); // tmp := tmp + *buffer_adr
200
201 // Record the previous value
202 __ movptr(Address(tmp, 0), pre_val);
203 __ jmp(done);
204
205 __ bind(runtime);
206 // save the live input values
207 if(tosca_live) __ push(rax);
208
209 if (obj != noreg && obj != rax)
210 __ push(obj);
211
212 if (pre_val != rax)
213 __ push(pre_val);
214
215 // Calling the runtime using the regular call_VM_leaf mechanism generates
216 // code (generated by InterpreterMacroAssember::call_VM_leaf_base)
217 // that checks that the *(ebp+frame::interpreter_frame_last_sp) == NULL.
218 //
219 // If we care generating the pre-barrier without a frame (e.g. in the
220 // intrinsified Reference.get() routine) then ebp might be pointing to
221 // the caller frame and so this check will most likely fail at runtime.
222 //
223 // Expanding the call directly bypasses the generation of the check.
224 // So when we do not have have a full interpreter frame on the stack
225 // expand_call should be passed true.
226
227 NOT_LP64( __ push(thread); )
228
229 if (expand_call) {
230 LP64_ONLY( assert(pre_val != c_rarg1, "smashed arg"); )
231 #ifdef _LP64
232 if (c_rarg1 != thread) {
233 __ mov(c_rarg1, thread);
234 }
235 if (c_rarg0 != pre_val) {
236 __ mov(c_rarg0, pre_val);
237 }
238 #else
239 __ push(thread);
240 __ push(pre_val);
241 #endif
242 __ MacroAssembler::call_VM_leaf_base(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), 2);
243 } else {
244 __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), pre_val, thread);
245 }
246
247 NOT_LP64( __ pop(thread); )
248
249 // save the live input values
250 if (pre_val != rax)
251 __ pop(pre_val);
252
253 if (obj != noreg && obj != rax)
254 __ pop(obj);
255
256 if(tosca_live) __ pop(rax);
257
258 __ bind(done);
259 }
260
g1_write_barrier_post(MacroAssembler * masm,Register store_addr,Register new_val,Register thread,Register tmp,Register tmp2)261 void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm,
262 Register store_addr,
263 Register new_val,
264 Register thread,
265 Register tmp,
266 Register tmp2) {
267 #ifdef _LP64
268 assert(thread == r15_thread, "must be");
269 #endif // _LP64
270
271 Address queue_index(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_index_offset()));
272 Address buffer(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_buffer_offset()));
273
274 CardTableBarrierSet* ct =
275 barrier_set_cast<CardTableBarrierSet>(BarrierSet::barrier_set());
276 assert(sizeof(*ct->card_table()->byte_map_base()) == sizeof(jbyte), "adjust this code");
277
278 Label done;
279 Label runtime;
280
281 // Does store cross heap regions?
282
283 __ movptr(tmp, store_addr);
284 __ xorptr(tmp, new_val);
285 __ shrptr(tmp, HeapRegion::LogOfHRGrainBytes);
286 __ jcc(Assembler::equal, done);
287
288 // crosses regions, storing NULL?
289
290 __ cmpptr(new_val, (int32_t) NULL_WORD);
291 __ jcc(Assembler::equal, done);
292
293 // storing region crossing non-NULL, is card already dirty?
294
295 const Register card_addr = tmp;
296 const Register cardtable = tmp2;
297
298 __ movptr(card_addr, store_addr);
299 __ shrptr(card_addr, CardTable::card_shift);
300 // Do not use ExternalAddress to load 'byte_map_base', since 'byte_map_base' is NOT
301 // a valid address and therefore is not properly handled by the relocation code.
302 __ movptr(cardtable, (intptr_t)ct->card_table()->byte_map_base());
303 __ addptr(card_addr, cardtable);
304
305 __ cmpb(Address(card_addr, 0), (int)G1CardTable::g1_young_card_val());
306 __ jcc(Assembler::equal, done);
307
308 __ membar(Assembler::Membar_mask_bits(Assembler::StoreLoad));
309 __ cmpb(Address(card_addr, 0), (int)G1CardTable::dirty_card_val());
310 __ jcc(Assembler::equal, done);
311
312
313 // storing a region crossing, non-NULL oop, card is clean.
314 // dirty card and log.
315
316 __ movb(Address(card_addr, 0), (int)G1CardTable::dirty_card_val());
317
318 __ cmpl(queue_index, 0);
319 __ jcc(Assembler::equal, runtime);
320 __ subl(queue_index, wordSize);
321 __ movptr(tmp2, buffer);
322 #ifdef _LP64
323 __ movslq(rscratch1, queue_index);
324 __ addq(tmp2, rscratch1);
325 __ movq(Address(tmp2, 0), card_addr);
326 #else
327 __ addl(tmp2, queue_index);
328 __ movl(Address(tmp2, 0), card_addr);
329 #endif
330 __ jmp(done);
331
332 __ bind(runtime);
333 // save the live input values
334 __ push(store_addr);
335 __ push(new_val);
336 #ifdef _LP64
337 __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), card_addr, r15_thread);
338 #else
339 __ push(thread);
340 __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), card_addr, thread);
341 __ pop(thread);
342 #endif
343 __ pop(new_val);
344 __ pop(store_addr);
345
346 __ bind(done);
347 }
348
oop_store_at(MacroAssembler * masm,DecoratorSet decorators,BasicType type,Address dst,Register val,Register tmp1,Register tmp2)349 void G1BarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
350 Address dst, Register val, Register tmp1, Register tmp2) {
351 bool in_heap = (decorators & IN_HEAP) != 0;
352 bool as_normal = (decorators & AS_NORMAL) != 0;
353 assert((decorators & IS_DEST_UNINITIALIZED) == 0, "unsupported");
354
355 bool needs_pre_barrier = as_normal;
356 bool needs_post_barrier = val != noreg && in_heap;
357
358 Register tmp3 = LP64_ONLY(r8) NOT_LP64(rsi);
359 Register rthread = LP64_ONLY(r15_thread) NOT_LP64(rcx);
360 // flatten object address if needed
361 // We do it regardless of precise because we need the registers
362 if (dst.index() == noreg && dst.disp() == 0) {
363 if (dst.base() != tmp1) {
364 __ movptr(tmp1, dst.base());
365 }
366 } else {
367 __ lea(tmp1, dst);
368 }
369
370 #ifndef _LP64
371 InterpreterMacroAssembler *imasm = static_cast<InterpreterMacroAssembler*>(masm);
372 #endif
373
374 NOT_LP64(__ get_thread(rcx));
375 NOT_LP64(imasm->save_bcp());
376
377 if (needs_pre_barrier) {
378 g1_write_barrier_pre(masm /*masm*/,
379 tmp1 /* obj */,
380 tmp2 /* pre_val */,
381 rthread /* thread */,
382 tmp3 /* tmp */,
383 val != noreg /* tosca_live */,
384 false /* expand_call */);
385 }
386 if (val == noreg) {
387 BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg);
388 } else {
389 Register new_val = val;
390 if (needs_post_barrier) {
391 // G1 barrier needs uncompressed oop for region cross check.
392 if (UseCompressedOops) {
393 new_val = tmp2;
394 __ movptr(new_val, val);
395 }
396 }
397 BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg);
398 if (needs_post_barrier) {
399 g1_write_barrier_post(masm /*masm*/,
400 tmp1 /* store_adr */,
401 new_val /* new_val */,
402 rthread /* thread */,
403 tmp3 /* tmp */,
404 tmp2 /* tmp2 */);
405 }
406 }
407 NOT_LP64(imasm->restore_bcp());
408 }
409
410 #ifdef COMPILER1
411
412 #undef __
413 #define __ ce->masm()->
414
gen_pre_barrier_stub(LIR_Assembler * ce,G1PreBarrierStub * stub)415 void G1BarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub) {
416 G1BarrierSetC1* bs = (G1BarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
417 // At this point we know that marking is in progress.
418 // If do_load() is true then we have to emit the
419 // load of the previous value; otherwise it has already
420 // been loaded into _pre_val.
421
422 __ bind(*stub->entry());
423 assert(stub->pre_val()->is_register(), "Precondition.");
424
425 Register pre_val_reg = stub->pre_val()->as_register();
426
427 if (stub->do_load()) {
428 ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /*wide*/, false /*unaligned*/);
429 }
430
431 __ cmpptr(pre_val_reg, (int32_t)NULL_WORD);
432 __ jcc(Assembler::equal, *stub->continuation());
433 ce->store_parameter(stub->pre_val()->as_register(), 0);
434 __ call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin()));
435 __ jmp(*stub->continuation());
436
437 }
438
gen_post_barrier_stub(LIR_Assembler * ce,G1PostBarrierStub * stub)439 void G1BarrierSetAssembler::gen_post_barrier_stub(LIR_Assembler* ce, G1PostBarrierStub* stub) {
440 G1BarrierSetC1* bs = (G1BarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
441 __ bind(*stub->entry());
442 assert(stub->addr()->is_register(), "Precondition.");
443 assert(stub->new_val()->is_register(), "Precondition.");
444 Register new_val_reg = stub->new_val()->as_register();
445 __ cmpptr(new_val_reg, (int32_t) NULL_WORD);
446 __ jcc(Assembler::equal, *stub->continuation());
447 ce->store_parameter(stub->addr()->as_pointer_register(), 0);
448 __ call(RuntimeAddress(bs->post_barrier_c1_runtime_code_blob()->code_begin()));
449 __ jmp(*stub->continuation());
450 }
451
452 #undef __
453
454 #define __ sasm->
455
generate_c1_pre_barrier_runtime_stub(StubAssembler * sasm)456 void G1BarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) {
457 __ prologue("g1_pre_barrier", false);
458 // arg0 : previous value of memory
459
460 __ push(rax);
461 __ push(rdx);
462
463 const Register pre_val = rax;
464 const Register thread = NOT_LP64(rax) LP64_ONLY(r15_thread);
465 const Register tmp = rdx;
466
467 NOT_LP64(__ get_thread(thread);)
468
469 Address queue_active(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()));
470 Address queue_index(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_index_offset()));
471 Address buffer(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset()));
472
473 Label done;
474 Label runtime;
475
476 // Is marking still active?
477 if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) {
478 __ cmpl(queue_active, 0);
479 } else {
480 assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption");
481 __ cmpb(queue_active, 0);
482 }
483 __ jcc(Assembler::equal, done);
484
485 // Can we store original value in the thread's buffer?
486
487 __ movptr(tmp, queue_index);
488 __ testptr(tmp, tmp);
489 __ jcc(Assembler::zero, runtime);
490 __ subptr(tmp, wordSize);
491 __ movptr(queue_index, tmp);
492 __ addptr(tmp, buffer);
493
494 // prev_val (rax)
495 __ load_parameter(0, pre_val);
496 __ movptr(Address(tmp, 0), pre_val);
497 __ jmp(done);
498
499 __ bind(runtime);
500
501 __ save_live_registers_no_oop_map(true);
502
503 // load the pre-value
504 __ load_parameter(0, rcx);
505 __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), rcx, thread);
506
507 __ restore_live_registers(true);
508
509 __ bind(done);
510
511 __ pop(rdx);
512 __ pop(rax);
513
514 __ epilogue();
515 }
516
generate_c1_post_barrier_runtime_stub(StubAssembler * sasm)517 void G1BarrierSetAssembler::generate_c1_post_barrier_runtime_stub(StubAssembler* sasm) {
518 __ prologue("g1_post_barrier", false);
519
520 // arg0: store_address
521 Address store_addr(rbp, 2*BytesPerWord);
522
523 CardTableBarrierSet* ct =
524 barrier_set_cast<CardTableBarrierSet>(BarrierSet::barrier_set());
525 assert(sizeof(*ct->card_table()->byte_map_base()) == sizeof(jbyte), "adjust this code");
526
527 Label done;
528 Label enqueued;
529 Label runtime;
530
531 // At this point we know new_value is non-NULL and the new_value crosses regions.
532 // Must check to see if card is already dirty
533
534 const Register thread = NOT_LP64(rax) LP64_ONLY(r15_thread);
535
536 Address queue_index(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_index_offset()));
537 Address buffer(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_buffer_offset()));
538
539 __ push(rax);
540 __ push(rcx);
541
542 const Register cardtable = rax;
543 const Register card_addr = rcx;
544
545 __ load_parameter(0, card_addr);
546 __ shrptr(card_addr, CardTable::card_shift);
547 // Do not use ExternalAddress to load 'byte_map_base', since 'byte_map_base' is NOT
548 // a valid address and therefore is not properly handled by the relocation code.
549 __ movptr(cardtable, (intptr_t)ct->card_table()->byte_map_base());
550 __ addptr(card_addr, cardtable);
551
552 NOT_LP64(__ get_thread(thread);)
553
554 __ cmpb(Address(card_addr, 0), (int)G1CardTable::g1_young_card_val());
555 __ jcc(Assembler::equal, done);
556
557 __ membar(Assembler::Membar_mask_bits(Assembler::StoreLoad));
558 __ cmpb(Address(card_addr, 0), (int)CardTable::dirty_card_val());
559 __ jcc(Assembler::equal, done);
560
561 // storing region crossing non-NULL, card is clean.
562 // dirty card and log.
563
564 __ movb(Address(card_addr, 0), (int)CardTable::dirty_card_val());
565
566 const Register tmp = rdx;
567 __ push(rdx);
568
569 __ movptr(tmp, queue_index);
570 __ testptr(tmp, tmp);
571 __ jcc(Assembler::zero, runtime);
572 __ subptr(tmp, wordSize);
573 __ movptr(queue_index, tmp);
574 __ addptr(tmp, buffer);
575 __ movptr(Address(tmp, 0), card_addr);
576 __ jmp(enqueued);
577
578 __ bind(runtime);
579
580 __ save_live_registers_no_oop_map(true);
581
582 __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), card_addr, thread);
583
584 __ restore_live_registers(true);
585
586 __ bind(enqueued);
587 __ pop(rdx);
588
589 __ bind(done);
590 __ pop(rcx);
591 __ pop(rax);
592
593 __ epilogue();
594 }
595
596 #undef __
597
598 #endif // COMPILER1
599