Merge pull request #4816 from BrzVlad/fix-remoting-exception
[mono.git] / mono / mini / memory-access.c
1 /**
2  * Emit memory access for the front-end.
3  *
4  */
5
6 #include <config.h>
7 #include <mono/utils/mono-compiler.h>
8
9 #ifndef DISABLE_JIT
10
11 #include <mono/utils/mono-memory-model.h>
12
13 #include "mini.h"
14 #include "ir-emit.h"
15
16 #define MAX_INLINE_COPIES 10
17
18 void 
19 mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align)
20 {
21         int val_reg;
22
23         g_assert (val == 0);
24
25         if (align == 0)
26                 align = 4;
27
28         if ((size <= SIZEOF_REGISTER) && (size <= align)) {
29                 switch (size) {
30                 case 1:
31                         MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, destreg, offset, val);
32                         return;
33                 case 2:
34                         MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI2_MEMBASE_IMM, destreg, offset, val);
35                         return;
36                 case 4:
37                         MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI4_MEMBASE_IMM, destreg, offset, val);
38                         return;
39 #if SIZEOF_REGISTER == 8
40                 case 8:
41                         MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI8_MEMBASE_IMM, destreg, offset, val);
42                         return;
43 #endif
44                 }
45         }
46
47         val_reg = alloc_preg (cfg);
48
49         if (SIZEOF_REGISTER == 8)
50                 MONO_EMIT_NEW_I8CONST (cfg, val_reg, val);
51         else
52                 MONO_EMIT_NEW_ICONST (cfg, val_reg, val);
53
54         if (align < 4) {
55                 /* This could be optimized further if neccesary */
56                 while (size >= 1) {
57                         MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
58                         offset += 1;
59                         size -= 1;
60                 }
61                 return;
62         }       
63
64         if (!cfg->backend->no_unaligned_access && SIZEOF_REGISTER == 8) {
65                 if (offset % 8) {
66                         MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
67                         offset += 4;
68                         size -= 4;
69                 }
70                 while (size >= 8) {
71                         MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, offset, val_reg);
72                         offset += 8;
73                         size -= 8;
74                 }
75         }       
76
77         while (size >= 4) {
78                 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
79                 offset += 4;
80                 size -= 4;
81         }
82         while (size >= 2) {
83                 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, val_reg);
84                 offset += 2;
85                 size -= 2;
86         }
87         while (size >= 1) {
88                 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
89                 offset += 1;
90                 size -= 1;
91         }
92 }
93
94 void 
95 mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align)
96 {
97         int cur_reg;
98
99         if (align == 0)
100                 align = 4;
101
102         /*FIXME arbitrary hack to avoid unbound code expansion.*/
103         g_assert (size < 10000);
104
105         if (align < 4) {
106                 /* This could be optimized further if neccesary */
107                 while (size >= 1) {
108                         cur_reg = alloc_preg (cfg);
109                         MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
110                         MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
111                         doffset += 1;
112                         soffset += 1;
113                         size -= 1;
114                 }
115         }
116
117         if (!cfg->backend->no_unaligned_access && SIZEOF_REGISTER == 8) {
118                 while (size >= 8) {
119                         cur_reg = alloc_preg (cfg);
120                         MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI8_MEMBASE, cur_reg, srcreg, soffset);
121                         MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, doffset, cur_reg);
122                         doffset += 8;
123                         soffset += 8;
124                         size -= 8;
125                 }
126         }       
127
128         while (size >= 4) {
129                 cur_reg = alloc_preg (cfg);
130                 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, cur_reg, srcreg, soffset);
131                 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, doffset, cur_reg);
132                 doffset += 4;
133                 soffset += 4;
134                 size -= 4;
135         }
136         while (size >= 2) {
137                 cur_reg = alloc_preg (cfg);
138                 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, cur_reg, srcreg, soffset);
139                 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, doffset, cur_reg);
140                 doffset += 2;
141                 soffset += 2;
142                 size -= 2;
143         }
144         while (size >= 1) {
145                 cur_reg = alloc_preg (cfg);
146                 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
147                 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
148                 doffset += 1;
149                 soffset += 1;
150                 size -= 1;
151         }
152 }
153
154 static void
155 mini_emit_memcpy_internal (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoInst *size_ins, int size, int align)
156 {
157         /* FIXME: Optimize the case when src/dest is OP_LDADDR */
158
159         /* We can't do copies at a smaller granule than the provided alignment */
160         if (size_ins || ((size / align > MAX_INLINE_COPIES) && !(cfg->opt & MONO_OPT_INTRINS))) {
161                 MonoInst *iargs [3];
162                 iargs [0] = dest;
163                 iargs [1] = src;
164
165                 if (!size_ins)
166                         EMIT_NEW_ICONST (cfg, size_ins, size);
167                 iargs [2] = size_ins;
168                 mono_emit_method_call (cfg, mini_get_memcpy_method (), iargs, NULL);
169         } else {
170                 mini_emit_memcpy (cfg, dest->dreg, 0, src->dreg, 0, size, align);
171         }
172 }
173
174 static void
175 mini_emit_memset_internal (MonoCompile *cfg, MonoInst *dest, MonoInst *value_ins, int value, MonoInst *size_ins, int size, int align)
176 {
177         /* FIXME: Optimize the case when dest is OP_LDADDR */
178
179         /* We can't do copies at a smaller granule than the provided alignment */
180         if (value_ins || size_ins || value != 0 || ((size / align > MAX_INLINE_COPIES) && !(cfg->opt & MONO_OPT_INTRINS))) {
181                 MonoInst *iargs [3];
182                 iargs [0] = dest;
183
184                 if (!value_ins)
185                         EMIT_NEW_ICONST (cfg, value_ins, value);
186                 iargs [1] = value_ins;
187
188                 if (!size_ins)
189                         EMIT_NEW_ICONST (cfg, size_ins, size);
190                 iargs [2] = size_ins;
191
192                 mono_emit_method_call (cfg, mini_get_memset_method (), iargs, NULL);
193         } else {
194                 mini_emit_memset (cfg, dest->dreg, 0, size, value, align);
195         }
196 }
197
198 static void
199 mini_emit_memcpy_const_size (MonoCompile *cfg, MonoInst *dest, MonoInst *src, int size, int align)
200 {
201         mini_emit_memcpy_internal (cfg, dest, src, NULL, size, align);
202 }
203
204 static void
205 mini_emit_memset_const_size (MonoCompile *cfg, MonoInst *dest, int value, int size, int align)
206 {
207         mini_emit_memset_internal (cfg, dest, NULL, value, NULL, size, align);
208 }
209
210 MonoInst*
211 mini_emit_memory_load (MonoCompile *cfg, MonoType *type, MonoInst *src, int offset, int ins_flag)
212 {
213         MonoInst *ins;
214
215         EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, type, src->dreg, offset);
216         ins->flags |= ins_flag;
217
218         if (ins_flag & MONO_INST_VOLATILE) {
219                 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
220                 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
221         }
222
223         return ins;
224 }
225
226
227 void
228 mini_emit_memory_store (MonoCompile *cfg, MonoType *type, MonoInst *dest, MonoInst *value, int ins_flag)
229 {
230         MonoInst *ins;
231
232         if (ins_flag & MONO_INST_VOLATILE) {
233                 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
234                 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
235         }
236         /* FIXME: should check item at sp [1] is compatible with the type of the store. */
237
238         EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, type, dest->dreg, 0, value->dreg);
239         ins->flags |= ins_flag;
240         if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER &&
241                 mini_type_is_reference (type) && !MONO_INS_IS_PCONST_NULL (value)) {
242                 /* insert call to write barrier */
243                 mini_emit_write_barrier (cfg, dest, value);
244         }
245 }
246
247 void
248 mini_emit_memory_copy_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoInst *size, int ins_flag)
249 {
250         int align = SIZEOF_VOID_P;
251
252         /*
253          * FIXME: It's unclear whether we should be emitting both the acquire
254          * and release barriers for cpblk. It is technically both a load and
255          * store operation, so it seems like that's the sensible thing to do.
256          *
257          * FIXME: We emit full barriers on both sides of the operation for
258          * simplicity. We should have a separate atomic memcpy method instead.
259          */
260         if (ins_flag & MONO_INST_VOLATILE) {
261                 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
262                 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
263         }
264
265         if ((cfg->opt & MONO_OPT_INTRINS) && (size->opcode == OP_ICONST)) {
266                 mini_emit_memcpy_const_size (cfg, dest, src, size->inst_c0, align);
267         } else {
268                 if (cfg->verbose_level > 3)
269                         printf ("EMITING REGULAR COPY\n");
270                 mini_emit_memcpy_internal (cfg, dest, src, size, 0, align);
271         }
272
273         if (ins_flag & MONO_INST_VOLATILE) {
274                 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
275                 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
276         }
277 }
278
279 void
280 mini_emit_memory_init_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *value, MonoInst *size, int ins_flag)
281 {
282         int align = SIZEOF_VOID_P;
283
284         if (ins_flag & MONO_INST_VOLATILE) {
285                 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
286                 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
287         }
288
289         //FIXME unrolled memset only supports zeroing
290         if ((cfg->opt & MONO_OPT_INTRINS) && (size->opcode == OP_ICONST) && (value->opcode == OP_ICONST) && (value->inst_c0 == 0)) {
291                 mini_emit_memset_const_size (cfg, dest, value->inst_c0, size->inst_c0, align);
292         } else {
293                 mini_emit_memset_internal (cfg, dest, value, 0, size, 0, align);
294         }
295
296 }
297
298 #endif