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