Skip to content

[OpenMP] [IR Builder] Changes to Support Scan Operation #136035

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 7, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 214 additions & 1 deletion llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

namespace llvm {
class CanonicalLoopInfo;
class ScanInfo;
struct TargetRegionEntryInfo;
class OffloadEntriesInfoManager;
class OpenMPIRBuilder;
Expand Down Expand Up @@ -511,6 +512,7 @@ class OpenMPIRBuilder {
return allocaInst;
}
};

/// Initialize the internal state, this will put structures types and
/// potentially other helpers into the underlying module. Must be called
/// before any other method and only once! This internal state includes types
Expand Down Expand Up @@ -707,6 +709,9 @@ class OpenMPIRBuilder {
LLVM_ABI InsertPointOrErrorTy createCancellationPoint(
const LocationDescription &Loc, omp::Directive CanceledDirective);

/// Creates a ScanInfo object, allocates and returns the pointer.
Expected<ScanInfo *> scanInfoInitialize();

/// Generator for '#omp parallel'
///
/// \param Loc The insert and source location description.
Expand Down Expand Up @@ -750,6 +755,42 @@ class OpenMPIRBuilder {
LoopBodyGenCallbackTy BodyGenCB, Value *TripCount,
const Twine &Name = "loop");

/// Generator for the control flow structure of an OpenMP canonical loops if
/// the parent directive has an `inscan` modifier specified.
/// If the `inscan` modifier is specified, the region of the parent is
/// expected to have a `scan` directive. Based on the clauses in
/// scan directive, the body of the loop is split into two loops: Input loop
/// and Scan Loop. Input loop contains the code generated for input phase of
/// scan and Scan loop contains the code generated for scan phase of scan.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also document on how it is to be used, i.g. that createScan is supposed to be called within the body callback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Thank you!

/// From the bodyGen callback of these loops, `createScan` would be called
/// when a scan directive is encountered from the loop body. `createScan`
/// based on whether 1. inclusive or exclusive scan is specified and, 2. input
/// loop or scan loop is generated, lowers the body of the for loop
/// accordingly.
///
/// \param Loc The insert and source location description.
/// \param BodyGenCB Callback that will generate the loop body code.
/// \param Start Value of the loop counter for the first iterations.
/// \param Stop Loop counter values past this will stop the loop.
/// \param Step Loop counter increment after each iteration; negative
/// means counting down.
/// \param IsSigned Whether Start, Stop and Step are signed integers.
/// \param InclusiveStop Whether \p Stop itself is a valid value for the loop
/// counter.
/// \param ComputeIP Insertion point for instructions computing the trip
/// count. Can be used to ensure the trip count is available
/// at the outermost loop of a loop nest. If not set,
/// defaults to the preheader of the generated loop.
/// \param Name Base name used to derive BB and instruction names.
/// \param ScanRedInfo Pointer to the ScanInfo objected created using
/// `ScanInfoInitialize`.
///
/// \returns A vector containing Loop Info of Input Loop and Scan Loop.
Expected<SmallVector<llvm::CanonicalLoopInfo *>> createCanonicalScanLoops(
const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB,
Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo);

/// Calculate the trip count of a canonical loop.
///
/// This allows specifying user-defined loop counter values using increment,
Expand Down Expand Up @@ -818,13 +859,17 @@ class OpenMPIRBuilder {
/// at the outermost loop of a loop nest. If not set,
/// defaults to the preheader of the generated loop.
/// \param Name Base name used to derive BB and instruction names.
/// \param InScan Whether loop has a scan reduction specified.
/// \param ScanRedInfo Pointer to the ScanInfo objected created using
/// `ScanInfoInitialize`.
///
/// \returns An object representing the created control flow structure which
/// can be used for loop-associated directives.
LLVM_ABI Expected<CanonicalLoopInfo *> createCanonicalLoop(
const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB,
Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
InsertPointTy ComputeIP = {}, const Twine &Name = "loop");
InsertPointTy ComputeIP = {}, const Twine &Name = "loop",
bool InScan = false, ScanInfo *ScanRedInfo = nullptr);

/// Collapse a loop nest into a single loop.
///
Expand Down Expand Up @@ -1556,6 +1601,46 @@ class OpenMPIRBuilder {
ArrayRef<OpenMPIRBuilder::ReductionInfo> ReductionInfos,
Function *ReduceFn, AttributeList FuncAttrs);

/// Helper function for CreateCanonicalScanLoops to create InputLoop
/// in the firstGen and Scan Loop in the SecondGen
/// \param InputLoopGen Callback for generating the loop for input phase
/// \param ScanLoopGen Callback for generating the loop for scan phase
/// \param ScanRedInfo Pointer to the ScanInfo objected created using
/// `ScanInfoInitialize`.
///
/// \return error if any produced, else return success.
Error emitScanBasedDirectiveIR(
llvm::function_ref<Error()> InputLoopGen,
llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
ScanInfo *ScanRedInfo);

/// Creates the basic blocks required for scan reduction.
/// \param ScanRedInfo Pointer to the ScanInfo objected created using
/// `ScanInfoInitialize`.
void createScanBBs(ScanInfo *ScanRedInfo);

/// Dynamically allocates the buffer needed for scan reduction.
/// \param AllocaIP The IP where possibly-shared pointer of buffer needs to be
/// declared. \param ScanVars Scan Variables.
/// \param ScanRedInfo Pointer to the ScanInfo objected created using
/// `ScanInfoInitialize`.
///
/// \return error if any produced, else return success.
Error emitScanBasedDirectiveDeclsIR(InsertPointTy AllocaIP,
ArrayRef<llvm::Value *> ScanVars,
ArrayRef<llvm::Type *> ScanVarsType,
ScanInfo *ScanRedInfo);

/// Copies the result back to the reduction variable.
/// \param ReductionInfos Array type containing the ReductionOps.
/// \param ScanRedInfo Pointer to the ScanInfo objected created using
/// `ScanInfoInitialize`.
///
/// \return error if any produced, else return success.
Error emitScanBasedDirectiveFinalsIR(
ArrayRef<llvm::OpenMPIRBuilder::ReductionInfo> ReductionInfos,
ScanInfo *ScanInfo);

/// This function emits a helper that gathers Reduce lists from the first
/// lane of every active warp to lanes in the first warp.
///
Expand Down Expand Up @@ -2183,6 +2268,7 @@ class OpenMPIRBuilder {
/// Collection of owned canonical loop objects that eventually need to be
/// free'd.
std::forward_list<CanonicalLoopInfo> LoopInfos;
std::forward_list<ScanInfo> ScanInfos;

/// Add a new region that will be outlined later.
void addOutlineInfo(OutlineInfo &&OI) { OutlineInfos.emplace_back(OI); }
Expand Down Expand Up @@ -2639,6 +2725,46 @@ class OpenMPIRBuilder {
FinalizeCallbackTy FiniCB,
Value *Filter);

/// This function performs the scan reduction of the values updated in
/// the input phase. The reduction logic needs to be emitted between input
/// and scan loop returned by `CreateCanonicalScanLoops`. The following
/// is the code that is generated, `buffer` and `span` are expected to be
/// populated before executing the generated code.
///
/// for (int k = 0; k != ceil(log2(span)); ++k) {
/// i=pow(2,k)
/// for (size cnt = last_iter; cnt >= i; --cnt)
/// buffer[cnt] op= buffer[cnt-i];
/// }
/// \param Loc The insert and source location description.
/// \param ReductionInfos Array type containing the ReductionOps.
/// \param ScanRedInfo Pointer to the ScanInfo objected created using
/// `ScanInfoInitialize`.
///
/// \returns The insertion position *after* the masked.
InsertPointOrErrorTy emitScanReduction(
const LocationDescription &Loc,
ArrayRef<llvm::OpenMPIRBuilder::ReductionInfo> ReductionInfos,
ScanInfo *ScanRedInfo);

/// This directive split and directs the control flow to input phase
/// blocks or scan phase blocks based on 1. whether input loop or scan loop
/// is executed, 2. whether exclusive or inclusive scan is used.
///
/// \param Loc The insert and source location description.
/// \param AllocaIP The IP where the temporary buffer for scan reduction
// needs to be allocated.
/// \param ScanVars Scan Variables.
/// \param IsInclusive Whether it is an inclusive or exclusive scan.
/// \param ScanRedInfo Pointer to the ScanInfo objected created using
/// `ScanInfoInitialize`.
///
/// \returns The insertion position *after* the scan.
InsertPointOrErrorTy createScan(const LocationDescription &Loc,
InsertPointTy AllocaIP,
ArrayRef<llvm::Value *> ScanVars,
ArrayRef<llvm::Type *> ScanVarsType,
bool IsInclusive, ScanInfo *ScanRedInfo);
/// Generator for '#omp critical'
///
/// \param Loc The insert and source location description.
Expand Down Expand Up @@ -3774,6 +3900,93 @@ class CanonicalLoopInfo {
LLVM_ABI void invalidate();
};

/// ScanInfo holds the information to assist in lowering of Scan reduction.
/// Before lowering, the body of the for loop specifying scan reduction is
/// expected to have the following structure
///
/// Loop Body Entry
/// |
/// Code before the scan directive
/// |
/// Scan Directive
/// |
/// Code after the scan directive
/// |
/// Loop Body Exit
/// When `createCanonicalScanLoops` is executed, the bodyGen callback of it
/// transforms the body to:
///
/// Loop Body Entry
/// |
/// OMPScanDispatch
///
/// OMPBeforeScanBlock
/// |
/// OMPScanLoopExit
/// |
/// Loop Body Exit
///
/// The insert point is updated to the first insert point of OMPBeforeScanBlock.
/// It dominates the control flow of code generated until
/// scan directive is encountered and OMPAfterScanBlock dominates the
/// control flow of code generated after scan is encountered. The successor
/// of OMPScanDispatch can be OMPBeforeScanBlock or OMPAfterScanBlock based
/// on 1.whether it is in Input phase or Scan Phase , 2. whether it is an
/// exclusive or inclusive scan. This jump is added when `createScan` is
/// executed. If input loop is being generated, if it is inclusive scan,
/// `OMPAfterScanBlock` succeeds `OMPScanDispatch` , if exclusive,
/// `OMPBeforeScanBlock` succeeds `OMPDispatch` and vice versa for scan loop. At
/// the end of the input loop, temporary buffer is populated and at the
/// beginning of the scan loop, temporary buffer is read. After scan directive
/// is encountered, insertion point is updated to `OMPAfterScanBlock` as it is
/// expected to dominate the code after the scan directive. Both Before and
/// After scan blocks are succeeded by `OMPScanLoopExit`.
/// Temporary buffer allocations are done in `ScanLoopInit` block before the
/// lowering of for-loop. The results are copied back to reduction variable in
/// `ScanLoopFinish` block.
class ScanInfo {
public:
/// Dominates the body of the loop before scan directive
llvm::BasicBlock *OMPBeforeScanBlock = nullptr;

/// Dominates the body of the loop before scan directive
llvm::BasicBlock *OMPAfterScanBlock = nullptr;

/// Controls the flow to before or after scan blocks
llvm::BasicBlock *OMPScanDispatch = nullptr;

/// Exit block of loop body
llvm::BasicBlock *OMPScanLoopExit = nullptr;

/// Block before loop body where scan initializations are done
llvm::BasicBlock *OMPScanInit = nullptr;

/// Block after loop body where scan finalizations are done
llvm::BasicBlock *OMPScanFinish = nullptr;

/// If true, it indicates Input phase is lowered; else it indicates
/// ScanPhase is lowered
bool OMPFirstScanLoop = false;

/// Maps the private reduction variable to the pointer of the temporary
/// buffer
llvm::SmallDenseMap<llvm::Value *, llvm::Value *> *ScanBuffPtrs;

/// Keeps track of value of iteration variable for input/scan loop to be
/// used for Scan directive lowering
llvm::Value *IV;

/// Stores the span of canonical loop being lowered to be used for temporary
/// buffer allocation or Finalization.
llvm::Value *Span;

ScanInfo() {
ScanBuffPtrs = new llvm::SmallDenseMap<llvm::Value *, llvm::Value *>();
}

~ScanInfo() { delete (ScanBuffPtrs); }
};

} // end namespace llvm

#endif // LLVM_FRONTEND_OPENMP_OMPIRBUILDER_H
Loading