1 //===------ LoopGeneratorsKMP.cpp - IR helper to create loops -------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file contains functions to create parallel loops as LLVM-IR. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "polly/CodeGen/LoopGeneratorsKMP.h" 14 #include "llvm/IR/Dominators.h" 15 #include "llvm/IR/Module.h" 16 17 using namespace llvm; 18 using namespace polly; 19 20 void ParallelLoopGeneratorKMP::createCallSpawnThreads(Value *SubFn, 21 Value *SubFnParam, 22 Value *LB, Value *UB, 23 Value *Stride) { 24 const std::string Name = "__kmpc_fork_call"; 25 Function *F = M->getFunction(Name); 26 Type *KMPCMicroTy = StructType::getTypeByName(M->getContext(), "kmpc_micro"); 27 28 if (!KMPCMicroTy) { 29 // void (*kmpc_micro)(kmp_int32 *global_tid, kmp_int32 *bound_tid, ...) 30 Type *MicroParams[] = {Builder.getInt32Ty()->getPointerTo(), 31 Builder.getInt32Ty()->getPointerTo()}; 32 33 KMPCMicroTy = FunctionType::get(Builder.getVoidTy(), MicroParams, true); 34 } 35 36 // If F is not available, declare it. 37 if (!F) { 38 StructType *IdentTy = 39 StructType::getTypeByName(M->getContext(), "struct.ident_t"); 40 41 GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 42 Type *Params[] = {IdentTy->getPointerTo(), Builder.getInt32Ty(), 43 KMPCMicroTy->getPointerTo()}; 44 45 FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, true); 46 F = Function::Create(Ty, Linkage, Name, M); 47 } 48 49 Value *Task = Builder.CreatePointerBitCastOrAddrSpaceCast( 50 SubFn, KMPCMicroTy->getPointerTo()); 51 52 Value *Args[] = {SourceLocationInfo, 53 Builder.getInt32(4) /* Number of arguments (w/o Task) */, 54 Task, 55 LB, 56 UB, 57 Stride, 58 SubFnParam}; 59 60 CallInst *Call = Builder.CreateCall(F, Args); 61 Call->setDebugLoc(DLGenerated); 62 } 63 64 void ParallelLoopGeneratorKMP::deployParallelExecution(Function *SubFn, 65 Value *SubFnParam, 66 Value *LB, Value *UB, 67 Value *Stride) { 68 // Inform OpenMP runtime about the number of threads if greater than zero 69 if (PollyNumThreads > 0) { 70 Value *GlobalThreadID = createCallGlobalThreadNum(); 71 createCallPushNumThreads(GlobalThreadID, Builder.getInt32(PollyNumThreads)); 72 } 73 74 // Tell the runtime we start a parallel loop 75 createCallSpawnThreads(SubFn, SubFnParam, LB, UB, Stride); 76 } 77 78 Function *ParallelLoopGeneratorKMP::prepareSubFnDefinition(Function *F) const { 79 std::vector<Type *> Arguments = {Builder.getInt32Ty()->getPointerTo(), 80 Builder.getInt32Ty()->getPointerTo(), 81 LongType, 82 LongType, 83 LongType, 84 Builder.getInt8PtrTy()}; 85 86 FunctionType *FT = FunctionType::get(Builder.getVoidTy(), Arguments, false); 87 Function *SubFn = Function::Create(FT, Function::InternalLinkage, 88 F->getName() + "_polly_subfn", M); 89 // Name the function's arguments 90 Function::arg_iterator AI = SubFn->arg_begin(); 91 AI->setName("polly.kmpc.global_tid"); 92 std::advance(AI, 1); 93 AI->setName("polly.kmpc.bound_tid"); 94 std::advance(AI, 1); 95 AI->setName("polly.kmpc.lb"); 96 std::advance(AI, 1); 97 AI->setName("polly.kmpc.ub"); 98 std::advance(AI, 1); 99 AI->setName("polly.kmpc.inc"); 100 std::advance(AI, 1); 101 AI->setName("polly.kmpc.shared"); 102 103 return SubFn; 104 } 105 106 // Create a subfunction of the following (preliminary) structure: 107 // 108 // PrevBB 109 // | 110 // v 111 // HeaderBB 112 // / | _____ 113 // / v v | 114 // / PreHeaderBB | 115 // | | | 116 // | v | 117 // | CheckNextBB | 118 // \ | \_____/ 119 // \ | 120 // v v 121 // ExitBB 122 // 123 // HeaderBB will hold allocations, loading of variables and kmp-init calls. 124 // CheckNextBB will check for more work (dynamic / static chunked) or will be 125 // empty (static non chunked). 126 // If there is more work to do: go to PreHeaderBB, otherwise go to ExitBB. 127 // PreHeaderBB loads the new boundaries (& will lead to the loop body later on). 128 // Just like CheckNextBB: PreHeaderBB is (preliminary) empty in the static non 129 // chunked scheduling case. ExitBB marks the end of the parallel execution. 130 // The possibly empty BasicBlocks will automatically be removed. 131 std::tuple<Value *, Function *> 132 ParallelLoopGeneratorKMP::createSubFn(Value *SequentialLoopStride, 133 AllocaInst *StructData, 134 SetVector<Value *> Data, ValueMapT &Map) { 135 Function *SubFn = createSubFnDefinition(); 136 LLVMContext &Context = SubFn->getContext(); 137 138 // Store the previous basic block. 139 BasicBlock *PrevBB = Builder.GetInsertBlock(); 140 141 // Create basic blocks. 142 BasicBlock *HeaderBB = BasicBlock::Create(Context, "polly.par.setup", SubFn); 143 BasicBlock *ExitBB = BasicBlock::Create(Context, "polly.par.exit", SubFn); 144 BasicBlock *CheckNextBB = 145 BasicBlock::Create(Context, "polly.par.checkNext", SubFn); 146 BasicBlock *PreHeaderBB = 147 BasicBlock::Create(Context, "polly.par.loadIVBounds", SubFn); 148 149 DT.addNewBlock(HeaderBB, PrevBB); 150 DT.addNewBlock(ExitBB, HeaderBB); 151 DT.addNewBlock(CheckNextBB, HeaderBB); 152 DT.addNewBlock(PreHeaderBB, HeaderBB); 153 154 // Fill up basic block HeaderBB. 155 Builder.SetInsertPoint(HeaderBB); 156 Value *LBPtr = Builder.CreateAlloca(LongType, nullptr, "polly.par.LBPtr"); 157 Value *UBPtr = Builder.CreateAlloca(LongType, nullptr, "polly.par.UBPtr"); 158 Value *IsLastPtr = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, 159 "polly.par.lastIterPtr"); 160 Value *StridePtr = 161 Builder.CreateAlloca(LongType, nullptr, "polly.par.StridePtr"); 162 163 // Get iterator for retrieving the previously defined parameters. 164 Function::arg_iterator AI = SubFn->arg_begin(); 165 // First argument holds "global thread ID". 166 Value *IDPtr = &*AI; 167 // Skip "bound thread ID" since it is not used (but had to be defined). 168 std::advance(AI, 2); 169 // Move iterator to: LB, UB, Stride, Shared variable struct. 170 Value *LB = &*AI; 171 std::advance(AI, 1); 172 Value *UB = &*AI; 173 std::advance(AI, 1); 174 Value *Stride = &*AI; 175 std::advance(AI, 1); 176 Value *Shared = &*AI; 177 178 Value *UserContext = Builder.CreateBitCast(Shared, StructData->getType(), 179 "polly.par.userContext"); 180 181 extractValuesFromStruct(Data, StructData->getAllocatedType(), UserContext, 182 Map); 183 184 const auto Alignment = llvm::Align(is64BitArch() ? 8 : 4); 185 Value *ID = Builder.CreateAlignedLoad(Builder.getInt32Ty(), IDPtr, Alignment, 186 "polly.par.global_tid"); 187 188 Builder.CreateAlignedStore(LB, LBPtr, Alignment); 189 Builder.CreateAlignedStore(UB, UBPtr, Alignment); 190 Builder.CreateAlignedStore(Builder.getInt32(0), IsLastPtr, Alignment); 191 Builder.CreateAlignedStore(Stride, StridePtr, Alignment); 192 193 // Subtract one as the upper bound provided by openmp is a < comparison 194 // whereas the codegenForSequential function creates a <= comparison. 195 Value *AdjustedUB = Builder.CreateAdd(UB, ConstantInt::get(LongType, -1), 196 "polly.indvar.UBAdjusted"); 197 198 Value *ChunkSize = 199 ConstantInt::get(LongType, std::max<int>(PollyChunkSize, 1)); 200 201 OMPGeneralSchedulingType Scheduling = 202 getSchedType(PollyChunkSize, PollyScheduling); 203 204 switch (Scheduling) { 205 case OMPGeneralSchedulingType::Dynamic: 206 case OMPGeneralSchedulingType::Guided: 207 case OMPGeneralSchedulingType::Runtime: 208 // "DYNAMIC" scheduling types are handled below (including 'runtime') 209 { 210 UB = AdjustedUB; 211 createCallDispatchInit(ID, LB, UB, Stride, ChunkSize); 212 Value *HasWork = 213 createCallDispatchNext(ID, IsLastPtr, LBPtr, UBPtr, StridePtr); 214 Value *HasIteration = 215 Builder.CreateICmp(llvm::CmpInst::Predicate::ICMP_EQ, HasWork, 216 Builder.getInt32(1), "polly.hasIteration"); 217 Builder.CreateCondBr(HasIteration, PreHeaderBB, ExitBB); 218 219 Builder.SetInsertPoint(CheckNextBB); 220 HasWork = createCallDispatchNext(ID, IsLastPtr, LBPtr, UBPtr, StridePtr); 221 HasIteration = 222 Builder.CreateICmp(llvm::CmpInst::Predicate::ICMP_EQ, HasWork, 223 Builder.getInt32(1), "polly.hasWork"); 224 Builder.CreateCondBr(HasIteration, PreHeaderBB, ExitBB); 225 226 Builder.SetInsertPoint(PreHeaderBB); 227 LB = Builder.CreateAlignedLoad(LongType, LBPtr, Alignment, 228 "polly.indvar.LB"); 229 UB = Builder.CreateAlignedLoad(LongType, UBPtr, Alignment, 230 "polly.indvar.UB"); 231 } 232 break; 233 case OMPGeneralSchedulingType::StaticChunked: 234 case OMPGeneralSchedulingType::StaticNonChunked: 235 // "STATIC" scheduling types are handled below 236 { 237 Builder.CreateAlignedStore(AdjustedUB, UBPtr, Alignment); 238 createCallStaticInit(ID, IsLastPtr, LBPtr, UBPtr, StridePtr, ChunkSize); 239 240 Value *ChunkedStride = Builder.CreateAlignedLoad( 241 LongType, StridePtr, Alignment, "polly.kmpc.stride"); 242 243 LB = Builder.CreateAlignedLoad(LongType, LBPtr, Alignment, 244 "polly.indvar.LB"); 245 UB = Builder.CreateAlignedLoad(LongType, UBPtr, Alignment, 246 "polly.indvar.UB.temp"); 247 248 Value *UBInRange = 249 Builder.CreateICmp(llvm::CmpInst::Predicate::ICMP_SLE, UB, AdjustedUB, 250 "polly.indvar.UB.inRange"); 251 UB = Builder.CreateSelect(UBInRange, UB, AdjustedUB, "polly.indvar.UB"); 252 Builder.CreateAlignedStore(UB, UBPtr, Alignment); 253 254 Value *HasIteration = Builder.CreateICmp( 255 llvm::CmpInst::Predicate::ICMP_SLE, LB, UB, "polly.hasIteration"); 256 Builder.CreateCondBr(HasIteration, PreHeaderBB, ExitBB); 257 258 if (Scheduling == OMPGeneralSchedulingType::StaticChunked) { 259 Builder.SetInsertPoint(PreHeaderBB); 260 LB = Builder.CreateAlignedLoad(LongType, LBPtr, Alignment, 261 "polly.indvar.LB.entry"); 262 UB = Builder.CreateAlignedLoad(LongType, UBPtr, Alignment, 263 "polly.indvar.UB.entry"); 264 } 265 266 Builder.SetInsertPoint(CheckNextBB); 267 268 if (Scheduling == OMPGeneralSchedulingType::StaticChunked) { 269 Value *NextLB = 270 Builder.CreateAdd(LB, ChunkedStride, "polly.indvar.nextLB"); 271 Value *NextUB = Builder.CreateAdd(UB, ChunkedStride); 272 273 Value *NextUBOutOfBounds = 274 Builder.CreateICmp(llvm::CmpInst::Predicate::ICMP_SGT, NextUB, 275 AdjustedUB, "polly.indvar.nextUB.outOfBounds"); 276 NextUB = Builder.CreateSelect(NextUBOutOfBounds, AdjustedUB, NextUB, 277 "polly.indvar.nextUB"); 278 279 Builder.CreateAlignedStore(NextLB, LBPtr, Alignment); 280 Builder.CreateAlignedStore(NextUB, UBPtr, Alignment); 281 282 Value *HasWork = 283 Builder.CreateICmp(llvm::CmpInst::Predicate::ICMP_SLE, NextLB, 284 AdjustedUB, "polly.hasWork"); 285 Builder.CreateCondBr(HasWork, PreHeaderBB, ExitBB); 286 } else { 287 Builder.CreateBr(ExitBB); 288 } 289 290 Builder.SetInsertPoint(PreHeaderBB); 291 } 292 break; 293 } 294 295 Builder.CreateBr(CheckNextBB); 296 Builder.SetInsertPoint(&*--Builder.GetInsertPoint()); 297 BasicBlock *AfterBB; 298 Value *IV = createLoop(LB, UB, SequentialLoopStride, Builder, LI, DT, AfterBB, 299 ICmpInst::ICMP_SLE, nullptr, true, 300 /* UseGuard */ false); 301 302 BasicBlock::iterator LoopBody = Builder.GetInsertPoint(); 303 304 // Add code to terminate this subfunction. 305 Builder.SetInsertPoint(ExitBB); 306 // Static (i.e. non-dynamic) scheduling types, are terminated with a fini-call 307 if (Scheduling == OMPGeneralSchedulingType::StaticChunked || 308 Scheduling == OMPGeneralSchedulingType::StaticNonChunked) { 309 createCallStaticFini(ID); 310 } 311 Builder.CreateRetVoid(); 312 Builder.SetInsertPoint(&*LoopBody); 313 314 return std::make_tuple(IV, SubFn); 315 } 316 317 Value *ParallelLoopGeneratorKMP::createCallGlobalThreadNum() { 318 const std::string Name = "__kmpc_global_thread_num"; 319 Function *F = M->getFunction(Name); 320 321 // If F is not available, declare it. 322 if (!F) { 323 StructType *IdentTy = 324 StructType::getTypeByName(M->getContext(), "struct.ident_t"); 325 326 GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 327 Type *Params[] = {IdentTy->getPointerTo()}; 328 329 FunctionType *Ty = FunctionType::get(Builder.getInt32Ty(), Params, false); 330 F = Function::Create(Ty, Linkage, Name, M); 331 } 332 333 CallInst *Call = Builder.CreateCall(F, {SourceLocationInfo}); 334 Call->setDebugLoc(DLGenerated); 335 return Call; 336 } 337 338 void ParallelLoopGeneratorKMP::createCallPushNumThreads(Value *GlobalThreadID, 339 Value *NumThreads) { 340 const std::string Name = "__kmpc_push_num_threads"; 341 Function *F = M->getFunction(Name); 342 343 // If F is not available, declare it. 344 if (!F) { 345 StructType *IdentTy = 346 StructType::getTypeByName(M->getContext(), "struct.ident_t"); 347 348 GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 349 Type *Params[] = {IdentTy->getPointerTo(), Builder.getInt32Ty(), 350 Builder.getInt32Ty()}; 351 352 FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false); 353 F = Function::Create(Ty, Linkage, Name, M); 354 } 355 356 Value *Args[] = {SourceLocationInfo, GlobalThreadID, NumThreads}; 357 358 CallInst *Call = Builder.CreateCall(F, Args); 359 Call->setDebugLoc(DLGenerated); 360 } 361 362 void ParallelLoopGeneratorKMP::createCallStaticInit(Value *GlobalThreadID, 363 Value *IsLastPtr, 364 Value *LBPtr, Value *UBPtr, 365 Value *StridePtr, 366 Value *ChunkSize) { 367 const std::string Name = 368 is64BitArch() ? "__kmpc_for_static_init_8" : "__kmpc_for_static_init_4"; 369 Function *F = M->getFunction(Name); 370 StructType *IdentTy = 371 StructType::getTypeByName(M->getContext(), "struct.ident_t"); 372 373 // If F is not available, declare it. 374 if (!F) { 375 GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 376 377 Type *Params[] = {IdentTy->getPointerTo(), 378 Builder.getInt32Ty(), 379 Builder.getInt32Ty(), 380 Builder.getInt32Ty()->getPointerTo(), 381 LongType->getPointerTo(), 382 LongType->getPointerTo(), 383 LongType->getPointerTo(), 384 LongType, 385 LongType}; 386 387 FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false); 388 F = Function::Create(Ty, Linkage, Name, M); 389 } 390 391 // The parameter 'ChunkSize' will hold strictly positive integer values, 392 // regardless of PollyChunkSize's value 393 Value *Args[] = { 394 SourceLocationInfo, 395 GlobalThreadID, 396 Builder.getInt32(int(getSchedType(PollyChunkSize, PollyScheduling))), 397 IsLastPtr, 398 LBPtr, 399 UBPtr, 400 StridePtr, 401 ConstantInt::get(LongType, 1), 402 ChunkSize}; 403 404 CallInst *Call = Builder.CreateCall(F, Args); 405 Call->setDebugLoc(DLGenerated); 406 } 407 408 void ParallelLoopGeneratorKMP::createCallStaticFini(Value *GlobalThreadID) { 409 const std::string Name = "__kmpc_for_static_fini"; 410 Function *F = M->getFunction(Name); 411 StructType *IdentTy = 412 StructType::getTypeByName(M->getContext(), "struct.ident_t"); 413 414 // If F is not available, declare it. 415 if (!F) { 416 GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 417 Type *Params[] = {IdentTy->getPointerTo(), Builder.getInt32Ty()}; 418 FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false); 419 F = Function::Create(Ty, Linkage, Name, M); 420 } 421 422 Value *Args[] = {SourceLocationInfo, GlobalThreadID}; 423 424 CallInst *Call = Builder.CreateCall(F, Args); 425 Call->setDebugLoc(DLGenerated); 426 } 427 428 void ParallelLoopGeneratorKMP::createCallDispatchInit(Value *GlobalThreadID, 429 Value *LB, Value *UB, 430 Value *Inc, 431 Value *ChunkSize) { 432 const std::string Name = 433 is64BitArch() ? "__kmpc_dispatch_init_8" : "__kmpc_dispatch_init_4"; 434 Function *F = M->getFunction(Name); 435 StructType *IdentTy = 436 StructType::getTypeByName(M->getContext(), "struct.ident_t"); 437 438 // If F is not available, declare it. 439 if (!F) { 440 GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 441 442 Type *Params[] = {IdentTy->getPointerTo(), 443 Builder.getInt32Ty(), 444 Builder.getInt32Ty(), 445 LongType, 446 LongType, 447 LongType, 448 LongType}; 449 450 FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false); 451 F = Function::Create(Ty, Linkage, Name, M); 452 } 453 454 // The parameter 'ChunkSize' will hold strictly positive integer values, 455 // regardless of PollyChunkSize's value 456 Value *Args[] = { 457 SourceLocationInfo, 458 GlobalThreadID, 459 Builder.getInt32(int(getSchedType(PollyChunkSize, PollyScheduling))), 460 LB, 461 UB, 462 Inc, 463 ChunkSize}; 464 465 CallInst *Call = Builder.CreateCall(F, Args); 466 Call->setDebugLoc(DLGenerated); 467 } 468 469 Value *ParallelLoopGeneratorKMP::createCallDispatchNext(Value *GlobalThreadID, 470 Value *IsLastPtr, 471 Value *LBPtr, 472 Value *UBPtr, 473 Value *StridePtr) { 474 const std::string Name = 475 is64BitArch() ? "__kmpc_dispatch_next_8" : "__kmpc_dispatch_next_4"; 476 Function *F = M->getFunction(Name); 477 StructType *IdentTy = 478 StructType::getTypeByName(M->getContext(), "struct.ident_t"); 479 480 // If F is not available, declare it. 481 if (!F) { 482 GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 483 484 Type *Params[] = {IdentTy->getPointerTo(), 485 Builder.getInt32Ty(), 486 Builder.getInt32Ty()->getPointerTo(), 487 LongType->getPointerTo(), 488 LongType->getPointerTo(), 489 LongType->getPointerTo()}; 490 491 FunctionType *Ty = FunctionType::get(Builder.getInt32Ty(), Params, false); 492 F = Function::Create(Ty, Linkage, Name, M); 493 } 494 495 Value *Args[] = {SourceLocationInfo, GlobalThreadID, IsLastPtr, LBPtr, UBPtr, 496 StridePtr}; 497 498 CallInst *Call = Builder.CreateCall(F, Args); 499 Call->setDebugLoc(DLGenerated); 500 return Call; 501 } 502 503 // TODO: This function currently creates a source location dummy. It might be 504 // necessary to (actually) provide information, in the future. 505 GlobalVariable *ParallelLoopGeneratorKMP::createSourceLocation() { 506 const std::string LocName = ".loc.dummy"; 507 GlobalVariable *SourceLocDummy = M->getGlobalVariable(LocName); 508 509 if (SourceLocDummy == nullptr) { 510 const std::string StructName = "struct.ident_t"; 511 StructType *IdentTy = 512 StructType::getTypeByName(M->getContext(), StructName); 513 514 // If the ident_t StructType is not available, declare it. 515 // in LLVM-IR: ident_t = type { i32, i32, i32, i32, i8* } 516 if (!IdentTy) { 517 Type *LocMembers[] = {Builder.getInt32Ty(), Builder.getInt32Ty(), 518 Builder.getInt32Ty(), Builder.getInt32Ty(), 519 Builder.getInt8PtrTy()}; 520 521 IdentTy = 522 StructType::create(M->getContext(), LocMembers, StructName, false); 523 } 524 525 const auto ArrayType = 526 llvm::ArrayType::get(Builder.getInt8Ty(), /* Length */ 23); 527 528 // Global Variable Definitions 529 GlobalVariable *StrVar = 530 new GlobalVariable(*M, ArrayType, true, GlobalValue::PrivateLinkage, 531 nullptr, ".str.ident"); 532 StrVar->setAlignment(llvm::Align(1)); 533 534 SourceLocDummy = new GlobalVariable( 535 *M, IdentTy, true, GlobalValue::PrivateLinkage, nullptr, LocName); 536 SourceLocDummy->setAlignment(llvm::Align(8)); 537 538 // Constant Definitions 539 Constant *InitStr = ConstantDataArray::getString( 540 M->getContext(), "Source location dummy.", true); 541 542 Constant *StrPtr = static_cast<Constant *>(Builder.CreateInBoundsGEP( 543 ArrayType, StrVar, {Builder.getInt32(0), Builder.getInt32(0)})); 544 545 Constant *LocInitStruct = ConstantStruct::get( 546 IdentTy, {Builder.getInt32(0), Builder.getInt32(0), Builder.getInt32(0), 547 Builder.getInt32(0), StrPtr}); 548 549 // Initialize variables 550 StrVar->setInitializer(InitStr); 551 SourceLocDummy->setInitializer(LocInitStruct); 552 } 553 554 return SourceLocDummy; 555 } 556 557 bool ParallelLoopGeneratorKMP::is64BitArch() { 558 return (LongType->getIntegerBitWidth() == 64); 559 } 560 561 OMPGeneralSchedulingType ParallelLoopGeneratorKMP::getSchedType( 562 int ChunkSize, OMPGeneralSchedulingType Scheduling) const { 563 if (ChunkSize == 0 && Scheduling == OMPGeneralSchedulingType::StaticChunked) 564 return OMPGeneralSchedulingType::StaticNonChunked; 565 566 return Scheduling; 567 } 568