| //===- Cloog.cpp - Cloog interface ----------------------------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Cloog[1] interface. |
| // |
| // The Cloog interface takes a Scop and generates a Cloog AST (clast). This |
| // clast can either be returned directly or it can be pretty printed to stdout. |
| // |
| // A typical clast output looks like this: |
| // |
| // for (c2 = max(0, ceild(n + m, 2); c2 <= min(511, floord(5 * n, 3)); c2++) { |
| // bb2(c2); |
| // } |
| // |
| // [1] http://www.cloog.org/ - The Chunky Loop Generator |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "polly/CodeGen/Cloog.h" |
| #ifdef CLOOG_FOUND |
| #include "polly/LinkAllPasses.h" |
| #include "polly/ScopInfo.h" |
| |
| #include "llvm/IR/Module.h" |
| #include "llvm/Support/Debug.h" |
| |
| #include "cloog/isl/domain.h" |
| #include "cloog/isl/cloog.h" |
| |
| #include <unistd.h> |
| |
| using namespace llvm; |
| using namespace polly; |
| |
| #define DEBUG_TYPE "polly-cloog" |
| |
| namespace polly { |
| class Cloog { |
| Scop *S; |
| CloogOptions *Options; |
| CloogState *State; |
| clast_stmt *ClastRoot; |
| |
| void buildCloogOptions(); |
| CloogUnionDomain *buildCloogUnionDomain(); |
| CloogInput *buildCloogInput(); |
| |
| public: |
| Cloog(Scop *Scop); |
| |
| ~Cloog(); |
| |
| /// Write a .cloog input file |
| void dump(FILE *F); |
| |
| /// Print a source code representation of the program. |
| void pprint(llvm::raw_ostream &OS); |
| |
| /// Create the Cloog AST from this program. |
| clast_root *getClast(); |
| }; |
| |
| Cloog::Cloog(Scop *Scop) : S(Scop) { |
| State = cloog_isl_state_malloc(Scop->getIslCtx()); |
| buildCloogOptions(); |
| ClastRoot = cloog_clast_create_from_input(buildCloogInput(), Options); |
| } |
| |
| Cloog::~Cloog() { |
| cloog_clast_free(ClastRoot); |
| cloog_options_free(Options); |
| cloog_state_free(State); |
| } |
| |
| // Create a FILE* write stream and get the output to it written |
| // to a std::string. |
| class FileToString { |
| int FD[2]; |
| FILE *input; |
| static const int BUFFERSIZE = 20; |
| |
| char buf[BUFFERSIZE + 1]; |
| |
| public: |
| FileToString() { |
| pipe(FD); |
| input = fdopen(FD[1], "w"); |
| } |
| ~FileToString() { |
| close(FD[0]); |
| // close(FD[1]); |
| } |
| |
| FILE *getInputFile() { return input; } |
| |
| void closeInput() { |
| fclose(input); |
| close(FD[1]); |
| } |
| |
| std::string getOutput() { |
| std::string output; |
| int readSize; |
| |
| while (true) { |
| readSize = read(FD[0], &buf, BUFFERSIZE); |
| |
| if (readSize <= 0) |
| break; |
| |
| output += std::string(buf, readSize); |
| } |
| |
| return output; |
| } |
| }; |
| |
| /// Write .cloog input file. |
| void Cloog::dump(FILE *F) { |
| CloogInput *Input = buildCloogInput(); |
| cloog_input_dump_cloog(F, Input, Options); |
| cloog_input_free(Input); |
| } |
| |
| /// Print a source code representation of the program. |
| void Cloog::pprint(raw_ostream &OS) { |
| FileToString *Output = new FileToString(); |
| clast_pprint(Output->getInputFile(), ClastRoot, 0, Options); |
| Output->closeInput(); |
| OS << Output->getOutput(); |
| delete (Output); |
| } |
| |
| /// Create the Cloog AST from this program. |
| clast_root *Cloog::getClast() { return (clast_root *)ClastRoot; } |
| |
| void Cloog::buildCloogOptions() { |
| Options = cloog_options_malloc(State); |
| Options->quiet = 1; |
| Options->strides = 1; |
| Options->save_domains = 1; |
| Options->noscalars = 1; |
| |
| // Compute simple hulls to reduce code generation time. |
| Options->sh = 1; |
| |
| // The last loop depth to optimize should be the last scattering dimension. |
| // CLooG by default will continue to split the loops even after the last |
| // scattering dimension. This splitting is problematic for the schedules |
| // calculated by the PoCC/isl/Pluto optimizer. Such schedules contain may |
| // not be fully defined, but statements without dependences may be mapped |
| // to the same exeuction time. For such schedules, continuing to split |
| // may lead to a larger set of if-conditions in the innermost loop. |
| Options->l = 0; |
| } |
| |
| CloogUnionDomain *Cloog::buildCloogUnionDomain() { |
| CloogUnionDomain *DU = cloog_union_domain_alloc(S->getNumParams()); |
| |
| for (Scop::iterator SI = S->begin(), SE = S->end(); SI != SE; ++SI) { |
| ScopStmt *Stmt = *SI; |
| CloogScattering *Scattering; |
| CloogDomain *Domain; |
| |
| Scattering = cloog_scattering_from_isl_map(Stmt->getScattering()); |
| Domain = cloog_domain_from_isl_set(Stmt->getDomain()); |
| |
| std::string entryName = Stmt->getBaseName(); |
| |
| DU = cloog_union_domain_add_domain(DU, entryName.c_str(), Domain, |
| Scattering, Stmt); |
| } |
| |
| return DU; |
| } |
| |
| CloogInput *Cloog::buildCloogInput() { |
| // XXX: We do not copy the context of the scop, but use an unconstrained |
| // context. This 'hack' is necessary as the context may contain bounds |
| // on parameters such as [n] -> {:0 <= n < 2^32}. Those large |
| // integers will cause CLooG to construct a clast that contains |
| // expressions that include these large integers. Such expressions can |
| // possibly not be evaluated correctly with i64 types. The cloog |
| // based code generation backend, however, can not derive types |
| // automatically and just assumes i64 types. Hence, it will break or |
| // generate incorrect code. |
| // This hack does not remove all possibilities of incorrectly generated |
| // code, but it is ensures that for most problems the problems do not |
| // show up. The correct solution, will be to automatically derive the |
| // minimal types for each expression. This could be added to CLooG and it |
| // will be available in the isl based code generation. |
| isl_set *EmptyContext = isl_set_universe(S->getParamSpace()); |
| CloogDomain *Context = cloog_domain_from_isl_set(EmptyContext); |
| CloogUnionDomain *Statements = buildCloogUnionDomain(); |
| |
| isl_set *ScopContext = S->getContext(); |
| |
| for (unsigned i = 0; i < isl_set_dim(ScopContext, isl_dim_param); i++) { |
| isl_id *id = isl_set_get_dim_id(ScopContext, isl_dim_param, i); |
| Statements = cloog_union_domain_set_name(Statements, CLOOG_PARAM, i, |
| isl_id_get_name(id)); |
| isl_id_free(id); |
| } |
| |
| isl_set_free(ScopContext); |
| |
| CloogInput *Input = cloog_input_alloc(Context, Statements); |
| return Input; |
| } |
| |
| void ClastVisitor::visit(const clast_stmt *stmt) { |
| if (CLAST_STMT_IS_A(stmt, stmt_root)) |
| assert(false && "No second root statement expected"); |
| else if (CLAST_STMT_IS_A(stmt, stmt_ass)) |
| return visitAssignment((const clast_assignment *)stmt); |
| else if (CLAST_STMT_IS_A(stmt, stmt_user)) |
| return visitUser((const clast_user_stmt *)stmt); |
| else if (CLAST_STMT_IS_A(stmt, stmt_block)) |
| return visitBlock((const clast_block *)stmt); |
| else if (CLAST_STMT_IS_A(stmt, stmt_for)) |
| return visitFor((const clast_for *)stmt); |
| else if (CLAST_STMT_IS_A(stmt, stmt_guard)) |
| return visitGuard((const clast_guard *)stmt); |
| |
| if (stmt->next) |
| visit(stmt->next); |
| } |
| |
| void ClastVisitor::visitAssignment(const clast_assignment *stmt) {} |
| |
| void ClastVisitor::visitBlock(const clast_block *stmt) { visit(stmt->body); } |
| |
| void ClastVisitor::visitFor(const clast_for *stmt) { visit(stmt->body); } |
| |
| void ClastVisitor::visitGuard(const clast_guard *stmt) { visit(stmt->then); } |
| |
| } // End namespace polly. |
| |
| namespace { |
| |
| struct CloogExporter : public ScopPass { |
| static char ID; |
| Scop *S; |
| explicit CloogExporter() : ScopPass(ID) {} |
| |
| std::string getFileName(Region *R) const; |
| virtual bool runOnScop(Scop &S); |
| void getAnalysisUsage(AnalysisUsage &AU) const; |
| }; |
| } |
| std::string CloogExporter::getFileName(Region *R) const { |
| std::string FunctionName = R->getEntry()->getParent()->getName(); |
| std::string ExitName, EntryName; |
| |
| raw_string_ostream ExitStr(ExitName); |
| raw_string_ostream EntryStr(EntryName); |
| |
| R->getEntry()->printAsOperand(EntryStr, false); |
| EntryStr.str(); |
| |
| if (R->getExit()) { |
| R->getExit()->printAsOperand(ExitStr, false); |
| ExitStr.str(); |
| } else |
| ExitName = "FunctionExit"; |
| |
| std::string RegionName = EntryName + "---" + ExitName; |
| std::string FileName = FunctionName + "___" + RegionName + ".cloog"; |
| |
| return FileName; |
| } |
| |
| char CloogExporter::ID = 0; |
| bool CloogExporter::runOnScop(Scop &S) { |
| Region &R = S.getRegion(); |
| CloogInfo &C = getAnalysis<CloogInfo>(); |
| |
| std::string FunctionName = R.getEntry()->getParent()->getName(); |
| std::string Filename = getFileName(&R); |
| |
| errs() << "Writing Scop '" << R.getNameStr() << "' in function '" |
| << FunctionName << "' to '" << Filename << "'...\n"; |
| |
| FILE *F = fopen(Filename.c_str(), "w"); |
| C.dump(F); |
| fclose(F); |
| |
| return false; |
| } |
| |
| void CloogExporter::getAnalysisUsage(AnalysisUsage &AU) const { |
| // Get the Common analysis usage of ScopPasses. |
| ScopPass::getAnalysisUsage(AU); |
| AU.addRequired<CloogInfo>(); |
| } |
| |
| static RegisterPass<CloogExporter> A("polly-export-cloog", |
| "Polly - Export the Cloog input file" |
| " (Writes a .cloog file for each Scop)"); |
| |
| llvm::Pass *polly::createCloogExporterPass() { return new CloogExporter(); } |
| |
| /// Write a .cloog input file |
| void CloogInfo::dump(FILE *F) { C->dump(F); } |
| |
| /// Print a source code representation of the program. |
| void CloogInfo::pprint(llvm::raw_ostream &OS) { C->pprint(OS); } |
| |
| /// Create the Cloog AST from this program. |
| const struct clast_root *CloogInfo::getClast() { return C->getClast(); } |
| |
| void CloogInfo::releaseMemory() { |
| if (C) { |
| delete C; |
| C = 0; |
| } |
| } |
| |
| bool CloogInfo::runOnScop(Scop &S) { |
| if (C) |
| delete C; |
| |
| scop = &S; |
| |
| C = new Cloog(&S); |
| |
| Function *F = S.getRegion().getEntry()->getParent(); |
| (void)F; |
| |
| DEBUG(dbgs() << ":: " << F->getName()); |
| DEBUG(dbgs() << " : " << S.getRegion().getNameStr() << "\n"); |
| DEBUG(C->pprint(dbgs())); |
| |
| return false; |
| } |
| |
| void CloogInfo::printScop(raw_ostream &OS) const { |
| Function *function = scop->getRegion().getEntry()->getParent(); |
| |
| OS << function->getName() << "():\n"; |
| |
| C->pprint(OS); |
| } |
| |
| void CloogInfo::getAnalysisUsage(AnalysisUsage &AU) const { |
| // Get the Common analysis usage of ScopPasses. |
| ScopPass::getAnalysisUsage(AU); |
| } |
| char CloogInfo::ID = 0; |
| |
| Pass *polly::createCloogInfoPass() { return new CloogInfo(); } |
| |
| INITIALIZE_PASS_BEGIN(CloogInfo, "polly-cloog", "Execute Cloog code generation", |
| false, false); |
| INITIALIZE_PASS_DEPENDENCY(ScopInfo); |
| INITIALIZE_PASS_END(CloogInfo, "polly-cloog", "Execute Cloog code generation", |
| false, false) |
| |
| #endif // CLOOG_FOUND |