1/* relax-opt pass of Andes NDS32 cpu for GNU compiler 2 Copyright (C) 2012-2022 Free Software Foundation, Inc. 3 Contributed by Andes Technology Corporation. 4 5 This file is part of GCC. 6 7 GCC is free software; you can redistribute it and/or modify it 8 under the terms of the GNU General Public License as published 9 by the Free Software Foundation; either version 3, or (at your 10 option) any later version. 11 12 GCC is distributed in the hope that it will be useful, but WITHOUT 13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 15 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/* ------------------------------------------------------------------------ */ 22 23#define IN_TARGET_CODE 1 24 25#include "config.h" 26#include "system.h" 27#include "coretypes.h" 28#include "backend.h" 29#include "target.h" 30#include "rtl.h" 31#include "tree.h" 32#include "stringpool.h" 33#include "attribs.h" 34#include "df.h" 35#include "memmodel.h" 36#include "tm_p.h" 37#include "optabs.h" /* For GEN_FCN. */ 38#include "regs.h" 39#include "emit-rtl.h" 40#include "recog.h" 41#include "diagnostic-core.h" 42#include "stor-layout.h" 43#include "varasm.h" 44#include "calls.h" 45#include "output.h" 46#include "explow.h" 47#include "expr.h" 48#include "tm-constrs.h" 49#include "builtins.h" 50#include "cpplib.h" 51#include "insn-attr.h" 52#include "cfgrtl.h" 53#include "tree-pass.h" 54 55using namespace nds32; 56 57/* This is used to create unique relax hint id value. 58 The initial value is 0. */ 59static int relax_group_id = 0; 60 61/* Group the following pattern as relax candidates: 62 63 1. sethi $ra, hi20(sym) 64 ori $ra, $ra, lo12(sym) 65 ==> 66 addi.gp $ra, sym 67 68 2. sethi $ra, hi20(sym) 69 lwi $rb, [$ra + lo12(sym)] 70 ==> 71 lwi.gp $rb, [(sym)] 72 73 3. sethi $ra, hi20(sym) 74 ori $ra, $ra, lo12(sym) 75 lwi $rb, [$ra] 76 swi $rc, [$ra] 77 ==> 78 lwi37 $rb, [(sym)] 79 swi37 $rc, [(sym)] */ 80 81int 82nds32_alloc_relax_group_id () 83{ 84 return relax_group_id++; 85} 86 87/* Return true if is load/store with REG addressing mode 88 and memory mode is SImode. */ 89static bool 90nds32_reg_base_load_store_p (rtx_insn *insn) 91{ 92 rtx mem_src = NULL_RTX; 93 94 switch (get_attr_type (insn)) 95 { 96 case TYPE_LOAD: 97 mem_src = SET_SRC (PATTERN (insn)); 98 break; 99 case TYPE_STORE: 100 mem_src = SET_DEST (PATTERN (insn)); 101 break; 102 default: 103 break; 104 } 105 106 /* Find load/store insn with addressing mode is REG. */ 107 if (mem_src != NULL_RTX) 108 { 109 if ((GET_CODE (mem_src) == ZERO_EXTEND) 110 || (GET_CODE (mem_src) == SIGN_EXTEND)) 111 mem_src = XEXP (mem_src, 0); 112 113 if (GET_CODE (XEXP (mem_src, 0)) == REG) 114 return true; 115 } 116 117 return false; 118} 119 120/* Return true if insn is a sp/fp base or sp/fp plus load-store instruction. */ 121 122static bool 123nds32_sp_base_or_plus_load_store_p (rtx_insn *insn) 124{ 125 rtx mem_src = NULL_RTX; 126 127 switch (get_attr_type (insn)) 128 { 129 case TYPE_LOAD: 130 mem_src = SET_SRC (PATTERN (insn)); 131 break; 132 case TYPE_STORE: 133 mem_src = SET_DEST (PATTERN (insn)); 134 break; 135 default: 136 break; 137 } 138 /* Find load/store insn with addressing mode is REG. */ 139 if (mem_src != NULL_RTX) 140 { 141 if ((GET_CODE (mem_src) == ZERO_EXTEND) 142 || (GET_CODE (mem_src) == SIGN_EXTEND)) 143 mem_src = XEXP (mem_src, 0); 144 145 if ((GET_CODE (XEXP (mem_src, 0)) == PLUS)) 146 mem_src = XEXP (mem_src, 0); 147 148 if (REG_P (XEXP (mem_src, 0)) 149 && ((frame_pointer_needed 150 && REGNO (XEXP (mem_src, 0)) == FP_REGNUM) 151 || REGNO (XEXP (mem_src, 0)) == SP_REGNUM)) 152 return true; 153 } 154 155 return false; 156} 157 158/* Return true if is load with [REG + REG/CONST_INT] addressing mode. */ 159static bool 160nds32_plus_reg_load_store_p (rtx_insn *insn) 161{ 162 rtx mem_src = NULL_RTX; 163 164 switch (get_attr_type (insn)) 165 { 166 case TYPE_LOAD: 167 mem_src = SET_SRC (PATTERN (insn)); 168 break; 169 case TYPE_STORE: 170 mem_src = SET_DEST (PATTERN (insn)); 171 break; 172 default: 173 break; 174 } 175 176 /* Find load/store insn with addressing mode is [REG + REG/CONST]. */ 177 if (mem_src != NULL_RTX) 178 { 179 if ((GET_CODE (mem_src) == ZERO_EXTEND) 180 || (GET_CODE (mem_src) == SIGN_EXTEND)) 181 mem_src = XEXP (mem_src, 0); 182 183 if ((GET_CODE (XEXP (mem_src, 0)) == PLUS)) 184 mem_src = XEXP (mem_src, 0); 185 else 186 return false; 187 188 if (GET_CODE (XEXP (mem_src, 0)) == REG) 189 return true; 190 191 } 192 193 return false; 194} 195 196/* Return true if x is const and the referance is ict symbol. */ 197static bool 198nds32_ict_const_p (rtx x) 199{ 200 if (GET_CODE (x) == CONST) 201 { 202 x = XEXP (x, 0); 203 return nds32_indirect_call_referenced_p (x); 204 } 205 return FALSE; 206} 207 208/* Group the following pattern as relax candidates: 209 210 GOT: 211 sethi $ra, hi20(sym) 212 ori $ra, $ra, lo12(sym) 213 lw $rb, [$ra + $gp] 214 215 GOTOFF, TLSLE: 216 sethi $ra, hi20(sym) 217 ori $ra, $ra, lo12(sym) 218 LS $rb, [$ra + $gp] 219 220 GOTOFF, TLSLE: 221 sethi $ra, hi20(sym) 222 ori $ra, $ra, lo12(sym) 223 add $rb, $ra, $gp($tp) 224 225 Initial GOT table: 226 sethi $gp,hi20(sym) 227 ori $gp, $gp, lo12(sym) 228 add5.pc $gp */ 229 230static auto_vec<rtx_insn *, 32> nds32_group_infos; 231/* Group the PIC and TLS relax candidate instructions for linker. */ 232static bool 233nds32_pic_tls_group (rtx_insn *def_insn, 234 enum nds32_relax_insn_type relax_type, 235 int sym_type) 236{ 237 df_ref def_record; 238 df_link *link; 239 rtx_insn *use_insn = NULL; 240 rtx pat, new_pat; 241 def_record = DF_INSN_DEFS (def_insn); 242 for (link = DF_REF_CHAIN (def_record); link; link = link->next) 243 { 244 if (!DF_REF_INSN_INFO (link->ref)) 245 continue; 246 247 use_insn = DF_REF_INSN (link->ref); 248 249 /* Skip if define insn and use insn not in the same basic block. */ 250 if (!dominated_by_p (CDI_DOMINATORS, 251 BLOCK_FOR_INSN (use_insn), 252 BLOCK_FOR_INSN (def_insn))) 253 return FALSE; 254 255 /* Skip if use_insn not active insn. */ 256 if (!active_insn_p (use_insn)) 257 return FALSE; 258 259 switch (relax_type) 260 { 261 case RELAX_ORI: 262 263 /* GOTOFF, TLSLE: 264 sethi $ra, hi20(sym) 265 ori $ra, $ra, lo12(sym) 266 add $rb, $ra, $gp($tp) */ 267 if ((sym_type == UNSPEC_TLSLE 268 || sym_type == UNSPEC_GOTOFF) 269 && (recog_memoized (use_insn) == CODE_FOR_addsi3)) 270 { 271 pat = XEXP (PATTERN (use_insn), 1); 272 new_pat = 273 gen_rtx_UNSPEC (SImode, 274 gen_rtvec (2, XEXP (pat, 0), XEXP (pat, 1)), 275 UNSPEC_ADD32); 276 validate_replace_rtx (pat, new_pat, use_insn); 277 nds32_group_infos.safe_push (use_insn); 278 } 279 else if (nds32_plus_reg_load_store_p (use_insn) 280 && !nds32_sp_base_or_plus_load_store_p (use_insn)) 281 nds32_group_infos.safe_push (use_insn); 282 else 283 return FALSE; 284 break; 285 286 default: 287 return FALSE; 288 } 289 } 290 return TRUE; 291} 292 293static int 294nds32_pic_tls_symbol_type (rtx x) 295{ 296 x = XEXP (SET_SRC (PATTERN (x)), 1); 297 298 if (GET_CODE (x) == CONST) 299 { 300 x = XEXP (x, 0); 301 302 if (GET_CODE (x) == PLUS) 303 x = XEXP (x, 0); 304 305 return XINT (x, 1); 306 } 307 308 return XINT (x, 1); 309} 310 311/* Group the relax candidates with group id. */ 312static void 313nds32_group_insns (rtx_insn *sethi) 314{ 315 df_ref def_record, use_record; 316 df_link *link; 317 rtx_insn *use_insn = NULL; 318 rtx group_id; 319 bool valid; 320 321 def_record = DF_INSN_DEFS (sethi); 322 323 for (link = DF_REF_CHAIN (def_record); link; link = link->next) 324 { 325 if (!DF_REF_INSN_INFO (link->ref)) 326 continue; 327 328 use_insn = DF_REF_INSN (link->ref); 329 330 /* Skip if define insn and use insn not in the same basic block. */ 331 if (!dominated_by_p (CDI_DOMINATORS, 332 BLOCK_FOR_INSN (use_insn), 333 BLOCK_FOR_INSN (sethi))) 334 return; 335 336 /* Skip if the low-part used register is from different high-part 337 instructions. */ 338 use_record = DF_INSN_USES (use_insn); 339 if (DF_REF_CHAIN (use_record) && DF_REF_CHAIN (use_record)->next) 340 return; 341 342 /* Skip if use_insn not active insn. */ 343 if (!active_insn_p (use_insn)) 344 return; 345 346 /* Initial use_insn_type. */ 347 if (!(recog_memoized (use_insn) == CODE_FOR_lo_sum 348 || nds32_symbol_load_store_p (use_insn) 349 || (nds32_reg_base_load_store_p (use_insn) 350 &&!nds32_sp_base_or_plus_load_store_p (use_insn)))) 351 return; 352 } 353 354 group_id = GEN_INT (nds32_alloc_relax_group_id ()); 355 /* Insert .relax_* directive for sethi. */ 356 emit_insn_before (gen_relax_group (group_id), sethi); 357 358 /* Scan the use insns and insert the directive. */ 359 for (link = DF_REF_CHAIN (def_record); link; link = link->next) 360 { 361 if (!DF_REF_INSN_INFO (link->ref)) 362 continue; 363 364 use_insn = DF_REF_INSN (link->ref); 365 366 /* Insert .relax_* directive. */ 367 if (active_insn_p (use_insn)) 368 emit_insn_before (gen_relax_group (group_id), use_insn); 369 370 /* Find ori ra, ra, unspec(symbol) instruction. */ 371 if (use_insn != NULL 372 && recog_memoized (use_insn) == CODE_FOR_lo_sum 373 && !nds32_const_unspec_p (XEXP (SET_SRC (PATTERN (use_insn)), 1))) 374 { 375 int sym_type = nds32_pic_tls_symbol_type (use_insn); 376 valid = nds32_pic_tls_group (use_insn, RELAX_ORI, sym_type); 377 378 /* Insert .relax_* directive. */ 379 while (!nds32_group_infos.is_empty ()) 380 { 381 use_insn = nds32_group_infos.pop (); 382 if (valid) 383 emit_insn_before (gen_relax_group (group_id), use_insn); 384 } 385 } 386 } 387} 388 389/* Convert relax group id in rtl. */ 390 391static void 392nds32_group_tls_insn (rtx insn) 393{ 394 rtx pat = PATTERN (insn); 395 rtx unspec_relax_group = XEXP (XVECEXP (pat, 0, 1), 0); 396 int group_id = nds32_alloc_relax_group_id (); 397 398 while (GET_CODE (pat) != SET && GET_CODE (pat) == PARALLEL) 399 { 400 pat = XVECEXP (pat, 0, 0); 401 } 402 403 if (GET_CODE (unspec_relax_group) == UNSPEC 404 && XINT (unspec_relax_group, 1) == UNSPEC_VOLATILE_RELAX_GROUP) 405 { 406 XVECEXP (unspec_relax_group, 0, 0) = GEN_INT (group_id); 407 } 408} 409 410static bool 411nds32_float_reg_load_store_p (rtx_insn *insn) 412{ 413 rtx pat = PATTERN (insn); 414 415 if (get_attr_type (insn) == TYPE_FLOAD 416 && GET_CODE (pat) == SET 417 && (GET_MODE (XEXP (pat, 0)) == SFmode 418 || GET_MODE (XEXP (pat, 0)) == DFmode) 419 && MEM_P (XEXP (pat, 1))) 420 { 421 rtx addr = XEXP (XEXP (pat, 1), 0); 422 423 /* [$ra] */ 424 if (REG_P (addr)) 425 return true; 426 /* [$ra + offset] */ 427 if (GET_CODE (addr) == PLUS 428 && REG_P (XEXP (addr, 0)) 429 && CONST_INT_P (XEXP (addr, 1))) 430 return true; 431 } 432 return false; 433} 434 435 436/* Group float load-store instructions: 437 la $ra, symbol 438 flsi $rt, [$ra + offset] */ 439 440static void 441nds32_group_float_insns (rtx_insn *insn) 442{ 443 df_ref def_record, use_record; 444 df_link *link; 445 rtx_insn *use_insn = NULL; 446 rtx group_id; 447 448 def_record = DF_INSN_DEFS (insn); 449 450 for (link = DF_REF_CHAIN (def_record); link; link = link->next) 451 { 452 if (!DF_REF_INSN_INFO (link->ref)) 453 continue; 454 455 use_insn = DF_REF_INSN (link->ref); 456 457 /* Skip if define insn and use insn not in the same basic block. */ 458 if (!dominated_by_p (CDI_DOMINATORS, 459 BLOCK_FOR_INSN (use_insn), 460 BLOCK_FOR_INSN (insn))) 461 return; 462 463 /* Skip if the low-part used register is from different high-part 464 instructions. */ 465 use_record = DF_INSN_USES (use_insn); 466 if (DF_REF_CHAIN (use_record) && DF_REF_CHAIN (use_record)->next) 467 return; 468 469 /* Skip if use_insn not active insn. */ 470 if (!active_insn_p (use_insn)) 471 return; 472 473 if (!nds32_float_reg_load_store_p (use_insn) 474 || find_post_update_rtx (use_insn) != -1) 475 return; 476 } 477 478 group_id = GEN_INT (nds32_alloc_relax_group_id ()); 479 /* Insert .relax_* directive for insn. */ 480 emit_insn_before (gen_relax_group (group_id), insn); 481 482 /* Scan the use insns and insert the directive. */ 483 for (link = DF_REF_CHAIN (def_record); link; link = link->next) 484 { 485 if (!DF_REF_INSN_INFO (link->ref)) 486 continue; 487 488 use_insn = DF_REF_INSN (link->ref); 489 490 /* Insert .relax_* directive. */ 491 emit_insn_before (gen_relax_group (group_id), use_insn); 492 } 493} 494 495/* Group the relax candidate instructions for linker. */ 496static void 497nds32_relax_group (void) 498{ 499 rtx_insn *insn; 500 501 compute_bb_for_insn (); 502 503 df_chain_add_problem (DF_DU_CHAIN | DF_UD_CHAIN); 504 df_insn_rescan_all (); 505 df_analyze (); 506 df_set_flags (DF_DEFER_INSN_RESCAN); 507 calculate_dominance_info (CDI_DOMINATORS); 508 509 insn = get_insns (); 510 gcc_assert (NOTE_P (insn)); 511 512 for (insn = next_active_insn (insn); insn; insn = next_active_insn (insn)) 513 { 514 if (NONJUMP_INSN_P (insn)) 515 { 516 /* Find sethi ra, symbol instruction. */ 517 if (recog_memoized (insn) == CODE_FOR_sethi 518 && nds32_symbolic_operand (XEXP (SET_SRC (PATTERN (insn)), 0), 519 SImode) 520 && !nds32_ict_const_p (XEXP (SET_SRC (PATTERN (insn)), 0))) 521 nds32_group_insns (insn); 522 else if (recog_memoized (insn) == CODE_FOR_tls_ie) 523 nds32_group_tls_insn (insn); 524 else if (TARGET_FPU_SINGLE 525 && recog_memoized (insn) == CODE_FOR_move_addr 526 && !nds32_ict_const_p (XEXP (SET_SRC (PATTERN (insn)), 0))) 527 { 528 nds32_group_float_insns (insn); 529 } 530 } 531 else if (CALL_P (insn) && recog_memoized (insn) == CODE_FOR_tls_desc) 532 { 533 nds32_group_tls_insn (insn); 534 } 535 } 536 537 /* We must call df_finish_pass manually because it should be invoked before 538 BB information is destroyed. Hence we cannot set the TODO_df_finish flag 539 to the pass manager. */ 540 df_insn_rescan_all (); 541 df_finish_pass (false); 542 free_dominance_info (CDI_DOMINATORS); 543} 544 545static unsigned int 546nds32_relax_opt (void) 547{ 548 if (TARGET_RELAX_HINT) 549 nds32_relax_group (); 550 return 1; 551} 552 553const pass_data pass_data_nds32_relax_opt = 554{ 555 RTL_PASS, /* type */ 556 "relax_opt", /* name */ 557 OPTGROUP_NONE, /* optinfo_flags */ 558 TV_MACH_DEP, /* tv_id */ 559 0, /* properties_required */ 560 0, /* properties_provided */ 561 0, /* properties_destroyed */ 562 0, /* todo_flags_start */ 563 TODO_df_finish, /* todo_flags_finish */ 564}; 565 566class pass_nds32_relax_opt : public rtl_opt_pass 567{ 568public: 569 pass_nds32_relax_opt (gcc::context *ctxt) 570 : rtl_opt_pass (pass_data_nds32_relax_opt, ctxt) 571 {} 572 573 /* opt_pass methods: */ 574 bool gate (function *) { return TARGET_RELAX_HINT; } 575 unsigned int execute (function *) { return nds32_relax_opt (); } 576}; 577 578rtl_opt_pass * 579make_pass_nds32_relax_opt (gcc::context *ctxt) 580{ 581 return new pass_nds32_relax_opt (ctxt); 582} 583