1 | //===------ NVPTXTagInvariantLoads.cpp - Tag invariant loads --------------===// |
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 invaraint load tagging. It traverses load instructions |
10 | // in a function, and determines if each load can be tagged as invariant. |
11 | // |
12 | // We currently infer invariance for loads from |
13 | // - constant global variables, and |
14 | // - kernel function pointer params that are noalias (i.e. __restrict) and |
15 | // never written to. |
16 | // |
17 | // TODO: Perform a more powerful invariance analysis (ideally IPO). |
18 | // |
19 | //===----------------------------------------------------------------------===// |
20 | |
21 | #include "NVPTXUtilities.h" |
22 | #include "llvm/Analysis/ValueTracking.h" |
23 | #include "llvm/IR/InstIterator.h" |
24 | #include "llvm/IR/Instructions.h" |
25 | #include "llvm/IR/Metadata.h" |
26 | #include "llvm/Support/NVPTXAddrSpace.h" |
27 | |
28 | using namespace llvm; |
29 | |
30 | static bool isInvariantLoad(const LoadInst *LI, const bool IsKernelFn) { |
31 | // Don't bother with non-global loads |
32 | if (LI->getPointerAddressSpace() != NVPTXAS::ADDRESS_SPACE_GLOBAL) |
33 | return false; |
34 | |
35 | // If the load is already marked as invariant, we don't need to do anything |
36 | if (LI->getMetadata(KindID: LLVMContext::MD_invariant_load)) |
37 | return false; |
38 | |
39 | // We use getUnderlyingObjects() here instead of getUnderlyingObject() |
40 | // mainly because the former looks through phi nodes while the latter does |
41 | // not. We need to look through phi nodes to handle pointer induction |
42 | // variables. |
43 | SmallVector<const Value *, 8> Objs; |
44 | getUnderlyingObjects(V: LI->getPointerOperand(), Objects&: Objs); |
45 | |
46 | return all_of(Range&: Objs, P: [&](const Value *V) { |
47 | if (const auto *A = dyn_cast<const Argument>(Val: V)) |
48 | return IsKernelFn && ((A->onlyReadsMemory() && A->hasNoAliasAttr()) || |
49 | isParamGridConstant(*A)); |
50 | if (const auto *GV = dyn_cast<const GlobalVariable>(Val: V)) |
51 | return GV->isConstant(); |
52 | return false; |
53 | }); |
54 | } |
55 | |
56 | static void markLoadsAsInvariant(LoadInst *LI) { |
57 | LI->setMetadata(KindID: LLVMContext::MD_invariant_load, |
58 | Node: MDNode::get(Context&: LI->getContext(), MDs: {})); |
59 | } |
60 | |
61 | static bool tagInvariantLoads(Function &F) { |
62 | const bool IsKernelFn = isKernelFunction(F); |
63 | |
64 | bool Changed = false; |
65 | for (auto &I : instructions(F)) { |
66 | if (auto *LI = dyn_cast<LoadInst>(Val: &I)) { |
67 | if (isInvariantLoad(LI, IsKernelFn)) { |
68 | markLoadsAsInvariant(LI); |
69 | Changed = true; |
70 | } |
71 | } |
72 | } |
73 | return Changed; |
74 | } |
75 | |
76 | namespace { |
77 | |
78 | struct NVPTXTagInvariantLoadLegacyPass : public FunctionPass { |
79 | static char ID; |
80 | |
81 | NVPTXTagInvariantLoadLegacyPass() : FunctionPass(ID) {} |
82 | bool runOnFunction(Function &F) override; |
83 | }; |
84 | |
85 | } // namespace |
86 | |
87 | INITIALIZE_PASS(NVPTXTagInvariantLoadLegacyPass, "nvptx-tag-invariant-loads" , |
88 | "NVPTX Tag Invariant Loads" , false, false) |
89 | |
90 | bool NVPTXTagInvariantLoadLegacyPass::runOnFunction(Function &F) { |
91 | return tagInvariantLoads(F); |
92 | } |
93 | |
94 | char NVPTXTagInvariantLoadLegacyPass::ID = 0; |
95 | |
96 | FunctionPass *llvm::createNVPTXTagInvariantLoadsPass() { |
97 | return new NVPTXTagInvariantLoadLegacyPass(); |
98 | } |
99 | |
100 | PreservedAnalyses NVPTXTagInvariantLoadsPass::run(Function &F, |
101 | FunctionAnalysisManager &) { |
102 | return tagInvariantLoads(F) ? PreservedAnalyses::none() |
103 | : PreservedAnalyses::all(); |
104 | } |
105 | |