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