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