1 /* Subroutines used for code generation for RISC-V. 2 Copyright (C) 2023-2024 Free Software Foundation, Inc. 3 Contributed by Christoph Mllner (christoph.muellner (at) vrull.eu). 4 5 This file is part of GCC. 6 7 GCC is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3, or (at your option) 10 any later version. 11 12 GCC is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GCC; see the file COPYING3. If not see 19 <http://www.gnu.org/licenses/>. */ 20 21 #define IN_TARGET_CODE 1 22 23 #include "config.h" 24 #include "system.h" 25 #include "coretypes.h" 26 #include "target.h" 27 #include "backend.h" 28 #include "tree.h" 29 #include "rtl.h" 30 #include "insn-attr.h" 31 #include "explow.h" 32 #include "memmodel.h" 33 #include "emit-rtl.h" 34 #include "optabs.h" 35 #include "poly-int.h" 36 #include "output.h" 37 #include "regs.h" 38 #include "riscv-protos.h" 39 40 /* If X is a PLUS of a CONST_INT, return the two terms in *BASE_PTR 41 and *OFFSET_PTR. Return X in *BASE_PTR and 0 in *OFFSET_PTR otherwise. */ 42 43 static void 44 split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr) 45 { 46 if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1))) 47 { 48 *base_ptr = XEXP (x, 0); 49 *offset_ptr = INTVAL (XEXP (x, 1)); 50 } 51 else 52 { 53 *base_ptr = x; 54 *offset_ptr = 0; 55 } 56 } 57 58 /* Output a mempair instruction with the provided OPERANDS. 59 LOAD_P is true if a we have a pair of loads (stores otherwise). 60 MODE is the access mode (DI or SI). 61 CODE is the extension code (UNKNOWN, SIGN_EXTEND or ZERO_EXTEND). 62 This instruction does not handle invalid inputs gracefully, 63 but is full of assertions to ensure that only valid instructions 64 are emitted. */ 65 66 const char * 67 th_mempair_output_move (rtx operands[4], bool load_p, 68 machine_mode mode, RTX_CODE code) 69 { 70 rtx reg1, reg2, mem1, mem2, base1, base2; 71 HOST_WIDE_INT offset1, offset2; 72 rtx output_operands[5]; 73 const char* format; 74 75 gcc_assert (mode == SImode || mode == DImode); 76 77 /* Paired 64-bit access instructions have a fixed shift amount of 4. 78 Paired 32-bit access instructions have a fixed shift amount of 3. */ 79 unsigned shamt = (mode == DImode) ? 4 : 3; 80 81 if (load_p) 82 { 83 reg1 = copy_rtx (operands[0]); 84 reg2 = copy_rtx (operands[2]); 85 mem1 = copy_rtx (operands[1]); 86 mem2 = copy_rtx (operands[3]); 87 88 if (mode == SImode) 89 if (code == ZERO_EXTEND) 90 format = "th.lwud\t%0, %1, (%2), %3, %4"; 91 else //SIGN_EXTEND or UNKNOWN 92 format = "th.lwd\t%0, %1, (%2), %3, %4"; 93 else 94 format = "th.ldd\t%0, %1, (%2), %3, %4"; 95 } 96 else 97 { 98 reg1 = copy_rtx (operands[1]); 99 reg2 = copy_rtx (operands[3]); 100 mem1 = copy_rtx (operands[0]); 101 mem2 = copy_rtx (operands[2]); 102 103 if (mode == SImode) 104 format = "th.swd\t%z0, %z1, (%2), %3, %4"; 105 else 106 format = "th.sdd\t%z0, %z1, (%2), %3, %4"; 107 } 108 109 split_plus (XEXP (mem1, 0), &base1, &offset1); 110 split_plus (XEXP (mem2, 0), &base2, &offset2); 111 gcc_assert (rtx_equal_p (base1, base2)); 112 auto size1 = MEM_SIZE (mem1); 113 auto size2 = MEM_SIZE (mem2); 114 gcc_assert (known_eq (size1, size2)); 115 gcc_assert (known_eq (offset1 + size1, offset2)); 116 117 HOST_WIDE_INT imm2 = offset1 >> shamt; 118 119 /* Make sure all mempair instruction constraints are met. */ 120 gcc_assert (imm2 >= 0 && imm2 < 4); 121 gcc_assert ((imm2 << shamt) == offset1); 122 gcc_assert (REG_P (reg1)); 123 gcc_assert (REG_P (reg2)); 124 gcc_assert (REG_P (base1)); 125 if (load_p) 126 { 127 gcc_assert (REGNO (reg1) != REGNO (reg2)); 128 gcc_assert (REGNO (reg1) != REGNO (base1)); 129 gcc_assert (REGNO (reg2) != REGNO (base1)); 130 } 131 132 /* Output the mempair instruction. */ 133 output_operands[0] = copy_rtx (reg1); 134 output_operands[1] = copy_rtx (reg2); 135 output_operands[2] = copy_rtx (base1); 136 output_operands[3] = gen_rtx_CONST_INT (mode, imm2); 137 output_operands[4] = gen_rtx_CONST_INT (mode, shamt); 138 output_asm_insn (format, output_operands); 139 140 return ""; 141 } 142 143 /* Analyse if a pair of loads/stores MEM1 and MEM2 with given MODE 144 are consecutive so they can be merged into a mempair instruction. 145 RESERVED will be set to true, if a reversal of the accesses is 146 required (false otherwise). Returns true if the accesses can be 147 merged (even if reversing is necessary) and false if not. */ 148 149 static bool 150 th_mempair_check_consecutive_mems (machine_mode mode, rtx *mem1, rtx *mem2, 151 bool *reversed) 152 { 153 rtx base1, base2, offset1, offset2; 154 extract_base_offset_in_addr (*mem1, &base1, &offset1); 155 extract_base_offset_in_addr (*mem2, &base2, &offset2); 156 157 /* Make sure both mems are in base+offset form. */ 158 if (!base1 || !base2) 159 return false; 160 161 /* If both mems use the same base register, just check the offsets. */ 162 if (rtx_equal_p (base1, base2)) 163 { 164 auto size = GET_MODE_SIZE (mode); 165 166 if (known_eq (UINTVAL (offset1) + size, UINTVAL (offset2))) 167 { 168 *reversed = false; 169 return true; 170 } 171 172 if (known_eq (UINTVAL (offset2) + size, UINTVAL (offset1))) 173 { 174 *reversed = true; 175 return true; 176 } 177 178 return false; 179 } 180 181 return false; 182 } 183 184 /* Check if the given MEM can be used to define the address of a mempair 185 instruction. */ 186 187 static bool 188 th_mempair_operand_p (rtx mem, machine_mode mode) 189 { 190 if (!MEM_SIZE_KNOWN_P (mem)) 191 return false; 192 193 /* Only DI or SI mempair instructions exist. */ 194 gcc_assert (mode == SImode || mode == DImode); 195 auto mem_sz = MEM_SIZE (mem); 196 auto mode_sz = GET_MODE_SIZE (mode); 197 if (!known_eq (mem_sz, mode_sz)) 198 return false; 199 200 /* Paired 64-bit access instructions have a fixed shift amount of 4. 201 Paired 32-bit access instructions have a fixed shift amount of 3. */ 202 machine_mode mem_mode = GET_MODE (mem); 203 unsigned shamt = (mem_mode == DImode) ? 4 : 3; 204 205 rtx base; 206 HOST_WIDE_INT offset; 207 split_plus (XEXP (mem, 0), &base, &offset); 208 HOST_WIDE_INT imm2 = offset >> shamt; 209 210 if (imm2 < 0 || imm2 >= 4) 211 return false; 212 213 if ((imm2 << shamt) != offset) 214 return false; 215 216 return true; 217 } 218 219 static bool 220 th_mempair_load_overlap_p (rtx reg1, rtx reg2, rtx mem) 221 { 222 if (REGNO (reg1) == REGNO (reg2)) 223 return true; 224 225 if (reg_overlap_mentioned_p (reg1, mem)) 226 return true; 227 228 rtx base; 229 HOST_WIDE_INT offset; 230 split_plus (XEXP (mem, 0), &base, &offset); 231 232 if (!REG_P (base)) 233 return true; 234 235 if (REG_P (base)) 236 { 237 if (REGNO (base) == REGNO (reg1) 238 || REGNO (base) == REGNO (reg2)) 239 return true; 240 } 241 242 return false; 243 } 244 245 /* Given OPERANDS of consecutive load/store, check if we can merge 246 them into load-pair or store-pair instructions. 247 LOAD is true if they are load instructions. 248 MODE is the mode of memory operation. */ 249 250 bool 251 th_mempair_operands_p (rtx operands[4], bool load_p, 252 machine_mode mode) 253 { 254 rtx mem_1, mem_2, reg_1, reg_2; 255 256 if (load_p) 257 { 258 reg_1 = operands[0]; 259 mem_1 = operands[1]; 260 reg_2 = operands[2]; 261 mem_2 = operands[3]; 262 if (!REG_P (reg_1) || !REG_P (reg_2)) 263 return false; 264 if (th_mempair_load_overlap_p (reg_1, reg_2, mem_1)) 265 return false; 266 if (th_mempair_load_overlap_p (reg_1, reg_2, mem_2)) 267 return false; 268 } 269 else 270 { 271 mem_1 = operands[0]; 272 reg_1 = operands[1]; 273 mem_2 = operands[2]; 274 reg_2 = operands[3]; 275 } 276 277 /* Check if the registers are GP registers. */ 278 if (!REG_P (reg_1) || !GP_REG_P (REGNO (reg_1)) 279 || !REG_P (reg_2) || !GP_REG_P (REGNO (reg_2))) 280 return false; 281 282 /* The mems cannot be volatile. */ 283 if (!MEM_P (mem_1) || !MEM_P (mem_2)) 284 return false; 285 if (MEM_VOLATILE_P (mem_1) || MEM_VOLATILE_P (mem_2)) 286 return false; 287 288 289 /* Check if the addresses are in the form of [base+offset]. */ 290 bool reversed = false; 291 if (!th_mempair_check_consecutive_mems (mode, &mem_1, &mem_2, &reversed)) 292 return false; 293 294 /* If necessary, reverse the local copy of the operands to simplify 295 testing of alignments and mempair operand. */ 296 if (reversed) 297 { 298 std::swap (mem_1, mem_2); 299 std::swap (reg_1, reg_2); 300 } 301 302 /* If we have slow unaligned access, we only accept aligned memory. */ 303 if (riscv_slow_unaligned_access_p 304 && known_lt (MEM_ALIGN (mem_1), GET_MODE_SIZE (mode) * BITS_PER_UNIT)) 305 return false; 306 307 /* The first memory accesses must be a mempair operand. */ 308 if (!th_mempair_operand_p (mem_1, mode)) 309 return false; 310 311 /* The operands must be of the same size. */ 312 gcc_assert (known_eq (GET_MODE_SIZE (GET_MODE (mem_1)), 313 GET_MODE_SIZE (GET_MODE (mem_2)))); 314 315 return true; 316 } 317 318 /* Given OPERANDS of consecutive load/store that can be merged, 319 swap them if they are not in ascending order. */ 320 321 void 322 th_mempair_order_operands (rtx operands[4], bool load_p, machine_mode mode) 323 { 324 int mem_op = load_p ? 1 : 0; 325 bool reversed = false; 326 if (!th_mempair_check_consecutive_mems (mode, 327 operands + mem_op, 328 operands + mem_op + 2, 329 &reversed)) 330 gcc_unreachable (); 331 332 if (reversed) 333 { 334 /* Irrespective of whether this is a load or a store, 335 we do the same swap. */ 336 std::swap (operands[0], operands[2]); 337 std::swap (operands[1], operands[3]); 338 } 339 } 340 341 /* Similar like riscv_save_reg, but saves two registers to memory 342 and marks the resulting instruction as frame-related. */ 343 344 static void 345 th_mempair_save_regs (rtx operands[4]) 346 { 347 rtx set1 = gen_rtx_SET (operands[0], operands[1]); 348 rtx set2 = gen_rtx_SET (operands[2], operands[3]); 349 rtx dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2)); 350 rtx insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set1, set2))); 351 RTX_FRAME_RELATED_P (insn) = 1; 352 353 XVECEXP (dwarf, 0, 0) = copy_rtx (set1); 354 XVECEXP (dwarf, 0, 1) = copy_rtx (set2); 355 RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 0)) = 1; 356 RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 1)) = 1; 357 add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf); 358 } 359 360 /* Similar like riscv_restore_reg, but restores two registers from memory 361 and marks the instruction frame-related. */ 362 363 static void 364 th_mempair_restore_regs (rtx operands[4]) 365 { 366 rtx set1 = gen_rtx_SET (operands[0], operands[1]); 367 rtx set2 = gen_rtx_SET (operands[2], operands[3]); 368 rtx insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set1, set2))); 369 RTX_FRAME_RELATED_P (insn) = 1; 370 add_reg_note (insn, REG_CFA_RESTORE, operands[0]); 371 add_reg_note (insn, REG_CFA_RESTORE, operands[2]); 372 } 373 374 /* Prepare the OPERANDS array to emit a mempair instruction using the 375 provided information. No checks are performed, the resulting array 376 should be validated using th_mempair_operands_p(). */ 377 378 void 379 th_mempair_prepare_save_restore_operands (rtx operands[4], 380 bool load_p, machine_mode mode, 381 int regno, HOST_WIDE_INT offset, 382 int regno2, HOST_WIDE_INT offset2) 383 { 384 int reg_op = load_p ? 0 : 1; 385 int mem_op = load_p ? 1 : 0; 386 387 rtx mem1 = plus_constant (mode, stack_pointer_rtx, offset); 388 mem1 = gen_frame_mem (mode, mem1); 389 rtx mem2 = plus_constant (mode, stack_pointer_rtx, offset2); 390 mem2 = gen_frame_mem (mode, mem2); 391 392 operands[reg_op] = gen_rtx_REG (mode, regno); 393 operands[mem_op] = mem1; 394 operands[2 + reg_op] = gen_rtx_REG (mode, regno2); 395 operands[2 + mem_op] = mem2; 396 } 397 398 /* Emit a mempair instruction to save/restore two registers to/from stack. */ 399 400 void 401 th_mempair_save_restore_regs (rtx operands[4], bool load_p, 402 machine_mode mode) 403 { 404 gcc_assert (th_mempair_operands_p (operands, load_p, mode)); 405 406 th_mempair_order_operands (operands, load_p, mode); 407 408 if (load_p) 409 th_mempair_restore_regs (operands); 410 else 411 th_mempair_save_regs (operands); 412 } 413 414 /* Return true if X can be represented as signed immediate of NBITS bits. 415 The immediate is assumed to be shifted by LSHAMT bits left. */ 416 417 static bool 418 valid_signed_immediate (rtx x, unsigned nbits, unsigned lshamt) 419 { 420 if (GET_CODE (x) != CONST_INT) 421 return false; 422 423 HOST_WIDE_INT v = INTVAL (x); 424 425 HOST_WIDE_INT vunshifted = v >> lshamt; 426 427 /* Make sure we did not shift out any bits. */ 428 if (vunshifted << lshamt != v) 429 return false; 430 431 unsigned HOST_WIDE_INT imm_reach = 1LL << nbits; 432 return ((unsigned HOST_WIDE_INT) vunshifted + imm_reach/2 < imm_reach); 433 } 434 435 /* Return the address RTX of a move to/from memory 436 instruction. */ 437 438 static rtx 439 th_get_move_mem_addr (rtx dest, rtx src, bool load) 440 { 441 rtx mem; 442 443 if (load) 444 mem = src; 445 else 446 mem = dest; 447 448 gcc_assert (GET_CODE (mem) == MEM); 449 return XEXP (mem, 0); 450 } 451 452 /* Return true if X is a valid address for T-Head's memory addressing modes 453 with pre/post modification for machine mode MODE. 454 If it is, fill in INFO appropriately (if non-NULL). 455 If STRICT_P is true then REG_OK_STRICT is in effect. */ 456 457 static bool 458 th_memidx_classify_address_modify (struct riscv_address_info *info, rtx x, 459 machine_mode mode, bool strict_p) 460 { 461 if (!TARGET_XTHEADMEMIDX) 462 return false; 463 464 if (GET_MODE_CLASS (mode) != MODE_INT 465 || GET_MODE_SIZE (mode).to_constant () > UNITS_PER_WORD) 466 return false; 467 468 if (GET_CODE (x) != POST_MODIFY 469 && GET_CODE (x) != PRE_MODIFY) 470 return false; 471 472 rtx reg = XEXP (x, 0); 473 rtx exp = XEXP (x, 1); 474 rtx expreg = XEXP (exp, 0); 475 rtx expoff = XEXP (exp, 1); 476 477 if (GET_CODE (exp) != PLUS 478 || !rtx_equal_p (expreg, reg) 479 || !CONST_INT_P (expoff) 480 || !riscv_valid_base_register_p (reg, mode, strict_p)) 481 return false; 482 483 /* The offset is calculated as (sign_extend(imm5) << imm2) */ 484 const int shamt_bits = 2; 485 for (int shamt = 0; shamt < (1 << shamt_bits); shamt++) 486 { 487 const int nbits = 5; 488 if (valid_signed_immediate (expoff, nbits, shamt)) 489 { 490 if (info) 491 { 492 info->type = ADDRESS_REG_WB; 493 info->reg = reg; 494 info->offset = expoff; 495 info->shift = shamt; 496 } 497 return true; 498 } 499 } 500 501 return false; 502 } 503 504 /* Return TRUE if X is a MEM with a legitimate modify address. */ 505 506 bool 507 th_memidx_legitimate_modify_p (rtx x) 508 { 509 if (!MEM_P (x)) 510 return false; 511 512 /* Get the mode from the MEM and unpack it. */ 513 machine_mode mode = GET_MODE (x); 514 x = XEXP (x, 0); 515 516 return th_memidx_classify_address_modify (NULL, x, mode, reload_completed); 517 } 518 519 /* Return TRUE if X is a MEM with a legitimate modify address 520 and the address is POST_MODIFY (if POST is true) or a PRE_MODIFY 521 (otherwise). */ 522 523 bool 524 th_memidx_legitimate_modify_p (rtx x, bool post) 525 { 526 if (!th_memidx_legitimate_modify_p (x)) 527 return false; 528 529 /* Unpack the MEM and check the code. */ 530 x = XEXP (x, 0); 531 if (post) 532 return GET_CODE (x) == POST_MODIFY; 533 else 534 return GET_CODE (x) == PRE_MODIFY; 535 } 536 537 /* Provide a buffer for a th.lXia/th.lXib/th.sXia/th.sXib instruction 538 for the given MODE. If LOAD is true, a load instruction will be 539 provided (otherwise, a store instruction). If X is not suitable 540 return NULL. */ 541 542 static const char * 543 th_memidx_output_modify (rtx dest, rtx src, machine_mode mode, bool load) 544 { 545 char format[24]; 546 rtx output_operands[2]; 547 rtx x = th_get_move_mem_addr (dest, src, load); 548 549 /* Validate x. */ 550 if (!th_memidx_classify_address_modify (NULL, x, mode, reload_completed)) 551 return NULL; 552 553 int index = exact_log2 (GET_MODE_SIZE (mode).to_constant ()); 554 bool post = GET_CODE (x) == POST_MODIFY; 555 556 const char *const insn[][4] = { 557 { 558 "th.sbi%s\t%%z1,%%0", 559 "th.shi%s\t%%z1,%%0", 560 "th.swi%s\t%%z1,%%0", 561 "th.sdi%s\t%%z1,%%0" 562 }, 563 { 564 "th.lbui%s\t%%0,%%1", 565 "th.lhui%s\t%%0,%%1", 566 "th.lwi%s\t%%0,%%1", 567 "th.ldi%s\t%%0,%%1" 568 } 569 }; 570 571 snprintf (format, sizeof (format), insn[load][index], post ? "a" : "b"); 572 output_operands[0] = dest; 573 output_operands[1] = src; 574 output_asm_insn (format, output_operands); 575 return ""; 576 } 577 578 static bool 579 is_memidx_mode (machine_mode mode) 580 { 581 if (mode == QImode || mode == HImode || mode == SImode) 582 return true; 583 584 if (mode == DImode && TARGET_64BIT) 585 return true; 586 587 return false; 588 } 589 590 static bool 591 is_fmemidx_mode (machine_mode mode) 592 { 593 if (mode == SFmode && TARGET_HARD_FLOAT) 594 return true; 595 596 if (mode == DFmode && TARGET_DOUBLE_FLOAT) 597 return true; 598 599 return false; 600 } 601 602 /* Return true if X is a valid address for T-Head's memory addressing modes 603 with scaled register offsets for machine mode MODE. 604 If it is, fill in INFO appropriately (if non-NULL). 605 If STRICT_P is true then REG_OK_STRICT is in effect. */ 606 607 static bool 608 th_memidx_classify_address_index (struct riscv_address_info *info, rtx x, 609 machine_mode mode, bool strict_p) 610 { 611 /* Ensure that the mode is supported. */ 612 if (!(TARGET_XTHEADMEMIDX && is_memidx_mode (mode)) 613 && !(TARGET_XTHEADMEMIDX 614 && TARGET_XTHEADFMEMIDX && is_fmemidx_mode (mode))) 615 return false; 616 617 if (GET_CODE (x) != PLUS) 618 return false; 619 620 rtx reg = XEXP (x, 0); 621 enum riscv_address_type type; 622 rtx offset = XEXP (x, 1); 623 int shift; 624 625 if (!riscv_valid_base_register_p (reg, mode, strict_p)) 626 return false; 627 628 /* (reg:X) */ 629 if (REG_P (offset) 630 && GET_MODE (offset) == Xmode) 631 { 632 type = ADDRESS_REG_REG; 633 shift = 0; 634 offset = offset; 635 } 636 /* (zero_extend:DI (reg:SI)) */ 637 else if (GET_CODE (offset) == ZERO_EXTEND 638 && GET_MODE (offset) == DImode 639 && GET_MODE (XEXP (offset, 0)) == SImode) 640 { 641 type = ADDRESS_REG_UREG; 642 shift = 0; 643 offset = XEXP (offset, 0); 644 } 645 /* (ashift:X (reg:X) (const_int shift)) */ 646 else if (GET_CODE (offset) == ASHIFT 647 && GET_MODE (offset) == Xmode 648 && REG_P (XEXP (offset, 0)) 649 && GET_MODE (XEXP (offset, 0)) == Xmode 650 && CONST_INT_P (XEXP (offset, 1)) 651 && IN_RANGE (INTVAL (XEXP (offset, 1)), 0, 3)) 652 { 653 type = ADDRESS_REG_REG; 654 shift = INTVAL (XEXP (offset, 1)); 655 offset = XEXP (offset, 0); 656 } 657 /* (ashift:DI (zero_extend:DI (reg:SI)) (const_int shift)) */ 658 else if (GET_CODE (offset) == ASHIFT 659 && GET_MODE (offset) == DImode 660 && GET_CODE (XEXP (offset, 0)) == ZERO_EXTEND 661 && GET_MODE (XEXP (offset, 0)) == DImode 662 && GET_MODE (XEXP (XEXP (offset, 0), 0)) == SImode 663 && CONST_INT_P (XEXP (offset, 1)) 664 && IN_RANGE(INTVAL (XEXP (offset, 1)), 0, 3)) 665 { 666 type = ADDRESS_REG_UREG; 667 shift = INTVAL (XEXP (offset, 1)); 668 offset = XEXP (XEXP (offset, 0), 0); 669 } 670 else 671 return false; 672 673 if (!strict_p && GET_CODE (offset) == SUBREG) 674 offset = SUBREG_REG (offset); 675 676 if (!REG_P (offset) 677 || !riscv_regno_mode_ok_for_base_p (REGNO (offset), mode, strict_p)) 678 return false; 679 680 if (info) 681 { 682 info->reg = reg; 683 info->type = type; 684 info->offset = offset; 685 info->shift = shift; 686 } 687 return true; 688 } 689 690 /* Return TRUE if X is a MEM with a legitimate indexed address. */ 691 692 bool 693 th_memidx_legitimate_index_p (rtx x) 694 { 695 if (!MEM_P (x)) 696 return false; 697 698 /* Get the mode from the MEM and unpack it. */ 699 machine_mode mode = GET_MODE (x); 700 x = XEXP (x, 0); 701 702 return th_memidx_classify_address_index (NULL, x, mode, reload_completed); 703 } 704 705 /* Return TRUE if X is a MEM with a legitimate indexed address 706 and the offset register is zero-extended (if UINDEX is true) 707 or sign-extended (otherwise). */ 708 709 bool 710 th_memidx_legitimate_index_p (rtx x, bool uindex) 711 { 712 if (!MEM_P (x)) 713 return false; 714 715 /* Get the mode from the MEM and unpack it. */ 716 machine_mode mode = GET_MODE (x); 717 x = XEXP (x, 0); 718 719 struct riscv_address_info info; 720 if (!th_memidx_classify_address_index (&info, x, mode, reload_completed)) 721 return false; 722 723 if (uindex) 724 return info.type == ADDRESS_REG_UREG; 725 else 726 return info.type == ADDRESS_REG_REG; 727 } 728 729 /* Provide a buffer for a th.lrX/th.lurX/th.srX/th.surX instruction 730 for the given MODE. If LOAD is true, a load instruction will be 731 provided (otherwise, a store instruction). If X is not suitable 732 return NULL. */ 733 734 static const char * 735 th_memidx_output_index (rtx dest, rtx src, machine_mode mode, bool load) 736 { 737 struct riscv_address_info info; 738 char format[24]; 739 rtx output_operands[2]; 740 rtx x = th_get_move_mem_addr (dest, src, load); 741 742 /* Validate x. */ 743 if (!th_memidx_classify_address_index (&info, x, mode, reload_completed)) 744 return NULL; 745 746 int index = exact_log2 (GET_MODE_SIZE (mode).to_constant ()); 747 bool uindex = info.type == ADDRESS_REG_UREG; 748 749 const char *const insn[][4] = { 750 { 751 "th.s%srb\t%%z1,%%0", 752 "th.s%srh\t%%z1,%%0", 753 "th.s%srw\t%%z1,%%0", 754 "th.s%srd\t%%z1,%%0" 755 }, 756 { 757 "th.l%srbu\t%%0,%%1", 758 "th.l%srhu\t%%0,%%1", 759 "th.l%srw\t%%0,%%1", 760 "th.l%srd\t%%0,%%1" 761 } 762 }; 763 764 snprintf (format, sizeof (format), insn[load][index], uindex ? "u" : ""); 765 output_operands[0] = dest; 766 output_operands[1] = src; 767 output_asm_insn (format, output_operands); 768 return ""; 769 } 770 771 /* Provide a buffer for a th.flX/th.fluX/th.fsX/th.fsuX instruction 772 for the given MODE. If LOAD is true, a load instruction will be 773 provided (otherwise, a store instruction). If X is not suitable 774 return NULL. */ 775 776 static const char * 777 th_fmemidx_output_index (rtx dest, rtx src, machine_mode mode, bool load) 778 { 779 struct riscv_address_info info; 780 char format[24]; 781 rtx output_operands[2]; 782 rtx x = th_get_move_mem_addr (dest, src, load); 783 784 /* Validate x. */ 785 if (!th_memidx_classify_address_index (&info, x, mode, false)) 786 return NULL; 787 788 int index = exact_log2 (GET_MODE_SIZE (mode).to_constant ()) - 2; 789 bool uindex = info.type == ADDRESS_REG_UREG; 790 791 const char *const insn[][2] = { 792 { 793 "th.fs%srw\t%%z1,%%0", 794 "th.fs%srd\t%%z1,%%0" 795 }, 796 { 797 "th.fl%srw\t%%0,%%1", 798 "th.fl%srd\t%%0,%%1" 799 } 800 }; 801 802 snprintf (format, sizeof (format), insn[load][index], uindex ? "u" : ""); 803 output_operands[0] = dest; 804 output_operands[1] = src; 805 output_asm_insn (format, output_operands); 806 return ""; 807 } 808 809 /* Return true if X is a valid address for T-Head's memory addressing modes 810 for machine mode MODE. If it is, fill in INFO appropriately (if non-NULL). 811 If STRICT_P is true then REG_OK_STRICT is in effect. */ 812 813 bool 814 th_classify_address (struct riscv_address_info *info, rtx x, 815 machine_mode mode, bool strict_p) 816 { 817 switch (GET_CODE (x)) 818 { 819 case PLUS: 820 if (th_memidx_classify_address_index (info, x, mode, strict_p)) 821 return true; 822 break; 823 824 case POST_MODIFY: 825 case PRE_MODIFY: 826 if (th_memidx_classify_address_modify (info, x, mode, strict_p)) 827 return true; 828 break; 829 830 default: 831 return false; 832 } 833 834 return false; 835 } 836 837 /* Provide a string containing a XTheadMemIdx instruction for the given 838 MODE from the provided SRC to the provided DEST. 839 A pointer to a NULL-terminated string containing the instruction will 840 be returned if a suitable instruction is available. Otherwise, this 841 function returns NULL. */ 842 843 const char * 844 th_output_move (rtx dest, rtx src) 845 { 846 enum rtx_code dest_code, src_code; 847 machine_mode mode; 848 const char *insn = NULL; 849 850 dest_code = GET_CODE (dest); 851 src_code = GET_CODE (src); 852 mode = GET_MODE (dest); 853 854 if (!(mode == GET_MODE (src) || src == CONST0_RTX (mode))) 855 return NULL; 856 857 if (dest_code == REG && src_code == MEM) 858 { 859 if (GET_MODE_CLASS (mode) == MODE_INT 860 || (GET_MODE_CLASS (mode) == MODE_FLOAT && GP_REG_P (REGNO (dest)))) 861 { 862 if ((insn = th_memidx_output_index (dest, src, mode, true))) 863 return insn; 864 if ((insn = th_memidx_output_modify (dest, src, mode, true))) 865 return insn; 866 } 867 else if (GET_MODE_CLASS (mode) == MODE_FLOAT && HARDFP_REG_P (REGNO (dest))) 868 { 869 if ((insn = th_fmemidx_output_index (dest, src, mode, true))) 870 return insn; 871 } 872 } 873 else if (dest_code == MEM && (src_code == REG || src == CONST0_RTX (mode))) 874 { 875 if (GET_MODE_CLASS (mode) == MODE_INT 876 || src == CONST0_RTX (mode) 877 || (GET_MODE_CLASS (mode) == MODE_FLOAT && GP_REG_P (REGNO (src)))) 878 { 879 if ((insn = th_memidx_output_index (dest, src, mode, false))) 880 return insn; 881 if ((insn = th_memidx_output_modify (dest, src, mode, false))) 882 return insn; 883 } 884 else if (GET_MODE_CLASS (mode) == MODE_FLOAT && HARDFP_REG_P (REGNO (src))) 885 { 886 if ((insn = th_fmemidx_output_index (dest, src, mode, false))) 887 return insn; 888 } 889 } 890 return NULL; 891 } 892 893 /* Define ASM_OUTPUT_OPCODE to do anything special before 894 emitting an opcode. */ 895 const char * 896 th_asm_output_opcode (FILE *asm_out_file, const char *p) 897 { 898 /* We need to add th. prefix to all the xtheadvector 899 instructions here.*/ 900 if (current_output_insn != NULL) 901 { 902 if (get_attr_type (current_output_insn) == TYPE_VSETVL) 903 { 904 if (strstr (p, "zero")) 905 { 906 if (strstr (p, "zero,zero")) 907 return "th.vsetvli\tzero,zero,e%0,%m1"; 908 else 909 return "th.vsetvli\tzero,%z0,e%1,%m2"; 910 } 911 else 912 { 913 return "th.vsetvli\t%z0,%z1,e%2,%m3"; 914 } 915 } 916 917 if (get_attr_type (current_output_insn) == TYPE_VLDE || 918 get_attr_type (current_output_insn) == TYPE_VSTE || 919 get_attr_type (current_output_insn) == TYPE_VLDFF) 920 { 921 if (strstr (p, "e8") || strstr (p, "e16") || 922 strstr (p, "e32") || strstr (p, "e64")) 923 { 924 get_attr_type (current_output_insn) == TYPE_VSTE 925 ? fputs ("th.vse", asm_out_file) 926 : fputs ("th.vle", asm_out_file); 927 if (strstr (p, "e8")) 928 return p+4; 929 else 930 return p+5; 931 } 932 } 933 934 if (get_attr_type (current_output_insn) == TYPE_VLDS || 935 get_attr_type (current_output_insn) == TYPE_VSTS) 936 { 937 if (strstr (p, "vle8") || strstr (p, "vse8") || 938 strstr (p, "vle16") || strstr (p, "vse16") || 939 strstr (p, "vle32") || strstr (p, "vse32") || 940 strstr (p, "vle64") || strstr (p, "vse64")) 941 { 942 get_attr_type (current_output_insn) == TYPE_VSTS 943 ? fputs ("th.vse", asm_out_file) 944 : fputs ("th.vle", asm_out_file); 945 if (strstr (p, "e8")) 946 return p+4; 947 else 948 return p+5; 949 } 950 else if (strstr (p, "vlse8") || strstr (p, "vsse8") || 951 strstr (p, "vlse16") || strstr (p, "vsse16") || 952 strstr (p, "vlse32") || strstr (p, "vsse32") || 953 strstr (p, "vlse64") || strstr (p, "vsse64")) 954 { 955 get_attr_type (current_output_insn) == TYPE_VSTS 956 ? fputs ("th.vsse", asm_out_file) 957 : fputs ("th.vlse", asm_out_file); 958 if (strstr (p, "e8")) 959 return p+5; 960 else 961 return p+6; 962 } 963 } 964 965 if (get_attr_type (current_output_insn) == TYPE_VLDUX || 966 get_attr_type (current_output_insn) == TYPE_VLDOX) 967 { 968 if (strstr (p, "ei")) 969 { 970 fputs ("th.vlxe", asm_out_file); 971 if (strstr (p, "ei8")) 972 return p+7; 973 else 974 return p+8; 975 } 976 } 977 978 if (get_attr_type (current_output_insn) == TYPE_VSTUX || 979 get_attr_type (current_output_insn) == TYPE_VSTOX) 980 { 981 if (strstr (p, "ei")) 982 { 983 get_attr_type (current_output_insn) == TYPE_VSTUX 984 ? fputs ("th.vsuxe", asm_out_file) 985 : fputs ("th.vsxe", asm_out_file); 986 if (strstr (p, "ei8")) 987 return p+7; 988 else 989 return p+8; 990 } 991 } 992 993 if (get_attr_type (current_output_insn) == TYPE_VLSEGDE || 994 get_attr_type (current_output_insn) == TYPE_VSSEGTE || 995 get_attr_type (current_output_insn) == TYPE_VLSEGDFF) 996 { 997 get_attr_type (current_output_insn) == TYPE_VSSEGTE 998 ? fputs ("th.vsseg", asm_out_file) 999 : fputs ("th.vlseg", asm_out_file); 1000 asm_fprintf (asm_out_file, "%c", p[5]); 1001 fputs ("e", asm_out_file); 1002 if (strstr (p, "e8")) 1003 return p+8; 1004 else 1005 return p+9; 1006 } 1007 1008 if (get_attr_type (current_output_insn) == TYPE_VLSEGDS || 1009 get_attr_type (current_output_insn) == TYPE_VSSEGTS) 1010 { 1011 get_attr_type (current_output_insn) == TYPE_VSSEGTS 1012 ? fputs ("th.vssseg", asm_out_file) 1013 : fputs ("th.vlsseg", asm_out_file); 1014 asm_fprintf (asm_out_file, "%c", p[6]); 1015 fputs ("e", asm_out_file); 1016 if (strstr (p, "e8")) 1017 return p+9; 1018 else 1019 return p+10; 1020 } 1021 1022 if (get_attr_type (current_output_insn) == TYPE_VLSEGDUX || 1023 get_attr_type (current_output_insn) == TYPE_VLSEGDOX) 1024 { 1025 fputs ("th.vlxseg", asm_out_file); 1026 asm_fprintf (asm_out_file, "%c", p[7]); 1027 fputs ("e", asm_out_file); 1028 if (strstr (p, "ei8")) 1029 return p+11; 1030 else 1031 return p+12; 1032 } 1033 1034 if (get_attr_type (current_output_insn) == TYPE_VSSEGTUX || 1035 get_attr_type (current_output_insn) == TYPE_VSSEGTOX) 1036 { 1037 fputs ("th.vsxseg", asm_out_file); 1038 asm_fprintf (asm_out_file, "%c", p[7]); 1039 fputs ("e", asm_out_file); 1040 if (strstr (p, "ei8")) 1041 return p+11; 1042 else 1043 return p+12; 1044 } 1045 1046 if (get_attr_type (current_output_insn) == TYPE_VNSHIFT) 1047 { 1048 if (strstr (p, "vncvt")) 1049 { 1050 fputs ("th.vncvt.x.x.v", asm_out_file); 1051 return p+11; 1052 } 1053 1054 strstr (p, "vnsrl") ? fputs ("th.vnsrl.v", asm_out_file) 1055 : fputs ("th.vnsra.v", asm_out_file); 1056 return p+7; 1057 } 1058 1059 if (get_attr_type (current_output_insn) == TYPE_VNCLIP) 1060 { 1061 if (strstr (p, "vnclipu")) 1062 { 1063 fputs ("th.vnclipu.v", asm_out_file); 1064 return p+9; 1065 } 1066 else 1067 { 1068 fputs ("th.vnclip.v", asm_out_file); 1069 return p+8; 1070 } 1071 } 1072 1073 if (get_attr_type (current_output_insn) == TYPE_VMPOP) 1074 { 1075 fputs ("th.vmpopc", asm_out_file); 1076 return p+5; 1077 } 1078 1079 if (get_attr_type (current_output_insn) == TYPE_VMFFS) 1080 { 1081 fputs ("th.vmfirst", asm_out_file); 1082 return p+6; 1083 } 1084 1085 if (get_attr_type (current_output_insn) == TYPE_VFNCVTFTOI || 1086 get_attr_type (current_output_insn) == TYPE_VFNCVTITOF) 1087 { 1088 if (strstr (p, "xu")) 1089 { 1090 get_attr_type (current_output_insn) == TYPE_VFNCVTFTOI 1091 ? fputs ("th.vfncvt.xu.f.v", asm_out_file) 1092 : fputs ("th.vfncvt.f.xu.v", asm_out_file); 1093 return p+13; 1094 } 1095 else 1096 { 1097 get_attr_type (current_output_insn) == TYPE_VFNCVTFTOI 1098 ? fputs ("th.vfncvt.x.f.v", asm_out_file) 1099 : fputs ("th.vfncvt.f.x.v", asm_out_file); 1100 return p+12; 1101 } 1102 } 1103 1104 if (get_attr_type (current_output_insn) == TYPE_VFNCVTFTOF) 1105 { 1106 fputs ("th.vfncvt.f.f.v", asm_out_file); 1107 return p+12; 1108 } 1109 1110 if (get_attr_type (current_output_insn) == TYPE_VFREDU 1111 && strstr (p, "sum")) 1112 { 1113 fputs ("th.vfredsum", asm_out_file); 1114 return p+9; 1115 } 1116 1117 if (get_attr_type (current_output_insn) == TYPE_VFWREDU 1118 && strstr (p, "sum")) 1119 { 1120 fputs ("th.vfwredsum", asm_out_file); 1121 return p+10; 1122 } 1123 1124 if (p[0] == 'v') 1125 fputs ("th.", asm_out_file); 1126 } 1127 1128 return p; 1129 } 1130 1131 /* Implement TARGET_PRINT_OPERAND_ADDRESS for XTheadMemIdx. */ 1132 1133 bool 1134 th_print_operand_address (FILE *file, machine_mode mode, rtx x) 1135 { 1136 struct riscv_address_info addr; 1137 1138 if (!th_classify_address (&addr, x, mode, reload_completed)) 1139 return false; 1140 1141 switch (addr.type) 1142 { 1143 case ADDRESS_REG_REG: 1144 case ADDRESS_REG_UREG: 1145 fprintf (file, "%s,%s,%u", reg_names[REGNO (addr.reg)], 1146 reg_names[REGNO (addr.offset)], addr.shift); 1147 return true; 1148 1149 case ADDRESS_REG_WB: 1150 fprintf (file, "(%s)," HOST_WIDE_INT_PRINT_DEC ",%u", 1151 reg_names[REGNO (addr.reg)], 1152 INTVAL (addr.offset) >> addr.shift, addr.shift); 1153 return true; 1154 1155 default: 1156 gcc_unreachable (); 1157 } 1158 1159 gcc_unreachable (); 1160 } 1161 1162 /* Number array of registers X1, X5-X7, X10-X17, X28-X31, to be 1163 operated on by instruction th.ipush/th.ipop in XTheadInt. */ 1164 1165 int th_int_regs[] ={ 1166 RETURN_ADDR_REGNUM, 1167 T0_REGNUM, T1_REGNUM, T2_REGNUM, 1168 A0_REGNUM, A1_REGNUM, A2_REGNUM, A3_REGNUM, 1169 A4_REGNUM, A5_REGNUM, A6_REGNUM, A7_REGNUM, 1170 T3_REGNUM, T4_REGNUM, T5_REGNUM, T6_REGNUM, 1171 }; 1172 1173 /* If MASK contains registers X1, X5-X7, X10-X17, X28-X31, then 1174 return the mask composed of these registers, otherwise return 1175 zero. */ 1176 1177 unsigned int 1178 th_int_get_mask (unsigned int mask) 1179 { 1180 unsigned int xtheadint_mask = 0; 1181 1182 if (!TARGET_XTHEADINT || TARGET_64BIT) 1183 return 0; 1184 1185 for (unsigned int i = 0; i < ARRAY_SIZE (th_int_regs); i++) 1186 { 1187 if (!BITSET_P (mask, th_int_regs[i])) 1188 return 0; 1189 1190 xtheadint_mask |= (1 << th_int_regs[i]); 1191 } 1192 1193 return xtheadint_mask; /* Usually 0xf003fce2. */ 1194 } 1195 1196 /* Returns the occupied frame needed to save registers X1, X5-X7, 1197 X10-X17, X28-X31. */ 1198 1199 unsigned int 1200 th_int_get_save_adjustment (void) 1201 { 1202 gcc_assert (TARGET_XTHEADINT && !TARGET_64BIT); 1203 return ARRAY_SIZE (th_int_regs) * UNITS_PER_WORD; 1204 } 1205 1206 rtx 1207 th_int_adjust_cfi_prologue (unsigned int mask) 1208 { 1209 gcc_assert (TARGET_XTHEADINT && !TARGET_64BIT); 1210 1211 rtx dwarf = NULL_RTX; 1212 rtx adjust_sp_rtx, reg, mem, insn; 1213 int saved_size = ARRAY_SIZE (th_int_regs) * UNITS_PER_WORD; 1214 int offset = saved_size; 1215 1216 for (int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) 1217 if (BITSET_P (mask, regno - GP_REG_FIRST)) 1218 { 1219 offset -= UNITS_PER_WORD; 1220 reg = gen_rtx_REG (SImode, regno); 1221 mem = gen_frame_mem (SImode, plus_constant (Pmode, 1222 stack_pointer_rtx, 1223 offset)); 1224 1225 insn = gen_rtx_SET (mem, reg); 1226 dwarf = alloc_reg_note (REG_CFA_OFFSET, insn, dwarf); 1227 } 1228 1229 /* Debug info for adjust sp. */ 1230 adjust_sp_rtx = 1231 gen_rtx_SET (stack_pointer_rtx, 1232 gen_rtx_PLUS (GET_MODE (stack_pointer_rtx), 1233 stack_pointer_rtx, GEN_INT (-saved_size))); 1234 dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx, dwarf); 1235 1236 return dwarf; 1237 } 1238