1 /* 2 * ARM SME Operations 3 * 4 * Copyright (c) 2022 Linaro, Ltd. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "qemu/osdep.h" 21 #include "cpu.h" 22 #include "internals.h" 23 #include "tcg/tcg-gvec-desc.h" 24 #include "exec/helper-proto.h" 25 #include "exec/cpu_ldst.h" 26 #include "exec/exec-all.h" 27 #include "qemu/int128.h" 28 #include "fpu/softfloat.h" 29 #include "vec_internal.h" 30 #include "sve_ldst_internal.h" 31 32 void helper_set_svcr(CPUARMState *env, uint32_t val, uint32_t mask) 33 { 34 aarch64_set_svcr(env, val, mask); 35 } 36 37 void helper_sme_zero(CPUARMState *env, uint32_t imm, uint32_t svl) 38 { 39 uint32_t i; 40 41 /* 42 * Special case clearing the entire ZA space. 43 * This falls into the CONSTRAINED UNPREDICTABLE zeroing of any 44 * parts of the ZA storage outside of SVL. 45 */ 46 if (imm == 0xff) { 47 memset(env->zarray, 0, sizeof(env->zarray)); 48 return; 49 } 50 51 /* 52 * Recall that ZAnH.D[m] is spread across ZA[n+8*m], 53 * so each row is discontiguous within ZA[]. 54 */ 55 for (i = 0; i < svl; i++) { 56 if (imm & (1 << (i % 8))) { 57 memset(&env->zarray[i], 0, svl); 58 } 59 } 60 } 61 62 63 /* 64 * When considering the ZA storage as an array of elements of 65 * type T, the index within that array of the Nth element of 66 * a vertical slice of a tile can be calculated like this, 67 * regardless of the size of type T. This is because the tiles 68 * are interleaved, so if type T is size N bytes then row 1 of 69 * the tile is N rows away from row 0. The division by N to 70 * convert a byte offset into an array index and the multiplication 71 * by N to convert from vslice-index-within-the-tile to 72 * the index within the ZA storage cancel out. 73 */ 74 #define tile_vslice_index(i) ((i) * sizeof(ARMVectorReg)) 75 76 /* 77 * When doing byte arithmetic on the ZA storage, the element 78 * byteoff bytes away in a tile vertical slice is always this 79 * many bytes away in the ZA storage, regardless of the 80 * size of the tile element, assuming that byteoff is a multiple 81 * of the element size. Again this is because of the interleaving 82 * of the tiles. For instance if we have 1 byte per element then 83 * each row of the ZA storage has one byte of the vslice data, 84 * and (counting from 0) byte 8 goes in row 8 of the storage 85 * at offset (8 * row-size-in-bytes). 86 * If we have 8 bytes per element then each row of the ZA storage 87 * has 8 bytes of the data, but there are 8 interleaved tiles and 88 * so byte 8 of the data goes into row 1 of the tile, 89 * which is again row 8 of the storage, so the offset is still 90 * (8 * row-size-in-bytes). Similarly for other element sizes. 91 */ 92 #define tile_vslice_offset(byteoff) ((byteoff) * sizeof(ARMVectorReg)) 93 94 95 /* 96 * Move Zreg vector to ZArray column. 97 */ 98 #define DO_MOVA_C(NAME, TYPE, H) \ 99 void HELPER(NAME)(void *za, void *vn, void *vg, uint32_t desc) \ 100 { \ 101 int i, oprsz = simd_oprsz(desc); \ 102 for (i = 0; i < oprsz; ) { \ 103 uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ 104 do { \ 105 if (pg & 1) { \ 106 *(TYPE *)(za + tile_vslice_offset(i)) = *(TYPE *)(vn + H(i)); \ 107 } \ 108 i += sizeof(TYPE); \ 109 pg >>= sizeof(TYPE); \ 110 } while (i & 15); \ 111 } \ 112 } 113 114 DO_MOVA_C(sme_mova_cz_b, uint8_t, H1) 115 DO_MOVA_C(sme_mova_cz_h, uint16_t, H1_2) 116 DO_MOVA_C(sme_mova_cz_s, uint32_t, H1_4) 117 118 void HELPER(sme_mova_cz_d)(void *za, void *vn, void *vg, uint32_t desc) 119 { 120 int i, oprsz = simd_oprsz(desc) / 8; 121 uint8_t *pg = vg; 122 uint64_t *n = vn; 123 uint64_t *a = za; 124 125 for (i = 0; i < oprsz; i++) { 126 if (pg[H1(i)] & 1) { 127 a[tile_vslice_index(i)] = n[i]; 128 } 129 } 130 } 131 132 void HELPER(sme_mova_cz_q)(void *za, void *vn, void *vg, uint32_t desc) 133 { 134 int i, oprsz = simd_oprsz(desc) / 16; 135 uint16_t *pg = vg; 136 Int128 *n = vn; 137 Int128 *a = za; 138 139 /* 140 * Int128 is used here simply to copy 16 bytes, and to simplify 141 * the address arithmetic. 142 */ 143 for (i = 0; i < oprsz; i++) { 144 if (pg[H2(i)] & 1) { 145 a[tile_vslice_index(i)] = n[i]; 146 } 147 } 148 } 149 150 #undef DO_MOVA_C 151 152 /* 153 * Move ZArray column to Zreg vector. 154 */ 155 #define DO_MOVA_Z(NAME, TYPE, H) \ 156 void HELPER(NAME)(void *vd, void *za, void *vg, uint32_t desc) \ 157 { \ 158 int i, oprsz = simd_oprsz(desc); \ 159 for (i = 0; i < oprsz; ) { \ 160 uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ 161 do { \ 162 if (pg & 1) { \ 163 *(TYPE *)(vd + H(i)) = *(TYPE *)(za + tile_vslice_offset(i)); \ 164 } \ 165 i += sizeof(TYPE); \ 166 pg >>= sizeof(TYPE); \ 167 } while (i & 15); \ 168 } \ 169 } 170 171 DO_MOVA_Z(sme_mova_zc_b, uint8_t, H1) 172 DO_MOVA_Z(sme_mova_zc_h, uint16_t, H1_2) 173 DO_MOVA_Z(sme_mova_zc_s, uint32_t, H1_4) 174 175 void HELPER(sme_mova_zc_d)(void *vd, void *za, void *vg, uint32_t desc) 176 { 177 int i, oprsz = simd_oprsz(desc) / 8; 178 uint8_t *pg = vg; 179 uint64_t *d = vd; 180 uint64_t *a = za; 181 182 for (i = 0; i < oprsz; i++) { 183 if (pg[H1(i)] & 1) { 184 d[i] = a[tile_vslice_index(i)]; 185 } 186 } 187 } 188 189 void HELPER(sme_mova_zc_q)(void *vd, void *za, void *vg, uint32_t desc) 190 { 191 int i, oprsz = simd_oprsz(desc) / 16; 192 uint16_t *pg = vg; 193 Int128 *d = vd; 194 Int128 *a = za; 195 196 /* 197 * Int128 is used here simply to copy 16 bytes, and to simplify 198 * the address arithmetic. 199 */ 200 for (i = 0; i < oprsz; i++, za += sizeof(ARMVectorReg)) { 201 if (pg[H2(i)] & 1) { 202 d[i] = a[tile_vslice_index(i)]; 203 } 204 } 205 } 206 207 #undef DO_MOVA_Z 208 209 /* 210 * Clear elements in a tile slice comprising len bytes. 211 */ 212 213 typedef void ClearFn(void *ptr, size_t off, size_t len); 214 215 static void clear_horizontal(void *ptr, size_t off, size_t len) 216 { 217 memset(ptr + off, 0, len); 218 } 219 220 static void clear_vertical_b(void *vptr, size_t off, size_t len) 221 { 222 for (size_t i = 0; i < len; ++i) { 223 *(uint8_t *)(vptr + tile_vslice_offset(i + off)) = 0; 224 } 225 } 226 227 static void clear_vertical_h(void *vptr, size_t off, size_t len) 228 { 229 for (size_t i = 0; i < len; i += 2) { 230 *(uint16_t *)(vptr + tile_vslice_offset(i + off)) = 0; 231 } 232 } 233 234 static void clear_vertical_s(void *vptr, size_t off, size_t len) 235 { 236 for (size_t i = 0; i < len; i += 4) { 237 *(uint32_t *)(vptr + tile_vslice_offset(i + off)) = 0; 238 } 239 } 240 241 static void clear_vertical_d(void *vptr, size_t off, size_t len) 242 { 243 for (size_t i = 0; i < len; i += 8) { 244 *(uint64_t *)(vptr + tile_vslice_offset(i + off)) = 0; 245 } 246 } 247 248 static void clear_vertical_q(void *vptr, size_t off, size_t len) 249 { 250 for (size_t i = 0; i < len; i += 16) { 251 memset(vptr + tile_vslice_offset(i + off), 0, 16); 252 } 253 } 254 255 /* 256 * Copy elements from an array into a tile slice comprising len bytes. 257 */ 258 259 typedef void CopyFn(void *dst, const void *src, size_t len); 260 261 static void copy_horizontal(void *dst, const void *src, size_t len) 262 { 263 memcpy(dst, src, len); 264 } 265 266 static void copy_vertical_b(void *vdst, const void *vsrc, size_t len) 267 { 268 const uint8_t *src = vsrc; 269 uint8_t *dst = vdst; 270 size_t i; 271 272 for (i = 0; i < len; ++i) { 273 dst[tile_vslice_index(i)] = src[i]; 274 } 275 } 276 277 static void copy_vertical_h(void *vdst, const void *vsrc, size_t len) 278 { 279 const uint16_t *src = vsrc; 280 uint16_t *dst = vdst; 281 size_t i; 282 283 for (i = 0; i < len / 2; ++i) { 284 dst[tile_vslice_index(i)] = src[i]; 285 } 286 } 287 288 static void copy_vertical_s(void *vdst, const void *vsrc, size_t len) 289 { 290 const uint32_t *src = vsrc; 291 uint32_t *dst = vdst; 292 size_t i; 293 294 for (i = 0; i < len / 4; ++i) { 295 dst[tile_vslice_index(i)] = src[i]; 296 } 297 } 298 299 static void copy_vertical_d(void *vdst, const void *vsrc, size_t len) 300 { 301 const uint64_t *src = vsrc; 302 uint64_t *dst = vdst; 303 size_t i; 304 305 for (i = 0; i < len / 8; ++i) { 306 dst[tile_vslice_index(i)] = src[i]; 307 } 308 } 309 310 static void copy_vertical_q(void *vdst, const void *vsrc, size_t len) 311 { 312 for (size_t i = 0; i < len; i += 16) { 313 memcpy(vdst + tile_vslice_offset(i), vsrc + i, 16); 314 } 315 } 316 317 /* 318 * Host and TLB primitives for vertical tile slice addressing. 319 */ 320 321 #define DO_LD(NAME, TYPE, HOST, TLB) \ 322 static inline void sme_##NAME##_v_host(void *za, intptr_t off, void *host) \ 323 { \ 324 TYPE val = HOST(host); \ 325 *(TYPE *)(za + tile_vslice_offset(off)) = val; \ 326 } \ 327 static inline void sme_##NAME##_v_tlb(CPUARMState *env, void *za, \ 328 intptr_t off, target_ulong addr, uintptr_t ra) \ 329 { \ 330 TYPE val = TLB(env, useronly_clean_ptr(addr), ra); \ 331 *(TYPE *)(za + tile_vslice_offset(off)) = val; \ 332 } 333 334 #define DO_ST(NAME, TYPE, HOST, TLB) \ 335 static inline void sme_##NAME##_v_host(void *za, intptr_t off, void *host) \ 336 { \ 337 TYPE val = *(TYPE *)(za + tile_vslice_offset(off)); \ 338 HOST(host, val); \ 339 } \ 340 static inline void sme_##NAME##_v_tlb(CPUARMState *env, void *za, \ 341 intptr_t off, target_ulong addr, uintptr_t ra) \ 342 { \ 343 TYPE val = *(TYPE *)(za + tile_vslice_offset(off)); \ 344 TLB(env, useronly_clean_ptr(addr), val, ra); \ 345 } 346 347 /* 348 * The ARMVectorReg elements are stored in host-endian 64-bit units. 349 * For 128-bit quantities, the sequence defined by the Elem[] pseudocode 350 * corresponds to storing the two 64-bit pieces in little-endian order. 351 */ 352 #define DO_LDQ(HNAME, VNAME, BE, HOST, TLB) \ 353 static inline void HNAME##_host(void *za, intptr_t off, void *host) \ 354 { \ 355 uint64_t val0 = HOST(host), val1 = HOST(host + 8); \ 356 uint64_t *ptr = za + off; \ 357 ptr[0] = BE ? val1 : val0, ptr[1] = BE ? val0 : val1; \ 358 } \ 359 static inline void VNAME##_v_host(void *za, intptr_t off, void *host) \ 360 { \ 361 HNAME##_host(za, tile_vslice_offset(off), host); \ 362 } \ 363 static inline void HNAME##_tlb(CPUARMState *env, void *za, intptr_t off, \ 364 target_ulong addr, uintptr_t ra) \ 365 { \ 366 uint64_t val0 = TLB(env, useronly_clean_ptr(addr), ra); \ 367 uint64_t val1 = TLB(env, useronly_clean_ptr(addr + 8), ra); \ 368 uint64_t *ptr = za + off; \ 369 ptr[0] = BE ? val1 : val0, ptr[1] = BE ? val0 : val1; \ 370 } \ 371 static inline void VNAME##_v_tlb(CPUARMState *env, void *za, intptr_t off, \ 372 target_ulong addr, uintptr_t ra) \ 373 { \ 374 HNAME##_tlb(env, za, tile_vslice_offset(off), addr, ra); \ 375 } 376 377 #define DO_STQ(HNAME, VNAME, BE, HOST, TLB) \ 378 static inline void HNAME##_host(void *za, intptr_t off, void *host) \ 379 { \ 380 uint64_t *ptr = za + off; \ 381 HOST(host, ptr[BE]); \ 382 HOST(host + 8, ptr[!BE]); \ 383 } \ 384 static inline void VNAME##_v_host(void *za, intptr_t off, void *host) \ 385 { \ 386 HNAME##_host(za, tile_vslice_offset(off), host); \ 387 } \ 388 static inline void HNAME##_tlb(CPUARMState *env, void *za, intptr_t off, \ 389 target_ulong addr, uintptr_t ra) \ 390 { \ 391 uint64_t *ptr = za + off; \ 392 TLB(env, useronly_clean_ptr(addr), ptr[BE], ra); \ 393 TLB(env, useronly_clean_ptr(addr + 8), ptr[!BE], ra); \ 394 } \ 395 static inline void VNAME##_v_tlb(CPUARMState *env, void *za, intptr_t off, \ 396 target_ulong addr, uintptr_t ra) \ 397 { \ 398 HNAME##_tlb(env, za, tile_vslice_offset(off), addr, ra); \ 399 } 400 401 DO_LD(ld1b, uint8_t, ldub_p, cpu_ldub_data_ra) 402 DO_LD(ld1h_be, uint16_t, lduw_be_p, cpu_lduw_be_data_ra) 403 DO_LD(ld1h_le, uint16_t, lduw_le_p, cpu_lduw_le_data_ra) 404 DO_LD(ld1s_be, uint32_t, ldl_be_p, cpu_ldl_be_data_ra) 405 DO_LD(ld1s_le, uint32_t, ldl_le_p, cpu_ldl_le_data_ra) 406 DO_LD(ld1d_be, uint64_t, ldq_be_p, cpu_ldq_be_data_ra) 407 DO_LD(ld1d_le, uint64_t, ldq_le_p, cpu_ldq_le_data_ra) 408 409 DO_LDQ(sve_ld1qq_be, sme_ld1q_be, 1, ldq_be_p, cpu_ldq_be_data_ra) 410 DO_LDQ(sve_ld1qq_le, sme_ld1q_le, 0, ldq_le_p, cpu_ldq_le_data_ra) 411 412 DO_ST(st1b, uint8_t, stb_p, cpu_stb_data_ra) 413 DO_ST(st1h_be, uint16_t, stw_be_p, cpu_stw_be_data_ra) 414 DO_ST(st1h_le, uint16_t, stw_le_p, cpu_stw_le_data_ra) 415 DO_ST(st1s_be, uint32_t, stl_be_p, cpu_stl_be_data_ra) 416 DO_ST(st1s_le, uint32_t, stl_le_p, cpu_stl_le_data_ra) 417 DO_ST(st1d_be, uint64_t, stq_be_p, cpu_stq_be_data_ra) 418 DO_ST(st1d_le, uint64_t, stq_le_p, cpu_stq_le_data_ra) 419 420 DO_STQ(sve_st1qq_be, sme_st1q_be, 1, stq_be_p, cpu_stq_be_data_ra) 421 DO_STQ(sve_st1qq_le, sme_st1q_le, 0, stq_le_p, cpu_stq_le_data_ra) 422 423 #undef DO_LD 424 #undef DO_ST 425 #undef DO_LDQ 426 #undef DO_STQ 427 428 /* 429 * Common helper for all contiguous predicated loads. 430 */ 431 432 static inline QEMU_ALWAYS_INLINE 433 void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, 434 const target_ulong addr, uint32_t desc, const uintptr_t ra, 435 const int esz, uint32_t mtedesc, bool vertical, 436 sve_ldst1_host_fn *host_fn, 437 sve_ldst1_tlb_fn *tlb_fn, 438 ClearFn *clr_fn, 439 CopyFn *cpy_fn) 440 { 441 const intptr_t reg_max = simd_oprsz(desc); 442 const intptr_t esize = 1 << esz; 443 intptr_t reg_off, reg_last; 444 SVEContLdSt info; 445 void *host; 446 int flags; 447 448 /* Find the active elements. */ 449 if (!sve_cont_ldst_elements(&info, addr, vg, reg_max, esz, esize)) { 450 /* The entire predicate was false; no load occurs. */ 451 clr_fn(za, 0, reg_max); 452 return; 453 } 454 455 /* Probe the page(s). Exit with exception for any invalid page. */ 456 sve_cont_ldst_pages(&info, FAULT_ALL, env, addr, MMU_DATA_LOAD, ra); 457 458 /* Handle watchpoints for all active elements. */ 459 sve_cont_ldst_watchpoints(&info, env, vg, addr, esize, esize, 460 BP_MEM_READ, ra); 461 462 /* 463 * Handle mte checks for all active elements. 464 * Since TBI must be set for MTE, !mtedesc => !mte_active. 465 */ 466 if (mtedesc) { 467 sve_cont_ldst_mte_check(&info, env, vg, addr, esize, esize, 468 mtedesc, ra); 469 } 470 471 flags = info.page[0].flags | info.page[1].flags; 472 if (unlikely(flags != 0)) { 473 #ifdef CONFIG_USER_ONLY 474 g_assert_not_reached(); 475 #else 476 /* 477 * At least one page includes MMIO. 478 * Any bus operation can fail with cpu_transaction_failed, 479 * which for ARM will raise SyncExternal. Perform the load 480 * into scratch memory to preserve register state until the end. 481 */ 482 ARMVectorReg scratch = { }; 483 484 reg_off = info.reg_off_first[0]; 485 reg_last = info.reg_off_last[1]; 486 if (reg_last < 0) { 487 reg_last = info.reg_off_split; 488 if (reg_last < 0) { 489 reg_last = info.reg_off_last[0]; 490 } 491 } 492 493 do { 494 uint64_t pg = vg[reg_off >> 6]; 495 do { 496 if ((pg >> (reg_off & 63)) & 1) { 497 tlb_fn(env, &scratch, reg_off, addr + reg_off, ra); 498 } 499 reg_off += esize; 500 } while (reg_off & 63); 501 } while (reg_off <= reg_last); 502 503 cpy_fn(za, &scratch, reg_max); 504 return; 505 #endif 506 } 507 508 /* The entire operation is in RAM, on valid pages. */ 509 510 reg_off = info.reg_off_first[0]; 511 reg_last = info.reg_off_last[0]; 512 host = info.page[0].host; 513 514 if (!vertical) { 515 memset(za, 0, reg_max); 516 } else if (reg_off) { 517 clr_fn(za, 0, reg_off); 518 } 519 520 set_helper_retaddr(ra); 521 522 while (reg_off <= reg_last) { 523 uint64_t pg = vg[reg_off >> 6]; 524 do { 525 if ((pg >> (reg_off & 63)) & 1) { 526 host_fn(za, reg_off, host + reg_off); 527 } else if (vertical) { 528 clr_fn(za, reg_off, esize); 529 } 530 reg_off += esize; 531 } while (reg_off <= reg_last && (reg_off & 63)); 532 } 533 534 clear_helper_retaddr(); 535 536 /* 537 * Use the slow path to manage the cross-page misalignment. 538 * But we know this is RAM and cannot trap. 539 */ 540 reg_off = info.reg_off_split; 541 if (unlikely(reg_off >= 0)) { 542 tlb_fn(env, za, reg_off, addr + reg_off, ra); 543 } 544 545 reg_off = info.reg_off_first[1]; 546 if (unlikely(reg_off >= 0)) { 547 reg_last = info.reg_off_last[1]; 548 host = info.page[1].host; 549 550 set_helper_retaddr(ra); 551 552 do { 553 uint64_t pg = vg[reg_off >> 6]; 554 do { 555 if ((pg >> (reg_off & 63)) & 1) { 556 host_fn(za, reg_off, host + reg_off); 557 } else if (vertical) { 558 clr_fn(za, reg_off, esize); 559 } 560 reg_off += esize; 561 } while (reg_off & 63); 562 } while (reg_off <= reg_last); 563 564 clear_helper_retaddr(); 565 } 566 } 567 568 static inline QEMU_ALWAYS_INLINE 569 void sme_ld1_mte(CPUARMState *env, void *za, uint64_t *vg, 570 target_ulong addr, uint32_t desc, uintptr_t ra, 571 const int esz, bool vertical, 572 sve_ldst1_host_fn *host_fn, 573 sve_ldst1_tlb_fn *tlb_fn, 574 ClearFn *clr_fn, 575 CopyFn *cpy_fn) 576 { 577 uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); 578 int bit55 = extract64(addr, 55, 1); 579 580 /* Remove mtedesc from the normal sve descriptor. */ 581 desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); 582 583 /* Perform gross MTE suppression early. */ 584 if (!tbi_check(mtedesc, bit55) || 585 tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { 586 mtedesc = 0; 587 } 588 589 sme_ld1(env, za, vg, addr, desc, ra, esz, mtedesc, vertical, 590 host_fn, tlb_fn, clr_fn, cpy_fn); 591 } 592 593 #define DO_LD(L, END, ESZ) \ 594 void HELPER(sme_ld1##L##END##_h)(CPUARMState *env, void *za, void *vg, \ 595 target_ulong addr, uint32_t desc) \ 596 { \ 597 sme_ld1(env, za, vg, addr, desc, GETPC(), ESZ, 0, false, \ 598 sve_ld1##L##L##END##_host, sve_ld1##L##L##END##_tlb, \ 599 clear_horizontal, copy_horizontal); \ 600 } \ 601 void HELPER(sme_ld1##L##END##_v)(CPUARMState *env, void *za, void *vg, \ 602 target_ulong addr, uint32_t desc) \ 603 { \ 604 sme_ld1(env, za, vg, addr, desc, GETPC(), ESZ, 0, true, \ 605 sme_ld1##L##END##_v_host, sme_ld1##L##END##_v_tlb, \ 606 clear_vertical_##L, copy_vertical_##L); \ 607 } \ 608 void HELPER(sme_ld1##L##END##_h_mte)(CPUARMState *env, void *za, void *vg, \ 609 target_ulong addr, uint32_t desc) \ 610 { \ 611 sme_ld1_mte(env, za, vg, addr, desc, GETPC(), ESZ, false, \ 612 sve_ld1##L##L##END##_host, sve_ld1##L##L##END##_tlb, \ 613 clear_horizontal, copy_horizontal); \ 614 } \ 615 void HELPER(sme_ld1##L##END##_v_mte)(CPUARMState *env, void *za, void *vg, \ 616 target_ulong addr, uint32_t desc) \ 617 { \ 618 sme_ld1_mte(env, za, vg, addr, desc, GETPC(), ESZ, true, \ 619 sme_ld1##L##END##_v_host, sme_ld1##L##END##_v_tlb, \ 620 clear_vertical_##L, copy_vertical_##L); \ 621 } 622 623 DO_LD(b, , MO_8) 624 DO_LD(h, _be, MO_16) 625 DO_LD(h, _le, MO_16) 626 DO_LD(s, _be, MO_32) 627 DO_LD(s, _le, MO_32) 628 DO_LD(d, _be, MO_64) 629 DO_LD(d, _le, MO_64) 630 DO_LD(q, _be, MO_128) 631 DO_LD(q, _le, MO_128) 632 633 #undef DO_LD 634 635 /* 636 * Common helper for all contiguous predicated stores. 637 */ 638 639 static inline QEMU_ALWAYS_INLINE 640 void sme_st1(CPUARMState *env, void *za, uint64_t *vg, 641 const target_ulong addr, uint32_t desc, const uintptr_t ra, 642 const int esz, uint32_t mtedesc, bool vertical, 643 sve_ldst1_host_fn *host_fn, 644 sve_ldst1_tlb_fn *tlb_fn) 645 { 646 const intptr_t reg_max = simd_oprsz(desc); 647 const intptr_t esize = 1 << esz; 648 intptr_t reg_off, reg_last; 649 SVEContLdSt info; 650 void *host; 651 int flags; 652 653 /* Find the active elements. */ 654 if (!sve_cont_ldst_elements(&info, addr, vg, reg_max, esz, esize)) { 655 /* The entire predicate was false; no store occurs. */ 656 return; 657 } 658 659 /* Probe the page(s). Exit with exception for any invalid page. */ 660 sve_cont_ldst_pages(&info, FAULT_ALL, env, addr, MMU_DATA_STORE, ra); 661 662 /* Handle watchpoints for all active elements. */ 663 sve_cont_ldst_watchpoints(&info, env, vg, addr, esize, esize, 664 BP_MEM_WRITE, ra); 665 666 /* 667 * Handle mte checks for all active elements. 668 * Since TBI must be set for MTE, !mtedesc => !mte_active. 669 */ 670 if (mtedesc) { 671 sve_cont_ldst_mte_check(&info, env, vg, addr, esize, esize, 672 mtedesc, ra); 673 } 674 675 flags = info.page[0].flags | info.page[1].flags; 676 if (unlikely(flags != 0)) { 677 #ifdef CONFIG_USER_ONLY 678 g_assert_not_reached(); 679 #else 680 /* 681 * At least one page includes MMIO. 682 * Any bus operation can fail with cpu_transaction_failed, 683 * which for ARM will raise SyncExternal. We cannot avoid 684 * this fault and will leave with the store incomplete. 685 */ 686 reg_off = info.reg_off_first[0]; 687 reg_last = info.reg_off_last[1]; 688 if (reg_last < 0) { 689 reg_last = info.reg_off_split; 690 if (reg_last < 0) { 691 reg_last = info.reg_off_last[0]; 692 } 693 } 694 695 do { 696 uint64_t pg = vg[reg_off >> 6]; 697 do { 698 if ((pg >> (reg_off & 63)) & 1) { 699 tlb_fn(env, za, reg_off, addr + reg_off, ra); 700 } 701 reg_off += esize; 702 } while (reg_off & 63); 703 } while (reg_off <= reg_last); 704 return; 705 #endif 706 } 707 708 reg_off = info.reg_off_first[0]; 709 reg_last = info.reg_off_last[0]; 710 host = info.page[0].host; 711 712 set_helper_retaddr(ra); 713 714 while (reg_off <= reg_last) { 715 uint64_t pg = vg[reg_off >> 6]; 716 do { 717 if ((pg >> (reg_off & 63)) & 1) { 718 host_fn(za, reg_off, host + reg_off); 719 } 720 reg_off += 1 << esz; 721 } while (reg_off <= reg_last && (reg_off & 63)); 722 } 723 724 clear_helper_retaddr(); 725 726 /* 727 * Use the slow path to manage the cross-page misalignment. 728 * But we know this is RAM and cannot trap. 729 */ 730 reg_off = info.reg_off_split; 731 if (unlikely(reg_off >= 0)) { 732 tlb_fn(env, za, reg_off, addr + reg_off, ra); 733 } 734 735 reg_off = info.reg_off_first[1]; 736 if (unlikely(reg_off >= 0)) { 737 reg_last = info.reg_off_last[1]; 738 host = info.page[1].host; 739 740 set_helper_retaddr(ra); 741 742 do { 743 uint64_t pg = vg[reg_off >> 6]; 744 do { 745 if ((pg >> (reg_off & 63)) & 1) { 746 host_fn(za, reg_off, host + reg_off); 747 } 748 reg_off += 1 << esz; 749 } while (reg_off & 63); 750 } while (reg_off <= reg_last); 751 752 clear_helper_retaddr(); 753 } 754 } 755 756 static inline QEMU_ALWAYS_INLINE 757 void sme_st1_mte(CPUARMState *env, void *za, uint64_t *vg, target_ulong addr, 758 uint32_t desc, uintptr_t ra, int esz, bool vertical, 759 sve_ldst1_host_fn *host_fn, 760 sve_ldst1_tlb_fn *tlb_fn) 761 { 762 uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); 763 int bit55 = extract64(addr, 55, 1); 764 765 /* Remove mtedesc from the normal sve descriptor. */ 766 desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); 767 768 /* Perform gross MTE suppression early. */ 769 if (!tbi_check(mtedesc, bit55) || 770 tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { 771 mtedesc = 0; 772 } 773 774 sme_st1(env, za, vg, addr, desc, ra, esz, mtedesc, 775 vertical, host_fn, tlb_fn); 776 } 777 778 #define DO_ST(L, END, ESZ) \ 779 void HELPER(sme_st1##L##END##_h)(CPUARMState *env, void *za, void *vg, \ 780 target_ulong addr, uint32_t desc) \ 781 { \ 782 sme_st1(env, za, vg, addr, desc, GETPC(), ESZ, 0, false, \ 783 sve_st1##L##L##END##_host, sve_st1##L##L##END##_tlb); \ 784 } \ 785 void HELPER(sme_st1##L##END##_v)(CPUARMState *env, void *za, void *vg, \ 786 target_ulong addr, uint32_t desc) \ 787 { \ 788 sme_st1(env, za, vg, addr, desc, GETPC(), ESZ, 0, true, \ 789 sme_st1##L##END##_v_host, sme_st1##L##END##_v_tlb); \ 790 } \ 791 void HELPER(sme_st1##L##END##_h_mte)(CPUARMState *env, void *za, void *vg, \ 792 target_ulong addr, uint32_t desc) \ 793 { \ 794 sme_st1_mte(env, za, vg, addr, desc, GETPC(), ESZ, false, \ 795 sve_st1##L##L##END##_host, sve_st1##L##L##END##_tlb); \ 796 } \ 797 void HELPER(sme_st1##L##END##_v_mte)(CPUARMState *env, void *za, void *vg, \ 798 target_ulong addr, uint32_t desc) \ 799 { \ 800 sme_st1_mte(env, za, vg, addr, desc, GETPC(), ESZ, true, \ 801 sme_st1##L##END##_v_host, sme_st1##L##END##_v_tlb); \ 802 } 803 804 DO_ST(b, , MO_8) 805 DO_ST(h, _be, MO_16) 806 DO_ST(h, _le, MO_16) 807 DO_ST(s, _be, MO_32) 808 DO_ST(s, _le, MO_32) 809 DO_ST(d, _be, MO_64) 810 DO_ST(d, _le, MO_64) 811 DO_ST(q, _be, MO_128) 812 DO_ST(q, _le, MO_128) 813 814 #undef DO_ST 815 816 void HELPER(sme_addha_s)(void *vzda, void *vzn, void *vpn, 817 void *vpm, uint32_t desc) 818 { 819 intptr_t row, col, oprsz = simd_oprsz(desc) / 4; 820 uint64_t *pn = vpn, *pm = vpm; 821 uint32_t *zda = vzda, *zn = vzn; 822 823 for (row = 0; row < oprsz; ) { 824 uint64_t pa = pn[row >> 4]; 825 do { 826 if (pa & 1) { 827 for (col = 0; col < oprsz; ) { 828 uint64_t pb = pm[col >> 4]; 829 do { 830 if (pb & 1) { 831 zda[tile_vslice_index(row) + H4(col)] += zn[H4(col)]; 832 } 833 pb >>= 4; 834 } while (++col & 15); 835 } 836 } 837 pa >>= 4; 838 } while (++row & 15); 839 } 840 } 841 842 void HELPER(sme_addha_d)(void *vzda, void *vzn, void *vpn, 843 void *vpm, uint32_t desc) 844 { 845 intptr_t row, col, oprsz = simd_oprsz(desc) / 8; 846 uint8_t *pn = vpn, *pm = vpm; 847 uint64_t *zda = vzda, *zn = vzn; 848 849 for (row = 0; row < oprsz; ++row) { 850 if (pn[H1(row)] & 1) { 851 for (col = 0; col < oprsz; ++col) { 852 if (pm[H1(col)] & 1) { 853 zda[tile_vslice_index(row) + col] += zn[col]; 854 } 855 } 856 } 857 } 858 } 859 860 void HELPER(sme_addva_s)(void *vzda, void *vzn, void *vpn, 861 void *vpm, uint32_t desc) 862 { 863 intptr_t row, col, oprsz = simd_oprsz(desc) / 4; 864 uint64_t *pn = vpn, *pm = vpm; 865 uint32_t *zda = vzda, *zn = vzn; 866 867 for (row = 0; row < oprsz; ) { 868 uint64_t pa = pn[row >> 4]; 869 do { 870 if (pa & 1) { 871 uint32_t zn_row = zn[H4(row)]; 872 for (col = 0; col < oprsz; ) { 873 uint64_t pb = pm[col >> 4]; 874 do { 875 if (pb & 1) { 876 zda[tile_vslice_index(row) + H4(col)] += zn_row; 877 } 878 pb >>= 4; 879 } while (++col & 15); 880 } 881 } 882 pa >>= 4; 883 } while (++row & 15); 884 } 885 } 886 887 void HELPER(sme_addva_d)(void *vzda, void *vzn, void *vpn, 888 void *vpm, uint32_t desc) 889 { 890 intptr_t row, col, oprsz = simd_oprsz(desc) / 8; 891 uint8_t *pn = vpn, *pm = vpm; 892 uint64_t *zda = vzda, *zn = vzn; 893 894 for (row = 0; row < oprsz; ++row) { 895 if (pn[H1(row)] & 1) { 896 uint64_t zn_row = zn[row]; 897 for (col = 0; col < oprsz; ++col) { 898 if (pm[H1(col)] & 1) { 899 zda[tile_vslice_index(row) + col] += zn_row; 900 } 901 } 902 } 903 } 904 } 905 906 void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, 907 void *vpm, void *vst, uint32_t desc) 908 { 909 intptr_t row, col, oprsz = simd_maxsz(desc); 910 uint32_t neg = simd_data(desc) << 31; 911 uint16_t *pn = vpn, *pm = vpm; 912 float_status fpst; 913 914 /* 915 * Make a copy of float_status because this operation does not 916 * update the cumulative fp exception status. It also produces 917 * default nans. 918 */ 919 fpst = *(float_status *)vst; 920 set_default_nan_mode(true, &fpst); 921 922 for (row = 0; row < oprsz; ) { 923 uint16_t pa = pn[H2(row >> 4)]; 924 do { 925 if (pa & 1) { 926 void *vza_row = vza + tile_vslice_offset(row); 927 uint32_t n = *(uint32_t *)(vzn + H1_4(row)) ^ neg; 928 929 for (col = 0; col < oprsz; ) { 930 uint16_t pb = pm[H2(col >> 4)]; 931 do { 932 if (pb & 1) { 933 uint32_t *a = vza_row + H1_4(col); 934 uint32_t *m = vzm + H1_4(col); 935 *a = float32_muladd(n, *m, *a, 0, &fpst); 936 } 937 col += 4; 938 pb >>= 4; 939 } while (col & 15); 940 } 941 } 942 row += 4; 943 pa >>= 4; 944 } while (row & 15); 945 } 946 } 947 948 void HELPER(sme_fmopa_d)(void *vza, void *vzn, void *vzm, void *vpn, 949 void *vpm, void *vst, uint32_t desc) 950 { 951 intptr_t row, col, oprsz = simd_oprsz(desc) / 8; 952 uint64_t neg = (uint64_t)simd_data(desc) << 63; 953 uint64_t *za = vza, *zn = vzn, *zm = vzm; 954 uint8_t *pn = vpn, *pm = vpm; 955 float_status fpst = *(float_status *)vst; 956 957 set_default_nan_mode(true, &fpst); 958 959 for (row = 0; row < oprsz; ++row) { 960 if (pn[H1(row)] & 1) { 961 uint64_t *za_row = &za[tile_vslice_index(row)]; 962 uint64_t n = zn[row] ^ neg; 963 964 for (col = 0; col < oprsz; ++col) { 965 if (pm[H1(col)] & 1) { 966 uint64_t *a = &za_row[col]; 967 *a = float64_muladd(n, zm[col], *a, 0, &fpst); 968 } 969 } 970 } 971 } 972 } 973 974 /* 975 * Alter PAIR as needed for controlling predicates being false, 976 * and for NEG on an enabled row element. 977 */ 978 static inline uint32_t f16mop_adj_pair(uint32_t pair, uint32_t pg, uint32_t neg) 979 { 980 /* 981 * The pseudocode uses a conditional negate after the conditional zero. 982 * It is simpler here to unconditionally negate before conditional zero. 983 */ 984 pair ^= neg; 985 if (!(pg & 1)) { 986 pair &= 0xffff0000u; 987 } 988 if (!(pg & 4)) { 989 pair &= 0x0000ffffu; 990 } 991 return pair; 992 } 993 994 static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2, 995 float_status *s_f16, float_status *s_std, 996 float_status *s_odd) 997 { 998 /* 999 * We need three different float_status for different parts of this 1000 * operation: 1001 * - the input conversion of the float16 values must use the 1002 * f16-specific float_status, so that the FPCR.FZ16 control is applied 1003 * - operations on float32 including the final accumulation must use 1004 * the normal float_status, so that FPCR.FZ is applied 1005 * - we have pre-set-up copy of s_std which is set to round-to-odd, 1006 * for the multiply (see below) 1007 */ 1008 float64 e1r = float16_to_float64(e1 & 0xffff, true, s_f16); 1009 float64 e1c = float16_to_float64(e1 >> 16, true, s_f16); 1010 float64 e2r = float16_to_float64(e2 & 0xffff, true, s_f16); 1011 float64 e2c = float16_to_float64(e2 >> 16, true, s_f16); 1012 float64 t64; 1013 float32 t32; 1014 1015 /* 1016 * The ARM pseudocode function FPDot performs both multiplies 1017 * and the add with a single rounding operation. Emulate this 1018 * by performing the first multiply in round-to-odd, then doing 1019 * the second multiply as fused multiply-add, and rounding to 1020 * float32 all in one step. 1021 */ 1022 t64 = float64_mul(e1r, e2r, s_odd); 1023 t64 = float64r32_muladd(e1c, e2c, t64, 0, s_std); 1024 1025 /* This conversion is exact, because we've already rounded. */ 1026 t32 = float64_to_float32(t64, s_std); 1027 1028 /* The final accumulation step is not fused. */ 1029 return float32_add(sum, t32, s_std); 1030 } 1031 1032 void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, 1033 void *vpm, CPUARMState *env, uint32_t desc) 1034 { 1035 intptr_t row, col, oprsz = simd_maxsz(desc); 1036 uint32_t neg = simd_data(desc) * 0x80008000u; 1037 uint16_t *pn = vpn, *pm = vpm; 1038 float_status fpst_odd, fpst_std, fpst_f16; 1039 1040 /* 1041 * Make copies of fp_status and fp_status_f16, because this operation 1042 * does not update the cumulative fp exception status. It also 1043 * produces default NaNs. We also need a second copy of fp_status with 1044 * round-to-odd -- see above. 1045 */ 1046 fpst_f16 = env->vfp.fp_status_f16; 1047 fpst_std = env->vfp.fp_status; 1048 set_default_nan_mode(true, &fpst_std); 1049 set_default_nan_mode(true, &fpst_f16); 1050 fpst_odd = fpst_std; 1051 set_float_rounding_mode(float_round_to_odd, &fpst_odd); 1052 1053 for (row = 0; row < oprsz; ) { 1054 uint16_t prow = pn[H2(row >> 4)]; 1055 do { 1056 void *vza_row = vza + tile_vslice_offset(row); 1057 uint32_t n = *(uint32_t *)(vzn + H1_4(row)); 1058 1059 n = f16mop_adj_pair(n, prow, neg); 1060 1061 for (col = 0; col < oprsz; ) { 1062 uint16_t pcol = pm[H2(col >> 4)]; 1063 do { 1064 if (prow & pcol & 0b0101) { 1065 uint32_t *a = vza_row + H1_4(col); 1066 uint32_t m = *(uint32_t *)(vzm + H1_4(col)); 1067 1068 m = f16mop_adj_pair(m, pcol, 0); 1069 *a = f16_dotadd(*a, n, m, 1070 &fpst_f16, &fpst_std, &fpst_odd); 1071 } 1072 col += 4; 1073 pcol >>= 4; 1074 } while (col & 15); 1075 } 1076 row += 4; 1077 prow >>= 4; 1078 } while (row & 15); 1079 } 1080 } 1081 1082 void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, void *vpn, 1083 void *vpm, uint32_t desc) 1084 { 1085 intptr_t row, col, oprsz = simd_maxsz(desc); 1086 uint32_t neg = simd_data(desc) * 0x80008000u; 1087 uint16_t *pn = vpn, *pm = vpm; 1088 1089 for (row = 0; row < oprsz; ) { 1090 uint16_t prow = pn[H2(row >> 4)]; 1091 do { 1092 void *vza_row = vza + tile_vslice_offset(row); 1093 uint32_t n = *(uint32_t *)(vzn + H1_4(row)); 1094 1095 n = f16mop_adj_pair(n, prow, neg); 1096 1097 for (col = 0; col < oprsz; ) { 1098 uint16_t pcol = pm[H2(col >> 4)]; 1099 do { 1100 if (prow & pcol & 0b0101) { 1101 uint32_t *a = vza_row + H1_4(col); 1102 uint32_t m = *(uint32_t *)(vzm + H1_4(col)); 1103 1104 m = f16mop_adj_pair(m, pcol, 0); 1105 *a = bfdotadd(*a, n, m); 1106 } 1107 col += 4; 1108 pcol >>= 4; 1109 } while (col & 15); 1110 } 1111 row += 4; 1112 prow >>= 4; 1113 } while (row & 15); 1114 } 1115 } 1116 1117 typedef uint32_t IMOPFn32(uint32_t, uint32_t, uint32_t, uint8_t, bool); 1118 static inline void do_imopa_s(uint32_t *za, uint32_t *zn, uint32_t *zm, 1119 uint8_t *pn, uint8_t *pm, 1120 uint32_t desc, IMOPFn32 *fn) 1121 { 1122 intptr_t row, col, oprsz = simd_oprsz(desc) / 4; 1123 bool neg = simd_data(desc); 1124 1125 for (row = 0; row < oprsz; ++row) { 1126 uint8_t pa = (pn[H1(row >> 1)] >> ((row & 1) * 4)) & 0xf; 1127 uint32_t *za_row = &za[tile_vslice_index(row)]; 1128 uint32_t n = zn[H4(row)]; 1129 1130 for (col = 0; col < oprsz; ++col) { 1131 uint8_t pb = pm[H1(col >> 1)] >> ((col & 1) * 4); 1132 uint32_t *a = &za_row[H4(col)]; 1133 1134 *a = fn(n, zm[H4(col)], *a, pa & pb, neg); 1135 } 1136 } 1137 } 1138 1139 typedef uint64_t IMOPFn64(uint64_t, uint64_t, uint64_t, uint8_t, bool); 1140 static inline void do_imopa_d(uint64_t *za, uint64_t *zn, uint64_t *zm, 1141 uint8_t *pn, uint8_t *pm, 1142 uint32_t desc, IMOPFn64 *fn) 1143 { 1144 intptr_t row, col, oprsz = simd_oprsz(desc) / 8; 1145 bool neg = simd_data(desc); 1146 1147 for (row = 0; row < oprsz; ++row) { 1148 uint8_t pa = pn[H1(row)]; 1149 uint64_t *za_row = &za[tile_vslice_index(row)]; 1150 uint64_t n = zn[row]; 1151 1152 for (col = 0; col < oprsz; ++col) { 1153 uint8_t pb = pm[H1(col)]; 1154 uint64_t *a = &za_row[col]; 1155 1156 *a = fn(n, zm[col], *a, pa & pb, neg); 1157 } 1158 } 1159 } 1160 1161 #define DEF_IMOP_32(NAME, NTYPE, MTYPE) \ 1162 static uint32_t NAME(uint32_t n, uint32_t m, uint32_t a, uint8_t p, bool neg) \ 1163 { \ 1164 uint32_t sum = 0; \ 1165 /* Apply P to N as a mask, making the inactive elements 0. */ \ 1166 n &= expand_pred_b(p); \ 1167 sum += (NTYPE)(n >> 0) * (MTYPE)(m >> 0); \ 1168 sum += (NTYPE)(n >> 8) * (MTYPE)(m >> 8); \ 1169 sum += (NTYPE)(n >> 16) * (MTYPE)(m >> 16); \ 1170 sum += (NTYPE)(n >> 24) * (MTYPE)(m >> 24); \ 1171 return neg ? a - sum : a + sum; \ 1172 } 1173 1174 #define DEF_IMOP_64(NAME, NTYPE, MTYPE) \ 1175 static uint64_t NAME(uint64_t n, uint64_t m, uint64_t a, uint8_t p, bool neg) \ 1176 { \ 1177 uint64_t sum = 0; \ 1178 /* Apply P to N as a mask, making the inactive elements 0. */ \ 1179 n &= expand_pred_h(p); \ 1180 sum += (int64_t)(NTYPE)(n >> 0) * (MTYPE)(m >> 0); \ 1181 sum += (int64_t)(NTYPE)(n >> 16) * (MTYPE)(m >> 16); \ 1182 sum += (int64_t)(NTYPE)(n >> 32) * (MTYPE)(m >> 32); \ 1183 sum += (int64_t)(NTYPE)(n >> 48) * (MTYPE)(m >> 48); \ 1184 return neg ? a - sum : a + sum; \ 1185 } 1186 1187 DEF_IMOP_32(smopa_s, int8_t, int8_t) 1188 DEF_IMOP_32(umopa_s, uint8_t, uint8_t) 1189 DEF_IMOP_32(sumopa_s, int8_t, uint8_t) 1190 DEF_IMOP_32(usmopa_s, uint8_t, int8_t) 1191 1192 DEF_IMOP_64(smopa_d, int16_t, int16_t) 1193 DEF_IMOP_64(umopa_d, uint16_t, uint16_t) 1194 DEF_IMOP_64(sumopa_d, int16_t, uint16_t) 1195 DEF_IMOP_64(usmopa_d, uint16_t, int16_t) 1196 1197 #define DEF_IMOPH(NAME, S) \ 1198 void HELPER(sme_##NAME##_##S)(void *vza, void *vzn, void *vzm, \ 1199 void *vpn, void *vpm, uint32_t desc) \ 1200 { do_imopa_##S(vza, vzn, vzm, vpn, vpm, desc, NAME##_##S); } 1201 1202 DEF_IMOPH(smopa, s) 1203 DEF_IMOPH(umopa, s) 1204 DEF_IMOPH(sumopa, s) 1205 DEF_IMOPH(usmopa, s) 1206 1207 DEF_IMOPH(smopa, d) 1208 DEF_IMOPH(umopa, d) 1209 DEF_IMOPH(sumopa, d) 1210 DEF_IMOPH(usmopa, d) 1211