1;; Machine description for the PCREL_OPT optimization. 2;; Copyright (C) 2020-2022 Free Software Foundation, Inc. 3;; Contributed by Michael Meissner (meissner@linux.ibm.com) 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;; Support for the PCREL_OPT optimization. PCREL_OPT looks for instances where 22;; an external variable is used only once, either for reading or for writing. 23;; 24;; If we are optimizing a single read, normally the code would look like: 25;; 26;; (set (reg:DI <ptr>) 27;; (symbol_ref:DI "<extern_addr>")) # <data> is currently dead 28;; 29;; ... # insns do not need to be adjacent 30;; 31;; (set (reg:SI <data>) 32;; (mem:SI (reg:DI <xxx>))) # <ptr> dies with this insn 33;; 34;; We transform this into: 35;; 36;; (parallel [(set (reg:DI <ptr>) 37;; (unspec:SI [(symbol_ref:DI <extern_addr>) 38;; (const_int <marker>)] 39;; UNSPEC_PCREL_OPT_LD_ADDR)) 40;; (set (reg:DI <data>) 41;; (unspec:DI [(const_int 0)] 42;; UNSPEC_PCREL_OPT_LD_DATA))]) 43;; 44;; ... 45;; 46;; (parallel [(set (reg:SI <data>) 47;; (unspec:SI [(mem:SI (reg:DI <ptr>)) 48;; (reg:DI <data>) 49;; (const_int <marker>)] 50;; UNSPEC_PCREL_OPT_LD_RELOC)) 51;; (clobber (reg:DI <ptr>))]) 52;; 53;; The marker is an integer constant that links the load of the external 54;; address to the load of the actual variable. 55;; 56;; In the first insn, we set both the address of the external variable, and 57;; mark that the variable being loaded both are created in that insn, and are 58;; consumed in the second insn. It doesn't matter what mode the register that 59;; we will ultimately do the load into, so we use DImode. We just need to mark 60;; that both registers may be set in the first insn, and will be used in the 61;; second insn. 62;; 63;; Since we use UNSPEC's and link both the register holding the external 64;; address and the value being loaded, it should prevent other passes from 65;; modifying it. 66;; 67;; If the register being loaded is the same as the base register, we use an 68;; alternate form of the insns. 69;; 70;; (set (reg:DI <data_ptr>) 71;; (unspec:DI [(symbol_ref:DI <extern_addr>) 72;; (const_int <marker>)] 73;; UNSPEC_PCREL_OPT_LD_SAME_REG)) 74;; 75;; ... 76;; 77;; (parallel [(set (reg:SI <data>) 78;; (unspec:SI [(mem:SI (reg:DI <ptr>)) 79;; (reg:DI <data>) 80;; (const_int <marker>)] 81;; UNSPEC_PCREL_OPT_LD_RELOC)) 82;; (clobber (reg:DI <ptr>))]) 83 84(define_c_enum "unspec" 85 [UNSPEC_PCREL_OPT_LD_ADDR 86 UNSPEC_PCREL_OPT_LD_DATA 87 UNSPEC_PCREL_OPT_LD_SAME_REG 88 UNSPEC_PCREL_OPT_LD_RELOC 89 UNSPEC_PCREL_OPT_ST_ADDR 90 UNSPEC_PCREL_OPT_ST_RELOC]) 91 92;; Modes that are supported for PCREL_OPT 93(define_mode_iterator PCRELOPT [QI HI SI DI TI SF DF KF 94 V1TI V2DI V4SI V8HI V16QI V2DF V4SF 95 (TF "TARGET_FLOAT128_TYPE && TARGET_IEEEQUAD")]) 96 97;; Vector modes for PCREL_OPT 98(define_mode_iterator PCRELOPT_VECT [TI KF V1TI V2DI V4SI V8HI V16QI V2DF V4SF 99 (TF "TARGET_FLOAT128_TYPE && TARGET_IEEEQUAD")]) 100 101;; Insn for loading the external address, where the register being loaded is not 102;; the same as the register being loaded with the data. 103(define_insn "pcrel_opt_ld_addr" 104 [(set (match_operand:DI 0 "base_reg_operand" "=&b,&b") 105 (unspec:DI [(match_operand:DI 1 "pcrel_external_address") 106 (match_operand 2 "const_int_operand" "n,n")] 107 UNSPEC_PCREL_OPT_LD_ADDR)) 108 (set (match_operand:DI 3 "gpc_reg_operand" "=r,wa") 109 (unspec:DI [(const_int 0)] 110 UNSPEC_PCREL_OPT_LD_DATA))] 111 "TARGET_PCREL_OPT 112 && reg_or_subregno (operands[0]) != reg_or_subregno (operands[3])" 113 "ld %0,%a1\n.Lpcrel%2:" 114 [(set_attr "prefixed" "yes") 115 (set_attr "type" "load") 116 (set_attr "loads_external_address" "yes")]) 117 118;; Alternate form of loading up the external address that is the same register 119;; as the final load. 120(define_insn "pcrel_opt_ld_addr_same_reg" 121 [(set (match_operand:DI 0 "base_reg_operand" "=b") 122 (unspec:DI [(match_operand:DI 1 "pcrel_external_address") 123 (match_operand 2 "const_int_operand" "n")] 124 UNSPEC_PCREL_OPT_LD_SAME_REG))] 125 "TARGET_PCREL_OPT" 126 "ld %0,%a1\n.Lpcrel%2:" 127 [(set_attr "prefixed" "yes") 128 (set_attr "type" "load") 129 (set_attr "loads_external_address" "yes")]) 130 131;; PCREL_OPT modes that are optimized for loading or storing GPRs. 132(define_mode_iterator PCRELOPT_GPR [QI HI SI DI SF DF]) 133 134(define_mode_attr PCRELOPT_GPR_LD [(QI "lbz") 135 (HI "lhz") 136 (SI "lwz") 137 (SF "lwz") 138 (DI "ld") 139 (DF "ld")]) 140 141;; PCREL_OPT load operation of GPRs. Operand 4 (the register used to hold the 142;; address of the external symbol) is SCRATCH if the same register is used for 143;; the normal load. 144(define_insn "*pcrel_opt_ld<mode>_gpr" 145 [(parallel [(set (match_operand:PCRELOPT_GPR 0 "int_reg_operand" "+r") 146 (unspec:PCRELOPT_GPR [ 147 (match_operand:PCRELOPT_GPR 1 "d_form_memory" "m") 148 (match_operand:DI 2 "int_reg_operand" "0") 149 (match_operand 3 "const_int_operand" "n")] 150 UNSPEC_PCREL_OPT_LD_RELOC)) 151 (clobber (match_scratch:DI 4 "=bX"))])] 152 "TARGET_PCREL_OPT 153 && (GET_CODE (operands[4]) == SCRATCH 154 || reg_mentioned_p (operands[4], operands[1]))" 155{ 156 output_pcrel_opt_reloc (operands[3]); 157 return "<PCRELOPT_GPR_LD> %0,%1"; 158} 159 [(set_attr "type" "load")]) 160 161;; PCREL_OPT load with sign/zero extension 162(define_insn "*pcrel_opt_ldsi_<u><mode>_gpr" 163 [(set (match_operand:EXTSI 0 "int_reg_operand" "+r") 164 (any_extend:EXTSI 165 (unspec:SI [(match_operand:SI 1 "d_form_memory" "m") 166 (match_operand:DI 2 "int_reg_operand" "0") 167 (match_operand 3 "const_int_operand" "n")] 168 UNSPEC_PCREL_OPT_LD_RELOC))) 169 (clobber (match_scratch:DI 4 "=bX"))] 170 "TARGET_PCREL_OPT" 171{ 172 output_pcrel_opt_reloc (operands[3]); 173 return "lw<az> %0,%1"; 174} 175 [(set_attr "type" "load")]) 176 177(define_insn "*pcrel_opt_ldhi_<u><mode>_gpr" 178 [(set (match_operand:EXTHI 0 "int_reg_operand" "+r") 179 (any_extend:EXTHI 180 (unspec:HI [(match_operand:HI 1 "d_form_memory" "m") 181 (match_operand:DI 2 "int_reg_operand" "0") 182 (match_operand 3 "const_int_operand" "n")] 183 UNSPEC_PCREL_OPT_LD_RELOC))) 184 (clobber (match_scratch:DI 4 "=bX"))] 185 "TARGET_PCREL_OPT" 186{ 187 output_pcrel_opt_reloc (operands[3]); 188 return "lh<az> %0,%1"; 189} 190 [(set_attr "type" "load")]) 191 192(define_insn "*pcrel_opt_ldqi_u<mode>_gpr" 193 [(set (match_operand:EXTQI 0 "int_reg_operand" "+r") 194 (zero_extend:EXTQI 195 (unspec:QI [(match_operand:QI 1 "d_form_memory" "m") 196 (match_operand:DI 2 "int_reg_operand" "0") 197 (match_operand 3 "const_int_operand" "n")] 198 UNSPEC_PCREL_OPT_LD_RELOC))) 199 (clobber (match_scratch:DI 4 "=bX"))] 200 "TARGET_PCREL_OPT" 201{ 202 output_pcrel_opt_reloc (operands[3]); 203 return "lbz %0,%1"; 204} 205 [(set_attr "type" "load")]) 206 207;; Scalar types that can be optimized by loading them into floating point 208;; or Altivec registers. 209(define_mode_iterator PCRELOPT_FP [DI DF SF]) 210 211;; Load instructions to load up scalar floating point or 64-bit integer values 212;; into floating point registers or Altivec registers. 213(define_mode_attr PCRELOPT_FPR_LD [(DI "lfd") (DF "lfd") (SF "lfs")]) 214(define_mode_attr PCRELOPT_VMX_LD [(DI "lxsd") (DF "lxsd") (SF "lxssp")]) 215 216;; PCREL_OPT load operation of scalar DF/DI/SF into vector registers. 217(define_insn "*pcrel_opt_ld<mode>_vsx" 218 [(set (match_operand:PCRELOPT_FP 0 "vsx_register_operand" "+d,v") 219 (unspec:PCRELOPT_FP [(match_operand:PCRELOPT_FP 1 "d_form_memory" "m,m") 220 (match_operand:DI 2 "vsx_register_operand" "0,0") 221 (match_operand 3 "const_int_operand" "n,n")] 222 UNSPEC_PCREL_OPT_LD_RELOC)) 223 (clobber (match_operand:DI 4 "base_reg_operand" "=b,b"))] 224 "TARGET_PCREL_OPT" 225{ 226 output_pcrel_opt_reloc (operands[3]); 227 return which_alternative ? "<PCRELOPT_VMX_LD> %0,%1" 228 : "<PCRELOPT_FPR_LD> %0,%1"; 229} 230 [(set_attr "type" "fpload")]) 231 232;; PCREL_OPT optimization extending SFmode to DFmode via a load. 233(define_insn "*pcrel_opt_ldsf_df" 234 [(set (match_operand:DF 0 "vsx_register_operand" "+d,v") 235 (float_extend:DF 236 (unspec:SF [(match_operand:SF 1 "d_form_memory" "m,m") 237 (match_operand:DI 2 "vsx_register_operand" "0,0") 238 (match_operand 3 "const_int_operand" "n,n")] 239 UNSPEC_PCREL_OPT_LD_RELOC))) 240 (clobber (match_operand:DI 4 "base_reg_operand" "=b,b"))] 241 "TARGET_PCREL_OPT" 242{ 243 output_pcrel_opt_reloc (operands[3]); 244 return which_alternative ? "lxssp %0,%1" : "lfs %0,%1"; 245} 246 [(set_attr "type" "fpload")]) 247 248;; PCREL_OPT load operation of vector/float128 types into vector registers. 249(define_insn "*pcrel_opt_ld<mode>" 250 [(set (match_operand:PCRELOPT_VECT 0 "vsx_register_operand" "+wa") 251 (unspec:PCRELOPT_VECT [(match_operand:PCRELOPT_VECT 1 "d_form_memory" "m") 252 (match_operand:DI 2 "vsx_register_operand" "0") 253 (match_operand 3 "const_int_operand" "n")] 254 UNSPEC_PCREL_OPT_LD_RELOC)) 255 (clobber (match_operand:DI 4 "base_reg_operand" "=b"))] 256 "TARGET_PCREL_OPT" 257{ 258 output_pcrel_opt_reloc (operands[3]); 259 return "lxv %x0,%1"; 260} 261 [(set_attr "type" "vecload")]) 262 263 264;; PCREL_OPT optimization for stores. We need to put the label after the PLD 265;; instruction, because the assembler might insert a NOP before the PLD for 266;; alignment. 267;; 268;; If we are optimizing a single write, normally the code would look like: 269;; 270;; (set (reg:DI <ptr>) 271;; (symbol_ref:DI "<extern_addr>")) # <data> must be live here 272;; 273;; ... # insns do not need to be adjacent 274;; 275;; (set (mem:SI (reg:DI <xxx>)) 276;; (reg:SI <data>)) # <ptr> dies with this insn 277;; 278;; We optimize this to be: 279;; 280;; (parallel [(set (reg:DI <ptr>) 281;; (unspec:DI [(symbol_ref:DI "<extern_addr>") 282;; (const_int <marker>)] 283;; UNSPEC_PCREL_OPT_ST_ADDR)) 284;; (use (reg:<MODE> <data>))]) 285;; 286;; ... # insns do not need to be adjacent 287;; 288;; (parallel [(set (mem:<MODE> (reg:DI <ptr>)) 289;; (unspec:<MODE> [(reg:<MODE> <data>) 290;; (const_int <marker>)] 291;; UNSPEC_PCREL_OPT_ST_RELOC)) 292;; (clobber (reg:DI <ptr>))]) 293 294(define_insn "*pcrel_opt_st_addr<mode>" 295 [(set (match_operand:DI 0 "gpc_reg_operand" "=b") 296 (unspec:DI [(match_operand:DI 1 "pcrel_external_address") 297 (match_operand 2 "const_int_operand" "n")] 298 UNSPEC_PCREL_OPT_ST_ADDR)) 299 (use (match_operand:PCRELOPT 3 "gpc_reg_operand" "rwa"))] 300 "TARGET_PCREL_OPT" 301 "ld %0,%a1\n.Lpcrel%2:" 302 [(set_attr "prefixed" "yes") 303 (set_attr "type" "load") 304 (set_attr "loads_external_address" "yes")]) 305 306;; PCREL_OPT stores. 307(define_insn "*pcrel_opt_st<mode>" 308 [(set (match_operand:QHSI 0 "d_form_memory" "=m") 309 (unspec:QHSI [(match_operand:QHSI 1 "gpc_reg_operand" "r") 310 (match_operand 2 "const_int_operand" "n")] 311 UNSPEC_PCREL_OPT_ST_RELOC)) 312 (clobber (match_operand:DI 3 "base_reg_operand" "=b"))] 313 "TARGET_PCREL_OPT" 314{ 315 output_pcrel_opt_reloc (operands[2]); 316 return "st<wd> %1,%0"; 317} 318 [(set_attr "type" "store")]) 319 320(define_insn "*pcrel_opt_stdi" 321 [(set (match_operand:DI 0 "d_form_memory" "=m,m,m") 322 (unspec:DI [(match_operand:DI 1 "gpc_reg_operand" "r,d,v") 323 (match_operand 2 "const_int_operand" "n,n,n")] 324 UNSPEC_PCREL_OPT_ST_RELOC)) 325 (clobber (match_operand:DI 3 "base_reg_operand" "=b,b,b"))] 326 "TARGET_PCREL_OPT && TARGET_POWERPC64" 327{ 328 output_pcrel_opt_reloc (operands[2]); 329 switch (which_alternative) 330 { 331 case 0: 332 return "std %1,%0"; 333 case 1: 334 return "stfd %1,%0"; 335 case 2: 336 return "stxsd %1,%0"; 337 default: 338 gcc_unreachable (); 339 } 340} 341 [(set_attr "type" "store,fpstore,fpstore")]) 342 343(define_insn "*pcrel_opt_stsf" 344 [(set (match_operand:SF 0 "d_form_memory" "=m,m,m") 345 (unspec:SF [(match_operand:SF 1 "gpc_reg_operand" "d,v,r") 346 (match_operand 2 "const_int_operand" "n,n,n")] 347 UNSPEC_PCREL_OPT_ST_RELOC)) 348 (clobber (match_operand:DI 3 "base_reg_operand" "=b,b,b"))] 349 "TARGET_PCREL_OPT" 350{ 351 output_pcrel_opt_reloc (operands[2]); 352 switch (which_alternative) 353 { 354 case 0: 355 return "stfs %1,%0"; 356 case 1: 357 return "stxssp %1,%0"; 358 case 2: 359 return "stw %1,%0"; 360 default: 361 gcc_unreachable (); 362 } 363} 364 [(set_attr "type" "fpstore,fpstore,store")]) 365 366(define_insn "*pcrel_opt_stdf" 367 [(set (match_operand:DF 0 "d_form_memory" "=m,m,m") 368 (unspec:DF [(match_operand:DF 1 "gpc_reg_operand" "d,v,r") 369 (match_operand 2 "const_int_operand" "n,n,n")] 370 UNSPEC_PCREL_OPT_ST_RELOC)) 371 (clobber (match_operand:DI 3 "base_reg_operand" "=b,b,b"))] 372 "TARGET_PCREL_OPT 373 && (TARGET_POWERPC64 || vsx_register_operand (operands[1], DFmode))" 374{ 375 output_pcrel_opt_reloc (operands[2]); 376 switch (which_alternative) 377 { 378 case 0: 379 return "stfd %1,%0"; 380 case 1: 381 return "stxsd %1,%0"; 382 case 2: 383 return "std %1,%0"; 384 default: 385 gcc_unreachable (); 386 } 387} 388 [(set_attr "type" "fpstore,fpstore,store")]) 389 390(define_insn "*pcrel_opt_st<mode>" 391 [(set (match_operand:PCRELOPT_VECT 0 "d_form_memory" "=m") 392 (unspec:PCRELOPT_VECT [(match_operand:PCRELOPT_VECT 1 "gpc_reg_operand" "wa") 393 (match_operand 2 "const_int_operand" "n")] 394 UNSPEC_PCREL_OPT_ST_RELOC)) 395 (clobber (match_operand:DI 3 "base_reg_operand" "=b"))] 396 "TARGET_PCREL_OPT" 397{ 398 output_pcrel_opt_reloc (operands[2]); 399 return "stxv %x1,%0"; 400} 401 [(set_attr "type" "vecstore")]) 402