1 //===- bolt/Passes/RegReAssign.cpp ----------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements the RegReAssign class. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "bolt/Passes/RegReAssign.h" 14 #include "bolt/Core/MCPlus.h" 15 #include "bolt/Passes/BinaryFunctionCallGraph.h" 16 #include "bolt/Passes/DataflowAnalysis.h" 17 #include "bolt/Passes/DataflowInfoManager.h" 18 #include "bolt/Utils/Utils.h" 19 #include <numeric> 20 21 #define DEBUG_TYPE "regreassign" 22 23 using namespace llvm; 24 25 namespace opts { 26 extern cl::OptionCategory BoltOptCategory; 27 extern cl::opt<bool> UpdateDebugSections; 28 29 static cl::opt<bool> AggressiveReAssign( 30 "use-aggr-reg-reassign", 31 cl::desc("use register liveness analysis to try to find more opportunities " 32 "for -reg-reassign optimization"), 33 cl::cat(BoltOptCategory)); 34 } 35 36 namespace llvm { 37 namespace bolt { 38 39 void RegReAssign::swap(BinaryFunction &Function, MCPhysReg A, MCPhysReg B) { 40 BinaryContext &BC = Function.getBinaryContext(); 41 const BitVector &AliasA = BC.MIB->getAliases(A, false); 42 const BitVector &AliasB = BC.MIB->getAliases(B, false); 43 44 // Regular instructions 45 for (BinaryBasicBlock &BB : Function) { 46 for (MCInst &Inst : BB) { 47 for (MCOperand &Operand : MCPlus::primeOperands(Inst)) { 48 if (!Operand.isReg()) 49 continue; 50 51 unsigned Reg = Operand.getReg(); 52 if (AliasA.test(Reg)) { 53 Operand.setReg(BC.MIB->getAliasSized(B, BC.MIB->getRegSize(Reg))); 54 --StaticBytesSaved; 55 DynBytesSaved -= BB.getKnownExecutionCount(); 56 continue; 57 } 58 if (!AliasB.test(Reg)) 59 continue; 60 Operand.setReg(BC.MIB->getAliasSized(A, BC.MIB->getRegSize(Reg))); 61 ++StaticBytesSaved; 62 DynBytesSaved += BB.getKnownExecutionCount(); 63 } 64 } 65 } 66 67 // CFI 68 DenseSet<const MCCFIInstruction *> Changed; 69 for (BinaryBasicBlock &BB : Function) { 70 for (MCInst &Inst : BB) { 71 if (!BC.MIB->isCFI(Inst)) 72 continue; 73 const MCCFIInstruction *CFI = Function.getCFIFor(Inst); 74 if (Changed.count(CFI)) 75 continue; 76 Changed.insert(CFI); 77 78 switch (CFI->getOperation()) { 79 case MCCFIInstruction::OpRegister: { 80 const unsigned CFIReg2 = CFI->getRegister2(); 81 const MCPhysReg Reg2 = *BC.MRI->getLLVMRegNum(CFIReg2, /*isEH=*/false); 82 if (AliasA.test(Reg2)) { 83 Function.setCFIFor( 84 Inst, MCCFIInstruction::createRegister( 85 nullptr, CFI->getRegister(), 86 BC.MRI->getDwarfRegNum( 87 BC.MIB->getAliasSized(B, BC.MIB->getRegSize(Reg2)), 88 false))); 89 } else if (AliasB.test(Reg2)) { 90 Function.setCFIFor( 91 Inst, MCCFIInstruction::createRegister( 92 nullptr, CFI->getRegister(), 93 BC.MRI->getDwarfRegNum( 94 BC.MIB->getAliasSized(A, BC.MIB->getRegSize(Reg2)), 95 false))); 96 } 97 } 98 [[fallthrough]]; 99 case MCCFIInstruction::OpUndefined: 100 case MCCFIInstruction::OpDefCfa: 101 case MCCFIInstruction::OpOffset: 102 case MCCFIInstruction::OpRestore: 103 case MCCFIInstruction::OpSameValue: 104 case MCCFIInstruction::OpDefCfaRegister: 105 case MCCFIInstruction::OpRelOffset: 106 case MCCFIInstruction::OpEscape: { 107 unsigned CFIReg; 108 if (CFI->getOperation() != MCCFIInstruction::OpEscape) { 109 CFIReg = CFI->getRegister(); 110 } else { 111 std::optional<uint8_t> Reg = 112 readDWARFExpressionTargetReg(CFI->getValues()); 113 // Handle DW_CFA_def_cfa_expression 114 if (!Reg) 115 break; 116 CFIReg = *Reg; 117 } 118 const MCPhysReg Reg = *BC.MRI->getLLVMRegNum(CFIReg, /*isEH=*/false); 119 if (AliasA.test(Reg)) 120 Function.mutateCFIRegisterFor( 121 Inst, 122 BC.MRI->getDwarfRegNum( 123 BC.MIB->getAliasSized(B, BC.MIB->getRegSize(Reg)), false)); 124 else if (AliasB.test(Reg)) 125 Function.mutateCFIRegisterFor( 126 Inst, 127 BC.MRI->getDwarfRegNum( 128 BC.MIB->getAliasSized(A, BC.MIB->getRegSize(Reg)), false)); 129 break; 130 } 131 default: 132 break; 133 } 134 } 135 } 136 } 137 138 void RegReAssign::rankRegisters(BinaryFunction &Function) { 139 BinaryContext &BC = Function.getBinaryContext(); 140 std::fill(RegScore.begin(), RegScore.end(), 0); 141 std::fill(RankedRegs.begin(), RankedRegs.end(), 0); 142 143 for (BinaryBasicBlock &BB : Function) { 144 for (MCInst &Inst : BB) { 145 const bool CannotUseREX = BC.MIB->cannotUseREX(Inst); 146 const MCInstrDesc &Desc = BC.MII->get(Inst.getOpcode()); 147 148 // Disallow substituitions involving regs in implicit uses lists 149 for (MCPhysReg ImplicitUse : Desc.implicit_uses()) { 150 const size_t RegEC = 151 BC.MIB->getAliases(ImplicitUse, false).find_first(); 152 RegScore[RegEC] = 153 std::numeric_limits<decltype(RegScore)::value_type>::min(); 154 } 155 156 // Disallow substituitions involving regs in implicit defs lists 157 for (MCPhysReg ImplicitDef : Desc.implicit_defs()) { 158 const size_t RegEC = 159 BC.MIB->getAliases(ImplicitDef, false).find_first(); 160 RegScore[RegEC] = 161 std::numeric_limits<decltype(RegScore)::value_type>::min(); 162 } 163 164 for (int I = 0, E = MCPlus::getNumPrimeOperands(Inst); I != E; ++I) { 165 const MCOperand &Operand = Inst.getOperand(I); 166 if (!Operand.isReg()) 167 continue; 168 169 if (Desc.getOperandConstraint(I, MCOI::TIED_TO) != -1) 170 continue; 171 172 unsigned Reg = Operand.getReg(); 173 size_t RegEC = BC.MIB->getAliases(Reg, false).find_first(); 174 if (RegEC == 0) 175 continue; 176 177 // Disallow substituitions involving regs in instrs that cannot use REX 178 if (CannotUseREX) { 179 RegScore[RegEC] = 180 std::numeric_limits<decltype(RegScore)::value_type>::min(); 181 continue; 182 } 183 184 // Unsupported substitution, cannot swap BH with R* regs, bail 185 if (BC.MIB->isUpper8BitReg(Reg) && ClassicCSR.test(Reg)) { 186 RegScore[RegEC] = 187 std::numeric_limits<decltype(RegScore)::value_type>::min(); 188 continue; 189 } 190 191 RegScore[RegEC] += BB.getKnownExecutionCount(); 192 } 193 } 194 } 195 std::iota(RankedRegs.begin(), RankedRegs.end(), 0); // 0, 1, 2, 3... 196 llvm::sort(RankedRegs, 197 [&](size_t A, size_t B) { return RegScore[A] > RegScore[B]; }); 198 199 LLVM_DEBUG({ 200 for (size_t Reg : RankedRegs) { 201 if (RegScore[Reg] == 0) 202 continue; 203 dbgs() << Reg << " "; 204 if (RegScore[Reg] > 0) 205 dbgs() << BC.MRI->getName(Reg) << ": " << RegScore[Reg] << "\n"; 206 else 207 dbgs() << BC.MRI->getName(Reg) << ": (blacklisted)\n"; 208 } 209 }); 210 } 211 212 void RegReAssign::aggressivePassOverFunction(BinaryFunction &Function) { 213 BinaryContext &BC = Function.getBinaryContext(); 214 rankRegisters(Function); 215 216 // Bail early if our registers are all black listed, before running expensive 217 // analysis passes 218 bool Bail = true; 219 int64_t LowScoreClassic = std::numeric_limits<int64_t>::max(); 220 for (int J : ClassicRegs.set_bits()) { 221 if (RegScore[J] <= 0) 222 continue; 223 Bail = false; 224 if (RegScore[J] < LowScoreClassic) 225 LowScoreClassic = RegScore[J]; 226 } 227 if (Bail) 228 return; 229 BitVector Extended = ClassicRegs; 230 Extended.flip(); 231 Extended &= GPRegs; 232 Bail = true; 233 int64_t HighScoreExtended = 0; 234 for (int J : Extended.set_bits()) { 235 if (RegScore[J] <= 0) 236 continue; 237 Bail = false; 238 if (RegScore[J] > HighScoreExtended) 239 HighScoreExtended = RegScore[J]; 240 } 241 // Also bail early if there is no profitable substitution even if we assume 242 // all registers can be exchanged 243 if (Bail || (LowScoreClassic << 1) >= HighScoreExtended) 244 return; 245 246 // -- expensive pass -- determine all regs alive during func start 247 DataflowInfoManager Info(Function, RA.get(), nullptr); 248 BitVector AliveAtStart = *Info.getLivenessAnalysis().getStateAt( 249 ProgramPoint::getFirstPointAt(*Function.begin())); 250 for (BinaryBasicBlock &BB : Function) 251 if (BB.pred_size() == 0) 252 AliveAtStart |= *Info.getLivenessAnalysis().getStateAt( 253 ProgramPoint::getFirstPointAt(BB)); 254 255 // Mark frame pointer alive because of CFI 256 AliveAtStart |= BC.MIB->getAliases(BC.MIB->getFramePointer(), false); 257 // Never touch return registers 258 BC.MIB->getDefaultLiveOut(AliveAtStart); 259 260 // Try swapping more profitable options first 261 auto Begin = RankedRegs.begin(); 262 auto End = std::prev(RankedRegs.end()); 263 while (Begin != End) { 264 MCPhysReg ClassicReg = *End; 265 if (!ClassicRegs[ClassicReg] || RegScore[ClassicReg] <= 0) { 266 --End; 267 continue; 268 } 269 270 MCPhysReg ExtReg = *Begin; 271 if (!Extended[ExtReg] || RegScore[ExtReg] <= 0) { 272 ++Begin; 273 continue; 274 } 275 276 if (RegScore[ClassicReg] << 1 >= RegScore[ExtReg]) { 277 LLVM_DEBUG(dbgs() << " Ending at " << BC.MRI->getName(ClassicReg) 278 << " with " << BC.MRI->getName(ExtReg) 279 << " because exchange is not profitable\n"); 280 break; 281 } 282 283 BitVector AnyAliasAlive = AliveAtStart; 284 AnyAliasAlive &= BC.MIB->getAliases(ClassicReg); 285 if (AnyAliasAlive.any()) { 286 LLVM_DEBUG(dbgs() << " Bailed on " << BC.MRI->getName(ClassicReg) 287 << " with " << BC.MRI->getName(ExtReg) 288 << " because classic reg is alive\n"); 289 --End; 290 continue; 291 } 292 AnyAliasAlive = AliveAtStart; 293 AnyAliasAlive &= BC.MIB->getAliases(ExtReg); 294 if (AnyAliasAlive.any()) { 295 LLVM_DEBUG(dbgs() << " Bailed on " << BC.MRI->getName(ClassicReg) 296 << " with " << BC.MRI->getName(ExtReg) 297 << " because extended reg is alive\n"); 298 ++Begin; 299 continue; 300 } 301 302 // Opportunity detected. Swap. 303 LLVM_DEBUG(dbgs() << "\n ** Swapping " << BC.MRI->getName(ClassicReg) 304 << " with " << BC.MRI->getName(ExtReg) << "\n\n"); 305 swap(Function, ClassicReg, ExtReg); 306 FuncsChanged.insert(&Function); 307 ++Begin; 308 if (Begin == End) 309 break; 310 --End; 311 } 312 } 313 314 bool RegReAssign::conservativePassOverFunction(BinaryFunction &Function) { 315 BinaryContext &BC = Function.getBinaryContext(); 316 rankRegisters(Function); 317 318 // Try swapping R12, R13, R14 or R15 with RBX (we work with all callee-saved 319 // regs except RBP) 320 MCPhysReg Candidate = 0; 321 for (int J : ExtendedCSR.set_bits()) 322 if (RegScore[J] > RegScore[Candidate]) 323 Candidate = J; 324 325 if (!Candidate || RegScore[Candidate] < 0) 326 return false; 327 328 // Check if our classic callee-saved reg (RBX is the only one) has lower 329 // score / utilization rate 330 MCPhysReg RBX = 0; 331 for (int I : ClassicCSR.set_bits()) { 332 int64_t ScoreRBX = RegScore[I]; 333 if (ScoreRBX <= 0) 334 continue; 335 336 if (RegScore[Candidate] > (ScoreRBX + 10)) 337 RBX = I; 338 } 339 340 if (!RBX) 341 return false; 342 343 LLVM_DEBUG(dbgs() << "\n ** Swapping " << BC.MRI->getName(RBX) << " with " 344 << BC.MRI->getName(Candidate) << "\n\n"); 345 (void)BC; 346 swap(Function, RBX, Candidate); 347 FuncsChanged.insert(&Function); 348 return true; 349 } 350 351 void RegReAssign::setupAggressivePass(BinaryContext &BC, 352 std::map<uint64_t, BinaryFunction> &BFs) { 353 setupConservativePass(BC, BFs); 354 CG.reset(new BinaryFunctionCallGraph(buildCallGraph(BC))); 355 RA.reset(new RegAnalysis(BC, &BFs, &*CG)); 356 357 GPRegs = BitVector(BC.MRI->getNumRegs(), false); 358 BC.MIB->getGPRegs(GPRegs); 359 } 360 361 void RegReAssign::setupConservativePass( 362 BinaryContext &BC, std::map<uint64_t, BinaryFunction> &BFs) { 363 // Set up constant bitvectors used throughout this analysis 364 ClassicRegs = BitVector(BC.MRI->getNumRegs(), false); 365 CalleeSaved = BitVector(BC.MRI->getNumRegs(), false); 366 ClassicCSR = BitVector(BC.MRI->getNumRegs(), false); 367 ExtendedCSR = BitVector(BC.MRI->getNumRegs(), false); 368 // Never consider the frame pointer 369 BC.MIB->getClassicGPRegs(ClassicRegs); 370 ClassicRegs.flip(); 371 ClassicRegs |= BC.MIB->getAliases(BC.MIB->getFramePointer(), false); 372 ClassicRegs.flip(); 373 BC.MIB->getCalleeSavedRegs(CalleeSaved); 374 ClassicCSR |= ClassicRegs; 375 ClassicCSR &= CalleeSaved; 376 BC.MIB->getClassicGPRegs(ClassicRegs); 377 ExtendedCSR |= ClassicRegs; 378 ExtendedCSR.flip(); 379 ExtendedCSR &= CalleeSaved; 380 381 LLVM_DEBUG({ 382 RegStatePrinter P(BC); 383 dbgs() << "Starting register reassignment\nClassicRegs: "; 384 P.print(dbgs(), ClassicRegs); 385 dbgs() << "\nCalleeSaved: "; 386 P.print(dbgs(), CalleeSaved); 387 dbgs() << "\nClassicCSR: "; 388 P.print(dbgs(), ClassicCSR); 389 dbgs() << "\nExtendedCSR: "; 390 P.print(dbgs(), ExtendedCSR); 391 dbgs() << "\n"; 392 }); 393 } 394 395 void RegReAssign::runOnFunctions(BinaryContext &BC) { 396 RegScore = std::vector<int64_t>(BC.MRI->getNumRegs(), 0); 397 RankedRegs = std::vector<size_t>(BC.MRI->getNumRegs(), 0); 398 399 if (opts::AggressiveReAssign) 400 setupAggressivePass(BC, BC.getBinaryFunctions()); 401 else 402 setupConservativePass(BC, BC.getBinaryFunctions()); 403 404 for (auto &I : BC.getBinaryFunctions()) { 405 BinaryFunction &Function = I.second; 406 407 if (!Function.isSimple() || Function.isIgnored()) 408 continue; 409 410 LLVM_DEBUG(dbgs() << "====================================\n"); 411 LLVM_DEBUG(dbgs() << " - " << Function.getPrintName() << "\n"); 412 if (!conservativePassOverFunction(Function) && opts::AggressiveReAssign) { 413 aggressivePassOverFunction(Function); 414 LLVM_DEBUG({ 415 if (FuncsChanged.count(&Function)) 416 dbgs() << "Aggressive pass successful on " << Function.getPrintName() 417 << "\n"; 418 }); 419 } 420 } 421 422 if (FuncsChanged.empty()) { 423 outs() << "BOLT-INFO: Reg Reassignment Pass: no changes were made.\n"; 424 return; 425 } 426 if (opts::UpdateDebugSections) 427 outs() << "BOLT-WARNING: You used -reg-reassign and -update-debug-sections." 428 << " Some registers were changed but associated AT_LOCATION for " 429 << "impacted variables were NOT updated! This operation is " 430 << "currently unsupported by BOLT.\n"; 431 outs() << "BOLT-INFO: Reg Reassignment Pass Stats:\n"; 432 outs() << "\t " << FuncsChanged.size() << " functions affected.\n"; 433 outs() << "\t " << StaticBytesSaved << " static bytes saved.\n"; 434 outs() << "\t " << DynBytesSaved << " dynamic bytes saved.\n"; 435 } 436 437 } // namespace bolt 438 } // namespace llvm 439