1 //===-- JSONExporter.cpp  - Export Scops as JSON  -------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Export the Scops build by ScopInfo pass as a JSON file.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "polly/LinkAllPasses.h"
15 #include "polly/DependenceInfo.h"
16 #include "polly/Options.h"
17 #include "polly/ScopInfo.h"
18 #include "polly/ScopPass.h"
19 #include "polly/Support/ScopLocation.h"
20 #include "llvm/ADT/Statistic.h"
21 #include "llvm/Analysis/RegionInfo.h"
22 #include "llvm/IR/Module.h"
23 #include "llvm/Support/FileSystem.h"
24 #include "llvm/Support/MemoryBuffer.h"
25 #include "llvm/Support/ToolOutputFile.h"
26 #include "isl/constraint.h"
27 #include "isl/map.h"
28 #include "isl/printer.h"
29 #include "isl/set.h"
30 #include "json/reader.h"
31 #include "json/writer.h"
32 #include <memory>
33 #include <string>
34 #include <system_error>
35 
36 using namespace llvm;
37 using namespace polly;
38 
39 #define DEBUG_TYPE "polly-import-jscop"
40 
41 STATISTIC(NewAccessMapFound, "Number of updated access functions");
42 
43 namespace {
44 static cl::opt<std::string>
45     ImportDir("polly-import-jscop-dir",
46               cl::desc("The directory to import the .jscop files from."),
47               cl::Hidden, cl::value_desc("Directory path"), cl::ValueRequired,
48               cl::init("."), cl::cat(PollyCategory));
49 
50 static cl::opt<std::string>
51     ImportPostfix("polly-import-jscop-postfix",
52                   cl::desc("Postfix to append to the import .jsop files."),
53                   cl::Hidden, cl::value_desc("File postfix"), cl::ValueRequired,
54                   cl::init(""), cl::cat(PollyCategory));
55 
56 struct JSONExporter : public ScopPass {
57   static char ID;
58   explicit JSONExporter() : ScopPass(ID) {}
59 
60   std::string getFileName(Scop &S) const;
61   Json::Value getJSON(Scop &S) const;
62   virtual bool runOnScop(Scop &S);
63   void printScop(raw_ostream &OS, Scop &S) const;
64   void getAnalysisUsage(AnalysisUsage &AU) const;
65 };
66 
67 struct JSONImporter : public ScopPass {
68   static char ID;
69   std::vector<std::string> newAccessStrings;
70   explicit JSONImporter() : ScopPass(ID) {}
71 
72   std::string getFileName(Scop &S) const;
73   virtual bool runOnScop(Scop &S);
74   void printScop(raw_ostream &OS, Scop &S) const;
75   void getAnalysisUsage(AnalysisUsage &AU) const;
76 };
77 }
78 
79 char JSONExporter::ID = 0;
80 std::string JSONExporter::getFileName(Scop &S) const {
81   std::string FunctionName = S.getRegion().getEntry()->getParent()->getName();
82   std::string FileName = FunctionName + "___" + S.getNameStr() + ".jscop";
83   return FileName;
84 }
85 
86 void JSONExporter::printScop(raw_ostream &OS, Scop &S) const { S.print(OS); }
87 
88 Json::Value JSONExporter::getJSON(Scop &S) const {
89   Json::Value root;
90   unsigned LineBegin, LineEnd;
91   std::string FileName;
92 
93   getDebugLocation(&S.getRegion(), LineBegin, LineEnd, FileName);
94   std::string Location;
95   if (LineBegin != (unsigned)-1)
96     Location = FileName + ":" + std::to_string(LineBegin) + "-" +
97                std::to_string(LineEnd);
98 
99   root["name"] = S.getRegion().getNameStr();
100   root["context"] = S.getContextStr();
101   if (LineBegin != (unsigned)-1)
102     root["location"] = Location;
103   root["statements"];
104 
105   for (Scop::iterator SI = S.begin(), SE = S.end(); SI != SE; ++SI) {
106     ScopStmt *Stmt = *SI;
107 
108     Json::Value statement;
109 
110     statement["name"] = Stmt->getBaseName();
111     statement["domain"] = Stmt->getDomainStr();
112     statement["schedule"] = Stmt->getScheduleStr();
113     statement["accesses"];
114 
115     for (MemoryAccess *MA : *Stmt) {
116       Json::Value access;
117 
118       access["kind"] = MA->isRead() ? "read" : "write";
119       access["relation"] = MA->getOriginalAccessRelationStr();
120 
121       statement["accesses"].append(access);
122     }
123 
124     root["statements"].append(statement);
125   }
126 
127   return root;
128 }
129 
130 bool JSONExporter::runOnScop(Scop &S) {
131   Region &R = S.getRegion();
132 
133   std::string FileName = ImportDir + "/" + getFileName(S);
134 
135   Json::Value jscop = getJSON(S);
136   Json::StyledWriter writer;
137   std::string fileContent = writer.write(jscop);
138 
139   // Write to file.
140   std::error_code EC;
141   tool_output_file F(FileName, EC, llvm::sys::fs::F_Text);
142 
143   std::string FunctionName = R.getEntry()->getParent()->getName();
144   errs() << "Writing JScop '" << R.getNameStr() << "' in function '"
145          << FunctionName << "' to '" << FileName << "'.\n";
146 
147   if (!EC) {
148     F.os() << fileContent;
149     F.os().close();
150     if (!F.os().has_error()) {
151       errs() << "\n";
152       F.keep();
153       return false;
154     }
155   }
156 
157   errs() << "  error opening file for writing!\n";
158   F.os().clear_error();
159 
160   return false;
161 }
162 
163 void JSONExporter::getAnalysisUsage(AnalysisUsage &AU) const {
164   AU.setPreservesAll();
165   AU.addRequired<ScopInfo>();
166 }
167 
168 Pass *polly::createJSONExporterPass() { return new JSONExporter(); }
169 
170 char JSONImporter::ID = 0;
171 std::string JSONImporter::getFileName(Scop &S) const {
172   std::string FunctionName = S.getRegion().getEntry()->getParent()->getName();
173   std::string FileName = FunctionName + "___" + S.getNameStr() + ".jscop";
174 
175   if (ImportPostfix != "")
176     FileName += "." + ImportPostfix;
177 
178   return FileName;
179 }
180 
181 void JSONImporter::printScop(raw_ostream &OS, Scop &S) const {
182   S.print(OS);
183   for (std::vector<std::string>::const_iterator I = newAccessStrings.begin(),
184                                                 E = newAccessStrings.end();
185        I != E; I++)
186     OS << "New access function '" << *I << "'detected in JSCOP file\n";
187 }
188 
189 typedef Dependences::StatementToIslMapTy StatementToIslMapTy;
190 
191 bool JSONImporter::runOnScop(Scop &S) {
192   Region &R = S.getRegion();
193   const Dependences &D = getAnalysis<DependenceInfo>().getDependences();
194   const DataLayout &DL =
195       S.getRegion().getEntry()->getParent()->getParent()->getDataLayout();
196 
197   std::string FileName = ImportDir + "/" + getFileName(S);
198 
199   std::string FunctionName = R.getEntry()->getParent()->getName();
200   errs() << "Reading JScop '" << R.getNameStr() << "' in function '"
201          << FunctionName << "' from '" << FileName << "'.\n";
202   ErrorOr<std::unique_ptr<MemoryBuffer>> result =
203       MemoryBuffer::getFile(FileName);
204   std::error_code ec = result.getError();
205 
206   if (ec) {
207     errs() << "File could not be read: " << ec.message() << "\n";
208     return false;
209   }
210 
211   Json::Reader reader;
212   Json::Value jscop;
213 
214   bool parsingSuccessful = reader.parse(result.get()->getBufferStart(), jscop);
215 
216   if (!parsingSuccessful) {
217     errs() << "JSCoP file could not be parsed\n";
218     return false;
219   }
220 
221   isl_set *OldContext = S.getContext();
222   isl_set *NewContext =
223       isl_set_read_from_str(S.getIslCtx(), jscop["context"].asCString());
224 
225   for (unsigned i = 0; i < isl_set_dim(OldContext, isl_dim_param); i++) {
226     isl_id *id = isl_set_get_dim_id(OldContext, isl_dim_param, i);
227     NewContext = isl_set_set_dim_id(NewContext, isl_dim_param, i, id);
228   }
229 
230   isl_set_free(OldContext);
231   S.setContext(NewContext);
232 
233   StatementToIslMapTy NewSchedule;
234 
235   int index = 0;
236 
237   for (Scop::iterator SI = S.begin(), SE = S.end(); SI != SE; ++SI) {
238     Json::Value schedule = jscop["statements"][index]["schedule"];
239     isl_map *m = isl_map_read_from_str(S.getIslCtx(), schedule.asCString());
240     isl_space *Space = (*SI)->getDomainSpace();
241 
242     // Copy the old tuple id. This is necessary to retain the user pointer,
243     // that stores the reference to the ScopStmt this schedule belongs to.
244     m = isl_map_set_tuple_id(m, isl_dim_in,
245                              isl_space_get_tuple_id(Space, isl_dim_set));
246     for (unsigned i = 0; i < isl_space_dim(Space, isl_dim_param); i++) {
247       isl_id *id = isl_space_get_dim_id(Space, isl_dim_param, i);
248       m = isl_map_set_dim_id(m, isl_dim_param, i, id);
249     }
250     isl_space_free(Space);
251     NewSchedule[*SI] = m;
252     index++;
253   }
254 
255   if (!D.isValidSchedule(S, &NewSchedule)) {
256     errs() << "JScop file contains a schedule that changes the "
257            << "dependences. Use -disable-polly-legality to continue anyways\n";
258     for (StatementToIslMapTy::iterator SI = NewSchedule.begin(),
259                                        SE = NewSchedule.end();
260          SI != SE; ++SI)
261       isl_map_free(SI->second);
262     return false;
263   }
264 
265   for (Scop::iterator SI = S.begin(), SE = S.end(); SI != SE; ++SI) {
266     ScopStmt *Stmt = *SI;
267 
268     if (NewSchedule.find(Stmt) != NewSchedule.end())
269       Stmt->setSchedule(NewSchedule[Stmt]);
270   }
271 
272   int statementIdx = 0;
273   for (Scop::iterator SI = S.begin(), SE = S.end(); SI != SE; ++SI) {
274     ScopStmt *Stmt = *SI;
275 
276     int memoryAccessIdx = 0;
277     for (MemoryAccess *MA : *Stmt) {
278       Json::Value accesses = jscop["statements"][statementIdx]["accesses"]
279                                   [memoryAccessIdx]["relation"];
280       isl_map *newAccessMap =
281           isl_map_read_from_str(S.getIslCtx(), accesses.asCString());
282       isl_map *currentAccessMap = MA->getAccessRelation();
283 
284       if (isl_map_dim(newAccessMap, isl_dim_param) !=
285           isl_map_dim(currentAccessMap, isl_dim_param)) {
286         errs() << "JScop file changes the number of parameter dimensions\n";
287         isl_map_free(currentAccessMap);
288         isl_map_free(newAccessMap);
289         return false;
290       }
291 
292       isl_id *OutId = isl_map_get_tuple_id(currentAccessMap, isl_dim_out);
293       newAccessMap = isl_map_set_tuple_id(newAccessMap, isl_dim_out, OutId);
294 
295       // We keep the old alignment, thus we cannot allow accesses to memory
296       // locations that were not accessed before if the alignment of the access
297       // is not the default alignment.
298       bool SpecialAlignment = true;
299       if (LoadInst *LoadI = dyn_cast<LoadInst>(MA->getAccessInstruction())) {
300         SpecialAlignment =
301             DL.getABITypeAlignment(LoadI->getType()) != LoadI->getAlignment();
302       } else if (StoreInst *StoreI =
303                      dyn_cast<StoreInst>(MA->getAccessInstruction())) {
304         SpecialAlignment =
305             DL.getABITypeAlignment(StoreI->getValueOperand()->getType()) !=
306             StoreI->getAlignment();
307       }
308 
309       if (SpecialAlignment) {
310         isl_set *newAccessSet = isl_map_range(isl_map_copy(newAccessMap));
311         isl_set *currentAccessSet =
312             isl_map_range(isl_map_copy(currentAccessMap));
313         bool isSubset = isl_set_is_subset(newAccessSet, currentAccessSet);
314         isl_set_free(newAccessSet);
315         isl_set_free(currentAccessSet);
316 
317         if (!isSubset) {
318           errs() << "JScop file changes the accessed memory\n";
319           isl_map_free(currentAccessMap);
320           isl_map_free(newAccessMap);
321           return false;
322         }
323       }
324 
325       // We need to copy the isl_ids for the parameter dimensions to the new
326       // map. Without doing this the current map would have different
327       // ids then the new one, even though both are named identically.
328       for (unsigned i = 0; i < isl_map_dim(currentAccessMap, isl_dim_param);
329            i++) {
330         isl_id *id = isl_map_get_dim_id(currentAccessMap, isl_dim_param, i);
331         newAccessMap = isl_map_set_dim_id(newAccessMap, isl_dim_param, i, id);
332       }
333 
334       // Copy the old tuple id. This is necessary to retain the user pointer,
335       // that stores the reference to the ScopStmt this access belongs to.
336       isl_id *Id = isl_map_get_tuple_id(currentAccessMap, isl_dim_in);
337       newAccessMap = isl_map_set_tuple_id(newAccessMap, isl_dim_in, Id);
338 
339       if (!isl_map_has_equal_space(currentAccessMap, newAccessMap)) {
340         errs() << "JScop file contains access function with incompatible "
341                << "dimensions\n";
342         isl_map_free(currentAccessMap);
343         isl_map_free(newAccessMap);
344         return false;
345       }
346       if (isl_map_dim(newAccessMap, isl_dim_out) != 1) {
347         errs() << "New access map in JScop file should be single dimensional\n";
348         isl_map_free(currentAccessMap);
349         isl_map_free(newAccessMap);
350         return false;
351       }
352       if (!isl_map_is_equal(newAccessMap, currentAccessMap)) {
353         // Statistics.
354         ++NewAccessMapFound;
355         newAccessStrings.push_back(accesses.asCString());
356         MA->setNewAccessRelation(newAccessMap);
357       } else {
358         isl_map_free(newAccessMap);
359       }
360       isl_map_free(currentAccessMap);
361       memoryAccessIdx++;
362     }
363     statementIdx++;
364   }
365 
366   return false;
367 }
368 
369 void JSONImporter::getAnalysisUsage(AnalysisUsage &AU) const {
370   ScopPass::getAnalysisUsage(AU);
371   AU.addRequired<DependenceInfo>();
372 }
373 Pass *polly::createJSONImporterPass() { return new JSONImporter(); }
374 
375 INITIALIZE_PASS_BEGIN(JSONExporter, "polly-export-jscop",
376                       "Polly - Export Scops as JSON"
377                       " (Writes a .jscop file for each Scop)",
378                       false, false);
379 INITIALIZE_PASS_DEPENDENCY(DependenceInfo)
380 INITIALIZE_PASS_END(JSONExporter, "polly-export-jscop",
381                     "Polly - Export Scops as JSON"
382                     " (Writes a .jscop file for each Scop)",
383                     false, false)
384 
385 INITIALIZE_PASS_BEGIN(JSONImporter, "polly-import-jscop",
386                       "Polly - Import Scops from JSON"
387                       " (Reads a .jscop file for each Scop)",
388                       false, false);
389 INITIALIZE_PASS_DEPENDENCY(DependenceInfo)
390 INITIALIZE_PASS_END(JSONImporter, "polly-import-jscop",
391                     "Polly - Import Scops from JSON"
392                     " (Reads a .jscop file for each Scop)",
393                     false, false)
394