@@ -536,6 +536,126 @@ LogicalResult ModuleTranslation::convertOmpMaster(Operation &opInst,
536
536
return success ();
537
537
}
538
538
539
+ // / Converts an OpenMP workshare loop into LLVM IR using OpenMPIRBuilder.
540
+ LogicalResult ModuleTranslation::convertOmpWsLoop (Operation &opInst,
541
+ llvm::IRBuilder<> &builder) {
542
+ auto loop = cast<omp::WsLoopOp>(opInst);
543
+ // TODO: this should be in the op verifier instead.
544
+ if (loop.lowerBound ().empty ())
545
+ return failure ();
546
+
547
+ if (loop.getNumLoops () != 1 )
548
+ return opInst.emitOpError (" collapsed loops not yet supported" );
549
+
550
+ if (loop.schedule_val ().hasValue () &&
551
+ omp::symbolizeClauseScheduleKind (loop.schedule_val ().getValue ()) !=
552
+ omp::ClauseScheduleKind::Static)
553
+ return opInst.emitOpError (
554
+ " only static (default) loop schedule is currently supported" );
555
+
556
+ llvm::Function *func = builder.GetInsertBlock ()->getParent ();
557
+ llvm::LLVMContext &llvmContext = llvmModule->getContext ();
558
+
559
+ // Find the loop configuration.
560
+ llvm::Value *lowerBound = valueMapping.lookup (loop.lowerBound ()[0 ]);
561
+ llvm::Value *upperBound = valueMapping.lookup (loop.upperBound ()[0 ]);
562
+ llvm::Value *step = valueMapping.lookup (loop.step ()[0 ]);
563
+ llvm::Type *ivType = step->getType ();
564
+ llvm::Value *chunk = loop.schedule_chunk_var ()
565
+ ? valueMapping[loop.schedule_chunk_var ()]
566
+ : llvm::ConstantInt::get (ivType, 1 );
567
+
568
+ // Set up the source location value for OpenMP runtime.
569
+ llvm::DISubprogram *subprogram =
570
+ builder.GetInsertBlock ()->getParent ()->getSubprogram ();
571
+ const llvm::DILocation *diLoc =
572
+ debugTranslation->translateLoc (opInst.getLoc (), subprogram);
573
+ llvm::OpenMPIRBuilder::LocationDescription ompLoc (builder.saveIP (),
574
+ llvm::DebugLoc (diLoc));
575
+
576
+ // Generator of the canonical loop body. Produces an SESE region of basic
577
+ // blocks.
578
+ // TODO: support error propagation in OpenMPIRBuilder and use it instead of
579
+ // relying on captured variables.
580
+ LogicalResult bodyGenStatus = success ();
581
+ auto bodyGen = [&](llvm::OpenMPIRBuilder::InsertPointTy ip, llvm::Value *iv) {
582
+ llvm::IRBuilder<>::InsertPointGuard guard (builder);
583
+
584
+ // Make sure further conversions know about the induction variable.
585
+ valueMapping[loop.getRegion ().front ().getArgument (0 )] = iv;
586
+
587
+ llvm::BasicBlock *entryBlock = ip.getBlock ();
588
+ llvm::BasicBlock *exitBlock =
589
+ entryBlock->splitBasicBlock (ip.getPoint (), " omp.wsloop.exit" );
590
+
591
+ // Convert the body of the loop.
592
+ Region ®ion = loop.region ();
593
+ for (Block &bb : region) {
594
+ llvm::BasicBlock *llvmBB =
595
+ llvm::BasicBlock::Create (llvmContext, " omp.wsloop.region" , func);
596
+ blockMapping[&bb] = llvmBB;
597
+
598
+ // Retarget the branch of the entry block to the entry block of the
599
+ // converted region (regions are single-entry).
600
+ if (bb.isEntryBlock ()) {
601
+ auto *branch = cast<llvm::BranchInst>(entryBlock->getTerminator ());
602
+ branch->setSuccessor (0 , llvmBB);
603
+ }
604
+ }
605
+
606
+ // Block conversion creates a new IRBuilder every time so need not bother
607
+ // about maintaining the insertion point.
608
+ llvm::SetVector<Block *> blocks = topologicalSort (region);
609
+ for (Block *bb : blocks) {
610
+ if (failed (convertBlock (*bb, bb->isEntryBlock ()))) {
611
+ bodyGenStatus = failure ();
612
+ return ;
613
+ }
614
+
615
+ // Special handling for `omp.yield` terminators (we may have more than
616
+ // one): they return the control to the parent WsLoop operation so replace
617
+ // them with the branch to the exit block. We handle this here to avoid
618
+ // relying inter-function communication through the ModuleTranslation
619
+ // class to set up the correct insertion point. This is also consistent
620
+ // with MLIR's idiom of handling special region terminators in the same
621
+ // code that handles the region-owning operation.
622
+ if (isa<omp::YieldOp>(bb->getTerminator ())) {
623
+ llvm::BasicBlock *llvmBB = blockMapping[bb];
624
+ builder.SetInsertPoint (llvmBB, llvmBB->end ());
625
+ builder.CreateBr (exitBlock);
626
+ }
627
+ }
628
+
629
+ connectPHINodes (region, valueMapping, blockMapping, branchMapping);
630
+ };
631
+
632
+ // Delegate actual loop construction to the OpenMP IRBuilder.
633
+ // TODO: this currently assumes WsLoop is semantically similar to SCF loop,
634
+ // i.e. it has a positive step, uses signed integer semantics, and its upper
635
+ // bound is not included. Reconsider this code when WsLoop clearly supports
636
+ // more cases.
637
+ llvm::BasicBlock *insertBlock = builder.GetInsertBlock ();
638
+ llvm::CanonicalLoopInfo *loopInfo = ompBuilder->createCanonicalLoop (
639
+ ompLoc, bodyGen, lowerBound, upperBound, step, /* IsSigned=*/ true ,
640
+ /* InclusiveStop=*/ false );
641
+ if (failed (bodyGenStatus))
642
+ return failure ();
643
+
644
+ // TODO: get the alloca insertion point from the parallel operation builder.
645
+ // If we insert the at the top of the current function, they will be passed as
646
+ // extra arguments into the function the parallel operation builder outlines.
647
+ // Put them at the start of the current block for now.
648
+ llvm::OpenMPIRBuilder::InsertPointTy allocaIP (
649
+ insertBlock, insertBlock->getFirstInsertionPt ());
650
+ loopInfo = ompBuilder->createStaticWorkshareLoop (
651
+ ompLoc, loopInfo, allocaIP,
652
+ !loop.nowait ().hasValue () || loop.nowait ().getValue (), chunk);
653
+
654
+ // Continue building IR after the loop.
655
+ builder.restoreIP (loopInfo->getAfterIP ());
656
+ return success ();
657
+ }
658
+
539
659
// / Given an OpenMP MLIR operation, create the corresponding LLVM IR
540
660
// / (including OpenMP runtime calls).
541
661
LogicalResult
@@ -577,6 +697,13 @@ ModuleTranslation::convertOmpOperation(Operation &opInst,
577
697
.Case (
578
698
[&](omp::ParallelOp) { return convertOmpParallel (opInst, builder); })
579
699
.Case ([&](omp::MasterOp) { return convertOmpMaster (opInst, builder); })
700
+ .Case ([&](omp::WsLoopOp) { return convertOmpWsLoop (opInst, builder); })
701
+ .Case ([&](omp::YieldOp op) {
702
+ // Yields are loop terminators that can be just omitted. The loop
703
+ // structure was created in the function that handles WsLoopOp.
704
+ assert (op.getNumOperands () == 0 && " unexpected yield with operands" );
705
+ return success ();
706
+ })
580
707
.Default ([&](Operation *inst) {
581
708
return inst->emitError (" unsupported OpenMP operation: " )
582
709
<< inst->getName ();
0 commit comments