1 //===- IslAst.cpp - isl code generator interface --------------------------===// 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 // The isl code generator interface takes a Scop and generates a isl_ast. This 11 // ist_ast can either be returned directly or it can be pretty printed to 12 // stdout. 13 // 14 // A typical isl_ast output looks like this: 15 // 16 // for (c2 = max(0, ceild(n + m, 2); c2 <= min(511, floord(5 * n, 3)); c2++) { 17 // bb2(c2); 18 // } 19 // 20 //===----------------------------------------------------------------------===// 21 22 #include "polly/CodeGen/CodeGeneration.h" 23 #include "polly/CodeGen/IslAst.h" 24 #include "polly/Dependences.h" 25 #include "polly/LinkAllPasses.h" 26 #include "polly/Options.h" 27 #include "polly/ScopInfo.h" 28 29 #define DEBUG_TYPE "polly-ast" 30 #include "llvm/Support/Debug.h" 31 32 #include "isl/union_map.h" 33 #include "isl/list.h" 34 #include "isl/ast_build.h" 35 #include "isl/set.h" 36 #include "isl/map.h" 37 #include "isl/aff.h" 38 39 using namespace llvm; 40 using namespace polly; 41 42 static cl::opt<bool> UseContext("polly-ast-use-context", 43 cl::desc("Use context"), cl::Hidden, 44 cl::init(false), cl::ZeroOrMore, 45 cl::cat(PollyCategory)); 46 47 static cl::opt<bool> DetectParallel("polly-ast-detect-parallel", 48 cl::desc("Detect parallelism"), cl::Hidden, 49 cl::init(false), cl::ZeroOrMore, 50 cl::cat(PollyCategory)); 51 52 namespace polly { 53 class IslAst { 54 public: 55 IslAst(Scop *Scop, Dependences &D); 56 57 ~IslAst(); 58 59 /// Print a source code representation of the program. 60 void pprint(llvm::raw_ostream &OS); 61 62 __isl_give isl_ast_node *getAst(); 63 64 private: 65 Scop *S; 66 isl_ast_node *Root; 67 68 __isl_give isl_union_map *getSchedule(); 69 }; 70 } // End namespace polly. 71 72 // Temporary information used when building the ast. 73 struct AstBuildUserInfo { 74 // The dependence information. 75 Dependences *Deps; 76 77 // We are inside a parallel for node. 78 int InParallelFor; 79 }; 80 81 // Print a loop annotated with OpenMP or vector pragmas. 82 static __isl_give isl_printer * 83 printParallelFor(__isl_keep isl_ast_node *Node, __isl_take isl_printer *Printer, 84 __isl_take isl_ast_print_options *PrintOptions, 85 IslAstUser *Info) { 86 if (Info) { 87 if (Info->IsInnermostParallel) { 88 Printer = isl_printer_start_line(Printer); 89 Printer = isl_printer_print_str(Printer, "#pragma simd"); 90 Printer = isl_printer_end_line(Printer); 91 } 92 if (Info->IsOutermostParallel) { 93 Printer = isl_printer_start_line(Printer); 94 Printer = isl_printer_print_str(Printer, "#pragma omp parallel for"); 95 Printer = isl_printer_end_line(Printer); 96 } 97 } 98 return isl_ast_node_for_print(Node, Printer, PrintOptions); 99 } 100 101 // Print an isl_ast_for. 102 static __isl_give isl_printer * 103 printFor(__isl_take isl_printer *Printer, 104 __isl_take isl_ast_print_options *PrintOptions, 105 __isl_keep isl_ast_node *Node, void *User) { 106 isl_id *Id = isl_ast_node_get_annotation(Node); 107 if (!Id) 108 return isl_ast_node_for_print(Node, Printer, PrintOptions); 109 110 struct IslAstUser *Info = (struct IslAstUser *)isl_id_get_user(Id); 111 Printer = printParallelFor(Node, Printer, PrintOptions, Info); 112 isl_id_free(Id); 113 return Printer; 114 } 115 116 // Allocate an AstNodeInfo structure and initialize it with default values. 117 static struct IslAstUser *allocateIslAstUser() { 118 struct IslAstUser *NodeInfo; 119 NodeInfo = (struct IslAstUser *)malloc(sizeof(struct IslAstUser)); 120 NodeInfo->PMA = 0; 121 NodeInfo->Context = 0; 122 NodeInfo->IsOutermostParallel = 0; 123 NodeInfo->IsInnermostParallel = 0; 124 return NodeInfo; 125 } 126 127 // Free the AstNodeInfo structure. 128 static void freeIslAstUser(void *Ptr) { 129 struct IslAstUser *UserStruct = (struct IslAstUser *)Ptr; 130 isl_ast_build_free(UserStruct->Context); 131 isl_pw_multi_aff_free(UserStruct->PMA); 132 free(UserStruct); 133 } 134 135 // Check if the current scheduling dimension is parallel. 136 // 137 // We check for parallelism by verifying that the loop does not carry any 138 // dependences. 139 // 140 // Parallelism test: if the distance is zero in all outer dimensions, then it 141 // has to be zero in the current dimension as well. 142 // 143 // Implementation: first, translate dependences into time space, then force 144 // outer dimensions to be equal. If the distance is zero in the current 145 // dimension, then the loop is parallel. The distance is zero in the current 146 // dimension if it is a subset of a map with equal values for the current 147 // dimension. 148 static bool astScheduleDimIsParallel(__isl_keep isl_ast_build *Build, 149 Dependences *D) { 150 isl_union_map *Schedule, *Deps; 151 isl_map *ScheduleDeps, *Test; 152 isl_space *ScheduleSpace; 153 unsigned Dimension, IsParallel; 154 155 Schedule = isl_ast_build_get_schedule(Build); 156 ScheduleSpace = isl_ast_build_get_schedule_space(Build); 157 158 Dimension = isl_space_dim(ScheduleSpace, isl_dim_out) - 1; 159 160 Deps = D->getDependences(Dependences::TYPE_ALL); 161 Deps = isl_union_map_apply_range(Deps, isl_union_map_copy(Schedule)); 162 Deps = isl_union_map_apply_domain(Deps, Schedule); 163 164 if (isl_union_map_is_empty(Deps)) { 165 isl_union_map_free(Deps); 166 isl_space_free(ScheduleSpace); 167 return 1; 168 } 169 170 ScheduleDeps = isl_map_from_union_map(Deps); 171 172 for (unsigned i = 0; i < Dimension; i++) 173 ScheduleDeps = isl_map_equate(ScheduleDeps, isl_dim_out, i, isl_dim_in, i); 174 175 Test = isl_map_universe(isl_map_get_space(ScheduleDeps)); 176 Test = isl_map_equate(Test, isl_dim_out, Dimension, isl_dim_in, Dimension); 177 IsParallel = isl_map_is_subset(ScheduleDeps, Test); 178 179 isl_space_free(ScheduleSpace); 180 isl_map_free(Test); 181 isl_map_free(ScheduleDeps); 182 183 return IsParallel; 184 } 185 186 // Mark a for node openmp parallel, if it is the outermost parallel for node. 187 static void markOpenmpParallel(__isl_keep isl_ast_build *Build, 188 struct AstBuildUserInfo *BuildInfo, 189 struct IslAstUser *NodeInfo) { 190 if (BuildInfo->InParallelFor) 191 return; 192 193 if (astScheduleDimIsParallel(Build, BuildInfo->Deps)) { 194 BuildInfo->InParallelFor = 1; 195 NodeInfo->IsOutermostParallel = 1; 196 } 197 } 198 199 // This method is executed before the construction of a for node. It creates 200 // an isl_id that is used to annotate the subsequently generated ast for nodes. 201 // 202 // In this function we also run the following analyses: 203 // 204 // - Detection of openmp parallel loops 205 // 206 static __isl_give isl_id *astBuildBeforeFor(__isl_keep isl_ast_build *Build, 207 void *User) { 208 struct AstBuildUserInfo *BuildInfo = (struct AstBuildUserInfo *)User; 209 struct IslAstUser *NodeInfo = allocateIslAstUser(); 210 isl_id *Id = isl_id_alloc(isl_ast_build_get_ctx(Build), "", NodeInfo); 211 Id = isl_id_set_free_user(Id, freeIslAstUser); 212 213 markOpenmpParallel(Build, BuildInfo, NodeInfo); 214 215 return Id; 216 } 217 218 // Returns 0 when Node contains loops, otherwise returns -1. This search 219 // function uses ISL's way to iterate over lists of isl_ast_nodes with 220 // isl_ast_node_list_foreach. Please use the single argument wrapper function 221 // that returns a bool instead of using this function directly. 222 static int containsLoops(__isl_take isl_ast_node *Node, void *User) { 223 if (!Node) 224 return -1; 225 226 switch (isl_ast_node_get_type(Node)) { 227 case isl_ast_node_for: 228 isl_ast_node_free(Node); 229 return 0; 230 case isl_ast_node_block: { 231 isl_ast_node_list *List = isl_ast_node_block_get_children(Node); 232 int Res = isl_ast_node_list_foreach(List, &containsLoops, NULL); 233 isl_ast_node_list_free(List); 234 isl_ast_node_free(Node); 235 return Res; 236 } 237 case isl_ast_node_if: { 238 int Res = -1; 239 if (0 == containsLoops(isl_ast_node_if_get_then(Node), NULL) || 240 (isl_ast_node_if_has_else(Node) && 241 0 == containsLoops(isl_ast_node_if_get_else(Node), NULL))) 242 Res = 0; 243 isl_ast_node_free(Node); 244 return Res; 245 } 246 case isl_ast_node_user: 247 default: 248 isl_ast_node_free(Node); 249 return -1; 250 } 251 } 252 253 // Returns true when Node contains loops. 254 static bool containsLoops(__isl_take isl_ast_node *Node) { 255 return 0 == containsLoops(Node, NULL); 256 } 257 258 // This method is executed after the construction of a for node. 259 // 260 // It performs the following actions: 261 // 262 // - Reset the 'InParallelFor' flag, as soon as we leave a for node, 263 // that is marked as openmp parallel. 264 // 265 static __isl_give isl_ast_node * 266 astBuildAfterFor(__isl_take isl_ast_node *Node, __isl_keep isl_ast_build *Build, 267 void *User) { 268 isl_id *Id = isl_ast_node_get_annotation(Node); 269 if (!Id) 270 return Node; 271 struct IslAstUser *Info = (struct IslAstUser *)isl_id_get_user(Id); 272 struct AstBuildUserInfo *BuildInfo = (struct AstBuildUserInfo *)User; 273 274 if (Info) { 275 if (Info->IsOutermostParallel) 276 BuildInfo->InParallelFor = 0; 277 if (!containsLoops(isl_ast_node_for_get_body(Node))) 278 if (astScheduleDimIsParallel(Build, BuildInfo->Deps)) 279 Info->IsInnermostParallel = 1; 280 if (!Info->Context) 281 Info->Context = isl_ast_build_copy(Build); 282 } 283 284 isl_id_free(Id); 285 return Node; 286 } 287 288 static __isl_give isl_ast_node *AtEachDomain(__isl_take isl_ast_node *Node, 289 __isl_keep isl_ast_build *Context, 290 void *User) { 291 struct IslAstUser *Info = NULL; 292 isl_id *Id = isl_ast_node_get_annotation(Node); 293 294 if (Id) 295 Info = (struct IslAstUser *)isl_id_get_user(Id); 296 297 if (!Info) { 298 // Allocate annotations once: parallel for detection might have already 299 // allocated the annotations for this node. 300 Info = allocateIslAstUser(); 301 Id = isl_id_alloc(isl_ast_node_get_ctx(Node), NULL, Info); 302 Id = isl_id_set_free_user(Id, &freeIslAstUser); 303 } 304 305 if (!Info->PMA) { 306 isl_map *Map = isl_map_from_union_map(isl_ast_build_get_schedule(Context)); 307 Info->PMA = isl_pw_multi_aff_from_map(isl_map_reverse(Map)); 308 } 309 if (!Info->Context) 310 Info->Context = isl_ast_build_copy(Context); 311 312 return isl_ast_node_set_annotation(Node, Id); 313 } 314 315 IslAst::IslAst(Scop *Scop, Dependences &D) : S(Scop) { 316 isl_ctx *Ctx = S->getIslCtx(); 317 isl_options_set_ast_build_atomic_upper_bound(Ctx, true); 318 isl_ast_build *Context; 319 struct AstBuildUserInfo BuildInfo; 320 321 if (UseContext) 322 Context = isl_ast_build_from_context(S->getContext()); 323 else 324 Context = isl_ast_build_from_context(isl_set_universe(S->getParamSpace())); 325 326 Context = isl_ast_build_set_at_each_domain(Context, AtEachDomain, NULL); 327 328 isl_union_map *Schedule = getSchedule(); 329 330 Function *F = Scop->getRegion().getEntry()->getParent(); 331 (void)F; 332 333 DEBUG(dbgs() << ":: isl ast :: " << F->getName() 334 << " :: " << Scop->getRegion().getNameStr() << "\n"); 335 336 DEBUG(dbgs() << S->getContextStr() << "\n"; isl_union_map_dump(Schedule)); 337 338 if (DetectParallel || PollyVectorizerChoice != VECTORIZER_NONE) { 339 BuildInfo.Deps = &D; 340 BuildInfo.InParallelFor = 0; 341 342 Context = isl_ast_build_set_before_each_for(Context, &astBuildBeforeFor, 343 &BuildInfo); 344 Context = isl_ast_build_set_after_each_for(Context, &astBuildAfterFor, 345 &BuildInfo); 346 } 347 348 Root = isl_ast_build_ast_from_schedule(Context, Schedule); 349 350 isl_ast_build_free(Context); 351 352 DEBUG(pprint(dbgs())); 353 } 354 355 __isl_give isl_union_map *IslAst::getSchedule() { 356 isl_union_map *Schedule = isl_union_map_empty(S->getParamSpace()); 357 358 for (Scop::iterator SI = S->begin(), SE = S->end(); SI != SE; ++SI) { 359 ScopStmt *Stmt = *SI; 360 isl_map *StmtSchedule = Stmt->getScattering(); 361 362 StmtSchedule = isl_map_intersect_domain(StmtSchedule, Stmt->getDomain()); 363 Schedule = 364 isl_union_map_union(Schedule, isl_union_map_from_map(StmtSchedule)); 365 } 366 367 return Schedule; 368 } 369 370 IslAst::~IslAst() { isl_ast_node_free(Root); } 371 372 /// Print a C like representation of the program. 373 void IslAst::pprint(llvm::raw_ostream &OS) { 374 isl_ast_node *Root; 375 isl_ast_print_options *Options; 376 377 Options = isl_ast_print_options_alloc(S->getIslCtx()); 378 Options = isl_ast_print_options_set_print_for(Options, &printFor, NULL); 379 380 isl_printer *P = isl_printer_to_str(S->getIslCtx()); 381 P = isl_printer_set_output_format(P, ISL_FORMAT_C); 382 Root = getAst(); 383 P = isl_ast_node_print(Root, P, Options); 384 char *result = isl_printer_get_str(P); 385 OS << result << "\n"; 386 isl_printer_free(P); 387 isl_ast_node_free(Root); 388 } 389 390 /// Create the isl_ast from this program. 391 __isl_give isl_ast_node *IslAst::getAst() { return isl_ast_node_copy(Root); } 392 393 void IslAstInfo::pprint(llvm::raw_ostream &OS) { Ast->pprint(OS); } 394 395 void IslAstInfo::releaseMemory() { 396 if (Ast) { 397 delete Ast; 398 Ast = 0; 399 } 400 } 401 402 bool IslAstInfo::runOnScop(Scop &Scop) { 403 if (Ast) 404 delete Ast; 405 406 S = &Scop; 407 408 Dependences &D = getAnalysis<Dependences>(); 409 410 Ast = new IslAst(&Scop, D); 411 412 return false; 413 } 414 415 __isl_give isl_ast_node *IslAstInfo::getAst() { return Ast->getAst(); } 416 417 void IslAstInfo::printScop(raw_ostream &OS) const { 418 Function *F = S->getRegion().getEntry()->getParent(); 419 420 OS << F->getName() << "():\n"; 421 422 Ast->pprint(OS); 423 } 424 425 void IslAstInfo::getAnalysisUsage(AnalysisUsage &AU) const { 426 // Get the Common analysis usage of ScopPasses. 427 ScopPass::getAnalysisUsage(AU); 428 AU.addRequired<ScopInfo>(); 429 AU.addRequired<Dependences>(); 430 } 431 432 char IslAstInfo::ID = 0; 433 434 Pass *polly::createIslAstInfoPass() { return new IslAstInfo(); } 435 436 INITIALIZE_PASS_BEGIN(IslAstInfo, "polly-ast", 437 "Polly - Generate an AST of the SCoP (isl)", false, 438 false); 439 INITIALIZE_PASS_DEPENDENCY(ScopInfo); 440 INITIALIZE_PASS_DEPENDENCY(Dependences); 441 INITIALIZE_PASS_END(IslAstInfo, "polly-ast", 442 "Polly - Generate an AST from the SCoP (isl)", false, false) 443