2 * Emit memory access for the front-end.
7 #include <mono/utils/mono-compiler.h>
11 #include <mono/utils/mono-memory-model.h>
16 #define MAX_INLINE_COPIES 10
19 mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align)
23 /*FIXME arbitrary hack to avoid unbound code expansion.*/
24 g_assert (size < 10000);
28 if ((size <= SIZEOF_REGISTER) && (size <= align)) {
31 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, destreg, offset, val);
34 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI2_MEMBASE_IMM, destreg, offset, val);
37 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI4_MEMBASE_IMM, destreg, offset, val);
39 #if SIZEOF_REGISTER == 8
41 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI8_MEMBASE_IMM, destreg, offset, val);
47 val_reg = alloc_preg (cfg);
49 if (SIZEOF_REGISTER == 8)
50 MONO_EMIT_NEW_I8CONST (cfg, val_reg, val);
52 MONO_EMIT_NEW_ICONST (cfg, val_reg, val);
54 if (align < SIZEOF_VOID_P) {
59 if (SIZEOF_VOID_P == 8 && align % 8 == 4)
63 //Unaligned offsets don't naturaly happen in the runtime, so it's ok to be conservative in how we copy
64 //We assume that input src and dest are be aligned to `align` so offset just worsen it
65 int offsets_mask = offset & 0x7; //we only care about the misalignment part
67 if (offsets_mask % 2 == 1)
69 if (offsets_mask % 4 == 2)
71 if (SIZEOF_VOID_P == 8 && offsets_mask % 8 == 4)
75 if (SIZEOF_REGISTER == 8) {
77 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, offset, val_reg);
85 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
93 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, val_reg);
100 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
107 mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align)
111 /*FIXME arbitrary hack to avoid unbound code expansion.*/
112 g_assert (size < 10000);
113 g_assert (align > 0);
115 if (align < SIZEOF_VOID_P) {
123 //Unaligned offsets don't naturaly happen in the runtime, so it's ok to be conservative in how we copy
124 //We assume that input src and dest are be aligned to `align` so offset just worsen it
125 int offsets_mask = (doffset | soffset) & 0x7; //we only care about the misalignment part
127 if (offsets_mask % 2 == 1)
129 if (offsets_mask % 4 == 2)
131 if (SIZEOF_VOID_P == 8 && offsets_mask % 8 == 4)
136 if (SIZEOF_REGISTER == 8) {
138 cur_reg = alloc_preg (cfg);
139 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI8_MEMBASE, cur_reg, srcreg, soffset);
140 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, doffset, cur_reg);
149 cur_reg = alloc_preg (cfg);
150 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, cur_reg, srcreg, soffset);
151 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, doffset, cur_reg);
159 cur_reg = alloc_preg (cfg);
160 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, cur_reg, srcreg, soffset);
161 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, doffset, cur_reg);
169 cur_reg = alloc_preg (cfg);
170 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
171 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
179 mini_emit_memcpy_internal (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoInst *size_ins, int size, int align)
181 /* FIXME: Optimize the case when src/dest is OP_LDADDR */
183 /* We can't do copies at a smaller granule than the provided alignment */
184 if (size_ins || ((size / align > MAX_INLINE_COPIES) && !(cfg->opt & MONO_OPT_INTRINS))) {
190 EMIT_NEW_ICONST (cfg, size_ins, size);
191 iargs [2] = size_ins;
192 mono_emit_method_call (cfg, mini_get_memcpy_method (), iargs, NULL);
194 mini_emit_memcpy (cfg, dest->dreg, 0, src->dreg, 0, size, align);
199 mini_emit_memset_internal (MonoCompile *cfg, MonoInst *dest, MonoInst *value_ins, int value, MonoInst *size_ins, int size, int align)
201 /* FIXME: Optimize the case when dest is OP_LDADDR */
203 /* We can't do copies at a smaller granule than the provided alignment */
204 if (value_ins || size_ins || value != 0 || ((size / align > MAX_INLINE_COPIES) && !(cfg->opt & MONO_OPT_INTRINS))) {
209 EMIT_NEW_ICONST (cfg, value_ins, value);
210 iargs [1] = value_ins;
213 EMIT_NEW_ICONST (cfg, size_ins, size);
214 iargs [2] = size_ins;
216 mono_emit_method_call (cfg, mini_get_memset_method (), iargs, NULL);
218 mini_emit_memset (cfg, dest->dreg, 0, size, value, align);
223 mini_emit_memcpy_const_size (MonoCompile *cfg, MonoInst *dest, MonoInst *src, int size, int align)
225 mini_emit_memcpy_internal (cfg, dest, src, NULL, size, align);
229 mini_emit_memset_const_size (MonoCompile *cfg, MonoInst *dest, int value, int size, int align)
231 mini_emit_memset_internal (cfg, dest, NULL, value, NULL, size, align);
235 mini_emit_memory_load (MonoCompile *cfg, MonoType *type, MonoInst *src, int offset, int ins_flag)
239 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, type, src->dreg, offset);
240 ins->flags |= ins_flag;
242 if (ins_flag & MONO_INST_VOLATILE) {
243 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
244 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
252 mini_emit_memory_store (MonoCompile *cfg, MonoType *type, MonoInst *dest, MonoInst *value, int ins_flag)
256 if (ins_flag & MONO_INST_VOLATILE) {
257 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
258 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
260 /* FIXME: should check item at sp [1] is compatible with the type of the store. */
262 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, type, dest->dreg, 0, value->dreg);
263 ins->flags |= ins_flag;
264 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER &&
265 mini_type_is_reference (type) && !MONO_INS_IS_PCONST_NULL (value)) {
266 /* insert call to write barrier */
267 mini_emit_write_barrier (cfg, dest, value);
272 mini_emit_memory_copy_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoInst *size, int ins_flag)
274 int align = SIZEOF_VOID_P;
277 * FIXME: It's unclear whether we should be emitting both the acquire
278 * and release barriers for cpblk. It is technically both a load and
279 * store operation, so it seems like that's the sensible thing to do.
281 * FIXME: We emit full barriers on both sides of the operation for
282 * simplicity. We should have a separate atomic memcpy method instead.
284 if (ins_flag & MONO_INST_VOLATILE) {
285 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
286 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
289 if ((cfg->opt & MONO_OPT_INTRINS) && (size->opcode == OP_ICONST) && size->inst_c0 < 10000) {
290 mini_emit_memcpy_const_size (cfg, dest, src, size->inst_c0, align);
292 if (cfg->verbose_level > 3)
293 printf ("EMITING REGULAR COPY\n");
294 mini_emit_memcpy_internal (cfg, dest, src, size, 0, align);
297 if (ins_flag & MONO_INST_VOLATILE) {
298 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
299 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
304 mini_emit_memory_init_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *value, MonoInst *size, int ins_flag)
306 int align = SIZEOF_VOID_P;
308 if (ins_flag & MONO_INST_VOLATILE) {
309 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
310 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
313 //FIXME unrolled memset only supports zeroing
314 if ((cfg->opt & MONO_OPT_INTRINS) && (size->opcode == OP_ICONST) && (value->opcode == OP_ICONST) && (value->inst_c0 == 0)) {
315 mini_emit_memset_const_size (cfg, dest, value->inst_c0, size->inst_c0, align);
317 mini_emit_memset_internal (cfg, dest, value, 0, size, 0, align);