1 //===-- JSONExporter.cpp - Export Scops as JSON -------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Export the Scops build by ScopInfo pass as a JSON file.
12 //===----------------------------------------------------------------------===//
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 "llvm/Support/raw_ostream.h"
27 #include "isl/constraint.h"
29 #include "isl/printer.h"
31 #include "isl/union_map.h"
32 #include "json/reader.h"
33 #include "json/writer.h"
36 #include <system_error>
39 using namespace polly
;
41 #define DEBUG_TYPE "polly-import-jscop"
43 STATISTIC(NewAccessMapFound
, "Number of updated access functions");
46 static cl::opt
<std::string
>
47 ImportDir("polly-import-jscop-dir",
48 cl::desc("The directory to import the .jscop files from."),
49 cl::Hidden
, cl::value_desc("Directory path"), cl::ValueRequired
,
50 cl::init("."), cl::cat(PollyCategory
));
52 static cl::opt
<std::string
>
53 ImportPostfix("polly-import-jscop-postfix",
54 cl::desc("Postfix to append to the import .jsop files."),
55 cl::Hidden
, cl::value_desc("File postfix"), cl::ValueRequired
,
56 cl::init(""), cl::cat(PollyCategory
));
58 struct JSONExporter
: public ScopPass
{
60 explicit JSONExporter() : ScopPass(ID
) {}
62 std::string
getFileName(Scop
&S
) const;
63 Json::Value
getJSON(Scop
&S
) const;
65 /// Export the SCoP @p S to a JSON file.
66 bool runOnScop(Scop
&S
) override
;
68 /// Print the SCoP @p S as it is exported.
69 void printScop(raw_ostream
&OS
, Scop
&S
) const override
;
71 /// Register all analyses and transformation required.
72 void getAnalysisUsage(AnalysisUsage
&AU
) const override
;
75 struct JSONImporter
: public ScopPass
{
77 std::vector
<std::string
> NewAccessStrings
;
78 explicit JSONImporter() : ScopPass(ID
) {}
80 /// Import a new context from JScop.
82 /// @param S The scop to update.
83 /// @param JScop The JScop file describing the new schedule.
85 /// @returns True if the import succeeded, otherwise False.
86 bool importContext(Scop
&S
, Json::Value
&JScop
);
88 /// Import a new schedule from JScop.
90 /// ... and verify that the new schedule does preserve existing data
93 /// @param S The scop to update.
94 /// @param JScop The JScop file describing the new schedule.
95 /// @param D The data dependences of the @p S.
97 /// @returns True if the import succeeded, otherwise False.
98 bool importSchedule(Scop
&S
, Json::Value
&JScop
, const Dependences
&D
);
100 /// Import new arrays from JScop.
102 /// @param S The scop to update.
103 /// @param JScop The JScop file describing new arrays.
105 /// @returns True if the import succeeded, otherwise False.
106 bool importArrays(Scop
&S
, Json::Value
&JScop
);
108 /// Import new memory accesses from JScop.
110 /// @param S The scop to update.
111 /// @param JScop The JScop file describing the new schedule.
112 /// @param DL The datalayout to assume.
114 /// @returns True if the import succeeded, otherwise False.
115 bool importAccesses(Scop
&S
, Json::Value
&JScop
, const DataLayout
&DL
);
117 std::string
getFileName(Scop
&S
) const;
119 /// Import new access functions for SCoP @p S from a JSON file.
120 bool runOnScop(Scop
&S
) override
;
122 /// Print the SCoP @p S and the imported access functions.
123 void printScop(raw_ostream
&OS
, Scop
&S
) const override
;
125 /// Register all analyses and transformation required.
126 void getAnalysisUsage(AnalysisUsage
&AU
) const override
;
130 char JSONExporter::ID
= 0;
131 std::string
JSONExporter::getFileName(Scop
&S
) const {
132 std::string FunctionName
= S
.getFunction().getName();
133 std::string FileName
= FunctionName
+ "___" + S
.getNameStr() + ".jscop";
137 void JSONExporter::printScop(raw_ostream
&OS
, Scop
&S
) const { S
.print(OS
); }
139 /// Export all arrays from the Scop.
141 /// @param S The Scop containing the arrays.
143 /// @returns Json::Value containing the arrays.
144 Json::Value
exportArrays(const Scop
&S
) {
147 llvm::raw_string_ostream
RawStringOstream(Buffer
);
149 for (auto &SAI
: S
.arrays()) {
150 if (!SAI
->isArrayKind())
154 Array
["name"] = SAI
->getName();
156 if (!SAI
->getDimensionSize(i
)) {
157 Array
["sizes"].append("*");
160 for (; i
< SAI
->getNumberOfDimensions(); i
++) {
161 SAI
->getDimensionSize(i
)->print(RawStringOstream
);
162 Array
["sizes"].append(RawStringOstream
.str());
165 SAI
->getElementType()->print(RawStringOstream
);
166 Array
["type"] = RawStringOstream
.str();
168 Arrays
.append(Array
);
173 Json::Value
JSONExporter::getJSON(Scop
&S
) const {
175 unsigned LineBegin
, LineEnd
;
176 std::string FileName
;
178 getDebugLocation(&S
.getRegion(), LineBegin
, LineEnd
, FileName
);
179 std::string Location
;
180 if (LineBegin
!= (unsigned)-1)
181 Location
= FileName
+ ":" + std::to_string(LineBegin
) + "-" +
182 std::to_string(LineEnd
);
184 root
["name"] = S
.getNameStr();
185 root
["context"] = S
.getContextStr();
186 if (LineBegin
!= (unsigned)-1)
187 root
["location"] = Location
;
189 root
["arrays"] = exportArrays(S
);
193 for (ScopStmt
&Stmt
: S
) {
194 Json::Value statement
;
196 statement
["name"] = Stmt
.getBaseName();
197 statement
["domain"] = Stmt
.getDomainStr();
198 statement
["schedule"] = Stmt
.getScheduleStr();
199 statement
["accesses"];
201 for (MemoryAccess
*MA
: Stmt
) {
204 access
["kind"] = MA
->isRead() ? "read" : "write";
205 access
["relation"] = MA
->getOriginalAccessRelationStr();
207 statement
["accesses"].append(access
);
210 root
["statements"].append(statement
);
216 bool JSONExporter::runOnScop(Scop
&S
) {
217 std::string FileName
= ImportDir
+ "/" + getFileName(S
);
219 Json::Value jscop
= getJSON(S
);
220 Json::StyledWriter writer
;
221 std::string fileContent
= writer
.write(jscop
);
225 tool_output_file
F(FileName
, EC
, llvm::sys::fs::F_Text
);
227 std::string FunctionName
= S
.getFunction().getName();
228 errs() << "Writing JScop '" << S
.getNameStr() << "' in function '"
229 << FunctionName
<< "' to '" << FileName
<< "'.\n";
232 F
.os() << fileContent
;
234 if (!F
.os().has_error()) {
241 errs() << " error opening file for writing!\n";
242 F
.os().clear_error();
247 void JSONExporter::getAnalysisUsage(AnalysisUsage
&AU
) const {
248 AU
.setPreservesAll();
249 AU
.addRequired
<ScopInfoRegionPass
>();
252 Pass
*polly::createJSONExporterPass() { return new JSONExporter(); }
254 char JSONImporter::ID
= 0;
255 std::string
JSONImporter::getFileName(Scop
&S
) const {
256 std::string FunctionName
= S
.getFunction().getName();
257 std::string FileName
= FunctionName
+ "___" + S
.getNameStr() + ".jscop";
259 if (ImportPostfix
!= "")
260 FileName
+= "." + ImportPostfix
;
265 void JSONImporter::printScop(raw_ostream
&OS
, Scop
&S
) const {
267 for (std::vector
<std::string
>::const_iterator I
= NewAccessStrings
.begin(),
268 E
= NewAccessStrings
.end();
270 OS
<< "New access function '" << *I
<< "' detected in JSCOP file\n";
273 typedef Dependences::StatementToIslMapTy StatementToIslMapTy
;
275 bool JSONImporter::importContext(Scop
&S
, Json::Value
&JScop
) {
276 isl_set
*OldContext
= S
.getContext();
277 isl_set
*NewContext
=
278 isl_set_read_from_str(S
.getIslCtx(), JScop
["context"].asCString());
280 for (unsigned i
= 0; i
< isl_set_dim(OldContext
, isl_dim_param
); i
++) {
281 isl_id
*Id
= isl_set_get_dim_id(OldContext
, isl_dim_param
, i
);
282 NewContext
= isl_set_set_dim_id(NewContext
, isl_dim_param
, i
, Id
);
285 isl_set_free(OldContext
);
286 S
.setContext(NewContext
);
290 bool JSONImporter::importSchedule(Scop
&S
, Json::Value
&JScop
,
291 const Dependences
&D
) {
292 StatementToIslMapTy NewSchedule
;
295 for (ScopStmt
&Stmt
: S
) {
296 Json::Value Schedule
= JScop
["statements"][Index
]["schedule"];
297 assert(!Schedule
.asString().empty() &&
298 "Schedules that contain extension nodes require special handling.");
299 isl_map
*Map
= isl_map_read_from_str(S
.getIslCtx(), Schedule
.asCString());
300 isl_space
*Space
= Stmt
.getDomainSpace();
302 // Copy the old tuple id. This is necessary to retain the user pointer,
303 // that stores the reference to the ScopStmt this schedule belongs to.
304 Map
= isl_map_set_tuple_id(Map
, isl_dim_in
,
305 isl_space_get_tuple_id(Space
, isl_dim_set
));
306 for (unsigned i
= 0; i
< isl_space_dim(Space
, isl_dim_param
); i
++) {
307 isl_id
*Id
= isl_space_get_dim_id(Space
, isl_dim_param
, i
);
308 Map
= isl_map_set_dim_id(Map
, isl_dim_param
, i
, Id
);
310 isl_space_free(Space
);
311 NewSchedule
[&Stmt
] = Map
;
315 if (!D
.isValidSchedule(S
, &NewSchedule
)) {
316 errs() << "JScop file contains a schedule that changes the "
317 << "dependences. Use -disable-polly-legality to continue anyways\n";
318 for (auto Element
: NewSchedule
)
319 isl_map_free(Element
.second
);
323 auto ScheduleMap
= isl_union_map_empty(S
.getParamSpace());
324 for (ScopStmt
&Stmt
: S
) {
325 if (NewSchedule
.find(&Stmt
) != NewSchedule
.end())
326 ScheduleMap
= isl_union_map_add_map(ScheduleMap
, NewSchedule
[&Stmt
]);
328 ScheduleMap
= isl_union_map_add_map(ScheduleMap
, Stmt
.getSchedule());
331 S
.setSchedule(ScheduleMap
);
336 bool JSONImporter::importAccesses(Scop
&S
, Json::Value
&JScop
,
337 const DataLayout
&DL
) {
338 int StatementIdx
= 0;
339 for (ScopStmt
&Stmt
: S
) {
340 int MemoryAccessIdx
= 0;
341 for (MemoryAccess
*MA
: Stmt
) {
342 Json::Value Accesses
= JScop
["statements"][StatementIdx
]["accesses"]
343 [MemoryAccessIdx
]["relation"];
344 isl_map
*NewAccessMap
=
345 isl_map_read_from_str(S
.getIslCtx(), Accesses
.asCString());
346 isl_map
*CurrentAccessMap
= MA
->getAccessRelation();
348 if (isl_map_dim(NewAccessMap
, isl_dim_param
) !=
349 isl_map_dim(CurrentAccessMap
, isl_dim_param
)) {
350 errs() << "JScop file changes the number of parameter dimensions\n";
351 isl_map_free(CurrentAccessMap
);
352 isl_map_free(NewAccessMap
);
358 // If the NewAccessMap has zero dimensions, it is the scalar access; it
359 // must be the same as before.
360 // If it has at least one dimension, it's an array access; search for its
362 if (isl_map_dim(NewAccessMap
, isl_dim_out
) >= 1) {
363 NewOutId
= isl_map_get_tuple_id(NewAccessMap
, isl_dim_out
);
364 auto *SAI
= S
.getArrayInfoByName(isl_id_get_name(NewOutId
));
365 isl_id
*OutId
= isl_map_get_tuple_id(CurrentAccessMap
, isl_dim_out
);
366 auto *OutSAI
= ScopArrayInfo::getFromId(OutId
);
367 if (!SAI
|| SAI
->getElementType() != OutSAI
->getElementType()) {
368 errs() << "JScop file contains access function with undeclared "
370 isl_map_free(CurrentAccessMap
);
371 isl_map_free(NewAccessMap
);
372 isl_id_free(NewOutId
);
375 isl_id_free(NewOutId
);
376 NewOutId
= SAI
->getBasePtrId();
378 NewOutId
= isl_map_get_tuple_id(CurrentAccessMap
, isl_dim_out
);
381 NewAccessMap
= isl_map_set_tuple_id(NewAccessMap
, isl_dim_out
, NewOutId
);
383 if (MA
->isArrayKind()) {
384 // We keep the old alignment, thus we cannot allow accesses to memory
385 // locations that were not accessed before if the alignment of the
386 // access is not the default alignment.
387 bool SpecialAlignment
= true;
388 if (LoadInst
*LoadI
= dyn_cast
<LoadInst
>(MA
->getAccessInstruction())) {
390 LoadI
->getAlignment() &&
391 DL
.getABITypeAlignment(LoadI
->getType()) != LoadI
->getAlignment();
392 } else if (StoreInst
*StoreI
=
393 dyn_cast
<StoreInst
>(MA
->getAccessInstruction())) {
395 StoreI
->getAlignment() &&
396 DL
.getABITypeAlignment(StoreI
->getValueOperand()->getType()) !=
397 StoreI
->getAlignment();
400 if (SpecialAlignment
) {
401 isl_set
*NewAccessSet
= isl_map_range(isl_map_copy(NewAccessMap
));
402 isl_set
*CurrentAccessSet
=
403 isl_map_range(isl_map_copy(CurrentAccessMap
));
404 bool IsSubset
= isl_set_is_subset(NewAccessSet
, CurrentAccessSet
);
405 isl_set_free(NewAccessSet
);
406 isl_set_free(CurrentAccessSet
);
409 errs() << "JScop file changes the accessed memory\n";
410 isl_map_free(CurrentAccessMap
);
411 isl_map_free(NewAccessMap
);
417 // We need to copy the isl_ids for the parameter dimensions to the new
418 // map. Without doing this the current map would have different
419 // ids then the new one, even though both are named identically.
420 for (unsigned i
= 0; i
< isl_map_dim(CurrentAccessMap
, isl_dim_param
);
422 isl_id
*Id
= isl_map_get_dim_id(CurrentAccessMap
, isl_dim_param
, i
);
423 NewAccessMap
= isl_map_set_dim_id(NewAccessMap
, isl_dim_param
, i
, Id
);
426 // Copy the old tuple id. This is necessary to retain the user pointer,
427 // that stores the reference to the ScopStmt this access belongs to.
428 isl_id
*Id
= isl_map_get_tuple_id(CurrentAccessMap
, isl_dim_in
);
429 NewAccessMap
= isl_map_set_tuple_id(NewAccessMap
, isl_dim_in
, Id
);
431 auto NewAccessDomain
= isl_map_domain(isl_map_copy(NewAccessMap
));
432 auto CurrentAccessDomain
= isl_map_domain(isl_map_copy(CurrentAccessMap
));
434 if (!isl_set_has_equal_space(NewAccessDomain
, CurrentAccessDomain
)) {
435 errs() << "JScop file contains access function with incompatible "
437 isl_map_free(CurrentAccessMap
);
438 isl_map_free(NewAccessMap
);
439 isl_set_free(NewAccessDomain
);
440 isl_set_free(CurrentAccessDomain
);
445 isl_set_intersect_params(NewAccessDomain
, S
.getContext());
446 CurrentAccessDomain
=
447 isl_set_intersect_params(CurrentAccessDomain
, S
.getContext());
449 if (isl_set_is_subset(CurrentAccessDomain
, NewAccessDomain
) ==
451 errs() << "Mapping not defined for all iteration domain elements\n";
452 isl_set_free(CurrentAccessDomain
);
453 isl_set_free(NewAccessDomain
);
454 isl_map_free(CurrentAccessMap
);
455 isl_map_free(NewAccessMap
);
459 isl_set_free(CurrentAccessDomain
);
460 isl_set_free(NewAccessDomain
);
462 if (!isl_map_is_equal(NewAccessMap
, CurrentAccessMap
)) {
465 NewAccessStrings
.push_back(Accesses
.asCString());
466 MA
->setNewAccessRelation(NewAccessMap
);
468 isl_map_free(NewAccessMap
);
470 isl_map_free(CurrentAccessMap
);
479 /// Check whether @p SAI and @p Array represent the same array.
480 bool areArraysEqual(ScopArrayInfo
*SAI
, Json::Value Array
) {
482 llvm::raw_string_ostream
RawStringOstream(Buffer
);
484 if (SAI
->getName() != Array
["name"].asCString())
487 if (SAI
->getNumberOfDimensions() != Array
["sizes"].size())
490 for (unsigned i
= 1; i
< Array
["sizes"].size(); i
++) {
491 SAI
->getDimensionSize(i
)->print(RawStringOstream
);
492 if (RawStringOstream
.str() != Array
["sizes"][i
].asCString())
497 SAI
->getElementType()->print(RawStringOstream
);
498 if (RawStringOstream
.str() != Array
["type"].asCString())
504 /// Get the accepted primitive type from its textual representation
505 /// @p TypeTextRepresentation.
507 /// @param TypeTextRepresentation The textual representation of the type.
508 /// @return The pointer to the primitive type, if this type is accepted
509 /// or nullptr otherwise.
510 Type
*parseTextType(const std::string
&TypeTextRepresentation
,
511 LLVMContext
&LLVMContext
) {
512 std::map
<std::string
, Type
*> MapStrToType
= {
513 {"void", Type::getVoidTy(LLVMContext
)},
514 {"half", Type::getHalfTy(LLVMContext
)},
515 {"float", Type::getFloatTy(LLVMContext
)},
516 {"double", Type::getDoubleTy(LLVMContext
)},
517 {"x86_fp80", Type::getX86_FP80Ty(LLVMContext
)},
518 {"fp128", Type::getFP128Ty(LLVMContext
)},
519 {"ppc_fp128", Type::getPPC_FP128Ty(LLVMContext
)},
520 {"i1", Type::getInt1Ty(LLVMContext
)},
521 {"i8", Type::getInt8Ty(LLVMContext
)},
522 {"i16", Type::getInt16Ty(LLVMContext
)},
523 {"i32", Type::getInt32Ty(LLVMContext
)},
524 {"i64", Type::getInt64Ty(LLVMContext
)},
525 {"i128", Type::getInt128Ty(LLVMContext
)}};
527 auto It
= MapStrToType
.find(TypeTextRepresentation
);
528 if (It
!= MapStrToType
.end())
531 errs() << "Textual representation can not be parsed: "
532 << TypeTextRepresentation
<< "\n";
536 bool JSONImporter::importArrays(Scop
&S
, Json::Value
&JScop
) {
537 Json::Value Arrays
= JScop
["arrays"];
539 if (Arrays
.size() == 0)
542 unsigned ArrayIdx
= 0;
543 for (auto &SAI
: S
.arrays()) {
544 if (!SAI
->isArrayKind())
546 if (ArrayIdx
+ 1 > Arrays
.size())
548 if (!areArraysEqual(SAI
, Arrays
[ArrayIdx
]))
553 for (; ArrayIdx
< Arrays
.size(); ArrayIdx
++) {
554 auto *ElementType
= parseTextType(Arrays
[ArrayIdx
]["type"].asCString(),
555 S
.getSE()->getContext());
558 std::vector
<unsigned> DimSizes
;
559 for (unsigned i
= 0; i
< Arrays
[ArrayIdx
]["sizes"].size(); i
++)
560 DimSizes
.push_back(std::stoi(Arrays
[ArrayIdx
]["sizes"][i
].asCString()));
561 S
.createScopArrayInfo(ElementType
, Arrays
[ArrayIdx
]["name"].asCString(),
568 bool JSONImporter::runOnScop(Scop
&S
) {
569 const Dependences
&D
=
570 getAnalysis
<DependenceInfo
>().getDependences(Dependences::AL_Statement
);
571 const DataLayout
&DL
= S
.getFunction().getParent()->getDataLayout();
573 std::string FileName
= ImportDir
+ "/" + getFileName(S
);
575 std::string FunctionName
= S
.getFunction().getName();
576 errs() << "Reading JScop '" << S
.getNameStr() << "' in function '"
577 << FunctionName
<< "' from '" << FileName
<< "'.\n";
578 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> result
=
579 MemoryBuffer::getFile(FileName
);
580 std::error_code ec
= result
.getError();
583 errs() << "File could not be read: " << ec
.message() << "\n";
590 bool parsingSuccessful
= reader
.parse(result
.get()->getBufferStart(), jscop
);
592 if (!parsingSuccessful
) {
593 errs() << "JSCoP file could not be parsed\n";
597 bool Success
= importContext(S
, jscop
);
602 Success
= importSchedule(S
, jscop
, D
);
607 Success
= importArrays(S
, jscop
);
612 Success
= importAccesses(S
, jscop
, DL
);
620 void JSONImporter::getAnalysisUsage(AnalysisUsage
&AU
) const {
621 ScopPass::getAnalysisUsage(AU
);
622 AU
.addRequired
<DependenceInfo
>();
625 Pass
*polly::createJSONImporterPass() { return new JSONImporter(); }
627 INITIALIZE_PASS_BEGIN(JSONExporter
, "polly-export-jscop",
628 "Polly - Export Scops as JSON"
629 " (Writes a .jscop file for each Scop)",
631 INITIALIZE_PASS_DEPENDENCY(DependenceInfo
)
632 INITIALIZE_PASS_END(JSONExporter
, "polly-export-jscop",
633 "Polly - Export Scops as JSON"
634 " (Writes a .jscop file for each Scop)",
637 INITIALIZE_PASS_BEGIN(JSONImporter
, "polly-import-jscop",
638 "Polly - Import Scops from JSON"
639 " (Reads a .jscop file for each Scop)",
641 INITIALIZE_PASS_DEPENDENCY(DependenceInfo
)
642 INITIALIZE_PASS_END(JSONImporter
, "polly-import-jscop",
643 "Polly - Import Scops from JSON"
644 " (Reads a .jscop file for each Scop)",