|
28 | 28 | #include "llvm/Transforms/IPO.h"
|
29 | 29 | #include "llvm/Transforms/Utils/CtorUtils.h"
|
30 | 30 | #include "llvm/Transforms/Utils/GlobalStatus.h"
|
| 31 | +#include "llvm/Transforms/Utils/ModuleUtils.h" |
31 | 32 |
|
32 | 33 | using namespace llvm;
|
33 | 34 |
|
@@ -158,6 +159,18 @@ void GlobalDCEPass::MarkLive(GlobalValue &GV,
|
158 | 159 | }
|
159 | 160 | }
|
160 | 161 |
|
| 162 | +void GlobalDCEPass::PropagateLivenessInGlobalValues() { |
| 163 | + // Propagate liveness from collected Global Values through the computed |
| 164 | + // dependencies. |
| 165 | + SmallVector<GlobalValue *, 8> NewLiveGVs{AliveGlobals.begin(), |
| 166 | + AliveGlobals.end()}; |
| 167 | + while (!NewLiveGVs.empty()) { |
| 168 | + GlobalValue *LGV = NewLiveGVs.pop_back_val(); |
| 169 | + for (auto *GVD : GVDependencies[LGV]) |
| 170 | + MarkLive(*GVD, &NewLiveGVs); |
| 171 | + } |
| 172 | +} |
| 173 | + |
161 | 174 | /// Recursively iterate over the (sub-)constants in the vtable and look for
|
162 | 175 | /// vptrs, if their offset is within [RangeStart..RangeEnd), add them to VFuncs.
|
163 | 176 | static void FindVirtualFunctionsInVTable(Module &M, Constant *C,
|
@@ -334,6 +347,135 @@ void GlobalDCEPass::AddVirtualFunctionDependencies(Module &M) {
|
334 | 347 | << " " << Entry.first->getName() << "\n";);
|
335 | 348 | }
|
336 | 349 |
|
| 350 | +static bool RemoveConditionalTargetsFromUsedList(Module &M) { |
| 351 | + auto *Used = M.getGlobalVariable("llvm.used"); |
| 352 | + if (!Used) |
| 353 | + return false; |
| 354 | + |
| 355 | + auto *UsedConditional = M.getNamedMetadata("llvm.used.conditional"); |
| 356 | + if (!UsedConditional) |
| 357 | + return false; |
| 358 | + if (UsedConditional->getNumOperands() == 0) |
| 359 | + return false; |
| 360 | + |
| 361 | + // Construct a set of conditionally used targets. |
| 362 | + SmallPtrSet<GlobalValue *, 8> Targets; |
| 363 | + for (auto *M : UsedConditional->operands()) { |
| 364 | + assert(M->getNumOperands() == 3); |
| 365 | + auto *V = mdconst::extract_or_null<GlobalValue>(M->getOperand(0)); |
| 366 | + if (!V) |
| 367 | + continue; |
| 368 | + Targets.insert(V); |
| 369 | + } |
| 370 | + |
| 371 | + if (Targets.empty()) |
| 372 | + return false; |
| 373 | + |
| 374 | + // Now remove all targets from @llvm.used. |
| 375 | + SmallPtrSet<GlobalValue *, 8> NewUsedArray; |
| 376 | + const ConstantArray *UsedList = cast<ConstantArray>(Used->getInitializer()); |
| 377 | + for (Value *Op : UsedList->operands()) { |
| 378 | + GlobalValue *G = cast<GlobalValue>(Op->stripPointerCasts()); |
| 379 | + if (Targets.contains(G)) |
| 380 | + continue; |
| 381 | + NewUsedArray.insert(G); |
| 382 | + } |
| 383 | + Used = setUsedInitializer(*Used, NewUsedArray); |
| 384 | + return true; |
| 385 | +} |
| 386 | + |
| 387 | +// Parse one entry from !llvm.used.conditional list as a triplet of |
| 388 | +// { target, type, dependencies } and evaluate the conditional dependency, i.e. |
| 389 | +// check liveness of all dependencies and based on type conclude whether the |
| 390 | +// target is supposed to be declared alive. If yes, return the target, otherwise |
| 391 | +// return nullptr. |
| 392 | +GlobalValue *GlobalDCEPass::TargetFromConditionalUsedIfLive(MDNode *M) { |
| 393 | + assert(M->getNumOperands() == 3); |
| 394 | + auto *Target = mdconst::extract_or_null<GlobalValue>(M->getOperand(0)); |
| 395 | + if (!Target) |
| 396 | + return nullptr; |
| 397 | + |
| 398 | + auto *DependenciesMD = dyn_cast_or_null<MDNode>(M->getOperand(2).get()); |
| 399 | + SmallPtrSet<GlobalValue *, 8> Dependencies; |
| 400 | + if (DependenciesMD == nullptr) { |
| 401 | + Dependencies.insert(nullptr); |
| 402 | + } else { |
| 403 | + for (auto &DependencyMD : DependenciesMD->operands()) { |
| 404 | + auto *Dependency = DependencyMD.get(); |
| 405 | + if (!Dependency) |
| 406 | + continue; // Allow null, skip. |
| 407 | + auto *C = |
| 408 | + mdconst::extract_or_null<Constant>(Dependency)->stripPointerCasts(); |
| 409 | + if (dyn_cast<UndefValue>(C)) |
| 410 | + continue; // Allow undef, skip. |
| 411 | + Dependencies.insert(cast<GlobalValue>(C)); |
| 412 | + } |
| 413 | + } |
| 414 | + |
| 415 | + bool AllDependenciesAlive = Dependencies.empty() ? false : true; |
| 416 | + bool AnyDependencyAlive = false; |
| 417 | + for (auto *Dep : Dependencies) { |
| 418 | + bool Live = AliveGlobals.count(Dep) != 0; |
| 419 | + if (Live) |
| 420 | + AnyDependencyAlive = true; |
| 421 | + else |
| 422 | + AllDependenciesAlive = false; |
| 423 | + } |
| 424 | + |
| 425 | + auto *Type = mdconst::extract_or_null<ConstantInt>(M->getOperand(1)); |
| 426 | + switch (Type->getValue().getSExtValue()) { |
| 427 | + case 0: |
| 428 | + return AnyDependencyAlive ? Target : nullptr; |
| 429 | + case 1: |
| 430 | + return AllDependenciesAlive ? Target : nullptr; |
| 431 | + default: |
| 432 | + llvm_unreachable("bad !llvm.used.conditional type"); |
| 433 | + } |
| 434 | +} |
| 435 | + |
| 436 | +void GlobalDCEPass::PropagateLivenessToConditionallyUsed(Module &M) { |
| 437 | + auto *Used = M.getGlobalVariable("llvm.used"); |
| 438 | + if (!Used) |
| 439 | + return; |
| 440 | + auto *UsedConditional = M.getNamedMetadata("llvm.used.conditional"); |
| 441 | + if (!UsedConditional) |
| 442 | + return; |
| 443 | + |
| 444 | + SmallPtrSet<GlobalValue *, 8> NewUsedArray; |
| 445 | + const ConstantArray *UsedList = cast<ConstantArray>(Used->getInitializer()); |
| 446 | + for (Value *Op : UsedList->operands()) { |
| 447 | + NewUsedArray.insert(cast<GlobalValue>(Op->stripPointerCasts())); |
| 448 | + } |
| 449 | + |
| 450 | + // Repeat the liveness propagation iteraticely, one iteration might force |
| 451 | + // other conditionally used globals to become alive. |
| 452 | + while (true) { |
| 453 | + PropagateLivenessInGlobalValues(); |
| 454 | + |
| 455 | + unsigned OldSize = NewUsedArray.size(); |
| 456 | + for (auto *M : UsedConditional->operands()) { |
| 457 | + auto *Target = TargetFromConditionalUsedIfLive(M); |
| 458 | + if (!Target) continue; |
| 459 | + |
| 460 | + NewUsedArray.insert(Target); |
| 461 | + MarkLive(*Target); |
| 462 | + LLVM_DEBUG(dbgs() << "Conditionally used target alive: " |
| 463 | + << Target->getName() << "\n"); |
| 464 | + } |
| 465 | + |
| 466 | + unsigned NewSize = NewUsedArray.size(); |
| 467 | + LLVM_DEBUG(dbgs() << "Conditionally used iteration end, old size: " |
| 468 | + << OldSize << " new size: " << NewSize << "\n"); |
| 469 | + |
| 470 | + // Stop the iteration once we reach a steady state (no new additions to |
| 471 | + // @llvm.used). |
| 472 | + if (NewSize == OldSize) break; |
| 473 | + } |
| 474 | + |
| 475 | + Used = setUsedInitializer(*Used, NewUsedArray); |
| 476 | + MarkLive(*Used); |
| 477 | +} |
| 478 | + |
337 | 479 | PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) {
|
338 | 480 | bool Changed = false;
|
339 | 481 |
|
@@ -363,6 +505,11 @@ PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) {
|
363 | 505 | // might call, if we have that information.
|
364 | 506 | AddVirtualFunctionDependencies(M);
|
365 | 507 |
|
| 508 | + // Process the !llvm.used.conditional list and (temporarily, see below) |
| 509 | + // remove all "targets" from @llvm.used. No effect if `!llvm.used.conditional` |
| 510 | + // is not present in the module. |
| 511 | + bool UsedConditionalPresent = RemoveConditionalTargetsFromUsedList(M); |
| 512 | + |
366 | 513 | // Loop over the module, adding globals which are obviously necessary.
|
367 | 514 | for (GlobalObject &GO : M.global_objects()) {
|
368 | 515 | Changed |= RemoveUnusedGlobalValue(GO);
|
@@ -396,16 +543,14 @@ PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) {
|
396 | 543 | UpdateGVDependencies(GIF);
|
397 | 544 | }
|
398 | 545 |
|
399 |
| - // Propagate liveness from collected Global Values through the computed |
400 |
| - // dependencies. |
401 |
| - SmallVector<GlobalValue *, 8> NewLiveGVs{AliveGlobals.begin(), |
402 |
| - AliveGlobals.end()}; |
403 |
| - while (!NewLiveGVs.empty()) { |
404 |
| - GlobalValue *LGV = NewLiveGVs.pop_back_val(); |
405 |
| - for (auto *GVD : GVDependencies[LGV]) |
406 |
| - MarkLive(*GVD, &NewLiveGVs); |
| 546 | + // Step 2 of !llvm.used.conditional processing: If any conditionally used |
| 547 | + // "targets" are alive, put them back into @llvm.used. |
| 548 | + if (UsedConditionalPresent) { |
| 549 | + PropagateLivenessToConditionallyUsed(M); |
407 | 550 | }
|
408 | 551 |
|
| 552 | + PropagateLivenessInGlobalValues(); |
| 553 | + |
409 | 554 | // Now that all globals which are needed are in the AliveGlobals set, we loop
|
410 | 555 | // through the program, deleting those which are not alive.
|
411 | 556 | //
|
|
0 commit comments