Skip to content

Commit 3c13f47

Browse files
committed
[TSAR, Tfm, Nocapture] Add nocapture analysis (merge pull request #11)
Refactor sources and merge pull request #11. Credit goes to @vladem.
1 parent a0d4125 commit 3c13f47

File tree

4 files changed

+334
-1
lines changed

4 files changed

+334
-1
lines changed

include/tsar/Transform/IR/Passes.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,11 @@ void initializeDependenceInlinerPassPass(PassRegistry &Registry);
9494
/// Create an inliner pass which handle functions which are necessary for
9595
/// analysis.
9696
Pass *createDependenceInlinerPass(bool InsertLifetime = true);
97+
98+
/// Initialize a pass calculating preserved parameters.
99+
void initializeNoCaptureAnalysisPass(PassRegistry &Registry);
100+
101+
/// Create a pass calculating preserved parameters.
102+
Pass * createNoCaptureAnalysisPass();
97103
}
98104
#endif//TSAR_IR_TRANSFORM_PASSES_H

lib/Transform/IR/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
set(TRANSFORM_SOURCES Passes.cpp DeadCodeElimination.cpp InterprocAttr.cpp
2-
MetadataUtils.cpp Utils.cpp CallExtractor.cpp DependenceInliner.cpp)
2+
MetadataUtils.cpp Utils.cpp CallExtractor.cpp DependenceInliner.cpp
3+
NoCaptureAnalysis.cpp)
34

45
if(MSVC_IDE)
56
file(GLOB_RECURSE TRANSFORM_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
//===--- NoCaptureAnalysis.cpp - Parameter capture analysis -----*- C++ -*-===//
2+
//
3+
// Traits Static Analyzer (SAPFOR)
4+
//
5+
// Copyright 2021 DVM System Group
6+
//
7+
// Licensed under the Apache License, Version 2.0 (the "License");
8+
// you may not use this file except in compliance with the License.
9+
// You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing, software
14+
// distributed under the License is distributed on an "AS IS" BASIS,
15+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
// See the License for the specific language governing permissions and
17+
// limitations under the License.
18+
//
19+
//===----------------------------------------------------------------------===//
20+
//
21+
// This file defines passes to determine formal arguments which can be
22+
// preserved by the function (e.g. saved to the global memory).
23+
//
24+
//===----------------------------------------------------------------------===//
25+
26+
#include "tsar/Analysis/Attributes.h"
27+
#include "tsar/Core/Query.h"
28+
#include "tsar/Transform/IR/Passes.h"
29+
#include "tsar/Unparse/Utils.h"
30+
#include <llvm/ADT/DenseSet.h>
31+
#include <llvm/ADT/SCCIterator.h>
32+
#include <llvm/Analysis/CallGraph.h>
33+
#include <llvm/Analysis/CallGraphSCCPass.h>
34+
#include <llvm/IR/Function.h>
35+
#include <llvm/IR/Instructions.h>
36+
#include <llvm/InitializePasses.h>
37+
#include <llvm/Support/Debug.h>
38+
#include <queue>
39+
40+
#undef DEBUG_TYPE
41+
#define DEBUG_TYPE "nocapture"
42+
43+
using namespace llvm;
44+
using namespace tsar;
45+
46+
namespace {
47+
class NoCaptureAnalysis : public ModulePass, private bcl::Uncopyable {
48+
public:
49+
static char ID;
50+
51+
NoCaptureAnalysis() : ModulePass(ID) {
52+
initializeNoCaptureAnalysisPass(*PassRegistry::getPassRegistry());
53+
}
54+
55+
void getAnalysisUsage(AnalysisUsage &AU) const override {
56+
AU.addRequired<CallGraphWrapperPass>();
57+
AU.setPreservesAll();
58+
}
59+
60+
bool runOnModule(Module &M) override {
61+
releaseMemory();
62+
CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph();
63+
for (scc_iterator<CallGraph *> SCCI = scc_begin(&CG); !SCCI.isAtEnd();
64+
++SCCI) {
65+
const std::vector<CallGraphNode *> &NextSCC = *SCCI;
66+
if (NextSCC.size() != 1) {
67+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] met recursion, failed"
68+
<< "\n";);
69+
return false;
70+
}
71+
Function *F = NextSCC.front()->getFunction();
72+
if (!F) {
73+
continue;
74+
}
75+
if (F->isIntrinsic()) {
76+
setNocaptureToAll(F);
77+
continue;
78+
}
79+
if (hasFnAttr(*F, AttrKind::LibFunc)) {
80+
setNocaptureToAll(F);
81+
continue;
82+
}
83+
if (F->isDeclaration()) {
84+
continue;
85+
}
86+
runOnFunction(F);
87+
}
88+
return false;
89+
}
90+
91+
static void runOnFunction(Function *F) {
92+
LLVM_DEBUG(dbgs() << "Analyzing function: ";);
93+
LLVM_DEBUG(dbgs() << F->getName(); dbgs() << "\n";);
94+
for (auto &Arg : F->args()) {
95+
if (!(Arg.getType()->isPointerTy()))
96+
continue;
97+
if (isCaptured(&Arg)) {
98+
LLVM_DEBUG(dbgs() << "Arg may be captured: ";);
99+
LLVM_DEBUG(dbgs() << Arg.getName(); dbgs() << "\n";);
100+
} else {
101+
LLVM_DEBUG(dbgs() << "Proved to be not captured: ";
102+
dbgs() << Arg.getName(); dbgs() << "\n";);
103+
Arg.addAttr(Attribute::AttrKind::NoCapture);
104+
}
105+
}
106+
}
107+
108+
static void setNocaptureToAll(Function *F) {
109+
for (auto &Arg : F->args())
110+
if (Arg.getType()->isPointerTy())
111+
Arg.addAttr(Attribute::AttrKind::NoCapture);
112+
}
113+
114+
static bool isCaptured(Argument *Arg) {
115+
if (Arg->hasNoCaptureAttr())
116+
return false;
117+
llvm::DenseMap<Value *, int> Registry;
118+
llvm::DenseSet<Instruction *> SeenInstrs;
119+
std::queue<Value *> Queue;
120+
Registry.insert(std::pair<Value *, int>(Arg, 0));
121+
Queue.push(Arg);
122+
while (!Queue.empty()) {
123+
auto CurValue = Queue.front();
124+
auto CurRang = Registry.find(CurValue)->second;
125+
Queue.pop();
126+
if (CurRang < 0)
127+
continue;
128+
auto [newNode, newRang, Success] =
129+
applyDefinitionConstraint(CurValue, CurRang);
130+
if (!Success)
131+
return true;
132+
if (newNode) {
133+
auto [iter, Success] =
134+
Registry.insert(std::pair<Value *, int>(newNode, newRang));
135+
if (Success)
136+
Queue.push(newNode);
137+
if (!Success && (iter->second != newRang)) {
138+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] application of the definition"
139+
" lead to an already seen node, stop\n";);
140+
return true;
141+
}
142+
}
143+
for (Use &Use : CurValue->uses()) {
144+
auto Instr = dyn_cast<Instruction>(Use.getUser());
145+
auto OpNo = Use.getOperandNo();
146+
if (!Instr) {
147+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] usage is not an instruction"
148+
<< "\n";);
149+
return true;
150+
}
151+
if (SeenInstrs.find(Instr) != SeenInstrs.end()) {
152+
LLVM_DEBUG(dbgs() << "[NOCAPTURE]\tMet seen Instr: ";
153+
Instr->print(dbgs()); dbgs() << "\n";);
154+
continue;
155+
} else
156+
SeenInstrs.insert(Instr);
157+
auto [newNode, newRang, Success] =
158+
applyUsageConstraint(Instr, OpNo, CurRang);
159+
if (!Success)
160+
return true;
161+
if (newNode) {
162+
auto [iter, Success] =
163+
Registry.insert(std::pair<Value *, int>(newNode, newRang));
164+
if (Success)
165+
Queue.push(newNode);
166+
if (!Success && (iter->second != newRang)) {
167+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] application of the usage "
168+
"lead to an already seen node\n";);
169+
return true;
170+
}
171+
}
172+
}
173+
}
174+
return false;
175+
}
176+
177+
static std::tuple<Value *, int, bool>
178+
applyDefinitionConstraint(Value *CurValue, int CurRang) {
179+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] CurRang: "; dbgs() << CurRang;
180+
dbgs() << "\n";);
181+
if (isa<Argument>(CurValue)) {
182+
if (CurRang != 0) {
183+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] CurValue an argument of rang "
184+
"!= 0: ";
185+
CurValue->print(dbgs()); dbgs() << "\n";);
186+
return std::tuple<Value *, int, bool>(nullptr, -1, false);
187+
}
188+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] CurValue is an argument of rang "
189+
"== 0: ";
190+
CurValue->print(dbgs()); dbgs() << "\n";);
191+
return std::tuple<Value *, int, bool>(nullptr, -1, true);
192+
} else if (isa<GlobalValue>(CurValue)) {
193+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] CurValue is a global value: ";
194+
CurValue->print(dbgs()); dbgs() << "\n";);
195+
return std::tuple<Value *, int, bool>(nullptr, -1, false);
196+
} else if (isa<LoadInst>(CurValue)) {
197+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] CurValue is a load: ";
198+
CurValue->print(dbgs()); dbgs() << "\n";);
199+
auto LI = dyn_cast<LoadInst>(CurValue);
200+
return std::tuple<Value *, int, bool>(LI->getPointerOperand(),
201+
CurRang + 1, true);
202+
} else if (isa<CallBase>(CurValue)) {
203+
if (dyn_cast<CallBase>(CurValue)
204+
->getCalledFunction()
205+
->returnDoesNotAlias()) {
206+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] CurValue is a no-alias call: ";
207+
CurValue->print(dbgs()); dbgs() << "\n";);
208+
return std::tuple<Value *, int, bool>(nullptr, -1, true);
209+
}
210+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] CurValue is a may-alias call: ";
211+
CurValue->print(dbgs()); dbgs() << "\n";);
212+
return std::tuple<Value *, int, bool>(nullptr, -1, false);
213+
} else if (isa<GetElementPtrInst>(CurValue)) {
214+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] CurValue is a gep: ";
215+
CurValue->print(dbgs()); dbgs() << "\n";);
216+
auto BasePtr = dyn_cast<GetElementPtrInst>(CurValue)->getPointerOperand();
217+
return std::tuple<Value *, int, bool>(BasePtr, CurRang, true);
218+
} else if (isa<AllocaInst>(CurValue)) {
219+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] CurValue is a alloca: ";
220+
CurValue->print(dbgs()); dbgs() << "\n";);
221+
return std::tuple<Value *, int, bool>(nullptr, -1, true);
222+
} else if (isa<BitCastInst>(CurValue)) {
223+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] CurValue is a bitcast: ";
224+
CurValue->print(dbgs()); dbgs() << "\n";);
225+
auto BasePtr = dyn_cast<BitCastInst>(CurValue)->getOperand(0);
226+
return std::tuple<Value *, int, bool>(BasePtr, CurRang, true);
227+
} else {
228+
LLVM_DEBUG(dbgs() << "[NOCAPTURE] CurValue is a something unknown: ";
229+
CurValue->print(dbgs()); dbgs() << "\n";);
230+
return std::tuple<Value *, int, bool>(nullptr, -1, false);
231+
}
232+
}
233+
234+
static std::tuple<Value *, int, bool>
235+
applyUsageConstraint(Instruction *Instr, int OpNo, int CurRang) {
236+
switch (Instr->getOpcode()) {
237+
case Instruction::Store: {
238+
if (OpNo !=
239+
StoreInst::getPointerOperandIndex()) { // ptr is being stored itself
240+
LLVM_DEBUG(dbgs() << "[NOCAPTURE]\tMet store (stored itself): ";
241+
Instr->print(dbgs()); dbgs() << "\n";);
242+
auto *V = dyn_cast<StoreInst>(Instr)->getPointerOperand();
243+
return std::tuple<Value *, int, bool>(V, CurRang + 1, true);
244+
} else {
245+
LLVM_DEBUG(dbgs() << "[NOCAPTURE]\tMet store (stored TO): ";
246+
Instr->print(dbgs()); dbgs() << "\n";);
247+
auto *V = dyn_cast<StoreInst>(Instr)->getValueOperand();
248+
return std::tuple<Value *, int, bool>(V, CurRang - 1, true);
249+
}
250+
}
251+
case Instruction::Load: {
252+
LLVM_DEBUG(dbgs() << "[NOCAPTURE]\tMet load (loaded from): ";
253+
Instr->print(dbgs()); dbgs() << "\n";);
254+
return std::tuple<Value *, int, bool>(Instr, CurRang - 1, true);
255+
}
256+
case Instruction::GetElementPtr: {
257+
LLVM_DEBUG(dbgs() << "[NOCAPTURE]\tMet gep (geped from): ";
258+
Instr->print(dbgs()); dbgs() << "\n";);
259+
return std::tuple<Value *, int, bool>(Instr, CurRang, true);
260+
}
261+
case Instruction::BitCast: {
262+
LLVM_DEBUG(dbgs() << "[NOCAPTURE]\tMet bcast (bcasted from): ";
263+
Instr->print(dbgs()); dbgs() << "\n";);
264+
return std::tuple<Value *, int, bool>(Instr, CurRang, true);
265+
}
266+
case Instruction::Call: {
267+
LLVM_DEBUG(dbgs() << "[NOCAPTURE]\tMet call "; Instr->print(dbgs());
268+
dbgs() << "\n";);
269+
bool Success;
270+
auto CalledFun = dyn_cast<CallBase>(Instr)->getCalledFunction();
271+
if (!CalledFun)
272+
Success = false;
273+
else {
274+
auto Fun = dyn_cast<CallBase>(Instr)->getCalledFunction();
275+
if (Fun->isVarArg() && hasFnAttr(*Fun, AttrKind::LibFunc))
276+
Success = true;
277+
else if (Fun->isVarArg())
278+
Success = false;
279+
else
280+
Success = Fun->getArg(OpNo)->hasNoCaptureAttr();
281+
}
282+
return std::tuple<Value *, int, bool>(nullptr, -1, Success);
283+
}
284+
default: {
285+
LLVM_DEBUG(dbgs() << "[NOCAPTURE]\tMet unknown usage: ";
286+
Instr->print(dbgs()); dbgs() << "\n";);
287+
return std::tuple<Value *, int, bool>(nullptr, -1, false);
288+
}
289+
}
290+
}
291+
292+
void print(raw_ostream &OS, const Module *M) const override {
293+
for (const Function &FF : *M) {
294+
const Function *F = &FF;
295+
if (F->isIntrinsic() || tsar::hasFnAttr(*F, AttrKind::LibFunc))
296+
continue;
297+
OS << "[NOCAPTURE] [" << F->getName().begin() << "]";
298+
auto Separator{':'};
299+
for (auto &Arg : F->args()) {
300+
if (Arg.getType()->isPointerTy() && Arg.hasNoCaptureAttr()) {
301+
OS << Separator << " ";
302+
Separator = ',';
303+
printLocationSource(OS, Arg);
304+
OS << "(" << Arg.getArgNo() << ")";
305+
}
306+
}
307+
OS << "\n";
308+
}
309+
}
310+
311+
void releaseMemory() override{};
312+
};
313+
} // namespace
314+
315+
char NoCaptureAnalysis::ID = 0;
316+
317+
INITIALIZE_PASS_IN_GROUP_BEGIN(
318+
NoCaptureAnalysis, "nocapture", "nocapture", false, false,
319+
DefaultQueryManager::PrintPassGroup::getPassRegistry())
320+
INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass)
321+
INITIALIZE_PASS_IN_GROUP_END(
322+
NoCaptureAnalysis, "nocapture", "nocapture", false, false,
323+
DefaultQueryManager::PrintPassGroup::getPassRegistry())
324+
325+
Pass *llvm::createNoCaptureAnalysisPass() { return new NoCaptureAnalysis(); }

lib/Transform/IR/Passes.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,5 @@ void llvm::initializeIRTransform(PassRegistry &Registry) {
3636
initializeFunctionMemoryAttrsAnalysisPass(Registry);
3737
initializeDependenceInlinerPassPass(Registry);
3838
initializeDependenceInlinerAttributerPass(Registry);
39+
initializeNoCaptureAnalysisPass(Registry);
3940
}

0 commit comments

Comments
 (0)