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/DependenceInfo.h"
15 #include "polly/LinkAllPasses.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 "isl/union_map.h"
31 #include "json/reader.h"
32 #include "json/writer.h"
33 #include <memory>
34 #include <string>
35 #include <system_error>
36 
37 using namespace llvm;
38 using namespace polly;
39 
40 #define DEBUG_TYPE "polly-import-jscop"
41 
42 STATISTIC(NewAccessMapFound, "Number of updated access functions");
43 
44 namespace {
45 static cl::opt<std::string>
46     ImportDir("polly-import-jscop-dir",
47               cl::desc("The directory to import the .jscop files from."),
48               cl::Hidden, cl::value_desc("Directory path"), cl::ValueRequired,
49               cl::init("."), cl::cat(PollyCategory));
50 
51 static cl::opt<std::string>
52     ImportPostfix("polly-import-jscop-postfix",
53                   cl::desc("Postfix to append to the import .jsop files."),
54                   cl::Hidden, cl::value_desc("File postfix"), cl::ValueRequired,
55                   cl::init(""), cl::cat(PollyCategory));
56 
57 struct JSONExporter : public ScopPass {
58   static char ID;
59   explicit JSONExporter() : ScopPass(ID) {}
60 
61   std::string getFileName(Scop &S) const;
62   Json::Value getJSON(Scop &S) const;
63 
64   /// @brief Export the SCoP @p S to a JSON file.
65   bool runOnScop(Scop &S) override;
66 
67   /// @brief Print the SCoP @p S as it is exported.
68   void printScop(raw_ostream &OS, Scop &S) const override;
69 
70   /// @brief Register all analyses and transformation required.
71   void getAnalysisUsage(AnalysisUsage &AU) const override;
72 };
73 
74 struct JSONImporter : public ScopPass {
75   static char ID;
76   std::vector<std::string> NewAccessStrings;
77   explicit JSONImporter() : ScopPass(ID) {}
78 
79   /// Import a new context from JScop.
80   ///
81   /// @param S The scop to update.
82   /// @param JScop The JScop file describing the new schedule.
83   ///
84   /// @returns True if the import succeeded, otherwise False.
85   bool importContext(Scop &S, Json::Value &JScop);
86 
87   /// Import a new schedule from JScop.
88   ///
89   /// ... and verify that the new schedule does preserve existing data
90   /// dependences.
91   ///
92   /// @param S The scop to update.
93   /// @param JScop The JScop file describing the new schedule.
94   /// @param D The data dependences of the @p S.
95   ///
96   /// @returns True if the import succeeded, otherwise False.
97   bool importSchedule(Scop &S, Json::Value &JScop, const Dependences &D);
98 
99   /// Import new memory accesses from JScop.
100   ///
101   /// @param S The scop to update.
102   /// @param JScop The JScop file describing the new schedule.
103   /// @param DL The datalayout to assume.
104   ///
105   /// @returns True if the import succeeded, otherwise False.
106   bool importAccesses(Scop &S, Json::Value &JScop, const DataLayout &DL);
107 
108   std::string getFileName(Scop &S) const;
109 
110   /// @brief Import new access functions for SCoP @p S from a JSON file.
111   bool runOnScop(Scop &S) override;
112 
113   /// @brief Print the SCoP @p S and the imported access functions.
114   void printScop(raw_ostream &OS, Scop &S) const override;
115 
116   /// @brief Register all analyses and transformation required.
117   void getAnalysisUsage(AnalysisUsage &AU) const override;
118 };
119 } // namespace
120 
121 char JSONExporter::ID = 0;
122 std::string JSONExporter::getFileName(Scop &S) const {
123   std::string FunctionName = S.getFunction().getName();
124   std::string FileName = FunctionName + "___" + S.getNameStr() + ".jscop";
125   return FileName;
126 }
127 
128 void JSONExporter::printScop(raw_ostream &OS, Scop &S) const { S.print(OS); }
129 
130 Json::Value JSONExporter::getJSON(Scop &S) const {
131   Json::Value root;
132   unsigned LineBegin, LineEnd;
133   std::string FileName;
134 
135   getDebugLocation(&S.getRegion(), LineBegin, LineEnd, FileName);
136   std::string Location;
137   if (LineBegin != (unsigned)-1)
138     Location = FileName + ":" + std::to_string(LineBegin) + "-" +
139                std::to_string(LineEnd);
140 
141   root["name"] = S.getNameStr();
142   root["context"] = S.getContextStr();
143   if (LineBegin != (unsigned)-1)
144     root["location"] = Location;
145   root["statements"];
146 
147   for (ScopStmt &Stmt : S) {
148     Json::Value statement;
149 
150     statement["name"] = Stmt.getBaseName();
151     statement["domain"] = Stmt.getDomainStr();
152     statement["schedule"] = Stmt.getScheduleStr();
153     statement["accesses"];
154 
155     for (MemoryAccess *MA : Stmt) {
156       Json::Value access;
157 
158       access["kind"] = MA->isRead() ? "read" : "write";
159       access["relation"] = MA->getOriginalAccessRelationStr();
160 
161       statement["accesses"].append(access);
162     }
163 
164     root["statements"].append(statement);
165   }
166 
167   return root;
168 }
169 
170 bool JSONExporter::runOnScop(Scop &S) {
171   std::string FileName = ImportDir + "/" + getFileName(S);
172 
173   Json::Value jscop = getJSON(S);
174   Json::StyledWriter writer;
175   std::string fileContent = writer.write(jscop);
176 
177   // Write to file.
178   std::error_code EC;
179   tool_output_file F(FileName, EC, llvm::sys::fs::F_Text);
180 
181   std::string FunctionName = S.getFunction().getName();
182   errs() << "Writing JScop '" << S.getNameStr() << "' in function '"
183          << FunctionName << "' to '" << FileName << "'.\n";
184 
185   if (!EC) {
186     F.os() << fileContent;
187     F.os().close();
188     if (!F.os().has_error()) {
189       errs() << "\n";
190       F.keep();
191       return false;
192     }
193   }
194 
195   errs() << "  error opening file for writing!\n";
196   F.os().clear_error();
197 
198   return false;
199 }
200 
201 void JSONExporter::getAnalysisUsage(AnalysisUsage &AU) const {
202   AU.setPreservesAll();
203   AU.addRequired<ScopInfoRegionPass>();
204 }
205 
206 Pass *polly::createJSONExporterPass() { return new JSONExporter(); }
207 
208 char JSONImporter::ID = 0;
209 std::string JSONImporter::getFileName(Scop &S) const {
210   std::string FunctionName = S.getFunction().getName();
211   std::string FileName = FunctionName + "___" + S.getNameStr() + ".jscop";
212 
213   if (ImportPostfix != "")
214     FileName += "." + ImportPostfix;
215 
216   return FileName;
217 }
218 
219 void JSONImporter::printScop(raw_ostream &OS, Scop &S) const {
220   S.print(OS);
221   for (std::vector<std::string>::const_iterator I = NewAccessStrings.begin(),
222                                                 E = NewAccessStrings.end();
223        I != E; I++)
224     OS << "New access function '" << *I << "'detected in JSCOP file\n";
225 }
226 
227 typedef Dependences::StatementToIslMapTy StatementToIslMapTy;
228 
229 bool JSONImporter::importContext(Scop &S, Json::Value &JScop) {
230   isl_set *OldContext = S.getContext();
231   isl_set *NewContext =
232       isl_set_read_from_str(S.getIslCtx(), JScop["context"].asCString());
233 
234   for (unsigned i = 0; i < isl_set_dim(OldContext, isl_dim_param); i++) {
235     isl_id *Id = isl_set_get_dim_id(OldContext, isl_dim_param, i);
236     NewContext = isl_set_set_dim_id(NewContext, isl_dim_param, i, Id);
237   }
238 
239   isl_set_free(OldContext);
240   S.setContext(NewContext);
241   return true;
242 }
243 
244 bool JSONImporter::importSchedule(Scop &S, Json::Value &JScop,
245                                   const Dependences &D) {
246   StatementToIslMapTy NewSchedule;
247 
248   int Index = 0;
249   for (ScopStmt &Stmt : S) {
250     Json::Value Schedule = JScop["statements"][Index]["schedule"];
251     isl_map *Map = isl_map_read_from_str(S.getIslCtx(), Schedule.asCString());
252     isl_space *Space = Stmt.getDomainSpace();
253 
254     // Copy the old tuple id. This is necessary to retain the user pointer,
255     // that stores the reference to the ScopStmt this schedule belongs to.
256     Map = isl_map_set_tuple_id(Map, isl_dim_in,
257                                isl_space_get_tuple_id(Space, isl_dim_set));
258     for (unsigned i = 0; i < isl_space_dim(Space, isl_dim_param); i++) {
259       isl_id *Id = isl_space_get_dim_id(Space, isl_dim_param, i);
260       Map = isl_map_set_dim_id(Map, isl_dim_param, i, Id);
261     }
262     isl_space_free(Space);
263     NewSchedule[&Stmt] = Map;
264     Index++;
265   }
266 
267   if (!D.isValidSchedule(S, &NewSchedule)) {
268     errs() << "JScop file contains a schedule that changes the "
269            << "dependences. Use -disable-polly-legality to continue anyways\n";
270     for (auto Element : NewSchedule)
271       isl_map_free(Element.second);
272     return false;
273   }
274 
275   auto ScheduleMap = isl_union_map_empty(S.getParamSpace());
276   for (ScopStmt &Stmt : S) {
277     if (NewSchedule.find(&Stmt) != NewSchedule.end())
278       ScheduleMap = isl_union_map_add_map(ScheduleMap, NewSchedule[&Stmt]);
279     else
280       ScheduleMap = isl_union_map_add_map(ScheduleMap, Stmt.getSchedule());
281   }
282 
283   S.setSchedule(ScheduleMap);
284 
285   return true;
286 }
287 
288 bool JSONImporter::importAccesses(Scop &S, Json::Value &JScop,
289                                   const DataLayout &DL) {
290   int StatementIdx = 0;
291   for (ScopStmt &Stmt : S) {
292     int MemoryAccessIdx = 0;
293     for (MemoryAccess *MA : Stmt) {
294       Json::Value Accesses = JScop["statements"][StatementIdx]["accesses"]
295                                   [MemoryAccessIdx]["relation"];
296       isl_map *NewAccessMap =
297           isl_map_read_from_str(S.getIslCtx(), Accesses.asCString());
298       isl_map *CurrentAccessMap = MA->getAccessRelation();
299 
300       if (isl_map_dim(NewAccessMap, isl_dim_param) !=
301           isl_map_dim(CurrentAccessMap, isl_dim_param)) {
302         errs() << "JScop file changes the number of parameter dimensions\n";
303         isl_map_free(CurrentAccessMap);
304         isl_map_free(NewAccessMap);
305         return false;
306       }
307 
308       isl_id *OutId = isl_map_get_tuple_id(CurrentAccessMap, isl_dim_out);
309       NewAccessMap = isl_map_set_tuple_id(NewAccessMap, isl_dim_out, OutId);
310 
311       if (MA->isArrayKind()) {
312         // We keep the old alignment, thus we cannot allow accesses to memory
313         // locations that were not accessed before if the alignment of the
314         // access is not the default alignment.
315         bool SpecialAlignment = true;
316         if (LoadInst *LoadI = dyn_cast<LoadInst>(MA->getAccessInstruction())) {
317           SpecialAlignment =
318               DL.getABITypeAlignment(LoadI->getType()) != LoadI->getAlignment();
319         } else if (StoreInst *StoreI =
320                        dyn_cast<StoreInst>(MA->getAccessInstruction())) {
321           SpecialAlignment =
322               DL.getABITypeAlignment(StoreI->getValueOperand()->getType()) !=
323               StoreI->getAlignment();
324         }
325 
326         if (SpecialAlignment) {
327           isl_set *NewAccessSet = isl_map_range(isl_map_copy(NewAccessMap));
328           isl_set *CurrentAccessSet =
329               isl_map_range(isl_map_copy(CurrentAccessMap));
330           bool IsSubset = isl_set_is_subset(NewAccessSet, CurrentAccessSet);
331           isl_set_free(NewAccessSet);
332           isl_set_free(CurrentAccessSet);
333 
334           if (!IsSubset) {
335             errs() << "JScop file changes the accessed memory\n";
336             isl_map_free(CurrentAccessMap);
337             isl_map_free(NewAccessMap);
338             return false;
339           }
340         }
341       }
342 
343       // We need to copy the isl_ids for the parameter dimensions to the new
344       // map. Without doing this the current map would have different
345       // ids then the new one, even though both are named identically.
346       for (unsigned i = 0; i < isl_map_dim(CurrentAccessMap, isl_dim_param);
347            i++) {
348         isl_id *Id = isl_map_get_dim_id(CurrentAccessMap, isl_dim_param, i);
349         NewAccessMap = isl_map_set_dim_id(NewAccessMap, isl_dim_param, i, Id);
350       }
351 
352       // Copy the old tuple id. This is necessary to retain the user pointer,
353       // that stores the reference to the ScopStmt this access belongs to.
354       isl_id *Id = isl_map_get_tuple_id(CurrentAccessMap, isl_dim_in);
355       NewAccessMap = isl_map_set_tuple_id(NewAccessMap, isl_dim_in, Id);
356 
357       if (!isl_map_has_equal_space(CurrentAccessMap, NewAccessMap)) {
358         errs() << "JScop file contains access function with incompatible "
359                << "dimensions\n";
360         isl_map_free(CurrentAccessMap);
361         isl_map_free(NewAccessMap);
362         return false;
363       }
364 
365       auto NewAccessDomain = isl_map_domain(isl_map_copy(NewAccessMap));
366       auto CurrentAccessDomain = isl_map_domain(isl_map_copy(CurrentAccessMap));
367 
368       NewAccessDomain =
369           isl_set_intersect_params(NewAccessDomain, S.getContext());
370       CurrentAccessDomain =
371           isl_set_intersect_params(CurrentAccessDomain, S.getContext());
372 
373       if (isl_set_is_subset(CurrentAccessDomain, NewAccessDomain) ==
374           isl_bool_false) {
375         errs() << "Mapping not defined for all iteration domain elements\n";
376         isl_set_free(CurrentAccessDomain);
377         isl_set_free(NewAccessDomain);
378         isl_map_free(CurrentAccessMap);
379         isl_map_free(NewAccessMap);
380         return false;
381       }
382 
383       isl_set_free(CurrentAccessDomain);
384       isl_set_free(NewAccessDomain);
385 
386       if (!isl_map_is_equal(NewAccessMap, CurrentAccessMap)) {
387         // Statistics.
388         ++NewAccessMapFound;
389         NewAccessStrings.push_back(Accesses.asCString());
390         MA->setNewAccessRelation(NewAccessMap);
391       } else {
392         isl_map_free(NewAccessMap);
393       }
394       isl_map_free(CurrentAccessMap);
395       MemoryAccessIdx++;
396     }
397     StatementIdx++;
398   }
399 
400   return true;
401 }
402 
403 bool JSONImporter::runOnScop(Scop &S) {
404   const Dependences &D =
405       getAnalysis<DependenceInfo>().getDependences(Dependences::AL_Statement);
406   const DataLayout &DL = S.getFunction().getParent()->getDataLayout();
407 
408   std::string FileName = ImportDir + "/" + getFileName(S);
409 
410   std::string FunctionName = S.getFunction().getName();
411   errs() << "Reading JScop '" << S.getNameStr() << "' in function '"
412          << FunctionName << "' from '" << FileName << "'.\n";
413   ErrorOr<std::unique_ptr<MemoryBuffer>> result =
414       MemoryBuffer::getFile(FileName);
415   std::error_code ec = result.getError();
416 
417   if (ec) {
418     errs() << "File could not be read: " << ec.message() << "\n";
419     return false;
420   }
421 
422   Json::Reader reader;
423   Json::Value jscop;
424 
425   bool parsingSuccessful = reader.parse(result.get()->getBufferStart(), jscop);
426 
427   if (!parsingSuccessful) {
428     errs() << "JSCoP file could not be parsed\n";
429     return false;
430   }
431 
432   bool Success = importContext(S, jscop);
433 
434   if (!Success)
435     return false;
436 
437   Success = importSchedule(S, jscop, D);
438 
439   if (!Success)
440     return false;
441 
442   Success = importAccesses(S, jscop, DL);
443 
444   if (!Success)
445     return false;
446 
447   return false;
448 }
449 
450 void JSONImporter::getAnalysisUsage(AnalysisUsage &AU) const {
451   ScopPass::getAnalysisUsage(AU);
452   AU.addRequired<DependenceInfo>();
453 }
454 
455 Pass *polly::createJSONImporterPass() { return new JSONImporter(); }
456 
457 INITIALIZE_PASS_BEGIN(JSONExporter, "polly-export-jscop",
458                       "Polly - Export Scops as JSON"
459                       " (Writes a .jscop file for each Scop)",
460                       false, false);
461 INITIALIZE_PASS_DEPENDENCY(DependenceInfo)
462 INITIALIZE_PASS_END(JSONExporter, "polly-export-jscop",
463                     "Polly - Export Scops as JSON"
464                     " (Writes a .jscop file for each Scop)",
465                     false, false)
466 
467 INITIALIZE_PASS_BEGIN(JSONImporter, "polly-import-jscop",
468                       "Polly - Import Scops from JSON"
469                       " (Reads a .jscop file for each Scop)",
470                       false, false);
471 INITIALIZE_PASS_DEPENDENCY(DependenceInfo)
472 INITIALIZE_PASS_END(JSONImporter, "polly-import-jscop",
473                     "Polly - Import Scops from JSON"
474                     " (Reads a .jscop file for each Scop)",
475                     false, false)
476