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