|
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 |
|
@@ -157,6 +158,18 @@ void GlobalDCEPass::MarkLive(GlobalValue &GV,
|
157 | 158 | }
|
158 | 159 | }
|
159 | 160 |
|
| 161 | +void GlobalDCEPass::PropagateLivenessInGlobalValues() { |
| 162 | + // Propagate liveness from collected Global Values through the computed |
| 163 | + // dependencies. |
| 164 | + SmallVector<GlobalValue *, 8> NewLiveGVs{AliveGlobals.begin(), |
| 165 | + AliveGlobals.end()}; |
| 166 | + while (!NewLiveGVs.empty()) { |
| 167 | + GlobalValue *LGV = NewLiveGVs.pop_back_val(); |
| 168 | + for (auto *GVD : GVDependencies[LGV]) |
| 169 | + MarkLive(*GVD, &NewLiveGVs); |
| 170 | + } |
| 171 | +} |
| 172 | + |
160 | 173 | void GlobalDCEPass::ScanVTables(Module &M) {
|
161 | 174 | SmallVector<MDNode *, 2> Types;
|
162 | 175 | LLVM_DEBUG(dbgs() << "Building type info -> vtable map\n");
|
@@ -286,6 +299,129 @@ void GlobalDCEPass::AddVirtualFunctionDependencies(Module &M) {
|
286 | 299 | );
|
287 | 300 | }
|
288 | 301 |
|
| 302 | +static bool RemoveConditionalTargetsFromUsedList(Module &M) { |
| 303 | + auto *Used = M.getGlobalVariable("llvm.used"); |
| 304 | + if (!Used) |
| 305 | + return false; |
| 306 | + |
| 307 | + auto *UsedConditional = M.getNamedMetadata("llvm.used.conditional"); |
| 308 | + if (!UsedConditional) |
| 309 | + return false; |
| 310 | + if (UsedConditional->getNumOperands() == 0) |
| 311 | + return false; |
| 312 | + |
| 313 | + // Construct a set of conditionally used targets. |
| 314 | + SmallPtrSet<GlobalValue *, 8> Targets; |
| 315 | + for (auto *M : UsedConditional->operands()) { |
| 316 | + assert(M->getNumOperands() == 3); |
| 317 | + auto *V = mdconst::extract_or_null<GlobalValue>(M->getOperand(0)); |
| 318 | + if (!V) |
| 319 | + continue; |
| 320 | + Targets.insert(V); |
| 321 | + } |
| 322 | + |
| 323 | + if (Targets.empty()) |
| 324 | + return false; |
| 325 | + |
| 326 | + // Now remove all targets from @llvm.used. |
| 327 | + SmallPtrSet<GlobalValue *, 8> NewUsedArray; |
| 328 | + const ConstantArray *UsedList = cast<ConstantArray>(Used->getInitializer()); |
| 329 | + for (Value *Op : UsedList->operands()) { |
| 330 | + GlobalValue *G = cast<GlobalValue>(Op->stripPointerCasts()); |
| 331 | + if (Targets.contains(G)) |
| 332 | + continue; |
| 333 | + NewUsedArray.insert(G); |
| 334 | + } |
| 335 | + Used = setUsedInitializer(*Used, NewUsedArray); |
| 336 | + return true; |
| 337 | +} |
| 338 | + |
| 339 | +// Parse one entry from !llvm.used.conditional list as a triplet of |
| 340 | +// { target, type, dependencies } and evaluate the conditional dependency, i.e. |
| 341 | +// check liveness of all dependencies and based on type conclude whether the |
| 342 | +// target is supposed to be declared alive. If yes, return the target, otherwise |
| 343 | +// return nullptr. |
| 344 | +GlobalValue *GlobalDCEPass::TargetFromConditionalUsedIfLive(MDNode *M) { |
| 345 | + assert(M->getNumOperands() == 3); |
| 346 | + auto *Target = mdconst::extract_or_null<GlobalValue>(M->getOperand(0)); |
| 347 | + if (!Target) |
| 348 | + return nullptr; |
| 349 | + |
| 350 | + auto *DependenciesMD = dyn_cast_or_null<MDNode>(M->getOperand(2).get()); |
| 351 | + SmallPtrSet<GlobalValue *, 8> Dependencies; |
| 352 | + if (DependenciesMD == nullptr) { |
| 353 | + Dependencies.insert(nullptr); |
| 354 | + } else { |
| 355 | + for (auto &DependencyMD : DependenciesMD->operands()) { |
| 356 | + auto *Dependency = DependencyMD.get(); |
| 357 | + if (!Dependency) |
| 358 | + continue; |
| 359 | + auto *C = |
| 360 | + mdconst::extract_or_null<Constant>(Dependency)->stripPointerCasts(); |
| 361 | + Dependencies.insert(cast<GlobalValue>(C)); |
| 362 | + } |
| 363 | + } |
| 364 | + |
| 365 | + bool AllDependenciesAlive = true; |
| 366 | + bool AnyDependencyAlive = false; |
| 367 | + for (auto *Dep : Dependencies) { |
| 368 | + bool Live = AliveGlobals.count(Dep) != 0; |
| 369 | + if (Live) |
| 370 | + AnyDependencyAlive = true; |
| 371 | + else |
| 372 | + AllDependenciesAlive = false; |
| 373 | + } |
| 374 | + |
| 375 | + auto *Type = mdconst::extract_or_null<ConstantInt>(M->getOperand(1)); |
| 376 | + switch (Type->getValue().getSExtValue()) { |
| 377 | + case 0: |
| 378 | + return AnyDependencyAlive ? Target : nullptr; |
| 379 | + case 1: |
| 380 | + return AllDependenciesAlive ? Target : nullptr; |
| 381 | + default: |
| 382 | + llvm_unreachable("bad !llvm.used.conditional type"); |
| 383 | + } |
| 384 | +} |
| 385 | + |
| 386 | +void GlobalDCEPass::PropagateLivenessToConditionallyUsed(Module &M) { |
| 387 | + auto *Used = M.getGlobalVariable("llvm.used"); |
| 388 | + auto *UsedConditional = M.getNamedMetadata("llvm.used.conditional"); |
| 389 | + |
| 390 | + SmallPtrSet<GlobalValue *, 8> NewUsedArray; |
| 391 | + const ConstantArray *UsedList = cast<ConstantArray>(Used->getInitializer()); |
| 392 | + for (Value *Op : UsedList->operands()) { |
| 393 | + NewUsedArray.insert(cast<GlobalValue>(Op->stripPointerCasts())); |
| 394 | + } |
| 395 | + |
| 396 | + // Repeat the liveness propagation iteraticely, one iteration might force |
| 397 | + // other conditionally used globals to become alive. |
| 398 | + while (true) { |
| 399 | + PropagateLivenessInGlobalValues(); |
| 400 | + |
| 401 | + unsigned OldSize = NewUsedArray.size(); |
| 402 | + for (auto *M : UsedConditional->operands()) { |
| 403 | + auto *Target = TargetFromConditionalUsedIfLive(M); |
| 404 | + if (!Target) continue; |
| 405 | + |
| 406 | + NewUsedArray.insert(Target); |
| 407 | + MarkLive(*Target); |
| 408 | + LLVM_DEBUG(dbgs() << "Conditionally used target alive: " |
| 409 | + << Target->getName() << "\n"); |
| 410 | + } |
| 411 | + |
| 412 | + unsigned NewSize = NewUsedArray.size(); |
| 413 | + LLVM_DEBUG(dbgs() << "Conditionally used iteration end, old size: " |
| 414 | + << OldSize << " new size: " << NewSize << "\n"); |
| 415 | + |
| 416 | + // Stop the iteration once we reach a steady state (no new additions to |
| 417 | + // @llvm.used). |
| 418 | + if (NewSize == OldSize) break; |
| 419 | + } |
| 420 | + |
| 421 | + Used = setUsedInitializer(*Used, NewUsedArray); |
| 422 | + MarkLive(*Used); |
| 423 | +} |
| 424 | + |
289 | 425 | PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) {
|
290 | 426 | bool Changed = false;
|
291 | 427 |
|
@@ -315,6 +451,11 @@ PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) {
|
315 | 451 | // might call, if we have that information.
|
316 | 452 | AddVirtualFunctionDependencies(M);
|
317 | 453 |
|
| 454 | + // Process the !llvm.used.conditional list and (temporarily, see below) |
| 455 | + // remove all "targets" from @llvm.used. No effect if `!llvm.used.conditional` |
| 456 | + // is not present in the module. |
| 457 | + bool UsedConditionalPresent = RemoveConditionalTargetsFromUsedList(M); |
| 458 | + |
318 | 459 | // Loop over the module, adding globals which are obviously necessary.
|
319 | 460 | for (GlobalObject &GO : M.global_objects()) {
|
320 | 461 | Changed |= RemoveUnusedGlobalValue(GO);
|
@@ -348,16 +489,14 @@ PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) {
|
348 | 489 | UpdateGVDependencies(GIF);
|
349 | 490 | }
|
350 | 491 |
|
351 |
| - // Propagate liveness from collected Global Values through the computed |
352 |
| - // dependencies. |
353 |
| - SmallVector<GlobalValue *, 8> NewLiveGVs{AliveGlobals.begin(), |
354 |
| - AliveGlobals.end()}; |
355 |
| - while (!NewLiveGVs.empty()) { |
356 |
| - GlobalValue *LGV = NewLiveGVs.pop_back_val(); |
357 |
| - for (auto *GVD : GVDependencies[LGV]) |
358 |
| - MarkLive(*GVD, &NewLiveGVs); |
| 492 | + // Step 2 of !llvm.used.conditional processing: If any conditionally used |
| 493 | + // "targets" are alive, put them back into @llvm.used. |
| 494 | + if (UsedConditionalPresent) { |
| 495 | + PropagateLivenessToConditionallyUsed(M); |
359 | 496 | }
|
360 | 497 |
|
| 498 | + PropagateLivenessInGlobalValues(); |
| 499 | + |
361 | 500 | // Now that all globals which are needed are in the AliveGlobals set, we loop
|
362 | 501 | // through the program, deleting those which are not alive.
|
363 | 502 | //
|
|
0 commit comments