[Mlir-commits] [flang] [mlir] [flang][OpenMP] Support ALLOCATABLE list items in LINEAR (PR #228827)
Aditya Trivedi
llvmlistbot at llvm.org
Tue Oct 13 05:14:06 PDT 2026
https://github.com/adit4443ya updated https://github.com/llvm/llvm-project/pull/228827
>From 24b0460bc6532be3a2f7e964f63bf49fbc6988a4 Mon Sep 17 00:00:00 2001
From: Aditya Trivedi <adit4443ya at gmail.com>
Date: Tue, 13 Oct 2026 17:43:32 +0530
Subject: [PATCH 1/2] [mlir][OpenMP] Add linear privatizers
Add type = linear to omp.private with an optional data_address region
that yields the address of the privatized value. A linear item with a
linear privatizer keeps its value in the private copy, which the
translation finds through that region.
---
.../mlir/Dialect/OpenMP/OpenMPEnums.td | 4 +-
mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td | 31 ++-
mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp | 62 ++++--
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 196 +++++++++++++++---
mlir/test/Dialect/OpenMP/invalid.mlir | 46 ++++
mlir/test/Dialect/OpenMP/ops.mlir | 24 +++
.../LLVMIR/openmp-linear-privatizer.mlir | 95 +++++++++
7 files changed, 407 insertions(+), 51 deletions(-)
create mode 100644 mlir/test/Target/LLVMIR/openmp-linear-privatizer.mlir
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td
index 50b02c0d9e61c9..a44d15fa408fd6 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td
@@ -200,12 +200,14 @@ def ClauseTaskDependAttr : OpenMP_EnumAttr<ClauseTaskDepend,
def DataSharingTypePrivate : I32EnumAttrCase<"Private", 0, "private">;
def DataSharingTypeFirstPrivate
: I32EnumAttrCase<"FirstPrivate", 1, "firstprivate">;
+def DataSharingTypeLinear : I32EnumAttrCase<"Linear", 2, "linear">;
def DataSharingClauseType : OpenMP_I32EnumAttr<
"DataSharingClauseType",
"Type of a data-sharing clause", [
DataSharingTypePrivate,
- DataSharingTypeFirstPrivate
+ DataSharingTypeFirstPrivate,
+ DataSharingTypeLinear
]>;
def DataSharingClauseTypeAttr : OpenMP_EnumAttr<DataSharingClauseType,
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 6bc99cf7656992..da92990a06e054 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -82,10 +82,24 @@ def PrivateClauseOp
}
```
+ * `linear(x)` for "allocatables" would be emitted as:
+ ```mlir
+ omp.private {type = linear} @x.privatizer : !some.type init {
+ ...
+ } data_address {
+ ^bb0(%arg0: !some.pointer<!some.type>):
+ // return the address of the data that %arg0 describes.
+ omp.yield(%addr : !llvm.ptr)
+ } dealloc {
+ ...
+ }
+ ```
+
There are no restrictions on the body except for:
- - The `dealloc` regions has a single argument.
+ - The `dealloc` & `data_address` regions have a single argument.
- The `init` & `copy` regions have 2 arguments.
- - All three regions are terminated by `omp.yield` ops.
+ - All regions are terminated by `omp.yield` ops.
+ - The `data_address` region yields a single value.
The above restrictions and other obvious restrictions (e.g. verifying the
type of yielded values) are verified by the custom op verifier. The actual
contents of the blocks inside all regions are not verified.
@@ -102,7 +116,10 @@ def PrivateClauseOp
the regions should be a type which represents a pointer to `type`.
The `data_sharing_type` attribute specifies whether privatizer corresponds
- to a `private` or a `firstprivate` clause.
+ to a `private`, `firstprivate` or `linear` clause. A `linear` privatizer is
+ used for a variable that is also in the `linear` clause of the same op; its
+ optional `data_address` region gives the address of the value the linear
+ clause updates. Without it, the variable itself holds that value.
}];
let arguments = (ins SymbolNameAttr:$sym_name,
@@ -112,13 +129,15 @@ def PrivateClauseOp
let regions = (region AnyRegion:$init_region,
AnyRegion:$copy_region,
- AnyRegion:$dealloc_region);
+ AnyRegion:$dealloc_region,
+ AnyRegion:$data_address_region);
let assemblyFormat = [{
` ` `{` `type` `=` enum($data_sharing_type) `}`
($sym_visibility^)? $sym_name `:` $type
(`init` $init_region^)?
(`copy` $copy_region^)?
+ (`data_address` $data_address_region^)?
(`dealloc` $dealloc_region^)? attr-dict
}];
@@ -145,6 +164,10 @@ def PrivateClauseOp
auto ®ion = getCopyRegion();
return region.empty() ? nullptr : region.getArgument(1);
}
+ BlockArgument getDataAddressArg() {
+ auto ®ion = getDataAddressRegion();
+ return region.empty() ? nullptr : region.getArgument(0);
+ }
BlockArgument getDeallocMoldArg() {
auto ®ion = getDeallocRegion();
return region.empty() ? nullptr : region.getArgument(0);
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index c6a424bd932146..4c02e392617980 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -3062,12 +3062,22 @@ static LogicalResult verifyPrivateVarList(OpType &op) {
if (privatizerType && (varType != privatizerType))
return op.emitError()
<< "type mismatch between a "
- << (privatizerOp.getDataSharingType() ==
- DataSharingClauseType::Private
- ? "private"
- : "firstprivate")
+ << stringifyDataSharingClauseType(
+ privatizerOp.getDataSharingType())
<< " variable and its privatizer op, var type: " << varType
<< " vs. privatizer op type: " << privatizerType;
+
+ if (privatizerOp.getDataSharingType() == DataSharingClauseType::Linear) {
+ bool isLinearVar = false;
+ if constexpr (std::is_same_v<OpType, SimdOp> ||
+ std::is_same_v<OpType, WsloopOp>)
+ isLinearVar =
+ llvm::is_contained(op.getLinearVars(), std::get<0>(privateVarInfo));
+ if (!isLinearVar)
+ return op.emitError() << "`linear` privatizer '" << privateSym
+ << "' used for a variable that is not in the "
+ "`linear` clause";
+ }
}
return success();
@@ -3655,7 +3665,14 @@ LogicalResult SimdOp::verify() {
getLinearVarTypes().value().size() != getLinearVars().size())
return emitError() << "Ill-formed type attributes for linear variables";
- llvm::DenseSet<Value> privateVars(llvm::from_range, getPrivateVars());
+ // A `linear` privatizer is how a linear variable is privatized.
+ llvm::DenseSet<Value> privateVars;
+ if (std::optional<ArrayAttr> privateSyms = getPrivateSyms())
+ for (auto [var, sym] : llvm::zip_equal(getPrivateVars(), *privateSyms))
+ if (SymbolTable::lookupNearestSymbolFrom<PrivateClauseOp>(
+ getOperation(), cast<SymbolRefAttr>(sym))
+ .getDataSharingType() != DataSharingClauseType::Linear)
+ privateVars.insert(var);
llvm::DenseSet<Value> reductionVars(llvm::from_range, getReductionVars());
// TODO Check lastprivate vars when their support is added to SimdOp.
for (Value var : getLinearVars()) {
@@ -5332,8 +5349,8 @@ void PrivateClauseOp::build(OpBuilder &odsBuilder, OperationState &odsState,
LogicalResult PrivateClauseOp::verifyRegions() {
Type argType = getArgType();
- auto verifyTerminator = [&](Operation *terminator,
- bool yieldsValue) -> LogicalResult {
+ auto verifyTerminator = [&](Operation *terminator, bool yieldsValue,
+ bool yieldsAnyType) -> LogicalResult {
if (!terminator->getBlock()->getSuccessors().empty())
return success();
@@ -5352,12 +5369,17 @@ LogicalResult PrivateClauseOp::verifyRegions() {
<< "Did not expect any values to be yielded.";
}
- if (yieldedTypes.size() == 1 && yieldedTypes.front() == argType)
+ if (yieldedTypes.size() == 1 &&
+ (yieldsAnyType || yieldedTypes.front() == argType))
return success();
auto error = mlir::emitError(yieldOp.getLoc())
- << "Invalid yielded value. Expected type: " << argType
- << ", got: ";
+ << "Invalid yielded value. Expected ";
+ if (yieldsAnyType)
+ error << "a single value";
+ else
+ error << "type: " << argType;
+ error << ", got: ";
if (yieldedTypes.empty())
error << "None";
@@ -5368,8 +5390,8 @@ LogicalResult PrivateClauseOp::verifyRegions() {
};
auto verifyRegion = [&](Region ®ion, unsigned expectedNumArgs,
- StringRef regionName,
- bool yieldsValue) -> LogicalResult {
+ StringRef regionName, bool yieldsValue,
+ bool yieldsAnyType = false) -> LogicalResult {
assert(!region.empty());
if (region.getNumArguments() != expectedNumArgs)
@@ -5382,7 +5404,8 @@ LogicalResult PrivateClauseOp::verifyRegions() {
if (!block.mightHaveTerminator())
continue;
- if (failed(verifyTerminator(block.getTerminator(), yieldsValue)))
+ if (failed(verifyTerminator(block.getTerminator(), yieldsValue,
+ yieldsAnyType)))
return failure();
}
@@ -5416,6 +5439,19 @@ LogicalResult PrivateClauseOp::verifyRegions() {
/*yieldsValue=*/true)))
return failure();
+ if (dsType == DataSharingClauseType::Linear && !getCopyRegion().empty())
+ return emitError("`linear` clauses do not require a `copy` region.");
+
+ if (!getDataAddressRegion().empty()) {
+ if (dsType != DataSharingClauseType::Linear)
+ return emitError(
+ "only `linear` clauses can have a `data_address` region.");
+ if (failed(verifyRegion(getDataAddressRegion(), /*expectedNumArgs=*/1,
+ "data_address", /*yieldsValue=*/true,
+ /*yieldsAnyType=*/true)))
+ return failure();
+ }
+
if (!getDeallocRegion().empty() &&
failed(verifyRegion(getDeallocRegion(), /*expectedNumArgs=*/1, "dealloc",
/*yieldsValue=*/false)))
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index ba9b30e69aacef..64ea78b76bb922 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -51,6 +51,11 @@
using namespace mlir;
+static llvm::Expected<llvm::Value *>
+getPrivateDataAddress(llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation,
+ omp::PrivateClauseOp privatizer, llvm::Value *var);
+
namespace {
static llvm::omp::ScheduleKind
convertToScheduleKind(std::optional<omp::ClauseScheduleKind> schedKind) {
@@ -147,6 +152,9 @@ class LinearClauseProcessor {
SmallVector<llvm::Value *> linearPreconditionVars;
SmallVector<llvm::Value *> linearLoopBodyTemps;
SmallVector<llvm::Value *> linearOrigVal;
+ // Set for linear vars privatized by a `linear` privatizer.
+ SmallVector<omp::PrivateClauseOp> linearPrivatizers;
+ SmallVector<llvm::Value *> linearPrivateVars;
SmallVector<llvm::Value *> linearSteps;
SmallVector<llvm::Type *> linearVarTypes;
llvm::BasicBlock *linearFinalizationBB;
@@ -165,13 +173,60 @@ class LinearClauseProcessor {
// Allocate space for linear variabes
void createLinearVar(llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation,
- llvm::Value *linearVar, int idx) {
+ llvm::Value *linearVar, int idx,
+ omp::PrivateClauseOp privatizer = nullptr,
+ llvm::Value *privateVar = nullptr) {
linearPreconditionVars.push_back(
builder.CreateAlloca(linearVarTypes[idx], nullptr, ".linear_var"));
llvm::Value *linearLoopBodyTemp =
- builder.CreateAlloca(linearVarTypes[idx], nullptr, ".linear_result");
+ privatizer ? nullptr
+ : builder.CreateAlloca(linearVarTypes[idx], nullptr,
+ ".linear_result");
linearOrigVal.push_back(linearVar);
linearLoopBodyTemps.push_back(linearLoopBodyTemp);
+ linearPrivatizers.push_back(privatizer);
+ linearPrivateVars.push_back(privateVar);
+ }
+
+ // Address of the original value of linear var `index`.
+ llvm::Expected<llvm::Value *>
+ getOrigAddress(llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation, size_t index) {
+ if (!linearPrivatizers[index])
+ return linearOrigVal[index];
+ return getPrivateDataAddress(builder, moduleTranslation,
+ linearPrivatizers[index],
+ linearOrigVal[index]);
+ }
+
+ // Address of the current value of linear var `index` in the loop body.
+ llvm::Expected<llvm::Value *>
+ getBodyAddress(llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation, size_t index) {
+ if (!linearPrivatizers[index])
+ return linearLoopBodyTemps[index];
+ return getPrivateDataAddress(builder, moduleTranslation,
+ linearPrivatizers[index],
+ linearPrivateVars[index]);
+ }
+
+ // Copy the current value of each linear var back to the original.
+ llvm::Error storeLinearVars(llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ for (size_t index = 0; index < linearOrigVal.size(); index++) {
+ llvm::Expected<llvm::Value *> from =
+ getBodyAddress(builder, moduleTranslation, index);
+ if (!from)
+ return from.takeError();
+ llvm::LoadInst *linearVarTemp =
+ builder.CreateLoad(linearVarTypes[index], *from);
+ llvm::Expected<llvm::Value *> to =
+ getOrigAddress(builder, moduleTranslation, index);
+ if (!to)
+ return to.takeError();
+ builder.CreateStore(linearVarTemp, *to);
+ }
+ return llvm::Error::success();
}
// Initialize linear step
@@ -181,15 +236,20 @@ class LinearClauseProcessor {
}
// Emit IR for initialization of linear variables
- void initLinearVar(llvm::IRBuilderBase &builder,
- LLVM::ModuleTranslation &moduleTranslation,
- llvm::BasicBlock *loopPreHeader) {
+ llvm::Error initLinearVar(llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation,
+ llvm::BasicBlock *loopPreHeader) {
builder.SetInsertPoint(loopPreHeader->getTerminator());
for (size_t index = 0; index < linearOrigVal.size(); index++) {
+ llvm::Expected<llvm::Value *> orig =
+ getOrigAddress(builder, moduleTranslation, index);
+ if (!orig)
+ return orig.takeError();
llvm::LoadInst *linearVarLoad =
- builder.CreateLoad(linearVarTypes[index], linearOrigVal[index]);
+ builder.CreateLoad(linearVarTypes[index], *orig);
builder.CreateStore(linearVarLoad, linearPreconditionVars[index]);
}
+ return llvm::Error::success();
}
// Find linear iteration variable and save it for later updates
@@ -222,13 +282,19 @@ class LinearClauseProcessor {
}
// Emit IR for updating Linear variables
- void updateLinearVar(llvm::IRBuilderBase &builder, llvm::BasicBlock *loopBody,
- llvm::Value *loopInductionVar) {
+ llvm::Error updateLinearVar(llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation,
+ llvm::BasicBlock *loopBody,
+ llvm::Value *loopInductionVar) {
builder.SetInsertPoint(loopBody->getTerminator());
for (size_t index = 0; index < linearPreconditionVars.size(); index++) {
llvm::Type *linearVarType = linearVarTypes[index];
llvm::Value *iv = loopInductionVar;
llvm::Value *step = linearSteps[index];
+ llvm::Expected<llvm::Value *> result =
+ getBodyAddress(builder, moduleTranslation, index);
+ if (!result)
+ return result.takeError();
if (!iv->getType()->isIntegerTy())
llvm_unreachable("OpenMP loop induction variable must be an integer "
@@ -243,7 +309,7 @@ class LinearClauseProcessor {
builder.CreateLoad(linearVarType, linearPreconditionVars[index]);
llvm::Value *mulInst = builder.CreateMul(iv, step);
llvm::Value *addInst = builder.CreateAdd(linearVarStart, mulInst);
- builder.CreateStore(addInst, linearLoopBodyTemps[index]);
+ builder.CreateStore(addInst, *result);
} else if (linearVarType->isFloatingPointTy()) {
// Float path: perform multiply in integer, then convert to float
step = builder.CreateSExtOrTrunc(step, iv->getType());
@@ -253,12 +319,13 @@ class LinearClauseProcessor {
builder.CreateLoad(linearVarType, linearPreconditionVars[index]);
llvm::Value *mulFp = builder.CreateSIToFP(mulInst, linearVarType);
llvm::Value *addInst = builder.CreateFAdd(linearVarStart, mulFp);
- builder.CreateStore(addInst, linearLoopBodyTemps[index]);
+ builder.CreateStore(addInst, *result);
} else {
llvm_unreachable(
"Linear variable must be of integer or floating-point type");
}
}
+ return llvm::Error::success();
}
// Emit IR for updating linear iteration variables on loop exit
@@ -273,7 +340,7 @@ class LinearClauseProcessor {
for (index = 0; index < linearOrigVal.size(); index++)
if (linearIV == linearOrigVal[index])
break;
- if (index == linearOrigVal.size())
+ if (index == linearOrigVal.size() || linearPrivatizers[index])
return;
// Add one more step to the linear iteration variable
@@ -316,11 +383,8 @@ class LinearClauseProcessor {
llvm::Type::getInt32Ty(builder.getContext()), 0));
// Store the linear variable values to original variables.
builder.SetInsertPoint(linearLastIterExitBB->getTerminator());
- for (size_t index = 0; index < linearOrigVal.size(); index++) {
- llvm::LoadInst *linearVarTemp =
- builder.CreateLoad(linearVarTypes[index], linearLoopBodyTemps[index]);
- builder.CreateStore(linearVarTemp, linearOrigVal[index]);
- }
+ if (llvm::Error err = storeLinearVars(builder, moduleTranslation))
+ return std::move(err);
// Create conditional branch such that the linear variable
// values are stored to original variables only at the
@@ -336,18 +400,19 @@ class LinearClauseProcessor {
// Emit stores for linear variables. Useful in case of SIMD
// construct.
- void emitStoresForLinearVar(llvm::IRBuilderBase &builder) {
- for (size_t index = 0; index < linearOrigVal.size(); index++) {
- llvm::LoadInst *linearVarTemp =
- builder.CreateLoad(linearVarTypes[index], linearLoopBodyTemps[index]);
- builder.CreateStore(linearVarTemp, linearOrigVal[index]);
- }
+ llvm::Error
+ emitStoresForLinearVar(llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ return storeLinearVars(builder, moduleTranslation);
}
// Rewrite all uses of the original variable, in the basic blocks in the
// [startBB, endBB] interval, with the linear variable in-place.
void rewriteInPlace(llvm::IRBuilderBase &builder, llvm::BasicBlock *startBB,
llvm::BasicBlock *endBB, size_t varIndex) {
+ // The loop body already uses the private copy.
+ if (linearPrivatizers[varIndex])
+ return;
llvm::SmallVector<llvm::BasicBlock *, 32> worklist;
llvm::SmallPtrSet<llvm::BasicBlock *, 32> collectedBBs;
@@ -1123,6 +1188,18 @@ static LogicalResult collectAndValidateTaskloopRedDecls(
return success();
}
+/// Returns the `linear` privatizer of `linearVar` and its private copy, if any.
+static std::pair<omp::PrivateClauseOp, llvm::Value *>
+findLinearPrivatizer(PrivateVarsInfo &privateVarsInfo, Value linearVar) {
+ for (auto [mlirVar, llvmVar, privatizer] :
+ llvm::zip_equal(privateVarsInfo.mlirVars, privateVarsInfo.llvmVars,
+ privateVarsInfo.privatizers))
+ if (mlirVar == linearVar &&
+ privatizer.getDataSharingType() == omp::DataSharingClauseType::Linear)
+ return {privatizer, llvmVar};
+ return {};
+}
+
/// Translates the blocks contained in the given region and appends them to at
/// the current insertion point of `builder`. The operations of the entry block
/// are appended to the current insertion block. If set, `continuationBlockArgs`
@@ -1872,6 +1949,32 @@ materializeRegionArgValue(llvm::IRBuilderBase &builder,
return builder.CreateLoad(regionArgType, value);
}
+/// Returns the address of the data in `var`, via the `data_address` region.
+static llvm::Expected<llvm::Value *>
+getPrivateDataAddress(llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation,
+ omp::PrivateClauseOp privatizer, llvm::Value *var) {
+ Region ®ion = privatizer.getDataAddressRegion();
+ if (region.empty())
+ return var;
+ if (!region.hasOneBlock()) {
+ privatizer.emitError(
+ "not yet implemented: multi-block `data_address` region");
+ return llvm::make_error<PreviouslyReportedError>();
+ }
+
+ BlockArgument arg = privatizer.getDataAddressArg();
+ moduleTranslation.mapValue(
+ arg, materializeRegionArgValue(builder, moduleTranslation, arg, var));
+ for (Operation &op : region.front().without_terminator())
+ if (failed(moduleTranslation.convertOperation(op, builder)))
+ return llvm::make_error<PreviouslyReportedError>();
+ llvm::Value *address = moduleTranslation.lookupValue(
+ region.front().getTerminator()->getOperand(0));
+ moduleTranslation.forgetMapping(region);
+ return address;
+}
+
/// Initialize a single (first)private variable. You probably want to use
/// allocateAndInitPrivateVars instead of this.
/// This returns the private variable which has been initialized. This
@@ -4850,10 +4953,13 @@ convertOmpWsloop(Operation &opInst, llvm::IRBuilderBase &builder,
for (mlir::Attribute linearVarType : linearVarTypes)
linearClauseProcessor.registerType(moduleTranslation, linearVarType);
- for (auto [idx, linearVar] : llvm::enumerate(wsloopOp.getLinearVars()))
+ for (auto [idx, linearVar] : llvm::enumerate(wsloopOp.getLinearVars())) {
+ auto [privatizer, privateVar] =
+ findLinearPrivatizer(privateVarsInfo, linearVar);
linearClauseProcessor.createLinearVar(
builder, moduleTranslation, moduleTranslation.lookupValue(linearVar),
- idx);
+ idx, privatizer, privateVar);
+ }
for (mlir::Value linearStep : wsloopOp.getLinearStepVars())
linearClauseProcessor.initLinearStep(moduleTranslation, linearStep);
}
@@ -4868,16 +4974,22 @@ convertOmpWsloop(Operation &opInst, llvm::IRBuilderBase &builder,
// Emit Initialization and Update IR for linear variables
if (!wsloopOp.getLinearVars().empty()) {
- linearClauseProcessor.initLinearVar(builder, moduleTranslation,
- loopInfo->getPreheader());
+ if (failed(handleError(
+ linearClauseProcessor.initLinearVar(builder, moduleTranslation,
+ loopInfo->getPreheader()),
+ opInst)))
+ return failure();
llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterBarrierIP =
moduleTranslation.getOpenMPBuilder()->createBarrier(
builder, llvm::omp::OMPD_barrier);
if (failed(handleError(afterBarrierIP, *loopOp)))
return failure();
builder.restoreIP(*afterBarrierIP);
- linearClauseProcessor.updateLinearVar(builder, loopInfo->getBody(),
- loopInfo->getIndVar());
+ if (failed(handleError(linearClauseProcessor.updateLinearVar(
+ builder, moduleTranslation, loopInfo->getBody(),
+ loopInfo->getIndVar()),
+ opInst)))
+ return failure();
linearClauseProcessor.splitLinearFiniBB(builder, loopInfo->getExit());
}
@@ -5218,6 +5330,15 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
for (mlir::Attribute linearVarType : linearVarTypes)
linearClauseProcessor.registerType(moduleTranslation, linearVarType);
for (auto [idx, linearVar] : llvm::enumerate(simdOp.getLinearVars())) {
+ auto [privatizer, privateVar] =
+ findLinearPrivatizer(privateVarsInfo, linearVar);
+ if (privatizer) {
+ linearClauseProcessor.createLinearVar(
+ builder, moduleTranslation,
+ moduleTranslation.lookupValue(linearVar), idx, privatizer,
+ privateVar);
+ continue;
+ }
bool isImplicit = false;
for (auto [mlirPrivVar, llvmPrivateVar] : llvm::zip_equal(
privateVarsInfo.mlirVars, privateVarsInfo.llvmVars)) {
@@ -5307,11 +5428,17 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
llvm::CanonicalLoopInfo *loopInfo = findCurrentLoopInfo(moduleTranslation);
// Emit Initialization for linear variables
if (simdOp.getLinearVars().size()) {
- linearClauseProcessor.initLinearVar(builder, moduleTranslation,
- loopInfo->getPreheader());
+ if (failed(handleError(
+ linearClauseProcessor.initLinearVar(builder, moduleTranslation,
+ loopInfo->getPreheader()),
+ opInst)))
+ return failure();
- linearClauseProcessor.updateLinearVar(builder, loopInfo->getBody(),
- loopInfo->getIndVar());
+ if (failed(handleError(linearClauseProcessor.updateLinearVar(
+ builder, moduleTranslation, loopInfo->getBody(),
+ loopInfo->getIndVar()),
+ opInst)))
+ return failure();
}
builder.SetInsertPoint((*regionBlock)->begin());
@@ -5326,7 +5453,10 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
order, simdlen, safelen);
linearClauseProcessor.updateLinearIV(builder, moduleTranslation);
- linearClauseProcessor.emitStoresForLinearVar(builder);
+ if (failed(handleError(linearClauseProcessor.emitStoresForLinearVar(
+ builder, moduleTranslation),
+ opInst)))
+ return failure();
// We now need to reduce the per-simd-lane reduction variable into the
// original variable. This works a bit differently to other reductions (e.g.
diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir
index c3fe4f60e41667..9375a29ec00300 100644
--- a/mlir/test/Dialect/OpenMP/invalid.mlir
+++ b/mlir/test/Dialect/OpenMP/invalid.mlir
@@ -3756,6 +3756,52 @@ omp.private {type = private} @x.privatizer : f32 copy {
// -----
+// expected-error @below {{`linear` clauses do not require a `copy` region.}}
+omp.private {type = linear} @x.privatizer : f32 copy {
+^bb0(%arg0: f32, %arg1 : f32):
+ omp.yield(%arg0 : f32)
+}
+
+// -----
+
+// expected-error @below {{only `linear` clauses can have a `data_address` region.}}
+omp.private {type = private} @x.privatizer : !llvm.ptr data_address {
+^bb0(%arg0: !llvm.ptr):
+ omp.yield(%arg0 : !llvm.ptr)
+}
+
+// -----
+
+// expected-error @below {{`data_address`: expected 1 region arguments, got: 2}}
+omp.private {type = linear} @x.privatizer : !llvm.ptr data_address {
+^bb0(%arg0: !llvm.ptr, %arg1: !llvm.ptr):
+ omp.yield(%arg0 : !llvm.ptr)
+}
+
+// -----
+
+omp.private {type = linear} @x.privatizer : !llvm.ptr data_address {
+^bb0(%arg0: !llvm.ptr):
+ // expected-error @below {{Invalid yielded value. Expected a single value, got: None}}
+ omp.yield
+}
+
+// -----
+
+omp.private {type = linear} @x.privatizer : !llvm.ptr
+
+func.func @linear_privatizer_not_linear(%x : !llvm.ptr, %lb : i32) {
+ // expected-error @below {{`linear` privatizer '@x.privatizer' used for a variable that is not in the `linear` clause}}
+ omp.simd private(@x.privatizer %x -> %arg0 : !llvm.ptr) {
+ omp.loop_nest (%iv) : i32 = (%lb) to (%lb) step (%lb) {
+ omp.yield
+ }
+ }
+ return
+}
+
+// -----
+
// expected-error @below {{`firstprivate` clauses require at least a `copy` region.}}
omp.private {type = firstprivate} @x.privatizer : f32 init {
^bb0(%arg0: f32, %arg1: f32):
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 9371d3599b72ae..d3ad0fb66f287c 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -4670,3 +4670,27 @@ func.func @omp_dispatch_nocontext_novariants(%cond : i1, %x : memref<i32>) -> ()
// CHECK-LABEL: func.func private @variant(memref<i32>)
func.func private @variant(memref<i32>) -> ()
+
+// CHECK-LABEL: omp.private {type = linear} @linear.privatizer : !llvm.ptr init {
+omp.private {type = linear} @linear.privatizer : !llvm.ptr init {
+^bb0(%arg0: !llvm.ptr, %arg1: !llvm.ptr):
+ omp.yield(%arg1 : !llvm.ptr)
+// CHECK: } data_address {
+} data_address {
+// CHECK: ^bb0(%{{.*}}: !llvm.ptr):
+^bb0(%arg0: !llvm.ptr):
+ %0 = llvm.load %arg0 : !llvm.ptr -> !llvm.ptr
+ // CHECK: omp.yield(%{{.*}} : !llvm.ptr)
+ omp.yield(%0 : !llvm.ptr)
+}
+
+// CHECK-LABEL: func @simd_linear_privatizer
+func.func @simd_linear_privatizer(%x : !llvm.ptr, %lb : i32, %ub : i32, %step : i32) {
+ // CHECK: omp.simd linear(%[[X:.*]] : !llvm.ptr = %{{.*}} : i32) linear_var_types([i32]) private(@linear.privatizer %[[X]] -> %{{.*}} : !llvm.ptr)
+ omp.simd linear(%x : !llvm.ptr = %step : i32) linear_var_types([i32]) private(@linear.privatizer %x -> %arg0 : !llvm.ptr) {
+ omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
+ omp.yield
+ }
+ }
+ return
+}
diff --git a/mlir/test/Target/LLVMIR/openmp-linear-privatizer.mlir b/mlir/test/Target/LLVMIR/openmp-linear-privatizer.mlir
new file mode 100644
index 00000000000000..cd2d40a915a4b1
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/openmp-linear-privatizer.mlir
@@ -0,0 +1,95 @@
+// Linear vars with a `linear` privatizer keep their value in the private copy.
+// RUN: mlir-translate -mlir-to-llvmir -split-input-file %s | FileCheck %s
+
+llvm.func @malloc(i64) -> !llvm.ptr
+llvm.func @free(!llvm.ptr)
+
+// A "box" holding a pointer to an i32.
+omp.private {type = linear} @box.linear : !llvm.ptr init {
+^bb0(%mold: !llvm.ptr, %priv: !llvm.ptr):
+ %c4 = llvm.mlir.constant(4 : i64) : i64
+ %mem = llvm.call @malloc(%c4) : (i64) -> !llvm.ptr
+ llvm.store %mem, %priv : !llvm.ptr, !llvm.ptr
+ omp.yield(%priv : !llvm.ptr)
+} data_address {
+^bb0(%box: !llvm.ptr):
+ %data = llvm.load %box : !llvm.ptr -> !llvm.ptr
+ omp.yield(%data : !llvm.ptr)
+} dealloc {
+^bb0(%box: !llvm.ptr):
+ %data = llvm.load %box : !llvm.ptr -> !llvm.ptr
+ llvm.call @free(%data) : (!llvm.ptr) -> ()
+ omp.yield
+}
+
+// CHECK-LABEL: define void @simd_linear_privatizer(
+// CHECK-SAME: ptr %[[BOX:.*]], ptr %{{.*}})
+// CHECK: %[[PRIV:.*]] = alloca ptr
+// CHECK-NOT: .linear_result
+// CHECK: %[[START:.linear_var]] = alloca i32
+// CHECK: omp_loop.preheader:
+// CHECK: %[[ORIG_DATA:.*]] = load ptr, ptr %[[BOX]]
+// CHECK: %[[V:.*]] = load i32, ptr %[[ORIG_DATA]]
+// CHECK: store i32 %[[V]], ptr %[[START]]
+// CHECK: omp_loop.body:
+// CHECK: %[[PRIV_DATA:.*]] = load ptr, ptr %[[PRIV]]
+// CHECK: %[[S:.*]] = load i32, ptr %[[START]]
+// CHECK: %[[MUL:.*]] = mul i32 %{{.*}}, 1
+// CHECK: %[[ADD:.*]] = add i32 %[[S]], %[[MUL]]
+// CHECK: store i32 %[[ADD]], ptr %[[PRIV_DATA]]
+// CHECK: omp.region.cont:
+// CHECK: %[[PRIV_DATA2:.*]] = load ptr, ptr %[[PRIV]]
+// CHECK: %[[LAST:.*]] = load i32, ptr %[[PRIV_DATA2]]
+// CHECK: %[[ORIG_DATA2:.*]] = load ptr, ptr %[[BOX]]
+// CHECK: store i32 %[[LAST]], ptr %[[ORIG_DATA2]]
+// CHECK: call void @free(
+llvm.func @simd_linear_privatizer(%box : !llvm.ptr, %out : !llvm.ptr) {
+ %c1 = llvm.mlir.constant(1 : i32) : i32
+ %c10 = llvm.mlir.constant(10 : i32) : i32
+ omp.simd linear(%box : !llvm.ptr = %c1 : i32) linear_var_types([i32]) private(@box.linear %box -> %arg0 : !llvm.ptr) {
+ omp.loop_nest (%iv) : i32 = (%c1) to (%c10) inclusive step (%c1) {
+ %data = llvm.load %arg0 : !llvm.ptr -> !llvm.ptr
+ %v = llvm.load %data : !llvm.ptr -> i32
+ llvm.store %v, %out : i32, !llvm.ptr
+ omp.yield
+ }
+ }
+ llvm.return
+}
+
+// CHECK-LABEL: define void @wsloop_linear_privatizer(
+// CHECK-SAME: ptr %[[BOX:.*]], ptr %{{.*}})
+// CHECK: %[[PRIV:.*]] = alloca ptr
+// CHECK-NOT: .linear_result
+// CHECK: %[[START:.linear_var]] = alloca i32
+// CHECK: omp_loop.preheader:
+// CHECK: %[[ORIG_DATA:.*]] = load ptr, ptr %[[BOX]]
+// CHECK: %[[V:.*]] = load i32, ptr %[[ORIG_DATA]]
+// CHECK: store i32 %[[V]], ptr %[[START]]
+// CHECK: omp_loop.body:
+// CHECK: %[[PRIV_DATA:.*]] = load ptr, ptr %[[PRIV]]
+// CHECK: %[[S:.*]] = load i32, ptr %[[START]]
+// CHECK: %[[MUL:.*]] = mul i32 %{{.*}}, 2
+// CHECK: %[[ADD:.*]] = add i32 %[[S]], %[[MUL]]
+// CHECK: store i32 %[[ADD]], ptr %[[PRIV_DATA]]
+// CHECK: omp_loop.linear_lastiter_exit:
+// CHECK: %[[PRIV_DATA2:.*]] = load ptr, ptr %[[PRIV]]
+// CHECK: %[[LAST:.*]] = load i32, ptr %[[PRIV_DATA2]]
+// CHECK: %[[ORIG_DATA2:.*]] = load ptr, ptr %[[BOX]]
+// CHECK: store i32 %[[LAST]], ptr %[[ORIG_DATA2]]
+// CHECK: omp.region.cont:
+// CHECK: call void @free(
+llvm.func @wsloop_linear_privatizer(%box : !llvm.ptr, %out : !llvm.ptr) {
+ %c1 = llvm.mlir.constant(1 : i32) : i32
+ %c2 = llvm.mlir.constant(2 : i32) : i32
+ %c10 = llvm.mlir.constant(10 : i32) : i32
+ omp.wsloop linear(%box : !llvm.ptr = %c2 : i32) linear_var_types([i32]) private(@box.linear %box -> %arg0 : !llvm.ptr) {
+ omp.loop_nest (%iv) : i32 = (%c1) to (%c10) inclusive step (%c1) {
+ %data = llvm.load %arg0 : !llvm.ptr -> !llvm.ptr
+ %v = llvm.load %data : !llvm.ptr -> i32
+ llvm.store %v, %out : i32, !llvm.ptr
+ omp.yield
+ }
+ }
+ llvm.return
+}
>From 32ec17cd3f28ff98106d8d9feccc5cf6676b1c95 Mon Sep 17 00:00:00 2001
From: Aditya Trivedi <adit4443ya at gmail.com>
Date: Tue, 13 Oct 2026 17:43:32 +0530
Subject: [PATCH 2/2] [flang][OpenMP] Support ALLOCATABLE list items in LINEAR
Privatize allocatable linear items with a linear privatizer whose
data_address region returns the allocatable's data address.
Fixes #182401
---
.../lib/Lower/OpenMP/DataSharingProcessor.cpp | 6 ++
flang/lib/Lower/OpenMP/OpenMP.cpp | 46 ++++++++-
flang/lib/Lower/Support/Utils.cpp | 22 ++++-
.../test/Lower/OpenMP/linear-allocatable.f90 | 96 +++++++++++++++++++
4 files changed, 165 insertions(+), 5 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/linear-allocatable.f90
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index 8d0d191058cfb8..db30f113f019c4 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -341,6 +341,12 @@ void DataSharingProcessor::collectSymbolsForPrivatization() {
} else {
collectOmpObjectListSymbol(objects, explicitlyPrivatizedSymbols);
}
+ } else if (const auto *linearClause =
+ std::get_if<omp::clause::Linear>(&clause.u)) {
+ // Allocatable linear items need a real private copy.
+ for (const omp::Object &object : std::get<ObjectList>(linearClause->t))
+ if (semantics::IsAllocatable(object.sym()->GetUltimate()))
+ allPrivatizedSymbols.insert(object.sym());
}
}
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 0b20eb1c16dc11..de14fff0735703 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -5296,6 +5296,30 @@ static mlir::omp::DistributeOp genCompositeDistributeParallelDo(
return distributeOp;
}
+/// Move `linear` privatizers to omp.wsloop, which holds the linear clause.
+static void moveLinearPrivatizers(
+ lower::AbstractConverter &converter, mlir::omp::PrivateClauseOps &simdOps,
+ llvm::SmallVectorImpl<const semantics::Symbol *> &simdSyms,
+ mlir::omp::PrivateClauseOps &wsloopOps,
+ llvm::SmallVectorImpl<const semantics::Symbol *> &wsloopSyms) {
+ mlir::ModuleOp module = converter.getFirOpBuilder().getModule();
+ for (size_t i = 0; i < simdSyms.size();) {
+ auto privatizer = module.lookupSymbol<mlir::omp::PrivateClauseOp>(
+ mlir::cast<mlir::SymbolRefAttr>(simdOps.privateSyms[i]));
+ if (privatizer.getDataSharingType() !=
+ mlir::omp::DataSharingClauseType::Linear) {
+ ++i;
+ continue;
+ }
+ wsloopOps.privateVars.push_back(simdOps.privateVars[i]);
+ wsloopOps.privateSyms.push_back(simdOps.privateSyms[i]);
+ wsloopSyms.push_back(simdSyms[i]);
+ simdOps.privateVars.erase(simdOps.privateVars.begin() + i);
+ simdOps.privateSyms.erase(simdOps.privateSyms.begin() + i);
+ simdSyms.erase(simdSyms.begin() + i);
+ }
+}
+
static mlir::omp::DistributeOp genCompositeDistributeParallelDoSimd(
lower::AbstractConverter &converter, lower::SymMap &symTable,
lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,
@@ -5371,6 +5395,12 @@ static mlir::omp::DistributeOp genCompositeDistributeParallelDoSimd(
genSimdImplicitLinear(converter, semaCtx, simdClauseOps, loopNestClauseOps,
iv);
+ llvm::SmallVector<const semantics::Symbol *> wsloopPrivSyms;
+ llvm::SmallVector<const semantics::Symbol *> simdPrivSyms(
+ simdItemDSP.getDelayedPrivSymbols());
+ moveLinearPrivatizers(converter, simdClauseOps, simdPrivSyms, wsloopClauseOps,
+ wsloopPrivSyms);
+
// Operation creation.
ObjectEntryBlockArgs distributeArgs;
// TODO: Add private syms and vars.
@@ -5379,7 +5409,8 @@ static mlir::omp::DistributeOp genCompositeDistributeParallelDoSimd(
distributeOp.setComposite(/*val=*/true);
ObjectEntryBlockArgs wsloopArgs;
- // TODO: Add private syms and vars.
+ wsloopArgs.priv.objects = makeObjects(wsloopPrivSyms);
+ wsloopArgs.priv.vars = wsloopClauseOps.privateVars;
wsloopArgs.reduction.objects = wsloopReductionObjects;
wsloopArgs.reduction.vars = wsloopClauseOps.reductionVars;
auto wsloopOp = genWrapperOp<mlir::omp::WsloopOp>(
@@ -5387,7 +5418,7 @@ static mlir::omp::DistributeOp genCompositeDistributeParallelDoSimd(
wsloopOp.setComposite(/*val=*/true);
ObjectEntryBlockArgs simdArgs;
- simdArgs.priv.objects = makeObjects(simdItemDSP.getDelayedPrivSymbols());
+ simdArgs.priv.objects = makeObjects(simdPrivSyms);
simdArgs.priv.vars = simdClauseOps.privateVars;
simdArgs.reduction.objects = simdReductionObjects;
simdArgs.reduction.vars = simdClauseOps.reductionVars;
@@ -5533,9 +5564,16 @@ static mlir::omp::WsloopOp genCompositeDoSimd(
genSimdImplicitLinear(converter, semaCtx, simdClauseOps, loopNestClauseOps,
iv);
+ llvm::SmallVector<const semantics::Symbol *> wsloopPrivSyms(
+ wsloopItemDSP.getDelayedPrivSymbols());
+ llvm::SmallVector<const semantics::Symbol *> simdPrivSyms(
+ simdItemDSP.getDelayedPrivSymbols());
+ moveLinearPrivatizers(converter, simdClauseOps, simdPrivSyms, wsloopClauseOps,
+ wsloopPrivSyms);
+
// Operation creation.
ObjectEntryBlockArgs wsloopArgs;
- wsloopArgs.priv.objects = makeObjects(wsloopItemDSP.getDelayedPrivSymbols());
+ wsloopArgs.priv.objects = makeObjects(wsloopPrivSyms);
wsloopArgs.priv.vars = wsloopClauseOps.privateVars;
wsloopArgs.reduction.objects = wsloopReductionObjects;
wsloopArgs.reduction.vars = wsloopClauseOps.reductionVars;
@@ -5544,7 +5582,7 @@ static mlir::omp::WsloopOp genCompositeDoSimd(
wsloopOp.setComposite(/*val=*/true);
ObjectEntryBlockArgs simdArgs;
- simdArgs.priv.objects = makeObjects(simdItemDSP.getDelayedPrivSymbols());
+ simdArgs.priv.objects = makeObjects(simdPrivSyms);
simdArgs.priv.vars = simdClauseOps.privateVars;
simdArgs.reduction.objects = simdReductionObjects;
simdArgs.reduction.vars = simdClauseOps.reductionVars;
diff --git a/flang/lib/Lower/Support/Utils.cpp b/flang/lib/Lower/Support/Utils.cpp
index e6b6e923308af0..7dba6eb5174d5b 100644
--- a/flang/lib/Lower/Support/Utils.cpp
+++ b/flang/lib/Lower/Support/Utils.cpp
@@ -710,6 +710,10 @@ void privatizeSymbol(
// not emit the copy region when dealing with the SIMD directive.
if (dir && dir == llvm::omp::Directive::OMPD_simd)
emitCopyRegion = false;
+ // Allocatable LINEAR items get a `linear` privatizer.
+ bool isLinear = std::is_same_v<OpType, mlir::omp::PrivateClauseOp> &&
+ symToPrivatize->test(semantics::Symbol::Flag::OmpLinear) &&
+ semantics::IsAllocatable(ultimate);
mlir::Value privVal = hsb.getAddr();
mlir::Type allocType = privVal.getType();
@@ -765,7 +769,8 @@ void privatizeSymbol(
auto moduleOp = firOpBuilder.getModule();
std::string privatizerPrefix =
converter.mangleName(*sym) +
- (emitCopyRegion ? "_firstprivate" : "_private");
+ (emitCopyRegion ? "_firstprivate"
+ : (isLinear ? "_linear" : "_private"));
if (forceHeapAllocation)
privatizerPrefix += "_heap";
auto uniquePrivatizerName = fir::getTypeAsString(
@@ -790,6 +795,7 @@ void privatizeSymbol(
firOpBuilder, symLoc, uniquePrivatizerName,
/*sym_visibility=*/nullptr, allocType,
emitCopyRegion ? mlir::omp::DataSharingClauseType::FirstPrivate
+ : isLinear ? mlir::omp::DataSharingClauseType::Linear
: mlir::omp::DataSharingClauseType::Private);
} else {
result =
@@ -888,6 +894,20 @@ void privatizeSymbol(
}
}
+ // Populate the `data_address` region of a `linear` privatizer.
+ if constexpr (std::is_same_v<OpType, mlir::omp::PrivateClauseOp>) {
+ if (isLinear) {
+ mlir::Block *block =
+ firOpBuilder.createBlock(&result.getDataAddressRegion(),
+ /*insertPt=*/{}, {argType}, {symLoc});
+ firOpBuilder.setInsertionPointToEnd(block);
+ mlir::Value box =
+ fir::LoadOp::create(firOpBuilder, symLoc, block->getArgument(0));
+ mlir::Value addr = fir::BoxAddrOp::create(firOpBuilder, symLoc, box);
+ mlir::omp::YieldOp::create(firOpBuilder, symLoc, addr);
+ }
+ }
+
return result;
}();
diff --git a/flang/test/Lower/OpenMP/linear-allocatable.f90 b/flang/test/Lower/OpenMP/linear-allocatable.f90
new file mode 100644
index 00000000000000..60c7f83f7d7fa1
--- /dev/null
+++ b/flang/test/Lower/OpenMP/linear-allocatable.f90
@@ -0,0 +1,96 @@
+! LINEAR on an allocatable uses a `linear` omp.private with a data_address.
+
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
+! RUN: bbc -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
+
+! CHECK-LABEL: omp.private {type = linear} @_QFsimd_linear_allocatableEa_linear_box_heap_i32 : !fir.box<!fir.heap<i32>> init {
+! CHECK: fir.allocmem i32
+! CHECK: } data_address {
+! CHECK: ^bb0(%[[ARG:.*]]: !fir.ref<!fir.box<!fir.heap<i32>>>):
+! CHECK: %[[BOX:.*]] = fir.load %[[ARG]] : !fir.ref<!fir.box<!fir.heap<i32>>>
+! CHECK: %[[ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<i32>>) -> !fir.heap<i32>
+! CHECK: omp.yield(%[[ADDR]] : !fir.heap<i32>)
+! CHECK: } dealloc {
+! CHECK: fir.freemem
+
+! CHECK-LABEL: func.func @_QPsimd_linear_allocatable
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} uniq_name("_QFsimd_linear_allocatableEa") fortran_attrs<allocatable>
+! CHECK: omp.simd linear(%[[A]]#0 : !fir.ref<!fir.box<!fir.heap<i32>>> = %{{.*}} : i32
+! CHECK-SAME: linear_var_types([i32
+! CHECK-SAME: private(@_QFsimd_linear_allocatableEa_linear_box_heap_i32 %[[A]]#0 -> %[[ARG:.*]] : !fir.ref<!fir.box<!fir.heap<i32>>>)
+! CHECK: omp.loop_nest
+! CHECK: %[[PRIV_A:.*]]:2 = hlfir.declare %[[ARG]] uniq_name("_QFsimd_linear_allocatableEa") fortran_attrs<allocatable>
+! CHECK: hlfir.assign %{{.*}} to %[[PRIV_A]]#0 realloc : i32, !fir.ref<!fir.box<!fir.heap<i32>>>
+! CHECK-NEXT: omp.yield
+subroutine simd_linear_allocatable()
+ integer, allocatable :: a
+ integer :: i
+ allocate(a)
+ a = 0
+ !$omp simd linear(a)
+ do i = 1, 2
+ a = 2
+ end do
+ !$omp end simd
+end subroutine
+
+! CHECK-LABEL: func.func @_QPdo_linear_allocatable
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} uniq_name("_QFdo_linear_allocatableEa") fortran_attrs<allocatable>
+! CHECK: omp.parallel
+! CHECK: omp.wsloop linear(%[[A]]#0 : !fir.ref<!fir.box<!fir.heap<i32>>> = %{{.*}} : i32)
+! CHECK-SAME: private(@_QFdo_linear_allocatableEa_linear_box_heap_i32 %[[A]]#0 -> %[[ARG:[^,]*]],
+! CHECK: omp.loop_nest
+! CHECK: hlfir.declare %[[ARG]] uniq_name("_QFdo_linear_allocatableEa") fortran_attrs<allocatable>
+subroutine do_linear_allocatable()
+ integer, allocatable :: a
+ integer :: i
+ allocate(a)
+ a = 0
+ !$omp parallel do linear(a:2)
+ do i = 1, 10
+ a = a + 2
+ end do
+ !$omp end parallel do
+end subroutine
+
+! Composite: the linear clause and its privatizer are both on omp.wsloop.
+! CHECK-LABEL: func.func @_QPdo_simd_linear_allocatable
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} uniq_name("_QFdo_simd_linear_allocatableEa") fortran_attrs<allocatable>
+! CHECK: omp.wsloop linear(%[[A]]#0 : !fir.ref<!fir.box<!fir.heap<i64>>> = %{{.*}} : i64)
+! CHECK-SAME: private(@_QFdo_simd_linear_allocatableEa_linear_box_heap_i64 %[[A]]#0 -> %[[ARG:.*]] : !fir.ref<!fir.box<!fir.heap<i64>>>)
+! CHECK-NEXT: omp.simd
+! CHECK-NOT: private
+! CHECK-SAME: {
+! CHECK: omp.loop_nest
+! CHECK: hlfir.declare %[[ARG]] uniq_name("_QFdo_simd_linear_allocatableEa") fortran_attrs<allocatable>
+subroutine do_simd_linear_allocatable()
+ integer(8), allocatable :: a
+ integer :: i
+ allocate(a)
+ a = 0
+ !$omp do simd linear(a)
+ do i = 1, 10
+ a = a + 1
+ end do
+ !$omp end do simd
+end subroutine
+
+! The original is used after the loop.
+! CHECK-LABEL: func.func @_QPoriginal_used_after_loop
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} uniq_name("_QForiginal_used_after_loopEa") fortran_attrs<allocatable>
+! CHECK: omp.simd linear
+! CHECK: omp.yield
+! CHECK: }
+! CHECK: hlfir.assign %{{.*}} to %[[A]]#0 realloc : i32, !fir.ref<!fir.box<!fir.heap<i32>>>
+subroutine original_used_after_loop()
+ integer, allocatable :: a
+ integer :: i
+ allocate(a)
+ a = 0
+ !$omp simd linear(a)
+ do i = 1, 2
+ a = 2
+ end do
+ !$omp end simd
+ a = 5
+end subroutine
More information about the Mlir-commits
mailing list