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 for (auto Element : NewSchedule) { 292 isl_map_free(Element.second); 293 } 294 return false; 295 } 296 Optional<StringRef> Schedule = 297 statements[Index].getAsObject()->getString("schedule"); 298 assert(Schedule.hasValue() && 299 "Schedules that contain extension nodes require special handling."); 300 isl_map *Map = isl_map_read_from_str(S.getIslCtx().get(), 301 Schedule.getValue().str().c_str()); 302 303 // Check whether the schedule was parsed successfully 304 if (!Map) { 305 errs() << "The schedule was not parsed successfully (index = " << Index 306 << ").\n"; 307 for (auto Element : NewSchedule) { 308 isl_map_free(Element.second); 309 } 310 return false; 311 } 312 313 isl_space *Space = Stmt.getDomainSpace().release(); 314 315 // Copy the old tuple id. This is necessary to retain the user pointer, 316 // that stores the reference to the ScopStmt this schedule belongs to. 317 Map = isl_map_set_tuple_id(Map, isl_dim_in, 318 isl_space_get_tuple_id(Space, isl_dim_set)); 319 for (unsigned i = 0; i < isl_space_dim(Space, isl_dim_param); i++) { 320 isl_id *Id = isl_space_get_dim_id(Space, isl_dim_param, i); 321 Map = isl_map_set_dim_id(Map, isl_dim_param, i, Id); 322 } 323 isl_space_free(Space); 324 NewSchedule[&Stmt] = Map; 325 Index++; 326 } 327 328 // Check whether the new schedule is valid or not. 329 if (!D.isValidSchedule(S, &NewSchedule)) { 330 errs() << "JScop file contains a schedule that changes the " 331 << "dependences. Use -disable-polly-legality to continue anyways\n"; 332 for (auto Element : NewSchedule) 333 isl_map_free(Element.second); 334 return false; 335 } 336 337 auto ScheduleMap = isl::union_map::empty(S.getParamSpace()); 338 for (ScopStmt &Stmt : S) { 339 if (NewSchedule.find(&Stmt) != NewSchedule.end()) 340 ScheduleMap = ScheduleMap.add_map(isl::manage(NewSchedule[&Stmt])); 341 else 342 ScheduleMap = ScheduleMap.add_map(Stmt.getSchedule()); 343 } 344 345 S.setSchedule(ScheduleMap); 346 347 return true; 348 } 349 350 /// Import new memory accesses from JScop. 351 /// 352 /// @param S The scop to update. 353 /// @param JScop The JScop file describing the new schedule. 354 /// @param DL The data layout to assume. 355 /// @param NewAccessStrings optionally record the imported access strings 356 /// 357 /// @returns True if the import succeeded, otherwise False. 358 static bool 359 importAccesses(Scop &S, const json::Object &JScop, const DataLayout &DL, 360 std::vector<std::string> *NewAccessStrings = nullptr) { 361 int StatementIdx = 0; 362 363 // Check if key 'statements' is present. 364 if (!JScop.get("statements")) { 365 errs() << "JScop file has no key name 'statements'.\n"; 366 return false; 367 } 368 const json::Array &statements = *JScop.getArray("statements"); 369 370 // Check whether the number of indices equals the number of statements 371 if (statements.size() != S.getSize()) { 372 errs() << "The number of indices and the number of statements differ.\n"; 373 return false; 374 } 375 376 for (ScopStmt &Stmt : S) { 377 int MemoryAccessIdx = 0; 378 const json::Object *Statement = statements[StatementIdx].getAsObject(); 379 assert(Statement); 380 381 // Check if key 'accesses' is present. 382 if (!Statement->get("accesses")) { 383 errs() 384 << "Statement from JScop file has no key name 'accesses' for index " 385 << StatementIdx << ".\n"; 386 return false; 387 } 388 const json::Array &JsonAccesses = *Statement->getArray("accesses"); 389 390 // Check whether the number of indices equals the number of memory 391 // accesses 392 if (Stmt.size() != JsonAccesses.size()) { 393 errs() << "The number of memory accesses in the JSop file and the number " 394 "of memory accesses differ for index " 395 << StatementIdx << ".\n"; 396 return false; 397 } 398 399 for (MemoryAccess *MA : Stmt) { 400 // Check if key 'relation' is present. 401 const json::Object *JsonMemoryAccess = 402 JsonAccesses[MemoryAccessIdx].getAsObject(); 403 assert(JsonMemoryAccess); 404 if (!JsonMemoryAccess->get("relation")) { 405 errs() << "Memory access number " << MemoryAccessIdx 406 << " has no key name 'relation' for statement number " 407 << StatementIdx << ".\n"; 408 return false; 409 } 410 StringRef Accesses = JsonMemoryAccess->getString("relation").getValue(); 411 isl_map *NewAccessMap = 412 isl_map_read_from_str(S.getIslCtx().get(), Accesses.str().c_str()); 413 414 // Check whether the access was parsed successfully 415 if (!NewAccessMap) { 416 errs() << "The access was not parsed successfully by ISL.\n"; 417 return false; 418 } 419 isl_map *CurrentAccessMap = MA->getAccessRelation().release(); 420 421 // Check if the number of parameter change 422 if (isl_map_dim(NewAccessMap, isl_dim_param) != 423 isl_map_dim(CurrentAccessMap, isl_dim_param)) { 424 errs() << "JScop file changes the number of parameter dimensions.\n"; 425 isl_map_free(CurrentAccessMap); 426 isl_map_free(NewAccessMap); 427 return false; 428 } 429 430 isl_id *NewOutId; 431 432 // If the NewAccessMap has zero dimensions, it is the scalar access; it 433 // must be the same as before. 434 // If it has at least one dimension, it's an array access; search for 435 // its ScopArrayInfo. 436 if (isl_map_dim(NewAccessMap, isl_dim_out) >= 1) { 437 NewOutId = isl_map_get_tuple_id(NewAccessMap, isl_dim_out); 438 auto *SAI = S.getArrayInfoByName(isl_id_get_name(NewOutId)); 439 isl_id *OutId = isl_map_get_tuple_id(CurrentAccessMap, isl_dim_out); 440 auto *OutSAI = ScopArrayInfo::getFromId(isl::manage(OutId)); 441 if (!SAI || SAI->getElementType() != OutSAI->getElementType()) { 442 errs() << "JScop file contains access function with undeclared " 443 "ScopArrayInfo\n"; 444 isl_map_free(CurrentAccessMap); 445 isl_map_free(NewAccessMap); 446 isl_id_free(NewOutId); 447 return false; 448 } 449 isl_id_free(NewOutId); 450 NewOutId = SAI->getBasePtrId().release(); 451 } else { 452 NewOutId = isl_map_get_tuple_id(CurrentAccessMap, isl_dim_out); 453 } 454 455 NewAccessMap = isl_map_set_tuple_id(NewAccessMap, isl_dim_out, NewOutId); 456 457 if (MA->isArrayKind()) { 458 // We keep the old alignment, thus we cannot allow accesses to memory 459 // locations that were not accessed before if the alignment of the 460 // access is not the default alignment. 461 bool SpecialAlignment = true; 462 if (LoadInst *LoadI = dyn_cast<LoadInst>(MA->getAccessInstruction())) { 463 SpecialAlignment = 464 LoadI->getAlignment() && 465 DL.getABITypeAlignment(LoadI->getType()) != LoadI->getAlignment(); 466 } else if (StoreInst *StoreI = 467 dyn_cast<StoreInst>(MA->getAccessInstruction())) { 468 SpecialAlignment = 469 StoreI->getAlignment() && 470 DL.getABITypeAlignment(StoreI->getValueOperand()->getType()) != 471 StoreI->getAlignment(); 472 } 473 474 if (SpecialAlignment) { 475 isl_set *NewAccessSet = isl_map_range(isl_map_copy(NewAccessMap)); 476 isl_set *CurrentAccessSet = 477 isl_map_range(isl_map_copy(CurrentAccessMap)); 478 bool IsSubset = isl_set_is_subset(NewAccessSet, CurrentAccessSet); 479 isl_set_free(NewAccessSet); 480 isl_set_free(CurrentAccessSet); 481 482 // Check if the JScop file changes the accessed memory. 483 if (!IsSubset) { 484 errs() << "JScop file changes the accessed memory\n"; 485 isl_map_free(CurrentAccessMap); 486 isl_map_free(NewAccessMap); 487 return false; 488 } 489 } 490 } 491 492 // We need to copy the isl_ids for the parameter dimensions to the new 493 // map. Without doing this the current map would have different 494 // ids then the new one, even though both are named identically. 495 for (unsigned i = 0; i < isl_map_dim(CurrentAccessMap, isl_dim_param); 496 i++) { 497 isl_id *Id = isl_map_get_dim_id(CurrentAccessMap, isl_dim_param, i); 498 NewAccessMap = isl_map_set_dim_id(NewAccessMap, isl_dim_param, i, Id); 499 } 500 501 // Copy the old tuple id. This is necessary to retain the user pointer, 502 // that stores the reference to the ScopStmt this access belongs to. 503 isl_id *Id = isl_map_get_tuple_id(CurrentAccessMap, isl_dim_in); 504 NewAccessMap = isl_map_set_tuple_id(NewAccessMap, isl_dim_in, Id); 505 506 auto NewAccessDomain = isl_map_domain(isl_map_copy(NewAccessMap)); 507 auto CurrentAccessDomain = isl_map_domain(isl_map_copy(CurrentAccessMap)); 508 509 if (!isl_set_has_equal_space(NewAccessDomain, CurrentAccessDomain)) { 510 errs() << "JScop file contains access function with incompatible " 511 << "dimensions\n"; 512 isl_map_free(CurrentAccessMap); 513 isl_map_free(NewAccessMap); 514 isl_set_free(NewAccessDomain); 515 isl_set_free(CurrentAccessDomain); 516 return false; 517 } 518 519 NewAccessDomain = 520 isl_set_intersect_params(NewAccessDomain, S.getContext().release()); 521 CurrentAccessDomain = isl_set_intersect_params(CurrentAccessDomain, 522 S.getContext().release()); 523 CurrentAccessDomain = 524 isl_set_intersect(CurrentAccessDomain, Stmt.getDomain().release()); 525 526 if (MA->isRead() && 527 isl_set_is_subset(CurrentAccessDomain, NewAccessDomain) == 528 isl_bool_false) { 529 errs() << "Mapping not defined for all iteration domain elements\n"; 530 isl_set_free(CurrentAccessDomain); 531 isl_set_free(NewAccessDomain); 532 isl_map_free(CurrentAccessMap); 533 isl_map_free(NewAccessMap); 534 return false; 535 } 536 537 isl_set_free(CurrentAccessDomain); 538 isl_set_free(NewAccessDomain); 539 540 if (!isl_map_is_equal(NewAccessMap, CurrentAccessMap)) { 541 // Statistics. 542 ++NewAccessMapFound; 543 if (NewAccessStrings) 544 NewAccessStrings->push_back(Accesses); 545 MA->setNewAccessRelation(isl::manage(NewAccessMap)); 546 } else { 547 isl_map_free(NewAccessMap); 548 } 549 isl_map_free(CurrentAccessMap); 550 MemoryAccessIdx++; 551 } 552 StatementIdx++; 553 } 554 555 return true; 556 } 557 558 /// Check whether @p SAI and @p Array represent the same array. 559 static bool areArraysEqual(ScopArrayInfo *SAI, const json::Object &Array) { 560 std::string Buffer; 561 llvm::raw_string_ostream RawStringOstream(Buffer); 562 563 // Check if key 'type' is present. 564 if (!Array.get("type")) { 565 errs() << "Array has no key 'type'.\n"; 566 return false; 567 } 568 569 // Check if key 'sizes' is present. 570 if (!Array.get("sizes")) { 571 errs() << "Array has no key 'sizes'.\n"; 572 return false; 573 } 574 575 // Check if key 'name' is present. 576 if (!Array.get("name")) { 577 errs() << "Array has no key 'name'.\n"; 578 return false; 579 } 580 581 if (SAI->getName() != Array.getString("name").getValue()) 582 return false; 583 584 if (SAI->getNumberOfDimensions() != Array.getArray("sizes")->size()) 585 return false; 586 587 for (unsigned i = 1; i < Array.getArray("sizes")->size(); i++) { 588 SAI->getDimensionSize(i)->print(RawStringOstream); 589 const json::Array &SizesArray = *Array.getArray("sizes"); 590 if (RawStringOstream.str() != SizesArray[i].getAsString().getValue()) 591 return false; 592 Buffer.clear(); 593 } 594 595 // Check if key 'type' differs from the current one or is not valid. 596 SAI->getElementType()->print(RawStringOstream); 597 if (RawStringOstream.str() != Array.getString("type").getValue()) { 598 errs() << "Array has not a valid type.\n"; 599 return false; 600 } 601 602 return true; 603 } 604 605 /// Get the accepted primitive type from its textual representation 606 /// @p TypeTextRepresentation. 607 /// 608 /// @param TypeTextRepresentation The textual representation of the type. 609 /// @return The pointer to the primitive type, if this type is accepted 610 /// or nullptr otherwise. 611 static Type *parseTextType(const std::string &TypeTextRepresentation, 612 LLVMContext &LLVMContext) { 613 std::map<std::string, Type *> MapStrToType = { 614 {"void", Type::getVoidTy(LLVMContext)}, 615 {"half", Type::getHalfTy(LLVMContext)}, 616 {"float", Type::getFloatTy(LLVMContext)}, 617 {"double", Type::getDoubleTy(LLVMContext)}, 618 {"x86_fp80", Type::getX86_FP80Ty(LLVMContext)}, 619 {"fp128", Type::getFP128Ty(LLVMContext)}, 620 {"ppc_fp128", Type::getPPC_FP128Ty(LLVMContext)}, 621 {"i1", Type::getInt1Ty(LLVMContext)}, 622 {"i8", Type::getInt8Ty(LLVMContext)}, 623 {"i16", Type::getInt16Ty(LLVMContext)}, 624 {"i32", Type::getInt32Ty(LLVMContext)}, 625 {"i64", Type::getInt64Ty(LLVMContext)}, 626 {"i128", Type::getInt128Ty(LLVMContext)}}; 627 628 auto It = MapStrToType.find(TypeTextRepresentation); 629 if (It != MapStrToType.end()) 630 return It->second; 631 632 errs() << "Textual representation can not be parsed: " 633 << TypeTextRepresentation << "\n"; 634 return nullptr; 635 } 636 637 /// Import new arrays from JScop. 638 /// 639 /// @param S The scop to update. 640 /// @param JScop The JScop file describing new arrays. 641 /// 642 /// @returns True if the import succeeded, otherwise False. 643 static bool importArrays(Scop &S, const json::Object &JScop) { 644 if (!JScop.get("arrays")) 645 return true; 646 const json::Array &Arrays = *JScop.getArray("arrays"); 647 if (Arrays.size() == 0) 648 return true; 649 650 unsigned ArrayIdx = 0; 651 for (auto &SAI : S.arrays()) { 652 if (!SAI->isArrayKind()) 653 continue; 654 if (ArrayIdx + 1 > Arrays.size()) { 655 errs() << "Not enough array entries in JScop file.\n"; 656 return false; 657 } 658 if (!areArraysEqual(SAI, *Arrays[ArrayIdx].getAsObject())) { 659 errs() << "No match for array '" << SAI->getName() << "' in JScop.\n"; 660 return false; 661 } 662 ArrayIdx++; 663 } 664 665 for (; ArrayIdx < Arrays.size(); ArrayIdx++) { 666 const json::Object &Array = *Arrays[ArrayIdx].getAsObject(); 667 auto *ElementType = parseTextType( 668 Array.get("type")->getAsString().getValue(), S.getSE()->getContext()); 669 if (!ElementType) { 670 errs() << "Error while parsing element type for new array.\n"; 671 return false; 672 } 673 const json::Array &SizesArray = *Array.getArray("sizes"); 674 std::vector<unsigned> DimSizes; 675 for (unsigned i = 0; i < SizesArray.size(); i++) { 676 auto Size = std::stoi(SizesArray[i].getAsString().getValue()); 677 678 // Check if the size if positive. 679 if (Size <= 0) { 680 errs() << "The size at index " << i << " is =< 0.\n"; 681 return false; 682 } 683 684 DimSizes.push_back(Size); 685 } 686 687 auto NewSAI = S.createScopArrayInfo( 688 ElementType, Array.getString("name").getValue(), DimSizes); 689 690 if (Array.get("allocation")) { 691 NewSAI->setIsOnHeap(Array.getString("allocation").getValue() == "heap"); 692 } 693 } 694 695 return true; 696 } 697 698 /// Import a Scop from a JSCOP file 699 /// @param S The scop to be modified 700 /// @param D Dependence Info 701 /// @param DL The DataLayout of the function 702 /// @param NewAccessStrings Optionally record the imported access strings 703 /// 704 /// @returns true on success, false otherwise. Beware that if this returns 705 /// false, the Scop may still have been modified. In this case the Scop contains 706 /// invalid information. 707 static bool importScop(Scop &S, const Dependences &D, const DataLayout &DL, 708 std::vector<std::string> *NewAccessStrings = nullptr) { 709 std::string FileName = ImportDir + "/" + getFileName(S, ImportPostfix); 710 711 std::string FunctionName = S.getFunction().getName(); 712 errs() << "Reading JScop '" << S.getNameStr() << "' in function '" 713 << FunctionName << "' from '" << FileName << "'.\n"; 714 ErrorOr<std::unique_ptr<MemoryBuffer>> result = 715 MemoryBuffer::getFile(FileName); 716 std::error_code ec = result.getError(); 717 718 if (ec) { 719 errs() << "File could not be read: " << ec.message() << "\n"; 720 return false; 721 } 722 723 Expected<json::Value> ParseResult = 724 json::parse(result.get().get()->getBuffer()); 725 726 if (Error E = ParseResult.takeError()) { 727 errs() << "JSCoP file could not be parsed\n"; 728 errs() << E << "\n"; 729 consumeError(std::move(E)); 730 return false; 731 } 732 json::Object &jscop = *ParseResult.get().getAsObject(); 733 734 bool Success = importContext(S, jscop); 735 736 if (!Success) 737 return false; 738 739 Success = importSchedule(S, jscop, D); 740 741 if (!Success) 742 return false; 743 744 Success = importArrays(S, jscop); 745 746 if (!Success) 747 return false; 748 749 Success = importAccesses(S, jscop, DL, NewAccessStrings); 750 751 if (!Success) 752 return false; 753 return true; 754 } 755 756 char JSONExporter::ID = 0; 757 void JSONExporter::printScop(raw_ostream &OS, Scop &S) const { OS << S; } 758 759 bool JSONExporter::runOnScop(Scop &S) { 760 exportScop(S); 761 return false; 762 } 763 764 void JSONExporter::getAnalysisUsage(AnalysisUsage &AU) const { 765 AU.setPreservesAll(); 766 AU.addRequired<ScopInfoRegionPass>(); 767 } 768 769 Pass *polly::createJSONExporterPass() { return new JSONExporter(); } 770 771 PreservedAnalyses JSONExportPass::run(Scop &S, ScopAnalysisManager &SAM, 772 ScopStandardAnalysisResults &SAR, 773 SPMUpdater &) { 774 exportScop(S); 775 return PreservedAnalyses::all(); 776 } 777 778 char JSONImporter::ID = 0; 779 780 void JSONImporter::printScop(raw_ostream &OS, Scop &S) const { 781 OS << S; 782 for (std::vector<std::string>::const_iterator I = NewAccessStrings.begin(), 783 E = NewAccessStrings.end(); 784 I != E; I++) 785 OS << "New access function '" << *I << "' detected in JSCOP file\n"; 786 } 787 788 bool JSONImporter::runOnScop(Scop &S) { 789 const Dependences &D = 790 getAnalysis<DependenceInfo>().getDependences(Dependences::AL_Statement); 791 const DataLayout &DL = S.getFunction().getParent()->getDataLayout(); 792 793 if (!importScop(S, D, DL, &NewAccessStrings)) 794 report_fatal_error("Tried to import a malformed jscop file."); 795 796 return false; 797 } 798 799 void JSONImporter::getAnalysisUsage(AnalysisUsage &AU) const { 800 ScopPass::getAnalysisUsage(AU); 801 AU.addRequired<DependenceInfo>(); 802 803 // TODO: JSONImporter should throw away DependenceInfo. 804 AU.addPreserved<DependenceInfo>(); 805 } 806 807 Pass *polly::createJSONImporterPass() { return new JSONImporter(); } 808 809 PreservedAnalyses JSONImportPass::run(Scop &S, ScopAnalysisManager &SAM, 810 ScopStandardAnalysisResults &SAR, 811 SPMUpdater &) { 812 const Dependences &D = 813 SAM.getResult<DependenceAnalysis>(S, SAR).getDependences( 814 Dependences::AL_Statement); 815 const DataLayout &DL = S.getFunction().getParent()->getDataLayout(); 816 817 if (!importScop(S, D, DL)) 818 report_fatal_error("Tried to import a malformed jscop file."); 819 820 // This invalidates all analyses on Scop. 821 PreservedAnalyses PA; 822 PA.preserveSet<AllAnalysesOn<Module>>(); 823 PA.preserveSet<AllAnalysesOn<Function>>(); 824 PA.preserveSet<AllAnalysesOn<Loop>>(); 825 return PA; 826 } 827 828 INITIALIZE_PASS_BEGIN(JSONExporter, "polly-export-jscop", 829 "Polly - Export Scops as JSON" 830 " (Writes a .jscop file for each Scop)", 831 false, false); 832 INITIALIZE_PASS_DEPENDENCY(DependenceInfo) 833 INITIALIZE_PASS_END(JSONExporter, "polly-export-jscop", 834 "Polly - Export Scops as JSON" 835 " (Writes a .jscop file for each Scop)", 836 false, false) 837 838 INITIALIZE_PASS_BEGIN(JSONImporter, "polly-import-jscop", 839 "Polly - Import Scops from JSON" 840 " (Reads a .jscop file for each Scop)", 841 false, false); 842 INITIALIZE_PASS_DEPENDENCY(DependenceInfo) 843 INITIALIZE_PASS_END(JSONImporter, "polly-import-jscop", 844 "Polly - Import Scops from JSON" 845 " (Reads a .jscop file for each Scop)", 846 false, false) 847