• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- JSONExporter.cpp  - Export Scops as JSON  -------------------------===//
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 // Export the Scops build by ScopInfo pass as a JSON file.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "polly/JSONExporter.h"
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/IR/Module.h"
22 #include "llvm/Support/FileSystem.h"
23 #include "llvm/Support/JSON.h"
24 #include "llvm/Support/MemoryBuffer.h"
25 #include "llvm/Support/ToolOutputFile.h"
26 #include "llvm/Support/raw_ostream.h"
27 #include "isl/map.h"
28 #include "isl/set.h"
29 #include <memory>
30 #include <string>
31 #include <system_error>
32 
33 using namespace llvm;
34 using namespace polly;
35 
36 #define DEBUG_TYPE "polly-import-jscop"
37 
38 STATISTIC(NewAccessMapFound, "Number of updated access functions");
39 
40 namespace {
41 static cl::opt<std::string>
42     ImportDir("polly-import-jscop-dir",
43               cl::desc("The directory to import the .jscop files from."),
44               cl::Hidden, cl::value_desc("Directory path"), cl::ValueRequired,
45               cl::init("."), cl::cat(PollyCategory));
46 
47 static cl::opt<std::string>
48     ImportPostfix("polly-import-jscop-postfix",
49                   cl::desc("Postfix to append to the import .jsop files."),
50                   cl::Hidden, cl::value_desc("File postfix"), cl::ValueRequired,
51                   cl::init(""), cl::cat(PollyCategory));
52 
53 struct JSONExporter : public ScopPass {
54   static char ID;
JSONExporter__anon72ad03610111::JSONExporter55   explicit JSONExporter() : ScopPass(ID) {}
56 
57   /// Export the SCoP @p S to a JSON file.
58   bool runOnScop(Scop &S) override;
59 
60   /// Print the SCoP @p S as it is exported.
61   void printScop(raw_ostream &OS, Scop &S) const override;
62 
63   /// Register all analyses and transformation required.
64   void getAnalysisUsage(AnalysisUsage &AU) const override;
65 };
66 
67 struct JSONImporter : public ScopPass {
68   static char ID;
69   std::vector<std::string> NewAccessStrings;
JSONImporter__anon72ad03610111::JSONImporter70   explicit JSONImporter() : ScopPass(ID) {}
71   /// Import new access functions for SCoP @p S from a JSON file.
72   bool runOnScop(Scop &S) override;
73 
74   /// Print the SCoP @p S and the imported access functions.
75   void printScop(raw_ostream &OS, Scop &S) const override;
76 
77   /// Register all analyses and transformation required.
78   void getAnalysisUsage(AnalysisUsage &AU) const override;
79 };
80 } // namespace
81 
getFileName(Scop & S,StringRef Suffix="")82 static std::string getFileName(Scop &S, StringRef Suffix = "") {
83   std::string FunctionName = S.getFunction().getName().str();
84   std::string FileName = FunctionName + "___" + S.getNameStr() + ".jscop";
85 
86   if (Suffix != "")
87     FileName += "." + Suffix.str();
88 
89   return FileName;
90 }
91 
92 /// Export all arrays from the Scop.
93 ///
94 /// @param S The Scop containing the arrays.
95 ///
96 /// @returns Json::Value containing the arrays.
exportArrays(const Scop & S)97 static json::Array exportArrays(const Scop &S) {
98   json::Array Arrays;
99   std::string Buffer;
100   llvm::raw_string_ostream RawStringOstream(Buffer);
101 
102   for (auto &SAI : S.arrays()) {
103     if (!SAI->isArrayKind())
104       continue;
105 
106     json::Object Array;
107     json::Array Sizes;
108     Array["name"] = SAI->getName();
109     unsigned i = 0;
110     if (!SAI->getDimensionSize(i)) {
111       Sizes.push_back("*");
112       i++;
113     }
114     for (; i < SAI->getNumberOfDimensions(); i++) {
115       SAI->getDimensionSize(i)->print(RawStringOstream);
116       Sizes.push_back(RawStringOstream.str());
117       Buffer.clear();
118     }
119     Array["sizes"] = std::move(Sizes);
120     SAI->getElementType()->print(RawStringOstream);
121     Array["type"] = RawStringOstream.str();
122     Buffer.clear();
123     Arrays.push_back(std::move(Array));
124   }
125   return Arrays;
126 }
127 
getJSON(Scop & S)128 static json::Value getJSON(Scop &S) {
129   json::Object root;
130   unsigned LineBegin, LineEnd;
131   std::string FileName;
132 
133   getDebugLocation(&S.getRegion(), LineBegin, LineEnd, FileName);
134   std::string Location;
135   if (LineBegin != (unsigned)-1)
136     Location = FileName + ":" + std::to_string(LineBegin) + "-" +
137                std::to_string(LineEnd);
138 
139   root["name"] = S.getNameStr();
140   root["context"] = S.getContextStr();
141   if (LineBegin != (unsigned)-1)
142     root["location"] = Location;
143 
144   root["arrays"] = exportArrays(S);
145 
146   root["statements"];
147 
148   json::Array Statements;
149   for (ScopStmt &Stmt : S) {
150     json::Object statement;
151 
152     statement["name"] = Stmt.getBaseName();
153     statement["domain"] = Stmt.getDomainStr();
154     statement["schedule"] = Stmt.getScheduleStr();
155 
156     json::Array Accesses;
157     for (MemoryAccess *MA : Stmt) {
158       json::Object access;
159 
160       access["kind"] = MA->isRead() ? "read" : "write";
161       access["relation"] = MA->getAccessRelationStr();
162 
163       Accesses.push_back(std::move(access));
164     }
165     statement["accesses"] = std::move(Accesses);
166 
167     Statements.push_back(std::move(statement));
168   }
169 
170   root["statements"] = std::move(Statements);
171   return json::Value(std::move(root));
172 }
173 
exportScop(Scop & S)174 static void exportScop(Scop &S) {
175   std::string FileName = ImportDir + "/" + getFileName(S);
176 
177   json::Value jscop = getJSON(S);
178 
179   // Write to file.
180   std::error_code EC;
181   ToolOutputFile F(FileName, EC, llvm::sys::fs::OF_Text);
182 
183   std::string FunctionName = S.getFunction().getName().str();
184   errs() << "Writing JScop '" << S.getNameStr() << "' in function '"
185          << FunctionName << "' to '" << FileName << "'.\n";
186 
187   if (!EC) {
188     F.os() << formatv("{0:3}", jscop);
189     F.os().close();
190     if (!F.os().has_error()) {
191       errs() << "\n";
192       F.keep();
193       return;
194     }
195   }
196 
197   errs() << "  error opening file for writing!\n";
198   F.os().clear_error();
199 }
200 
201 typedef Dependences::StatementToIslMapTy StatementToIslMapTy;
202 
203 /// Import a new context from JScop.
204 ///
205 /// @param S The scop to update.
206 /// @param JScop The JScop file describing the new schedule.
207 ///
208 /// @returns True if the import succeeded, otherwise False.
importContext(Scop & S,const json::Object & JScop)209 static bool importContext(Scop &S, const json::Object &JScop) {
210   isl::set OldContext = S.getContext();
211 
212   // Check if key 'context' is present.
213   if (!JScop.get("context")) {
214     errs() << "JScop file has no key named 'context'.\n";
215     return false;
216   }
217 
218   isl::set NewContext = isl::set{S.getIslCtx().get(),
219                                  JScop.getString("context").getValue().str()};
220 
221   // Check whether the context was parsed successfully.
222   if (!NewContext) {
223     errs() << "The context was not parsed successfully by ISL.\n";
224     return false;
225   }
226 
227   // Check if the isl_set is a parameter set.
228   if (!NewContext.is_params()) {
229     errs() << "The isl_set is not a parameter set.\n";
230     return false;
231   }
232 
233   unsigned OldContextDim = OldContext.dim(isl::dim::param);
234   unsigned NewContextDim = NewContext.dim(isl::dim::param);
235 
236   // Check if the imported context has the right number of parameters.
237   if (OldContextDim != NewContextDim) {
238     errs() << "Imported context has the wrong number of parameters : "
239            << "Found " << NewContextDim << " Expected " << OldContextDim
240            << "\n";
241     return false;
242   }
243 
244   for (unsigned i = 0; i < OldContextDim; i++) {
245     isl::id Id = OldContext.get_dim_id(isl::dim::param, i);
246     NewContext = NewContext.set_dim_id(isl::dim::param, i, Id);
247   }
248 
249   S.setContext(NewContext);
250   return true;
251 }
252 
253 /// Import a new schedule from JScop.
254 ///
255 /// ... and verify that the new schedule does preserve existing data
256 /// dependences.
257 ///
258 /// @param S The scop to update.
259 /// @param JScop The JScop file describing the new schedule.
260 /// @param D The data dependences of the @p S.
261 ///
262 /// @returns True if the import succeeded, otherwise False.
importSchedule(Scop & S,const json::Object & JScop,const Dependences & D)263 static bool importSchedule(Scop &S, const json::Object &JScop,
264                            const Dependences &D) {
265   StatementToIslMapTy NewSchedule;
266 
267   // Check if key 'statements' is present.
268   if (!JScop.get("statements")) {
269     errs() << "JScop file has no key name 'statements'.\n";
270     return false;
271   }
272 
273   const json::Array &statements = *JScop.getArray("statements");
274 
275   // Check whether the number of indices equals the number of statements
276   if (statements.size() != S.getSize()) {
277     errs() << "The number of indices and the number of statements differ.\n";
278     return false;
279   }
280 
281   int Index = 0;
282   for (ScopStmt &Stmt : S) {
283     // Check if key 'schedule' is present.
284     if (!statements[Index].getAsObject()->get("schedule")) {
285       errs() << "Statement " << Index << " has no 'schedule' key.\n";
286       return false;
287     }
288     Optional<StringRef> Schedule =
289         statements[Index].getAsObject()->getString("schedule");
290     assert(Schedule.hasValue() &&
291            "Schedules that contain extension nodes require special handling.");
292     isl_map *Map = isl_map_read_from_str(S.getIslCtx().get(),
293                                          Schedule.getValue().str().c_str());
294 
295     // Check whether the schedule was parsed successfully
296     if (!Map) {
297       errs() << "The schedule was not parsed successfully (index = " << Index
298              << ").\n";
299       return false;
300     }
301 
302     isl_space *Space = Stmt.getDomainSpace().release();
303 
304     // Copy the old tuple id. This is necessary to retain the user pointer,
305     // that stores the reference to the ScopStmt this schedule belongs to.
306     Map = isl_map_set_tuple_id(Map, isl_dim_in,
307                                isl_space_get_tuple_id(Space, isl_dim_set));
308     for (isl_size i = 0; i < isl_space_dim(Space, isl_dim_param); i++) {
309       isl_id *Id = isl_space_get_dim_id(Space, isl_dim_param, i);
310       Map = isl_map_set_dim_id(Map, isl_dim_param, i, Id);
311     }
312     isl_space_free(Space);
313     NewSchedule[&Stmt] = isl::manage(Map);
314     Index++;
315   }
316 
317   // Check whether the new schedule is valid or not.
318   if (!D.isValidSchedule(S, NewSchedule)) {
319     errs() << "JScop file contains a schedule that changes the "
320            << "dependences. Use -disable-polly-legality to continue anyways\n";
321     return false;
322   }
323 
324   auto ScheduleMap = isl::union_map::empty(S.getParamSpace());
325   for (ScopStmt &Stmt : S) {
326     if (NewSchedule.find(&Stmt) != NewSchedule.end())
327       ScheduleMap = ScheduleMap.add_map(NewSchedule[&Stmt]);
328     else
329       ScheduleMap = ScheduleMap.add_map(Stmt.getSchedule());
330   }
331 
332   S.setSchedule(ScheduleMap);
333 
334   return true;
335 }
336 
337 /// Import new memory accesses from JScop.
338 ///
339 /// @param S The scop to update.
340 /// @param JScop The JScop file describing the new schedule.
341 /// @param DL The data layout to assume.
342 /// @param NewAccessStrings optionally record the imported access strings
343 ///
344 /// @returns True if the import succeeded, otherwise False.
345 static bool
importAccesses(Scop & S,const json::Object & JScop,const DataLayout & DL,std::vector<std::string> * NewAccessStrings=nullptr)346 importAccesses(Scop &S, const json::Object &JScop, const DataLayout &DL,
347                std::vector<std::string> *NewAccessStrings = nullptr) {
348   int StatementIdx = 0;
349 
350   // Check if key 'statements' is present.
351   if (!JScop.get("statements")) {
352     errs() << "JScop file has no key name 'statements'.\n";
353     return false;
354   }
355   const json::Array &statements = *JScop.getArray("statements");
356 
357   // Check whether the number of indices equals the number of statements
358   if (statements.size() != S.getSize()) {
359     errs() << "The number of indices and the number of statements differ.\n";
360     return false;
361   }
362 
363   for (ScopStmt &Stmt : S) {
364     int MemoryAccessIdx = 0;
365     const json::Object *Statement = statements[StatementIdx].getAsObject();
366     assert(Statement);
367 
368     // Check if key 'accesses' is present.
369     if (!Statement->get("accesses")) {
370       errs()
371           << "Statement from JScop file has no key name 'accesses' for index "
372           << StatementIdx << ".\n";
373       return false;
374     }
375     const json::Array &JsonAccesses = *Statement->getArray("accesses");
376 
377     // Check whether the number of indices equals the number of memory
378     // accesses
379     if (Stmt.size() != JsonAccesses.size()) {
380       errs() << "The number of memory accesses in the JSop file and the number "
381                 "of memory accesses differ for index "
382              << StatementIdx << ".\n";
383       return false;
384     }
385 
386     for (MemoryAccess *MA : Stmt) {
387       // Check if key 'relation' is present.
388       const json::Object *JsonMemoryAccess =
389           JsonAccesses[MemoryAccessIdx].getAsObject();
390       assert(JsonMemoryAccess);
391       if (!JsonMemoryAccess->get("relation")) {
392         errs() << "Memory access number " << MemoryAccessIdx
393                << " has no key name 'relation' for statement number "
394                << StatementIdx << ".\n";
395         return false;
396       }
397       StringRef Accesses = JsonMemoryAccess->getString("relation").getValue();
398       isl_map *NewAccessMap =
399           isl_map_read_from_str(S.getIslCtx().get(), Accesses.str().c_str());
400 
401       // Check whether the access was parsed successfully
402       if (!NewAccessMap) {
403         errs() << "The access was not parsed successfully by ISL.\n";
404         return false;
405       }
406       isl_map *CurrentAccessMap = MA->getAccessRelation().release();
407 
408       // Check if the number of parameter change
409       if (isl_map_dim(NewAccessMap, isl_dim_param) !=
410           isl_map_dim(CurrentAccessMap, isl_dim_param)) {
411         errs() << "JScop file changes the number of parameter dimensions.\n";
412         isl_map_free(CurrentAccessMap);
413         isl_map_free(NewAccessMap);
414         return false;
415       }
416 
417       isl_id *NewOutId;
418 
419       // If the NewAccessMap has zero dimensions, it is the scalar access; it
420       // must be the same as before.
421       // If it has at least one dimension, it's an array access; search for
422       // its ScopArrayInfo.
423       if (isl_map_dim(NewAccessMap, isl_dim_out) >= 1) {
424         NewOutId = isl_map_get_tuple_id(NewAccessMap, isl_dim_out);
425         auto *SAI = S.getArrayInfoByName(isl_id_get_name(NewOutId));
426         isl_id *OutId = isl_map_get_tuple_id(CurrentAccessMap, isl_dim_out);
427         auto *OutSAI = ScopArrayInfo::getFromId(isl::manage(OutId));
428         if (!SAI || SAI->getElementType() != OutSAI->getElementType()) {
429           errs() << "JScop file contains access function with undeclared "
430                     "ScopArrayInfo\n";
431           isl_map_free(CurrentAccessMap);
432           isl_map_free(NewAccessMap);
433           isl_id_free(NewOutId);
434           return false;
435         }
436         isl_id_free(NewOutId);
437         NewOutId = SAI->getBasePtrId().release();
438       } else {
439         NewOutId = isl_map_get_tuple_id(CurrentAccessMap, isl_dim_out);
440       }
441 
442       NewAccessMap = isl_map_set_tuple_id(NewAccessMap, isl_dim_out, NewOutId);
443 
444       if (MA->isArrayKind()) {
445         // We keep the old alignment, thus we cannot allow accesses to memory
446         // locations that were not accessed before if the alignment of the
447         // access is not the default alignment.
448         bool SpecialAlignment = true;
449         if (LoadInst *LoadI = dyn_cast<LoadInst>(MA->getAccessInstruction())) {
450           SpecialAlignment =
451               LoadI->getAlignment() &&
452               DL.getABITypeAlignment(LoadI->getType()) != LoadI->getAlignment();
453         } else if (StoreInst *StoreI =
454                        dyn_cast<StoreInst>(MA->getAccessInstruction())) {
455           SpecialAlignment =
456               StoreI->getAlignment() &&
457               DL.getABITypeAlignment(StoreI->getValueOperand()->getType()) !=
458                   StoreI->getAlignment();
459         }
460 
461         if (SpecialAlignment) {
462           isl_set *NewAccessSet = isl_map_range(isl_map_copy(NewAccessMap));
463           isl_set *CurrentAccessSet =
464               isl_map_range(isl_map_copy(CurrentAccessMap));
465           bool IsSubset = isl_set_is_subset(NewAccessSet, CurrentAccessSet);
466           isl_set_free(NewAccessSet);
467           isl_set_free(CurrentAccessSet);
468 
469           // Check if the JScop file changes the accessed memory.
470           if (!IsSubset) {
471             errs() << "JScop file changes the accessed memory\n";
472             isl_map_free(CurrentAccessMap);
473             isl_map_free(NewAccessMap);
474             return false;
475           }
476         }
477       }
478 
479       // We need to copy the isl_ids for the parameter dimensions to the new
480       // map. Without doing this the current map would have different
481       // ids then the new one, even though both are named identically.
482       for (isl_size i = 0; i < isl_map_dim(CurrentAccessMap, isl_dim_param);
483            i++) {
484         isl_id *Id = isl_map_get_dim_id(CurrentAccessMap, isl_dim_param, i);
485         NewAccessMap = isl_map_set_dim_id(NewAccessMap, isl_dim_param, i, Id);
486       }
487 
488       // Copy the old tuple id. This is necessary to retain the user pointer,
489       // that stores the reference to the ScopStmt this access belongs to.
490       isl_id *Id = isl_map_get_tuple_id(CurrentAccessMap, isl_dim_in);
491       NewAccessMap = isl_map_set_tuple_id(NewAccessMap, isl_dim_in, Id);
492 
493       auto NewAccessDomain = isl_map_domain(isl_map_copy(NewAccessMap));
494       auto CurrentAccessDomain = isl_map_domain(isl_map_copy(CurrentAccessMap));
495 
496       if (!isl_set_has_equal_space(NewAccessDomain, CurrentAccessDomain)) {
497         errs() << "JScop file contains access function with incompatible "
498                << "dimensions\n";
499         isl_map_free(CurrentAccessMap);
500         isl_map_free(NewAccessMap);
501         isl_set_free(NewAccessDomain);
502         isl_set_free(CurrentAccessDomain);
503         return false;
504       }
505 
506       NewAccessDomain =
507           isl_set_intersect_params(NewAccessDomain, S.getContext().release());
508       CurrentAccessDomain = isl_set_intersect_params(CurrentAccessDomain,
509                                                      S.getContext().release());
510       CurrentAccessDomain =
511           isl_set_intersect(CurrentAccessDomain, Stmt.getDomain().release());
512 
513       if (MA->isRead() &&
514           isl_set_is_subset(CurrentAccessDomain, NewAccessDomain) ==
515               isl_bool_false) {
516         errs() << "Mapping not defined for all iteration domain elements\n";
517         isl_set_free(CurrentAccessDomain);
518         isl_set_free(NewAccessDomain);
519         isl_map_free(CurrentAccessMap);
520         isl_map_free(NewAccessMap);
521         return false;
522       }
523 
524       isl_set_free(CurrentAccessDomain);
525       isl_set_free(NewAccessDomain);
526 
527       if (!isl_map_is_equal(NewAccessMap, CurrentAccessMap)) {
528         // Statistics.
529         ++NewAccessMapFound;
530         if (NewAccessStrings)
531           NewAccessStrings->push_back(Accesses.str());
532         MA->setNewAccessRelation(isl::manage(NewAccessMap));
533       } else {
534         isl_map_free(NewAccessMap);
535       }
536       isl_map_free(CurrentAccessMap);
537       MemoryAccessIdx++;
538     }
539     StatementIdx++;
540   }
541 
542   return true;
543 }
544 
545 /// Check whether @p SAI and @p Array represent the same array.
areArraysEqual(ScopArrayInfo * SAI,const json::Object & Array)546 static bool areArraysEqual(ScopArrayInfo *SAI, const json::Object &Array) {
547   std::string Buffer;
548   llvm::raw_string_ostream RawStringOstream(Buffer);
549 
550   // Check if key 'type' is present.
551   if (!Array.get("type")) {
552     errs() << "Array has no key 'type'.\n";
553     return false;
554   }
555 
556   // Check if key 'sizes' is present.
557   if (!Array.get("sizes")) {
558     errs() << "Array has no key 'sizes'.\n";
559     return false;
560   }
561 
562   // Check if key 'name' is present.
563   if (!Array.get("name")) {
564     errs() << "Array has no key 'name'.\n";
565     return false;
566   }
567 
568   if (SAI->getName() != Array.getString("name").getValue())
569     return false;
570 
571   if (SAI->getNumberOfDimensions() != Array.getArray("sizes")->size())
572     return false;
573 
574   for (unsigned i = 1; i < Array.getArray("sizes")->size(); i++) {
575     SAI->getDimensionSize(i)->print(RawStringOstream);
576     const json::Array &SizesArray = *Array.getArray("sizes");
577     if (RawStringOstream.str() != SizesArray[i].getAsString().getValue())
578       return false;
579     Buffer.clear();
580   }
581 
582   // Check if key 'type' differs from the current one or is not valid.
583   SAI->getElementType()->print(RawStringOstream);
584   if (RawStringOstream.str() != Array.getString("type").getValue()) {
585     errs() << "Array has not a valid type.\n";
586     return false;
587   }
588 
589   return true;
590 }
591 
592 /// Get the accepted primitive type from its textual representation
593 ///        @p TypeTextRepresentation.
594 ///
595 /// @param TypeTextRepresentation The textual representation of the type.
596 /// @return The pointer to the primitive type, if this type is accepted
597 ///         or nullptr otherwise.
parseTextType(const std::string & TypeTextRepresentation,LLVMContext & LLVMContext)598 static Type *parseTextType(const std::string &TypeTextRepresentation,
599                            LLVMContext &LLVMContext) {
600   std::map<std::string, Type *> MapStrToType = {
601       {"void", Type::getVoidTy(LLVMContext)},
602       {"half", Type::getHalfTy(LLVMContext)},
603       {"float", Type::getFloatTy(LLVMContext)},
604       {"double", Type::getDoubleTy(LLVMContext)},
605       {"x86_fp80", Type::getX86_FP80Ty(LLVMContext)},
606       {"fp128", Type::getFP128Ty(LLVMContext)},
607       {"ppc_fp128", Type::getPPC_FP128Ty(LLVMContext)},
608       {"i1", Type::getInt1Ty(LLVMContext)},
609       {"i8", Type::getInt8Ty(LLVMContext)},
610       {"i16", Type::getInt16Ty(LLVMContext)},
611       {"i32", Type::getInt32Ty(LLVMContext)},
612       {"i64", Type::getInt64Ty(LLVMContext)},
613       {"i128", Type::getInt128Ty(LLVMContext)}};
614 
615   auto It = MapStrToType.find(TypeTextRepresentation);
616   if (It != MapStrToType.end())
617     return It->second;
618 
619   errs() << "Textual representation can not be parsed: "
620          << TypeTextRepresentation << "\n";
621   return nullptr;
622 }
623 
624 /// Import new arrays from JScop.
625 ///
626 /// @param S The scop to update.
627 /// @param JScop The JScop file describing new arrays.
628 ///
629 /// @returns True if the import succeeded, otherwise False.
importArrays(Scop & S,const json::Object & JScop)630 static bool importArrays(Scop &S, const json::Object &JScop) {
631   if (!JScop.get("arrays"))
632     return true;
633   const json::Array &Arrays = *JScop.getArray("arrays");
634   if (Arrays.size() == 0)
635     return true;
636 
637   unsigned ArrayIdx = 0;
638   for (auto &SAI : S.arrays()) {
639     if (!SAI->isArrayKind())
640       continue;
641     if (ArrayIdx + 1 > Arrays.size()) {
642       errs() << "Not enough array entries in JScop file.\n";
643       return false;
644     }
645     if (!areArraysEqual(SAI, *Arrays[ArrayIdx].getAsObject())) {
646       errs() << "No match for array '" << SAI->getName() << "' in JScop.\n";
647       return false;
648     }
649     ArrayIdx++;
650   }
651 
652   for (; ArrayIdx < Arrays.size(); ArrayIdx++) {
653     const json::Object &Array = *Arrays[ArrayIdx].getAsObject();
654     auto *ElementType =
655         parseTextType(Array.get("type")->getAsString().getValue().str(),
656                       S.getSE()->getContext());
657     if (!ElementType) {
658       errs() << "Error while parsing element type for new array.\n";
659       return false;
660     }
661     const json::Array &SizesArray = *Array.getArray("sizes");
662     std::vector<unsigned> DimSizes;
663     for (unsigned i = 0; i < SizesArray.size(); i++) {
664       auto Size = std::stoi(SizesArray[i].getAsString().getValue().str());
665 
666       // Check if the size if positive.
667       if (Size <= 0) {
668         errs() << "The size at index " << i << " is =< 0.\n";
669         return false;
670       }
671 
672       DimSizes.push_back(Size);
673     }
674 
675     auto NewSAI = S.createScopArrayInfo(
676         ElementType, Array.getString("name").getValue().str(), DimSizes);
677 
678     if (Array.get("allocation")) {
679       NewSAI->setIsOnHeap(Array.getString("allocation").getValue() == "heap");
680     }
681   }
682 
683   return true;
684 }
685 
686 /// Import a Scop from a JSCOP file
687 /// @param S The scop to be modified
688 /// @param D Dependence Info
689 /// @param DL The DataLayout of the function
690 /// @param NewAccessStrings Optionally record the imported access strings
691 ///
692 /// @returns true on success, false otherwise. Beware that if this returns
693 /// false, the Scop may still have been modified. In this case the Scop contains
694 /// invalid information.
importScop(Scop & S,const Dependences & D,const DataLayout & DL,std::vector<std::string> * NewAccessStrings=nullptr)695 static bool importScop(Scop &S, const Dependences &D, const DataLayout &DL,
696                        std::vector<std::string> *NewAccessStrings = nullptr) {
697   std::string FileName = ImportDir + "/" + getFileName(S, ImportPostfix);
698 
699   std::string FunctionName = S.getFunction().getName().str();
700   errs() << "Reading JScop '" << S.getNameStr() << "' in function '"
701          << FunctionName << "' from '" << FileName << "'.\n";
702   ErrorOr<std::unique_ptr<MemoryBuffer>> result =
703       MemoryBuffer::getFile(FileName);
704   std::error_code ec = result.getError();
705 
706   if (ec) {
707     errs() << "File could not be read: " << ec.message() << "\n";
708     return false;
709   }
710 
711   Expected<json::Value> ParseResult =
712       json::parse(result.get().get()->getBuffer());
713 
714   if (Error E = ParseResult.takeError()) {
715     errs() << "JSCoP file could not be parsed\n";
716     errs() << E << "\n";
717     consumeError(std::move(E));
718     return false;
719   }
720   json::Object &jscop = *ParseResult.get().getAsObject();
721 
722   bool Success = importContext(S, jscop);
723 
724   if (!Success)
725     return false;
726 
727   Success = importSchedule(S, jscop, D);
728 
729   if (!Success)
730     return false;
731 
732   Success = importArrays(S, jscop);
733 
734   if (!Success)
735     return false;
736 
737   Success = importAccesses(S, jscop, DL, NewAccessStrings);
738 
739   if (!Success)
740     return false;
741   return true;
742 }
743 
744 char JSONExporter::ID = 0;
printScop(raw_ostream & OS,Scop & S) const745 void JSONExporter::printScop(raw_ostream &OS, Scop &S) const { OS << S; }
746 
runOnScop(Scop & S)747 bool JSONExporter::runOnScop(Scop &S) {
748   exportScop(S);
749   return false;
750 }
751 
getAnalysisUsage(AnalysisUsage & AU) const752 void JSONExporter::getAnalysisUsage(AnalysisUsage &AU) const {
753   AU.setPreservesAll();
754   AU.addRequired<ScopInfoRegionPass>();
755 }
756 
createJSONExporterPass()757 Pass *polly::createJSONExporterPass() { return new JSONExporter(); }
758 
run(Scop & S,ScopAnalysisManager & SAM,ScopStandardAnalysisResults & SAR,SPMUpdater &)759 PreservedAnalyses JSONExportPass::run(Scop &S, ScopAnalysisManager &SAM,
760                                       ScopStandardAnalysisResults &SAR,
761                                       SPMUpdater &) {
762   exportScop(S);
763   return PreservedAnalyses::all();
764 }
765 
766 char JSONImporter::ID = 0;
767 
printScop(raw_ostream & OS,Scop & S) const768 void JSONImporter::printScop(raw_ostream &OS, Scop &S) const {
769   OS << S;
770   for (std::vector<std::string>::const_iterator I = NewAccessStrings.begin(),
771                                                 E = NewAccessStrings.end();
772        I != E; I++)
773     OS << "New access function '" << *I << "' detected in JSCOP file\n";
774 }
775 
runOnScop(Scop & S)776 bool JSONImporter::runOnScop(Scop &S) {
777   const Dependences &D =
778       getAnalysis<DependenceInfo>().getDependences(Dependences::AL_Statement);
779   const DataLayout &DL = S.getFunction().getParent()->getDataLayout();
780 
781   if (!importScop(S, D, DL, &NewAccessStrings))
782     report_fatal_error("Tried to import a malformed jscop file.");
783 
784   return false;
785 }
786 
getAnalysisUsage(AnalysisUsage & AU) const787 void JSONImporter::getAnalysisUsage(AnalysisUsage &AU) const {
788   ScopPass::getAnalysisUsage(AU);
789   AU.addRequired<DependenceInfo>();
790 
791   // TODO: JSONImporter should throw away DependenceInfo.
792   AU.addPreserved<DependenceInfo>();
793 }
794 
createJSONImporterPass()795 Pass *polly::createJSONImporterPass() { return new JSONImporter(); }
796 
run(Scop & S,ScopAnalysisManager & SAM,ScopStandardAnalysisResults & SAR,SPMUpdater &)797 PreservedAnalyses JSONImportPass::run(Scop &S, ScopAnalysisManager &SAM,
798                                       ScopStandardAnalysisResults &SAR,
799                                       SPMUpdater &) {
800   const Dependences &D =
801       SAM.getResult<DependenceAnalysis>(S, SAR).getDependences(
802           Dependences::AL_Statement);
803   const DataLayout &DL = S.getFunction().getParent()->getDataLayout();
804 
805   if (!importScop(S, D, DL))
806     report_fatal_error("Tried to import a malformed jscop file.");
807 
808   // This invalidates all analyses on Scop.
809   PreservedAnalyses PA;
810   PA.preserveSet<AllAnalysesOn<Module>>();
811   PA.preserveSet<AllAnalysesOn<Function>>();
812   PA.preserveSet<AllAnalysesOn<Loop>>();
813   return PA;
814 }
815 
816 INITIALIZE_PASS_BEGIN(JSONExporter, "polly-export-jscop",
817                       "Polly - Export Scops as JSON"
818                       " (Writes a .jscop file for each Scop)",
819                       false, false);
820 INITIALIZE_PASS_DEPENDENCY(DependenceInfo)
821 INITIALIZE_PASS_END(JSONExporter, "polly-export-jscop",
822                     "Polly - Export Scops as JSON"
823                     " (Writes a .jscop file for each Scop)",
824                     false, false)
825 
826 INITIALIZE_PASS_BEGIN(JSONImporter, "polly-import-jscop",
827                       "Polly - Import Scops from JSON"
828                       " (Reads a .jscop file for each Scop)",
829                       false, false);
830 INITIALIZE_PASS_DEPENDENCY(DependenceInfo)
831 INITIALIZE_PASS_END(JSONImporter, "polly-import-jscop",
832                     "Polly - Import Scops from JSON"
833                     " (Reads a .jscop file for each Scop)",
834                     false, false)
835