2 * Emit memory access for the front-end.
7 #include <mono/utils/mono-compiler.h>
11 #include <mono/metadata/gc-internals.h>
12 #include <mono/utils/mono-memory-model.h>
16 #include "jit-icalls.h"
18 #define MAX_INLINE_COPIES 10
21 mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align)
25 /*FIXME arbitrary hack to avoid unbound code expansion.*/
26 g_assert (size < 10000);
30 if ((size <= SIZEOF_REGISTER) && (size <= align)) {
33 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, destreg, offset, val);
36 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI2_MEMBASE_IMM, destreg, offset, val);
39 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI4_MEMBASE_IMM, destreg, offset, val);
41 #if SIZEOF_REGISTER == 8
43 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI8_MEMBASE_IMM, destreg, offset, val);
49 val_reg = alloc_preg (cfg);
51 if (SIZEOF_REGISTER == 8)
52 MONO_EMIT_NEW_I8CONST (cfg, val_reg, val);
54 MONO_EMIT_NEW_ICONST (cfg, val_reg, val);
56 if (align < SIZEOF_VOID_P) {
61 if (SIZEOF_VOID_P == 8 && align % 8 == 4)
65 //Unaligned offsets don't naturaly happen in the runtime, so it's ok to be conservative in how we copy
66 //We assume that input src and dest are be aligned to `align` so offset just worsen it
67 int offsets_mask = offset & 0x7; //we only care about the misalignment part
69 if (offsets_mask % 2 == 1)
71 if (offsets_mask % 4 == 2)
73 if (SIZEOF_VOID_P == 8 && offsets_mask % 8 == 4)
77 if (SIZEOF_REGISTER == 8) {
79 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, offset, val_reg);
87 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
95 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, val_reg);
102 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
109 mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align)
113 /*FIXME arbitrary hack to avoid unbound code expansion.*/
114 g_assert (size < 10000);
115 g_assert (align > 0);
117 if (align < SIZEOF_VOID_P) {
125 //Unaligned offsets don't naturaly happen in the runtime, so it's ok to be conservative in how we copy
126 //We assume that input src and dest are be aligned to `align` so offset just worsen it
127 int offsets_mask = (doffset | soffset) & 0x7; //we only care about the misalignment part
129 if (offsets_mask % 2 == 1)
131 if (offsets_mask % 4 == 2)
133 if (SIZEOF_VOID_P == 8 && offsets_mask % 8 == 4)
138 if (SIZEOF_REGISTER == 8) {
140 cur_reg = alloc_preg (cfg);
141 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI8_MEMBASE, cur_reg, srcreg, soffset);
142 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, doffset, cur_reg);
151 cur_reg = alloc_preg (cfg);
152 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, cur_reg, srcreg, soffset);
153 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, doffset, cur_reg);
161 cur_reg = alloc_preg (cfg);
162 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, cur_reg, srcreg, soffset);
163 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, doffset, cur_reg);
171 cur_reg = alloc_preg (cfg);
172 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
173 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
181 mini_emit_memcpy_internal (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoInst *size_ins, int size, int align)
183 /* FIXME: Optimize the case when src/dest is OP_LDADDR */
185 /* We can't do copies at a smaller granule than the provided alignment */
186 if (size_ins || (size / align > MAX_INLINE_COPIES) || !(cfg->opt & MONO_OPT_INTRINS)) {
192 EMIT_NEW_ICONST (cfg, size_ins, size);
193 iargs [2] = size_ins;
194 mono_emit_method_call (cfg, mini_get_memcpy_method (), iargs, NULL);
196 mini_emit_memcpy (cfg, dest->dreg, 0, src->dreg, 0, size, align);
201 mini_emit_memset_internal (MonoCompile *cfg, MonoInst *dest, MonoInst *value_ins, int value, MonoInst *size_ins, int size, int align)
203 /* FIXME: Optimize the case when dest is OP_LDADDR */
205 /* We can't do copies at a smaller granule than the provided alignment */
206 if (value_ins || size_ins || value != 0 || (size / align > MAX_INLINE_COPIES) || !(cfg->opt & MONO_OPT_INTRINS)) {
211 EMIT_NEW_ICONST (cfg, value_ins, value);
212 iargs [1] = value_ins;
215 EMIT_NEW_ICONST (cfg, size_ins, size);
216 iargs [2] = size_ins;
218 mono_emit_method_call (cfg, mini_get_memset_method (), iargs, NULL);
220 mini_emit_memset (cfg, dest->dreg, 0, size, value, align);
225 mini_emit_memcpy_const_size (MonoCompile *cfg, MonoInst *dest, MonoInst *src, int size, int align)
227 mini_emit_memcpy_internal (cfg, dest, src, NULL, size, align);
231 mini_emit_memset_const_size (MonoCompile *cfg, MonoInst *dest, int value, int size, int align)
233 mini_emit_memset_internal (cfg, dest, NULL, value, NULL, size, align);
238 create_write_barrier_bitmap (MonoCompile *cfg, MonoClass *klass, unsigned *wb_bitmap, int offset)
240 MonoClassField *field;
241 gpointer iter = NULL;
243 while ((field = mono_class_get_fields (klass, &iter))) {
246 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
248 foffset = klass->valuetype ? field->offset - sizeof (MonoObject): field->offset;
249 if (mini_type_is_reference (mono_field_get_type (field))) {
250 g_assert ((foffset % SIZEOF_VOID_P) == 0);
251 *wb_bitmap |= 1 << ((offset + foffset) / SIZEOF_VOID_P);
253 MonoClass *field_class = mono_class_from_mono_type (field->type);
254 if (field_class->has_references)
255 create_write_barrier_bitmap (cfg, field_class, wb_bitmap, offset + foffset);
261 mini_emit_wb_aware_memcpy (MonoCompile *cfg, MonoClass *klass, MonoInst *iargs[4], int size, int align)
263 int dest_ptr_reg, tmp_reg, destreg, srcreg, offset;
264 unsigned need_wb = 0;
269 /*types with references can't have alignment smaller than sizeof(void*) */
270 if (align < SIZEOF_VOID_P)
273 if (size > 5 * SIZEOF_VOID_P)
276 create_write_barrier_bitmap (cfg, klass, &need_wb, 0);
278 destreg = iargs [0]->dreg;
279 srcreg = iargs [1]->dreg;
282 dest_ptr_reg = alloc_preg (cfg);
283 tmp_reg = alloc_preg (cfg);
286 EMIT_NEW_UNALU (cfg, iargs [0], OP_MOVE, dest_ptr_reg, destreg);
288 while (size >= SIZEOF_VOID_P) {
290 MONO_INST_NEW (cfg, load_inst, OP_LOAD_MEMBASE);
291 load_inst->dreg = tmp_reg;
292 load_inst->inst_basereg = srcreg;
293 load_inst->inst_offset = offset;
294 MONO_ADD_INS (cfg->cbb, load_inst);
296 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, dest_ptr_reg, 0, tmp_reg);
299 mini_emit_write_barrier (cfg, iargs [0], load_inst);
301 offset += SIZEOF_VOID_P;
302 size -= SIZEOF_VOID_P;
305 /*tmp += sizeof (void*)*/
306 if (size >= SIZEOF_VOID_P) {
307 NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dest_ptr_reg, dest_ptr_reg, SIZEOF_VOID_P);
308 MONO_ADD_INS (cfg->cbb, iargs [0]);
312 /* Those cannot be references since size < sizeof (void*) */
314 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, tmp_reg, srcreg, offset);
315 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, tmp_reg);
321 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, tmp_reg, srcreg, offset);
322 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, tmp_reg);
328 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, tmp_reg, srcreg, offset);
329 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, tmp_reg);
338 mini_emit_memory_copy_internal (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native)
343 MonoInst *size_ins = NULL;
344 MonoInst *memcpy_ins = NULL;
348 Fun fact about @native. It's false that @klass will have no ref when @native is true.
349 This happens in pinvoke2. What goes is that marshal.c uses CEE_MONO_LDOBJNATIVE and pass klass.
350 The actual stuff being copied will have no refs, but @klass might.
351 This means we can't assert !(klass->has_references && native).
355 klass = mono_class_from_mono_type (mini_get_underlying_type (&klass->byval_arg));
358 * This check breaks with spilled vars... need to handle it during verification anyway.
359 * g_assert (klass && klass == src->klass && klass == dest->klass);
362 if (mini_is_gsharedvt_klass (klass)) {
364 size_ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
365 memcpy_ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_MEMCPY);
369 size = mono_class_native_size (klass, &align);
371 size = mono_class_value_size (klass, &align);
374 align = SIZEOF_VOID_P;
376 if (mini_type_is_reference (&klass->byval_arg)) {
377 MonoInst *store, *load;
378 int dreg = alloc_ireg_ref (cfg);
380 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, dreg, src->dreg, 0);
381 MONO_ADD_INS (cfg->cbb, load);
383 NEW_STORE_MEMBASE (cfg, store, OP_STORE_MEMBASE_REG, dest->dreg, 0, dreg);
384 MONO_ADD_INS (cfg->cbb, store);
386 mini_emit_write_barrier (cfg, dest, src);
387 } else if (cfg->gen_write_barriers && (klass->has_references || size_ins) && !native) { /* if native is true there should be no references in the struct */
388 /* Avoid barriers when storing to the stack */
389 if (!((dest->opcode == OP_ADD_IMM && dest->sreg1 == cfg->frame_reg) ||
390 (dest->opcode == OP_LDADDR))) {
396 context_used = mini_class_check_context_used (cfg, klass);
398 /* It's ok to intrinsify under gsharing since shared code types are layout stable. */
399 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && mini_emit_wb_aware_memcpy (cfg, klass, iargs, size, align)) {
400 } else if (size_ins || align < SIZEOF_VOID_P) {
402 iargs [2] = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
404 iargs [2] = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
405 if (!cfg->compile_aot)
406 mono_class_compute_gc_descriptor (klass);
409 mono_emit_jit_icall (cfg, mono_gsharedvt_value_copy, iargs);
411 mono_emit_jit_icall (cfg, mono_value_copy, iargs);
413 /* We don't unroll more than 5 stores to avoid code bloat. */
414 /*This is harmless and simplify mono_gc_get_range_copy_func */
415 size += (SIZEOF_VOID_P - 1);
416 size &= ~(SIZEOF_VOID_P - 1);
418 EMIT_NEW_ICONST (cfg, iargs [2], size);
419 mono_emit_jit_icall (cfg, mono_gc_get_range_copy_func (), iargs);
428 iargs [2] = size_ins;
429 mini_emit_calli (cfg, mono_method_signature (mini_get_memcpy_method ()), iargs, memcpy_ins, NULL, NULL);
431 mini_emit_memcpy_const_size (cfg, dest, src, size, align);
436 mini_emit_memory_load (MonoCompile *cfg, MonoType *type, MonoInst *src, int offset, int ins_flag)
440 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, type, src->dreg, offset);
441 ins->flags |= ins_flag;
443 if (ins_flag & MONO_INST_VOLATILE) {
444 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
445 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
453 mini_emit_memory_store (MonoCompile *cfg, MonoType *type, MonoInst *dest, MonoInst *value, int ins_flag)
457 if (ins_flag & MONO_INST_VOLATILE) {
458 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
459 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
461 /* FIXME: should check item at sp [1] is compatible with the type of the store. */
463 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, type, dest->dreg, 0, value->dreg);
464 ins->flags |= ins_flag;
465 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER &&
466 mini_type_is_reference (type) && !MONO_INS_IS_PCONST_NULL (value)) {
467 /* insert call to write barrier */
468 mini_emit_write_barrier (cfg, dest, value);
473 mini_emit_memory_copy_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoInst *size, int ins_flag)
475 int align = SIZEOF_VOID_P;
478 * FIXME: It's unclear whether we should be emitting both the acquire
479 * and release barriers for cpblk. It is technically both a load and
480 * store operation, so it seems like that's the sensible thing to do.
482 * FIXME: We emit full barriers on both sides of the operation for
483 * simplicity. We should have a separate atomic memcpy method instead.
485 if (ins_flag & MONO_INST_VOLATILE) {
486 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
487 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
490 if ((cfg->opt & MONO_OPT_INTRINS) && (size->opcode == OP_ICONST) && size->inst_c0 < 10000) {
491 mini_emit_memcpy_const_size (cfg, dest, src, size->inst_c0, align);
493 if (cfg->verbose_level > 3)
494 printf ("EMITING REGULAR COPY\n");
495 mini_emit_memcpy_internal (cfg, dest, src, size, 0, align);
498 if (ins_flag & MONO_INST_VOLATILE) {
499 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
500 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
505 mini_emit_memory_init_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *value, MonoInst *size, int ins_flag)
507 int align = SIZEOF_VOID_P;
509 if (ins_flag & MONO_INST_VOLATILE) {
510 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
511 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
514 //FIXME unrolled memset only supports zeroing
515 if ((cfg->opt & MONO_OPT_INTRINS) && (size->opcode == OP_ICONST) && (value->opcode == OP_ICONST) && (value->inst_c0 == 0)) {
516 mini_emit_memset_const_size (cfg, dest, value->inst_c0, size->inst_c0, align);
518 mini_emit_memset_internal (cfg, dest, value, 0, size, 0, align);
524 * If @klass is a valuetype, emit code to copy a value with source address in @src and destination address in @dest.
525 * If @klass is a ref type, copy a pointer instead.
529 mini_emit_memory_copy (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native, int ins_flag)
532 * FIXME: It's unclear whether we should be emitting both the acquire
533 * and release barriers for cpblk. It is technically both a load and
534 * store operation, so it seems like that's the sensible thing to do.
536 * FIXME: We emit full barriers on both sides of the operation for
537 * simplicity. We should have a separate atomic memcpy method instead.
539 if (ins_flag & MONO_INST_VOLATILE) {
540 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
541 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
544 mini_emit_memory_copy_internal (cfg, dest, src, klass, native);
546 if (ins_flag & MONO_INST_VOLATILE) {
547 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
548 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);