[Mlir-commits] [mlir] [mlir][OpenMP] Translate reductions on taskloop (PR #199670)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue May 26 06:24:10 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-llvm
@llvm/pr-subscribers-mlir-openmp
@llvm/pr-subscribers-flang-openmp
Author: Sairudra More (Saieiei)
<details>
<summary>Changes</summary>
This is stacked on #<!-- -->199565. Please review only the second commit / taskloop changes for now.
Once #<!-- -->199565 lands, I will rebase this branch onto `main` so the diff contains only this patch.
This patch adds LLVM IR translation for `reduction` and `in_reduction` clauses on `omp.taskloop.context`.
For `taskloop reduction`, the lowering emits the implicit taskgroup reduction setup, builds the task-reduction descriptor array, and maps each generated task to runtime-provided private reduction storage through `__kmpc_task_reduction_get_th_data`.
For `taskloop in_reduction`, the lowering uses the same runtime lookup path with a null descriptor so the runtime can find the enclosing task-reduction context.
Unsupported byref, cleanup, and two-argument initializer forms remain diagnosed.
Part of #<!-- -->199560.
---
Patch is 43.17 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/199670.diff
4 Files Affected:
- (modified) mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp (+426-12)
- (added) mlir/test/Target/LLVMIR/openmp-taskgroup-task-reduction.mlir (+129)
- (added) mlir/test/Target/LLVMIR/openmp-taskloop-reduction.mlir (+198)
- (modified) mlir/test/Target/LLVMIR/openmp-todo.mlir (+67-13)
``````````diff
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index f0511bb4be7dd..1120d9fc38d0a 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -362,7 +362,7 @@ static LogicalResult checkImplementationStatus(Operation &op) {
result = todo("privatization");
};
auto checkReduction = [&todo](auto op, LogicalResult &result) {
- if (isa<omp::TeamsOp>(op) || isa<omp::TaskloopContextOp>(op))
+ if (isa<omp::TeamsOp>(op))
if (!op.getReductionVars().empty() || op.getReductionByref() ||
op.getReductionSyms())
result = todo("reduction");
@@ -370,10 +370,29 @@ static LogicalResult checkImplementationStatus(Operation &op) {
op.getReductionMod().value() != omp::ReductionModifier::defaultmod)
result = todo("reduction with modifier");
};
- auto checkTaskReduction = [&todo](auto op, LogicalResult &result) {
- if (!op.getTaskReductionVars().empty() || op.getTaskReductionByref() ||
- op.getTaskReductionSyms())
- result = todo("task_reduction");
+ auto checkTaskReductionByref = [&todo](auto op, LogicalResult &result) {
+ if (auto byrefAttr = op.getTaskReductionByref())
+ for (bool isByRef : *byrefAttr)
+ if (isByRef) {
+ result = todo("task_reduction with byref modifier");
+ return;
+ }
+ };
+ auto checkReductionByref = [&todo](auto op, LogicalResult &result) {
+ if (auto byrefAttr = op.getReductionByref())
+ for (bool isByRef : *byrefAttr)
+ if (isByRef) {
+ result = todo("reduction with byref modifier");
+ return;
+ }
+ };
+ auto checkInReductionByref = [&todo](auto op, LogicalResult &result) {
+ if (auto byrefAttr = op.getInReductionByref())
+ for (bool isByRef : *byrefAttr)
+ if (isByRef) {
+ result = todo("in_reduction with byref modifier");
+ return;
+ }
};
auto checkNumTeams = [&todo](auto op, LogicalResult &result) {
if (op.hasNumTeamsMultiDim())
@@ -426,7 +445,7 @@ static LogicalResult checkImplementationStatus(Operation &op) {
})
.Case([&](omp::TaskgroupOp op) {
checkAllocate(op, result);
- checkTaskReduction(op, result);
+ checkTaskReductionByref(op, result);
})
.Case([&](omp::TaskwaitOp op) {
checkDepend(op, result);
@@ -434,8 +453,9 @@ static LogicalResult checkImplementationStatus(Operation &op) {
})
.Case([&](omp::TaskloopContextOp op) {
checkAllocate(op, result);
- checkInReduction(op, result);
+ checkInReductionByref(op, result);
checkReduction(op, result);
+ checkReductionByref(op, result);
})
.Case([&](omp::WsloopOp op) {
checkAllocate(op, result);
@@ -3324,6 +3344,15 @@ computeTaskloopBounds(omp::LoopNestOp loopOp, llvm::IRBuilderBase &builder,
return llvm::Error::success();
}
+// Forward declaration: defined alongside the taskgroup task_reduction
+// lowering further down in this file. Shared between omp.taskgroup and
+// omp.taskloop.context translation.
+static llvm::Value *emitTaskReductionInitCall(
+ ArrayRef<omp::DeclareReductionOp> redDecls,
+ ArrayRef<llvm::Value *> origPtrs, StringRef helperNamePrefix,
+ llvm::IRBuilderBase &builder, llvm::OpenMPIRBuilder::InsertPointTy allocaIP,
+ LLVM::ModuleTranslation &moduleTranslation);
+
// Converts an OpenMP taskloop construct into LLVM IR using OpenMPIRBuilder.
static LogicalResult
convertOmpTaskloopContextOp(omp::TaskloopContextOp contextOp,
@@ -3414,6 +3443,90 @@ convertOmpTaskloopContextOp(omp::TaskloopContextOp contextOp,
// Set up inserttion point for call to createTaskloop()
builder.SetInsertPoint(taskloopStartBlock);
+ // Resolve and validate reduction / in_reduction declarations. Only the
+ // non-byref, single-init-arg, no-cleanup form is supported in this first
+ // cut; richer shapes have been rejected by checkImplementationStatus
+ // (byref) or are rejected here.
+ auto resolveRedDecls =
+ [&](std::optional<ArrayAttr> syms, StringRef clauseName,
+ SmallVectorImpl<omp::DeclareReductionOp> &out) -> LogicalResult {
+ if (!syms)
+ return success();
+ out.reserve(syms->size());
+ for (auto sym : syms->getAsRange<SymbolRefAttr>()) {
+ auto decl = SymbolTable::lookupNearestSymbolFrom<omp::DeclareReductionOp>(
+ contextOp, sym);
+ if (!decl)
+ return contextOp.emitError()
+ << "failed to resolve " << clauseName
+ << " declare_reduction symbol " << sym.getRootReference()
+ << " in omp.taskloop.context";
+ if (decl.getInitializerRegion().front().getNumArguments() != 1)
+ return contextOp.emitError()
+ << "not yet implemented: " << clauseName
+ << " with two-argument initializer in omp.taskloop.context";
+ if (!decl.getCleanupRegion().empty())
+ return contextOp.emitError()
+ << "not yet implemented: " << clauseName
+ << " with cleanup region in omp.taskloop.context";
+ if (decl.getReductionRegion().empty())
+ return contextOp.emitError()
+ << clauseName
+ << " declare_reduction is missing a combiner region";
+ out.push_back(decl);
+ }
+ return success();
+ };
+
+ SmallVector<omp::DeclareReductionOp> redDecls;
+ if (failed(
+ resolveRedDecls(contextOp.getReductionSyms(), "reduction", redDecls)))
+ return failure();
+ SmallVector<omp::DeclareReductionOp> inRedDecls;
+ if (failed(resolveRedDecls(contextOp.getInReductionSyms(), "in_reduction",
+ inRedDecls)))
+ return failure();
+
+ // The op verifier rejects nogroup + reduction, so no check is needed here.
+
+ SmallVector<llvm::Value *> redOrigPtrs;
+ redOrigPtrs.reserve(redDecls.size());
+ for (Value v : contextOp.getReductionVars())
+ redOrigPtrs.push_back(moduleTranslation.lookupValue(v));
+ SmallVector<llvm::Value *> inRedOrigPtrs;
+ inRedOrigPtrs.reserve(inRedDecls.size());
+ for (Value v : contextOp.getInReductionVars())
+ inRedOrigPtrs.push_back(moduleTranslation.lookupValue(v));
+
+ llvm::OpenMPIRBuilder &ompBuilderRef = *moduleTranslation.getOpenMPBuilder();
+ llvm::Module *llvmModuleForRed = moduleTranslation.getLLVMModule();
+
+ // If we have task_reduction items, we must emit our own implicit
+ // __kmpc_taskgroup so that the descriptor returned by __kmpc_taskred_init
+ // is associated with that taskgroup. We then force NoGroup=true so that
+ // OpenMPIRBuilder::createTaskloop does not emit a second taskgroup.
+ bool implicitTaskgroup = !redDecls.empty();
+ llvm::Value *redDesc = nullptr;
+ if (implicitTaskgroup) {
+ uint32_t srcLocSize;
+ llvm::Constant *srcLocStr =
+ ompBuilderRef.getOrCreateDefaultSrcLocStr(srcLocSize);
+ llvm::Value *ident = ompBuilderRef.getOrCreateIdent(srcLocStr, srcLocSize);
+ llvm::Function *gtidFn = ompBuilderRef.getOrCreateRuntimeFunctionPtr(
+ llvm::omp::OMPRTL___kmpc_global_thread_num);
+ llvm::Value *outerGtid =
+ builder.CreateCall(gtidFn, {ident}, "omp_global_thread_num");
+ llvm::FunctionCallee taskgroupFn = ompBuilderRef.getOrCreateRuntimeFunction(
+ *llvmModuleForRed, llvm::omp::OMPRTL___kmpc_taskgroup);
+ builder.CreateCall(taskgroupFn, {ident, outerGtid});
+
+ redDesc = emitTaskReductionInitCall(redDecls, redOrigPtrs,
+ "__omp_taskloop_taskred_", builder,
+ allocaIP, moduleTranslation);
+ if (!redDesc)
+ return failure();
+ }
+
auto loopOp = cast<omp::LoopNestOp>(loopWrapperOp.getWrappedLoop());
llvm::Value *lbVal = nullptr;
llvm::Value *ubVal = nullptr;
@@ -3488,6 +3601,49 @@ convertOmpTaskloopContextOp(omp::TaskloopContextOp contextOp,
moduleTranslation.mapValue(blockArg, llvmPrivateVar);
}
+ // Map reduction and in_reduction block arguments to the per-task private
+ // storage returned by __kmpc_task_reduction_get_th_data. This call must
+ // be emitted inside the to-be-outlined task body so that it returns the
+ // *executing* thread's gtid (not the encountering thread's). The
+ // taskgroup descriptor `redDesc` is computed in the outer scope and is
+ // auto-captured into the task shareds aggregate by CodeExtractor during
+ // OpenMPIRBuilder::finalize. For in_reduction the descriptor is NULL:
+ // the runtime walks up enclosing taskgroups to find the matching
+ // task_reduction registration for `origPtr`.
+ if (!redDecls.empty() || !inRedDecls.empty()) {
+ auto iface =
+ cast<omp::BlockArgOpenMPOpInterface>(contextOp.getOperation());
+ llvm::OpenMPIRBuilder &ompB = *moduleTranslation.getOpenMPBuilder();
+ llvm::Module *m = moduleTranslation.getLLVMModule();
+ llvm::LLVMContext &llvmCtx = m->getContext();
+ uint32_t srcLocSize;
+ llvm::Constant *srcLocStr = ompB.getOrCreateDefaultSrcLocStr(srcLocSize);
+ llvm::Value *bodyIdent = ompB.getOrCreateIdent(srcLocStr, srcLocSize);
+ llvm::Function *gtidFn = ompB.getOrCreateRuntimeFunctionPtr(
+ llvm::omp::OMPRTL___kmpc_global_thread_num);
+ llvm::Value *bodyGtid =
+ builder.CreateCall(gtidFn, {bodyIdent}, "omp_global_thread_num");
+ llvm::FunctionCallee getThData = ompB.getOrCreateRuntimeFunction(
+ *m, llvm::omp::OMPRTL___kmpc_task_reduction_get_th_data);
+ llvm::Type *ptrTy = llvm::PointerType::getUnqual(llvmCtx);
+
+ ArrayRef<BlockArgument> redBlockArgs = iface.getReductionBlockArgs();
+ for (auto [blockArg, origPtr] :
+ llvm::zip_equal(redBlockArgs, redOrigPtrs)) {
+ llvm::Value *priv = builder.CreateCall(
+ getThData, {bodyGtid, redDesc, origPtr}, "omp.taskred.priv");
+ moduleTranslation.mapValue(blockArg, priv);
+ }
+ ArrayRef<BlockArgument> inRedBlockArgs = iface.getInReductionBlockArgs();
+ llvm::Value *nullDesc = llvm::ConstantPointerNull::get(ptrTy);
+ for (auto [blockArg, origPtr] :
+ llvm::zip_equal(inRedBlockArgs, inRedOrigPtrs)) {
+ llvm::Value *priv = builder.CreateCall(
+ getThData, {bodyGtid, nullDesc, origPtr}, "omp.inred.priv");
+ moduleTranslation.mapValue(blockArg, priv);
+ }
+ }
+
// Lower the contents of the taskloop context region: this is the body of
// the generated task, not the loop.
auto continuationBlockOrError = convertOmpOpRegions(
@@ -3623,12 +3779,12 @@ convertOmpTaskloopContextOp(omp::TaskloopContextOp contextOp,
llvm::omp::Directive::OMPD_taskgroup);
llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);
+ bool effectiveNoGroup = contextOp.getNogroup() || implicitTaskgroup;
llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =
moduleTranslation.getOpenMPBuilder()->createTaskloop(
ompLoc, allocaIP, deallocBlocks, bodyCB, loopInfo, lbVal, ubVal,
- stepVal, contextOp.getUntied(), ifCond, grainsize,
- contextOp.getNogroup(), sched,
- moduleTranslation.lookupValue(contextOp.getFinal()),
+ stepVal, contextOp.getUntied(), ifCond, grainsize, effectiveNoGroup,
+ sched, moduleTranslation.lookupValue(contextOp.getFinal()),
contextOp.getMergeable(),
moduleTranslation.lookupValue(contextOp.getPriority()),
loopOp.getCollapseNumLoops(), taskDupOrNull,
@@ -3640,9 +3796,214 @@ convertOmpTaskloopContextOp(omp::TaskloopContextOp contextOp,
popCancelFinalizationCB(cancelTerminators, ompBuilder, afterIP.get());
builder.restoreIP(*afterIP);
+
+ // Close the implicit taskgroup we opened for task_reduction. The end call
+ // must execute on the encountering thread, so use the outer-scope gtid.
+ if (implicitTaskgroup) {
+ uint32_t srcLocSize;
+ llvm::Constant *srcLocStr =
+ ompBuilder.getOrCreateDefaultSrcLocStr(srcLocSize);
+ llvm::Value *ident = ompBuilder.getOrCreateIdent(srcLocStr, srcLocSize);
+ llvm::Function *gtidFn = ompBuilder.getOrCreateRuntimeFunctionPtr(
+ llvm::omp::OMPRTL___kmpc_global_thread_num);
+ llvm::Value *outerGtid =
+ builder.CreateCall(gtidFn, {ident}, "omp_global_thread_num");
+ llvm::FunctionCallee endTgFn = ompBuilder.getOrCreateRuntimeFunction(
+ *moduleTranslation.getLLVMModule(),
+ llvm::omp::OMPRTL___kmpc_end_taskgroup);
+ builder.CreateCall(endTgFn, {ident, outerGtid});
+ }
return success();
}
+/// Build an outlined init helper for a task_reduction declare_reduction op.
+/// Signature: void(ptr %priv, ptr %orig). For non-byref reductions, the init
+/// region's mold argument is mapped to the value loaded from %orig, and the
+/// yielded scalar is stored into %priv.
+static llvm::Function *
+emitTaskReductionInitFn(omp::DeclareReductionOp decl, StringRef baseName,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
+ llvm::LLVMContext &ctx = llvmModule->getContext();
+ llvm::Type *voidTy = llvm::Type::getVoidTy(ctx);
+ llvm::Type *ptrTy = llvm::PointerType::getUnqual(ctx);
+ llvm::FunctionType *fty =
+ llvm::FunctionType::get(voidTy, {ptrTy, ptrTy}, false);
+ llvm::Function *fn =
+ llvm::Function::Create(fty, llvm::GlobalValue::InternalLinkage,
+ baseName + ".red.init", llvmModule);
+ fn->setDoesNotRecurse();
+ fn->getArg(0)->setName("priv");
+ fn->getArg(1)->setName("orig");
+
+ llvm::BasicBlock *entry = llvm::BasicBlock::Create(ctx, "entry", fn);
+ llvm::IRBuilder<> b(entry);
+
+ llvm::Type *elemTy = moduleTranslation.convertType(decl.getType());
+ llvm::Value *origVal = b.CreateLoad(elemTy, fn->getArg(1), "omp.orig");
+ moduleTranslation.mapValue(decl.getInitializerMoldArg(), origVal);
+ SmallVector<llvm::Value *, 1> phis;
+ if (failed(inlineConvertOmpRegions(decl.getInitializerRegion(),
+ "omp.taskred.init", b, moduleTranslation,
+ &phis))) {
+ fn->eraseFromParent();
+ return nullptr;
+ }
+ assert(phis.size() == 1 &&
+ "expected one value yielded from reduction initializer");
+ b.CreateStore(phis[0], fn->getArg(0));
+ b.CreateRetVoid();
+
+ moduleTranslation.forgetMapping(decl.getInitializerRegion());
+ return fn;
+}
+
+/// Build an outlined combiner helper for a task_reduction declare_reduction op.
+/// Signature: void(ptr %lhs, ptr %rhs). For non-byref reductions, the values
+/// at *%lhs and *%rhs are loaded, fed into the combiner region, and the
+/// yielded scalar is stored back into *%lhs.
+static llvm::Function *
+emitTaskReductionCombFn(omp::DeclareReductionOp decl, StringRef baseName,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
+ llvm::LLVMContext &ctx = llvmModule->getContext();
+ llvm::Type *voidTy = llvm::Type::getVoidTy(ctx);
+ llvm::Type *ptrTy = llvm::PointerType::getUnqual(ctx);
+ llvm::FunctionType *fty =
+ llvm::FunctionType::get(voidTy, {ptrTy, ptrTy}, false);
+ llvm::Function *fn =
+ llvm::Function::Create(fty, llvm::GlobalValue::InternalLinkage,
+ baseName + ".red.comb", llvmModule);
+ fn->setDoesNotRecurse();
+ fn->getArg(0)->setName("lhs");
+ fn->getArg(1)->setName("rhs");
+
+ llvm::BasicBlock *entry = llvm::BasicBlock::Create(ctx, "entry", fn);
+ llvm::IRBuilder<> b(entry);
+
+ llvm::Type *elemTy = moduleTranslation.convertType(decl.getType());
+ Block &combBlock = decl.getReductionRegion().front();
+ assert(combBlock.getNumArguments() == 2 &&
+ "expected two arguments in declare_reduction combiner");
+ llvm::Value *lhsVal = b.CreateLoad(elemTy, fn->getArg(0), "omp.lhs");
+ llvm::Value *rhsVal = b.CreateLoad(elemTy, fn->getArg(1), "omp.rhs");
+ moduleTranslation.mapValue(combBlock.getArgument(0), lhsVal);
+ moduleTranslation.mapValue(combBlock.getArgument(1), rhsVal);
+
+ SmallVector<llvm::Value *, 1> phis;
+ if (failed(inlineConvertOmpRegions(decl.getReductionRegion(),
+ "omp.taskred.comb", b, moduleTranslation,
+ &phis))) {
+ fn->eraseFromParent();
+ return nullptr;
+ }
+ assert(phis.size() == 1 &&
+ "expected one value yielded from reduction combiner");
+ b.CreateStore(phis[0], fn->getArg(0));
+ b.CreateRetVoid();
+
+ moduleTranslation.forgetMapping(decl.getReductionRegion());
+ return fn;
+}
+
+/// Emit the per-taskgroup task_reduction descriptor array and the
+/// `__kmpc_taskred_init` runtime call. \p origPtrs holds the LLVM values for
+/// the original (shared) variables, one per declaration in \p redDecls.
+/// `builder` must be set to the point at which the descriptor stores and the
+/// init call should be emitted; the descriptor array itself is allocated at
+/// \p allocaIP. \p helperNamePrefix is used to disambiguate the generated
+/// init/combiner helper symbol names between taskgroup and taskloop callers.
+///
+/// Returns the `ptr` value produced by `__kmpc_taskred_init` (the taskgroup
+/// reduction handle), or null on failure.
+///
+/// Only the non-byref form is handled here. Byref reductions have already
+/// been rejected by `checkImplementationStatus`.
+static llvm::Value *emitTaskReductionInitCall(
+ ArrayRef<omp::DeclareReductionOp> redDecls,
+ ArrayRef<llvm::Value *> origPtrs, StringRef helperNamePrefix,
+ llvm::IRBuilderBase &builder, llvm::OpenMPIRBuilder::InsertPointTy allocaIP,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ assert(redDecls.size() == origPtrs.size() &&
+ "expected one orig pointer per reduction decl");
+ llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
+ llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
+ llvm::LLVMContext &ctx = llvmModule->getContext();
+ const llvm::DataLayout &dl = llvmModule->getDataLayout();
+
+ llvm::Type *ptrTy = llvm::PointerType::getUnqual(ctx);
+ llvm::Type *i32Ty = llvm::Type::getInt32Ty(ctx);
+ llvm::Type *sizeTy =
+ llvm::Type::getIntNTy(ctx, dl.getPointerSizeInBits(/*AddrSpace=*/0));
+
+ // Identified `kmp_taskred_input_t` struct, matching the layout used by
+ // Clang's CGOpenMPRuntime::emitTaskReductionInit.
+ llvm::StructType *redInputTy =
+ llvm::StructType::getTypeByName(ctx, "kmp_taskred_input_t");
+ if (!redInputTy)
+ redInputTy = llvm::StructType::create(
+ ctx, {ptrTy, ptrTy, sizeTy, ptrTy, ptrTy, ptrTy, i32Ty},
+ "kmp_taskred_input_t");
+
+ unsigned n = redDecls.size();
+ llvm::ArrayType *arrTy = llvm::ArrayType::get(redInputTy, n);
+
+ // Allocate the descriptor array in the enclosing function's alloca block.
+ llvm::AllocaInst *arrAlloca;
+ {
+ llvm::IRBuilderBase::InsertPointGuard guard(builder);
+ builder.restoreIP(allocaIP);
+ arrAlloca =
+ builder.CreateAlloca(arrTy, /*ArraySize=*/nullptr, ".taskred.input");
+ }
+
+ // Fill each descriptor entry at the current builder insertion point.
+ llvm::Value *zero = builder.getInt32(0);
+ for (unsigned i = 0; i < n; ++i) {
+ omp::DeclareReductionOp decl = redDecls[i];
+ llvm::Value *orig = origPtrs[i];
+ llvm::Type *elemTy = moduleTranslation.convertType(decl.getType());
+ uint64_t size = dl.getTypeAllocSize(elemTy).getFixedValue();
+
+ std::string baseName =
+ (llvm::Twine(helperNamePrefix) + decl.getSymName()).str();
+ llvm::Function *initFn =
+ emitTaskReductionInitFn(decl, baseName, moduleTranslation);
+ llvm::Function *combFn =
+ emitTaskReductionCombFn(decl, baseName, moduleTranslation);
+ if (!initFn || !combFn)
+ return nullptr;
+ llvm::Value *elemPtr = builder.CreateInBoundsGEP(
+ arrTy, arrAlloca, {zero, builder.getInt32(i)}, ".taskred.elem");
+ auto storeField = [&](unsigned fieldIdx, llvm::Value *val) {
+ llvm::Value *fieldPtr =
+ builder.CreateStructGEP(redInputTy, elemPtr, fieldIdx);
+ builder.CreateStore(val, fieldPtr);
+ };
+ storeField(0, orig); // reduce_shar
+ storeField(1, orig);...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/199670
More information about the Mlir-commits
mailing list