78 } |
78 } |
79 } |
79 } |
80 return h_array(); |
80 return h_array(); |
81 } |
81 } |
82 |
82 |
83 void objArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, |
83 // Either oop or narrowOop depending on UseCompressedOops. |
84 int dst_pos, int length, TRAPS) { |
84 template <class T> void objArrayKlass::do_copy(arrayOop s, T* src, |
85 assert(s->is_objArray(), "must be obj array"); |
85 arrayOop d, T* dst, int length, TRAPS) { |
86 |
86 |
87 if (!d->is_objArray()) { |
87 const size_t word_len = objArrayOopDesc::array_size(length); |
88 THROW(vmSymbols::java_lang_ArrayStoreException()); |
|
89 } |
|
90 |
|
91 // Check is all offsets and lengths are non negative |
|
92 if (src_pos < 0 || dst_pos < 0 || length < 0) { |
|
93 THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); |
|
94 } |
|
95 // Check if the ranges are valid |
|
96 if ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length()) |
|
97 || (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) { |
|
98 THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); |
|
99 } |
|
100 |
|
101 // Special case. Boundary cases must be checked first |
|
102 // This allows the following call: copy_array(s, s.length(), d.length(), 0). |
|
103 // This is correct, since the position is supposed to be an 'in between point', i.e., s.length(), |
|
104 // points to the right of the last element. |
|
105 if (length==0) { |
|
106 return; |
|
107 } |
|
108 |
|
109 oop* const src = objArrayOop(s)->obj_at_addr(src_pos); |
|
110 oop* const dst = objArrayOop(d)->obj_at_addr(dst_pos); |
|
111 const size_t word_len = length * HeapWordsPerOop; |
|
112 |
88 |
113 // For performance reasons, we assume we are using a card marking write |
89 // For performance reasons, we assume we are using a card marking write |
114 // barrier. The assert will fail if this is not the case. |
90 // barrier. The assert will fail if this is not the case. |
115 BarrierSet* bs = Universe::heap()->barrier_set(); |
91 BarrierSet* bs = Universe::heap()->barrier_set(); |
116 assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt"); |
92 assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt"); |
127 // elements are guaranteed to be subtypes, so no check necessary |
103 // elements are guaranteed to be subtypes, so no check necessary |
128 Copy::conjoint_oops_atomic(src, dst, length); |
104 Copy::conjoint_oops_atomic(src, dst, length); |
129 } else { |
105 } else { |
130 // slow case: need individual subtype checks |
106 // slow case: need individual subtype checks |
131 // note: don't use obj_at_put below because it includes a redundant store check |
107 // note: don't use obj_at_put below because it includes a redundant store check |
132 oop* from = src; |
108 T* from = src; |
133 oop* end = from + length; |
109 T* end = from + length; |
134 for (oop* p = dst; from < end; from++, p++) { |
110 for (T* p = dst; from < end; from++, p++) { |
135 oop element = *from; |
111 // XXX this is going to be slow. |
136 if (element == NULL || Klass::cast(element->klass())->is_subtype_of(bound)) { |
112 T element = *from; |
137 *p = element; |
113 if (oopDesc::is_null(element) || |
|
114 Klass::cast(oopDesc::decode_heap_oop_not_null(element)->klass())->is_subtype_of(bound)) { |
|
115 *p = *from; |
138 } else { |
116 } else { |
139 // We must do a barrier to cover the partial copy. |
117 // We must do a barrier to cover the partial copy. |
140 const size_t done_word_len = pointer_delta(p, dst, oopSize) * |
118 const size_t pd = pointer_delta(p, dst, (size_t)heapOopSize); |
141 HeapWordsPerOop; |
119 // pointer delta is scaled to number of elements (length field in |
|
120 // objArrayOop) which we assume is 32 bit. |
|
121 assert(pd == (size_t)(int)pd, "length field overflow"); |
|
122 const size_t done_word_len = objArrayOopDesc::array_size((int)pd); |
142 bs->write_ref_array(MemRegion((HeapWord*)dst, done_word_len)); |
123 bs->write_ref_array(MemRegion((HeapWord*)dst, done_word_len)); |
143 THROW(vmSymbols::java_lang_ArrayStoreException()); |
124 THROW(vmSymbols::java_lang_ArrayStoreException()); |
144 return; |
125 return; |
145 } |
126 } |
146 } |
127 } |
147 } |
128 } |
148 } |
129 } |
149 bs->write_ref_array(MemRegion((HeapWord*)dst, word_len)); |
130 bs->write_ref_array(MemRegion((HeapWord*)dst, word_len)); |
|
131 } |
|
132 |
|
133 void objArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, |
|
134 int dst_pos, int length, TRAPS) { |
|
135 assert(s->is_objArray(), "must be obj array"); |
|
136 |
|
137 if (!d->is_objArray()) { |
|
138 THROW(vmSymbols::java_lang_ArrayStoreException()); |
|
139 } |
|
140 |
|
141 // Check is all offsets and lengths are non negative |
|
142 if (src_pos < 0 || dst_pos < 0 || length < 0) { |
|
143 THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); |
|
144 } |
|
145 // Check if the ranges are valid |
|
146 if ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length()) |
|
147 || (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) { |
|
148 THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); |
|
149 } |
|
150 |
|
151 // Special case. Boundary cases must be checked first |
|
152 // This allows the following call: copy_array(s, s.length(), d.length(), 0). |
|
153 // This is correct, since the position is supposed to be an 'in between point', i.e., s.length(), |
|
154 // points to the right of the last element. |
|
155 if (length==0) { |
|
156 return; |
|
157 } |
|
158 if (UseCompressedOops) { |
|
159 narrowOop* const src = objArrayOop(s)->obj_at_addr<narrowOop>(src_pos); |
|
160 narrowOop* const dst = objArrayOop(d)->obj_at_addr<narrowOop>(dst_pos); |
|
161 do_copy<narrowOop>(s, src, d, dst, length, CHECK); |
|
162 } else { |
|
163 oop* const src = objArrayOop(s)->obj_at_addr<oop>(src_pos); |
|
164 oop* const dst = objArrayOop(d)->obj_at_addr<oop>(dst_pos); |
|
165 do_copy<oop> (s, src, d, dst, length, CHECK); |
|
166 } |
150 } |
167 } |
151 |
168 |
152 |
169 |
153 klassOop objArrayKlass::array_klass_impl(bool or_null, int n, TRAPS) { |
170 klassOop objArrayKlass::array_klass_impl(bool or_null, int n, TRAPS) { |
154 objArrayKlassHandle h_this(THREAD, as_klassOop()); |
171 objArrayKlassHandle h_this(THREAD, as_klassOop()); |
240 |
257 |
241 objArrayKlass* oak = objArrayKlass::cast(k); |
258 objArrayKlass* oak = objArrayKlass::cast(k); |
242 return element_klass()->klass_part()->is_subtype_of(oak->element_klass()); |
259 return element_klass()->klass_part()->is_subtype_of(oak->element_klass()); |
243 } |
260 } |
244 |
261 |
245 |
|
246 void objArrayKlass::initialize(TRAPS) { |
262 void objArrayKlass::initialize(TRAPS) { |
247 Klass::cast(bottom_klass())->initialize(THREAD); // dispatches to either instanceKlass or typeArrayKlass |
263 Klass::cast(bottom_klass())->initialize(THREAD); // dispatches to either instanceKlass or typeArrayKlass |
248 } |
264 } |
249 |
265 |
|
266 #define ObjArrayKlass_SPECIALIZED_OOP_ITERATE(T, a, p, do_oop) \ |
|
267 { \ |
|
268 T* p = (T*)(a)->base(); \ |
|
269 T* const end = p + (a)->length(); \ |
|
270 while (p < end) { \ |
|
271 do_oop; \ |
|
272 p++; \ |
|
273 } \ |
|
274 } |
|
275 |
|
276 #define ObjArrayKlass_SPECIALIZED_BOUNDED_OOP_ITERATE(T, a, p, low, high, do_oop) \ |
|
277 { \ |
|
278 T* const l = (T*)(low); \ |
|
279 T* const h = (T*)(high); \ |
|
280 T* p = (T*)(a)->base(); \ |
|
281 T* end = p + (a)->length(); \ |
|
282 if (p < l) p = l; \ |
|
283 if (end > h) end = h; \ |
|
284 while (p < end) { \ |
|
285 do_oop; \ |
|
286 ++p; \ |
|
287 } \ |
|
288 } |
|
289 |
|
290 #define ObjArrayKlass_OOP_ITERATE(a, p, do_oop) \ |
|
291 if (UseCompressedOops) { \ |
|
292 ObjArrayKlass_SPECIALIZED_OOP_ITERATE(narrowOop, \ |
|
293 a, p, do_oop) \ |
|
294 } else { \ |
|
295 ObjArrayKlass_SPECIALIZED_OOP_ITERATE(oop, \ |
|
296 a, p, do_oop) \ |
|
297 } |
|
298 |
|
299 #define ObjArrayKlass_BOUNDED_OOP_ITERATE(a, p, low, high, do_oop) \ |
|
300 if (UseCompressedOops) { \ |
|
301 ObjArrayKlass_SPECIALIZED_BOUNDED_OOP_ITERATE(narrowOop, \ |
|
302 a, p, low, high, do_oop) \ |
|
303 } else { \ |
|
304 ObjArrayKlass_SPECIALIZED_BOUNDED_OOP_ITERATE(oop, \ |
|
305 a, p, low, high, do_oop) \ |
|
306 } |
250 |
307 |
251 void objArrayKlass::oop_follow_contents(oop obj) { |
308 void objArrayKlass::oop_follow_contents(oop obj) { |
252 assert (obj->is_array(), "obj must be array"); |
309 assert (obj->is_array(), "obj must be array"); |
253 arrayOop a = arrayOop(obj); |
310 objArrayOop a = objArrayOop(obj); |
254 a->follow_header(); |
311 a->follow_header(); |
255 oop* base = (oop*)a->base(T_OBJECT); |
312 ObjArrayKlass_OOP_ITERATE( \ |
256 oop* const end = base + a->length(); |
313 a, p, \ |
257 while (base < end) { |
314 /* we call mark_and_follow here to avoid excessive marking stack usage */ \ |
258 if (*base != NULL) |
315 MarkSweep::mark_and_follow(p)) |
259 // we call mark_and_follow here to avoid excessive marking stack usage |
|
260 MarkSweep::mark_and_follow(base); |
|
261 base++; |
|
262 } |
|
263 } |
316 } |
264 |
317 |
265 #ifndef SERIALGC |
318 #ifndef SERIALGC |
266 void objArrayKlass::oop_follow_contents(ParCompactionManager* cm, |
319 void objArrayKlass::oop_follow_contents(ParCompactionManager* cm, |
267 oop obj) { |
320 oop obj) { |
268 assert (obj->is_array(), "obj must be array"); |
321 assert (obj->is_array(), "obj must be array"); |
269 arrayOop a = arrayOop(obj); |
322 objArrayOop a = objArrayOop(obj); |
270 a->follow_header(cm); |
323 a->follow_header(cm); |
271 oop* base = (oop*)a->base(T_OBJECT); |
324 ObjArrayKlass_OOP_ITERATE( \ |
272 oop* const end = base + a->length(); |
325 a, p, \ |
273 while (base < end) { |
326 /* we call mark_and_follow here to avoid excessive marking stack usage */ \ |
274 if (*base != NULL) |
327 PSParallelCompact::mark_and_follow(cm, p)) |
275 // we call mark_and_follow here to avoid excessive marking stack usage |
|
276 PSParallelCompact::mark_and_follow(cm, base); |
|
277 base++; |
|
278 } |
|
279 } |
328 } |
280 #endif // SERIALGC |
329 #endif // SERIALGC |
281 |
|
282 #define invoke_closure_on(base, closure, nv_suffix) { \ |
|
283 if (*(base) != NULL) { \ |
|
284 (closure)->do_oop##nv_suffix(base); \ |
|
285 } \ |
|
286 } |
|
287 |
330 |
288 #define ObjArrayKlass_OOP_OOP_ITERATE_DEFN(OopClosureType, nv_suffix) \ |
331 #define ObjArrayKlass_OOP_OOP_ITERATE_DEFN(OopClosureType, nv_suffix) \ |
289 \ |
332 \ |
290 int objArrayKlass::oop_oop_iterate##nv_suffix(oop obj, \ |
333 int objArrayKlass::oop_oop_iterate##nv_suffix(oop obj, \ |
291 OopClosureType* closure) { \ |
334 OopClosureType* closure) { \ |
328 /* Don't call size() or oop_size() since that is a virtual call */ \ |
357 /* Don't call size() or oop_size() since that is a virtual call */ \ |
329 int size = a->object_size(); \ |
358 int size = a->object_size(); \ |
330 if (closure->do_header()) { \ |
359 if (closure->do_header()) { \ |
331 a->oop_iterate_header(closure, mr); \ |
360 a->oop_iterate_header(closure, mr); \ |
332 } \ |
361 } \ |
333 oop* bottom = (oop*)mr.start(); \ |
362 ObjArrayKlass_BOUNDED_OOP_ITERATE( \ |
334 oop* top = (oop*)mr.end(); \ |
363 a, p, mr.start(), mr.end(), (closure)->do_oop##nv_suffix(p)) \ |
335 oop* base = a->base(); \ |
364 return size; \ |
336 oop* end = base + a->length(); \ |
365 } |
337 if (base < bottom) { \ |
366 |
338 base = bottom; \ |
367 // Like oop_oop_iterate but only iterates over a specified range and only used |
339 } \ |
368 // for objArrayOops. |
340 if (end > top) { \ |
369 #define ObjArrayKlass_OOP_OOP_ITERATE_DEFN_r(OopClosureType, nv_suffix) \ |
341 end = top; \ |
370 \ |
342 } \ |
371 int objArrayKlass::oop_oop_iterate_range##nv_suffix(oop obj, \ |
343 const intx field_offset = PrefetchFieldsAhead; \ |
372 OopClosureType* closure, \ |
344 if (field_offset > 0) { \ |
373 int start, int end) { \ |
345 while (base < end) { \ |
374 SpecializationStats::record_iterate_call##nv_suffix(SpecializationStats::oa); \ |
346 prefetch_beyond(base, end, field_offset, closure->prefetch_style()); \ |
375 assert(obj->is_array(), "obj must be array"); \ |
347 invoke_closure_on(base, closure, nv_suffix); \ |
376 objArrayOop a = objArrayOop(obj); \ |
348 base++; \ |
377 /* Get size before changing pointers. */ \ |
|
378 /* Don't call size() or oop_size() since that is a virtual call */ \ |
|
379 int size = a->object_size(); \ |
|
380 if (UseCompressedOops) { \ |
|
381 HeapWord* low = start == 0 ? (HeapWord*)a : (HeapWord*)a->obj_at_addr<narrowOop>(start);\ |
|
382 /* this might be wierd if end needs to be aligned on HeapWord boundary */ \ |
|
383 HeapWord* high = (HeapWord*)((narrowOop*)a->base() + end); \ |
|
384 MemRegion mr(low, high); \ |
|
385 if (closure->do_header()) { \ |
|
386 a->oop_iterate_header(closure, mr); \ |
349 } \ |
387 } \ |
|
388 ObjArrayKlass_SPECIALIZED_BOUNDED_OOP_ITERATE(narrowOop, \ |
|
389 a, p, low, high, (closure)->do_oop##nv_suffix(p)) \ |
350 } else { \ |
390 } else { \ |
351 while (base < end) { \ |
391 HeapWord* low = start == 0 ? (HeapWord*)a : (HeapWord*)a->obj_at_addr<oop>(start); \ |
352 invoke_closure_on(base, closure, nv_suffix); \ |
392 HeapWord* high = (HeapWord*)((oop*)a->base() + end); \ |
353 base++; \ |
393 MemRegion mr(low, high); \ |
|
394 if (closure->do_header()) { \ |
|
395 a->oop_iterate_header(closure, mr); \ |
354 } \ |
396 } \ |
|
397 ObjArrayKlass_SPECIALIZED_BOUNDED_OOP_ITERATE(oop, \ |
|
398 a, p, low, high, (closure)->do_oop##nv_suffix(p)) \ |
355 } \ |
399 } \ |
356 return size; \ |
400 return size; \ |
357 } |
401 } |
358 |
402 |
359 ALL_OOP_OOP_ITERATE_CLOSURES_1(ObjArrayKlass_OOP_OOP_ITERATE_DEFN) |
403 ALL_OOP_OOP_ITERATE_CLOSURES_1(ObjArrayKlass_OOP_OOP_ITERATE_DEFN) |
360 ALL_OOP_OOP_ITERATE_CLOSURES_3(ObjArrayKlass_OOP_OOP_ITERATE_DEFN) |
404 ALL_OOP_OOP_ITERATE_CLOSURES_3(ObjArrayKlass_OOP_OOP_ITERATE_DEFN) |
361 ALL_OOP_OOP_ITERATE_CLOSURES_1(ObjArrayKlass_OOP_OOP_ITERATE_DEFN_m) |
405 ALL_OOP_OOP_ITERATE_CLOSURES_1(ObjArrayKlass_OOP_OOP_ITERATE_DEFN_m) |
362 ALL_OOP_OOP_ITERATE_CLOSURES_3(ObjArrayKlass_OOP_OOP_ITERATE_DEFN_m) |
406 ALL_OOP_OOP_ITERATE_CLOSURES_3(ObjArrayKlass_OOP_OOP_ITERATE_DEFN_m) |
|
407 ALL_OOP_OOP_ITERATE_CLOSURES_1(ObjArrayKlass_OOP_OOP_ITERATE_DEFN_r) |
|
408 ALL_OOP_OOP_ITERATE_CLOSURES_3(ObjArrayKlass_OOP_OOP_ITERATE_DEFN_r) |
363 |
409 |
364 int objArrayKlass::oop_adjust_pointers(oop obj) { |
410 int objArrayKlass::oop_adjust_pointers(oop obj) { |
365 assert(obj->is_objArray(), "obj must be obj array"); |
411 assert(obj->is_objArray(), "obj must be obj array"); |
366 objArrayOop a = objArrayOop(obj); |
412 objArrayOop a = objArrayOop(obj); |
367 // Get size before changing pointers. |
413 // Get size before changing pointers. |
368 // Don't call size() or oop_size() since that is a virtual call. |
414 // Don't call size() or oop_size() since that is a virtual call. |
369 int size = a->object_size(); |
415 int size = a->object_size(); |
370 a->adjust_header(); |
416 a->adjust_header(); |
371 oop* base = a->base(); |
417 ObjArrayKlass_OOP_ITERATE(a, p, MarkSweep::adjust_pointer(p)) |
372 oop* const end = base + a->length(); |
|
373 while (base < end) { |
|
374 MarkSweep::adjust_pointer(base); |
|
375 base++; |
|
376 } |
|
377 return size; |
418 return size; |
378 } |
419 } |
379 |
420 |
380 #ifndef SERIALGC |
421 #ifndef SERIALGC |
381 void objArrayKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { |
422 void objArrayKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { |
382 assert(!pm->depth_first(), "invariant"); |
423 assert(!pm->depth_first(), "invariant"); |
383 assert(obj->is_objArray(), "obj must be obj array"); |
424 assert(obj->is_objArray(), "obj must be obj array"); |
384 // Compute oop range |
425 ObjArrayKlass_OOP_ITERATE( \ |
385 oop* curr = objArrayOop(obj)->base(); |
426 objArrayOop(obj), p, \ |
386 oop* end = curr + objArrayOop(obj)->length(); |
427 if (PSScavenge::should_scavenge(p)) { \ |
387 // assert(align_object_size(end - (oop*)obj) == oop_size(obj), "checking size"); |
428 pm->claim_or_forward_breadth(p); \ |
388 assert(align_object_size(pointer_delta(end, obj, sizeof(oop*))) |
429 }) |
389 == oop_size(obj), "checking size"); |
|
390 |
|
391 // Iterate over oops |
|
392 while (curr < end) { |
|
393 if (PSScavenge::should_scavenge(*curr)) { |
|
394 pm->claim_or_forward_breadth(curr); |
|
395 } |
|
396 ++curr; |
|
397 } |
|
398 } |
430 } |
399 |
431 |
400 void objArrayKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { |
432 void objArrayKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { |
401 assert(pm->depth_first(), "invariant"); |
433 assert(pm->depth_first(), "invariant"); |
402 assert(obj->is_objArray(), "obj must be obj array"); |
434 assert(obj->is_objArray(), "obj must be obj array"); |
403 // Compute oop range |
435 ObjArrayKlass_OOP_ITERATE( \ |
404 oop* curr = objArrayOop(obj)->base(); |
436 objArrayOop(obj), p, \ |
405 oop* end = curr + objArrayOop(obj)->length(); |
437 if (PSScavenge::should_scavenge(p)) { \ |
406 // assert(align_object_size(end - (oop*)obj) == oop_size(obj), "checking size"); |
438 pm->claim_or_forward_depth(p); \ |
407 assert(align_object_size(pointer_delta(end, obj, sizeof(oop*))) |
439 }) |
408 == oop_size(obj), "checking size"); |
|
409 |
|
410 // Iterate over oops |
|
411 while (curr < end) { |
|
412 if (PSScavenge::should_scavenge(*curr)) { |
|
413 pm->claim_or_forward_depth(curr); |
|
414 } |
|
415 ++curr; |
|
416 } |
|
417 } |
440 } |
418 |
441 |
419 int objArrayKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { |
442 int objArrayKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { |
420 assert (obj->is_objArray(), "obj must be obj array"); |
443 assert (obj->is_objArray(), "obj must be obj array"); |
421 objArrayOop a = objArrayOop(obj); |
444 objArrayOop a = objArrayOop(obj); |
422 |
445 ObjArrayKlass_OOP_ITERATE(a, p, PSParallelCompact::adjust_pointer(p)) |
423 oop* const base = a->base(); |
|
424 oop* const beg_oop = base; |
|
425 oop* const end_oop = base + a->length(); |
|
426 for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { |
|
427 PSParallelCompact::adjust_pointer(cur_oop); |
|
428 } |
|
429 return a->object_size(); |
446 return a->object_size(); |
430 } |
447 } |
431 |
448 |
432 int objArrayKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, |
449 int objArrayKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, |
433 HeapWord* beg_addr, HeapWord* end_addr) { |
450 HeapWord* beg_addr, HeapWord* end_addr) { |
434 assert (obj->is_objArray(), "obj must be obj array"); |
451 assert (obj->is_objArray(), "obj must be obj array"); |
435 objArrayOop a = objArrayOop(obj); |
452 objArrayOop a = objArrayOop(obj); |
436 |
453 ObjArrayKlass_BOUNDED_OOP_ITERATE( \ |
437 oop* const base = a->base(); |
454 a, p, beg_addr, end_addr, \ |
438 oop* const beg_oop = MAX2((oop*)beg_addr, base); |
455 PSParallelCompact::adjust_pointer(p)) |
439 oop* const end_oop = MIN2((oop*)end_addr, base + a->length()); |
|
440 for (oop* cur_oop = beg_oop; cur_oop < end_oop; ++cur_oop) { |
|
441 PSParallelCompact::adjust_pointer(cur_oop); |
|
442 } |
|
443 return a->object_size(); |
456 return a->object_size(); |
444 } |
457 } |
445 #endif // SERIALGC |
458 #endif // SERIALGC |
446 |
459 |
447 // JVM support |
460 // JVM support |