Coverage Report

Created: 2023-10-30 17:15

/builds/2mk6rsew/0/parcoach/parcoach/src/rma/RMAInstrumentation.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "parcoach/RMAPasses.h"
2
3
#include "llvm/ADT/StringSwitch.h"
4
#include "llvm/Analysis/DominanceFrontier.h"
5
#include "llvm/Analysis/MemoryDependenceAnalysis.h"
6
#include "llvm/Analysis/MemorySSA.h"
7
#include "llvm/Analysis/PostDominators.h"
8
#include "llvm/Support/WithColor.h"
9
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
10
11
using namespace llvm;
12
13
namespace parcoach::rma {
14
15
namespace {
16
17
constexpr StringRef ParcoachPrefix = "parcoach_rma_";
18
19
1.75k
std::pair<bool, bool> getInstrumentationInfo(CallBase const &CB) {
20
1.75k
  if (!CB.getCalledFunction()) {
21
0
    return {};
22
0
  }
23
1.75k
  return StringSwitch<std::pair<bool, bool>>(CB.getCalledFunction()->getName())
24
1.75k
#define RMA_INSTRUMENTED(Name, FName, ChangesEpoch)                            \
25
22.8k
  .Case(#Name, {true, ChangesEpoch}).Case(#FName, {true, ChangesEpoch})
26
1.75k
#include "InstrumentedFunctions.def"
27
1.75k
      .Default({});
28
1.75k
}
29
30
352
Twine getInstrumentedName(Function const &F) {
31
352
  return ParcoachPrefix + F.getName();
32
352
}
33
34
352
FunctionType *getInstrumentedFunctionType(Function const &F) {
35
352
  FunctionType *Original = F.getFunctionType();
36
352
  LLVMContext &Ctx = F.getContext();
37
  // FIXME: in LLVM 16 we can construct a SmallVector from an ArrayRef.
38
352
  SmallVector<Type *> InstrumentedParamsTypes(Original->params().begin(),
39
352
                                              Original->params().end());
40
352
  InstrumentedParamsTypes.emplace_back(Type::getInt32Ty(Ctx));
41
352
  InstrumentedParamsTypes.emplace_back(Type::getInt8PtrTy(Ctx));
42
352
  return FunctionType::get(F.getReturnType(), InstrumentedParamsTypes, false);
43
352
}
44
45
352
FunctionCallee getInstrumentedFunction(Function &F) {
46
352
  return F.getParent()->getOrInsertFunction(getInstrumentedName(F).str(),
47
352
                                            getInstrumentedFunctionType(F));
48
352
}
49
50
352
CallInst *createInstrumentedCall(CallBase &CB) {
51
352
  IRBuilder<> B(&CB);
52
352
  SmallVector<Value *> Args(CB.args());
53
54
352
  DebugLoc Dbg = CB.getDebugLoc();
55
56
352
  Args.push_back(B.getInt32(Dbg ? Dbg.getLine() : 0));
57
  // FIXME: this creates one constant per call, even if the filename is the
58
  // same.
59
  // We should do a getOrInsertGlobal (where the name is the filename) to
60
  // reuse existing constant.
61
352
  StringRef Filename = Dbg ? Dbg->getFilename() : "?";
62
352
  Args.push_back(B.CreateGlobalStringPtr(Filename));
63
64
  // We assume shouldInstrumentFunction is true and that getCalledFunction is
65
  // not null.
66
352
  FunctionCallee InstrumentedF =
67
352
      getInstrumentedFunction(*CB.getCalledFunction());
68
352
  return B.CreateCall(InstrumentedF, Args);
69
352
}
70
71
FunctionCallee getInstrumentationMemAccessFunction(Module &M,
72
562
                                                   StringRef InstName) {
73
562
  LLVMContext &Ctx = M.getContext();
74
562
  std::array<Type *, 4> Args = {
75
562
      Type::getInt8PtrTy(Ctx),
76
562
      Type::getInt64Ty(Ctx),
77
562
      Type::getInt32Ty(Ctx),
78
562
      Type::getInt8PtrTy(Ctx),
79
562
  };
80
562
  auto *CalledFTy = FunctionType::get(Type::getVoidTy(Ctx), Args, false);
81
562
  return M.getOrInsertFunction((ParcoachPrefix + InstName).str(), CalledFTy);
82
562
}
83
84
void insertInstrumentationCall(Instruction &I, Value *Addr, Type *Ty,
85
562
                               StringRef InstName) {
86
562
  FunctionCallee CalledF =
87
562
      getInstrumentationMemAccessFunction(*I.getModule(), InstName);
88
562
  IRBuilder<> B(&I);
89
90
562
  Constant *Size =
91
562
      B.getInt64(I.getModule()->getDataLayout().getTypeSizeInBits(Ty));
92
562
  DebugLoc Dbg = I.getDebugLoc();
93
562
  Constant *Line = B.getInt32(Dbg ? Dbg.getLine() : 0);
94
562
  StringRef Filename = Dbg ? Dbg->getFilename() : "?";
95
562
  Constant *ConstantFilename = B.CreateGlobalStringPtr(Filename);
96
97
562
  B.CreateCall(CalledF, {Addr, Size, Line, ConstantFilename});
98
562
}
99
100
135
void insertInstrumentationCall(StoreInst &SI) {
101
135
  insertInstrumentationCall(SI, SI.getPointerOperand(),
102
135
                            SI.getValueOperand()->getType(), "store");
103
135
}
104
105
427
void insertInstrumentationCall(LoadInst &LI) {
106
427
  insertInstrumentationCall(LI, LI.getPointerOperand(), LI.getType(), "load");
107
427
}
108
109
268
auto MagentaErr = []() {
110
268
  return WithColor(errs(), raw_ostream::Colors::MAGENTA);
111
268
};
112
46
auto GreenErr = []() { return WithColor(errs(), raw_ostream::Colors::GREEN); };
113
46
auto RedErr = []() { return WithColor(errs(), raw_ostream::Colors::RED); };
114
115
class LocalConcurrencyDetection {
116
117
public:
118
67
  LocalConcurrencyDetection(){};
119
  bool runOnModule(Module &M, ModuleAnalysisManager &AM);
120
121
private:
122
  static int CountLoad, CountStore, CountInstStore, CountInstLoad;
123
124
  // Instrumentation for dynamic analysis
125
  bool hasWhiteSucc(BasicBlock *BB);
126
  void instrumentMemAccessesIt(Function &F);
127
  void dfsBb(BasicBlock *Bb, bool InEpoch);
128
  static bool instrumentBb(BasicBlock &BB, bool InEpoch);
129
  // Debug
130
#ifndef NDEBUG
131
  static void printBB(BasicBlock *BB);
132
#endif
133
  // Utils
134
  static void resetCounters();
135
  enum Color { WHITE, GREY, BLACK };
136
  using BBColorMap = DenseMap<BasicBlock const *, Color>;
137
  using MemMap = ValueMap<Value *, Instruction *>;
138
  BBColorMap ColorMap;
139
};
140
141
532
bool LocalConcurrencyDetection::instrumentBb(BasicBlock &Bb, bool InEpoch) {
142
532
  bool NewEpoch = false;
143
144
  // We use make_early_inc_range here because we may have to erase the
145
  // current instruction.
146
5.14k
  for (Instruction &I : make_early_inc_range(Bb)) {
147
5.14k
    if (CallInst *CI = dyn_cast<CallInst>(&I)) {
148
1.75k
      auto [shouldInstrument, changesEpoch] = getInstrumentationInfo(*CI);
149
1.75k
      if (shouldInstrument) {
150
352
        CI->replaceAllUsesWith(createInstrumentedCall(*CI));
151
352
        CI->eraseFromParent();
152
352
        NewEpoch = changesEpoch;
153
352
      }
154
1.75k
    }
155
5.14k
    if (StoreInst *SI = dyn_cast<StoreInst>(&I)) {
156
539
      if (InEpoch || NewEpoch) {
157
135
        insertInstrumentationCall(*SI);
158
135
        CountInstStore++;
159
135
      }
160
539
      CountStore++;
161
539
    }
162
5.14k
    if (LoadInst *LI = dyn_cast<LoadInst>(&I)) {
163
858
      if (InEpoch || NewEpoch) {
164
427
        insertInstrumentationCall(*LI);
165
427
        CountInstLoad++;
166
427
      }
167
858
      CountLoad++;
168
858
    }
169
5.14k
  }
170
532
  return NewEpoch ? !InEpoch : InEpoch;
171
532
}
172
173
// Instrument memory accesses (DFS is used to instrument only in epochs)
174
// TODO: interprocedural info: start with the main function
175
176
775
bool LocalConcurrencyDetection::hasWhiteSucc(BasicBlock *BB) {
177
775
  succ_iterator SI = succ_begin(BB);
178
775
  succ_iterator E = succ_end(BB);
179
1.47k
  for (; SI != E; ++SI) {
180
946
    BasicBlock *Succ = *SI;
181
946
    if (ColorMap[Succ] == WHITE) {
182
243
      return true;
183
243
    }
184
946
  }
185
532
  return false;
186
775
}
187
188
// Iterative version
189
67
void LocalConcurrencyDetection::instrumentMemAccessesIt(Function &F) {
190
  // All BB must be white at the beginning
191
532
  for (BasicBlock &BB : F) {
192
532
    ColorMap[&BB] = WHITE;
193
532
  }
194
  // start with entry BB
195
67
  BasicBlock &Entry = F.getEntryBlock();
196
67
  bool InEpoch = false;
197
67
  std::deque<BasicBlock *> Unvisited;
198
67
  Unvisited.push_back(&Entry);
199
67
  InEpoch = instrumentBb(Entry, InEpoch);
200
67
  ColorMap[&Entry] = GREY;
201
202
842
  while (!Unvisited.empty()) {
203
775
    BasicBlock *Header = *Unvisited.begin();
204
205
775
    if (hasWhiteSucc(Header)) {
206
      // inEpoch = InstrumentBB(*header, Ctx, datalayout, inEpoch);
207
243
      succ_iterator SI = succ_begin(Header);
208
243
      succ_iterator E = succ_end(Header);
209
721
      for (; SI != E; ++SI) {
210
478
        BasicBlock *Succ = *SI;
211
478
        if (ColorMap[Succ] == WHITE) {
212
465
          Unvisited.push_front(Succ);
213
465
          InEpoch = instrumentBb(*Succ, InEpoch);
214
465
          ColorMap[Succ] = GREY;
215
465
        }
216
478
      }
217
532
    } else {
218
532
      Unvisited.pop_front();
219
532
      ColorMap[Header] = BLACK;
220
532
    }
221
775
  }
222
67
}
223
224
#ifndef NDEBUG
225
void LocalConcurrencyDetection::printBB(BasicBlock *Bb) {
226
  errs() << "DEBUG: BB ";
227
  Bb->printAsOperand(errs(), false);
228
  errs() << "\n";
229
}
230
#endif
231
232
67
void LocalConcurrencyDetection::resetCounters() {
233
67
  CountLoad = 0;
234
67
  CountStore = 0;
235
67
  CountInstLoad = 0;
236
67
  CountInstStore = 0;
237
67
}
238
239
// Main function of the pass
240
bool LocalConcurrencyDetection::runOnModule(Module &M,
241
67
                                            ModuleAnalysisManager &AM) {
242
1.00k
  auto CyanErr = []() { return WithColor(errs(), raw_ostream::Colors::CYAN); };
243
244
67
  MagentaErr() << "===========================\n";
245
67
  MagentaErr() << "===  PARCOACH ANALYSIS  ===\n";
246
67
  MagentaErr() << "===========================\n";
247
248
67
  auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
249
250
1.42k
  for (Function &F : M) {
251
1.42k
    if (F.isDeclaration()) {
252
1.35k
      continue;
253
1.35k
    }
254
255
67
    resetCounters();
256
257
67
    CyanErr() << "===========================\n";
258
67
    CyanErr() << "ANALYZING function " << F.getName() << "...\n";
259
260
    // Get statistics
261
67
    CyanErr() << "(1) Get statistics ...";
262
67
    auto const &Stats = FAM.getResult<RMAStatisticsAnalysis>(F);
263
67
    CyanErr() << "done \n";
264
265
    // Detection of local concurrency errors - BFS
266
67
    CyanErr() << "(2) Local concurrency errors detection ...";
267
67
    if (Stats.getTotalOneSided() != 0) {
268
55
      auto &Res = FAM.getResult<LocalConcurrencyAnalysis>(F);
269
55
      for (auto [I1, I2] : Res) {
270
23
        RedErr() << "LocalConcurrency detected: conflit with the "
271
23
                    "following instructions: \n";
272
23
        I1->print(errs());
273
23
        DebugLoc Dbg = I1->getDebugLoc();
274
23
        if (Dbg) {
275
23
          GreenErr() << " - LINE " << Dbg.getLine() << " in "
276
23
                     << Dbg->getFilename();
277
23
        }
278
23
        RedErr() << "\nAND\n";
279
23
        I2->print(errs());
280
23
        Dbg = I2->getDebugLoc();
281
23
        if (Dbg) {
282
23
          GreenErr() << " - LINE " << Dbg.getLine() << " in "
283
23
                     << Dbg->getFilename();
284
23
        }
285
23
        errs() << "\n";
286
23
      }
287
55
    }
288
67
    CyanErr() << "done \n";
289
290
    // Instrumentation of memory accesses for dynamic analysis
291
67
    CyanErr() << "(3) Instrumentation for dynamic analysis ...";
292
67
    instrumentMemAccessesIt(F);
293
67
    CyanErr() << "done \n";
294
295
    // Print statistics per function
296
67
    CyanErr() << "=== STATISTICS === \n";
297
67
    CyanErr() << Stats.Mpi << " MPI functions including " << Stats.getTotalRMA()
298
67
              << " RMA functions \n";
299
67
    CyanErr() << "= WINDOW CREATION/DESTRUCTION: " << Stats.Free
300
67
              << " MPI_Win_free, " << Stats.Win << " MPI_Win_create \n";
301
67
    CyanErr() << "= EPOCH CREATION/DESTRUCTION: " << Stats.Fence
302
67
              << " MPI_Win_fence, " << Stats.Lock << " MPI_Lock, "
303
67
              << Stats.Lockall << " MPI_Lockall " << Stats.Unlock
304
67
              << " MPI_Unlock, " << Stats.Unlockall << " MPI_Unlockall \n";
305
67
    CyanErr() << "= ONE-SIDED COMMUNICATIONS: " << Stats.Get << " MPI_Get, "
306
67
              << Stats.Put << " MPI_Put, " << Stats.Acc << " MPI_Accumulate \n";
307
67
    CyanErr() << "= SYNCHRONIZATION: " << Stats.Flush << " MPI_Win_Flush \n";
308
309
67
    CyanErr() << "LOAD/STORE STATISTICS: " << CountInstLoad << " (/"
310
67
              << CountLoad << ") LOAD and " << CountInstStore << " (/"
311
67
              << CountStore << ") STORE are instrumented\n";
312
    // DEBUG INFO: dump the module// F.getParent()->print(errs(),nullptr);
313
67
  }
314
67
  MagentaErr() << "===========================\n";
315
67
  return true;
316
67
}
317
318
int LocalConcurrencyDetection::CountLoad = 0;
319
int LocalConcurrencyDetection::CountStore = 0;
320
int LocalConcurrencyDetection::CountInstLoad = 0;
321
int LocalConcurrencyDetection::CountInstStore = 0;
322
323
} // namespace
324
325
PreservedAnalyses RMAInstrumentationPass::run(Module &M,
326
67
                                              ModuleAnalysisManager &AM) {
327
67
  TimeTraceScope TTS("LocalConcurrencyDetectionPass");
328
67
  LocalConcurrencyDetection LCD;
329
67
  return LCD.runOnModule(M, AM) ? PreservedAnalyses::none()
330
67
                                : PreservedAnalyses::all();
331
67
}
332
333
} // namespace parcoach::rma