122#include "llvm/ADT/ArrayRef.h"
123#include "llvm/ADT/DenseMap.h"
124#include "llvm/ADT/STLExtras.h"
125#include "llvm/ADT/StringExtras.h"
126#include "llvm/ADT/Twine.h"
127#include "llvm/Support/Debug.h"
134#define GEN_PASS_DEF_ACCCGTOGPU
135#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
139#define DEBUG_TYPE "acc-cg-to-gpu"
145enum class PrivateMemScope { Thread, Worker, Gang,
None };
148static std::string getDeviceRemarkQualifier(DeviceType deviceType) {
149 switch (deviceType) {
150 case DeviceType::None:
151 case DeviceType::Star:
152 case DeviceType::Default:
156 llvm::StringRef deviceName = stringifyDeviceType(deviceType);
157 name.reserve(deviceName.size());
158 for (
char c : deviceName)
159 name.push_back(llvm::toUpper(c));
160 return name +
" GPU";
167 FunctionOpInterface funcOp = op->
getParentOfType<FunctionOpInterface>();
172static GPUParallelDimAttr
173getAccRoutineParDim(RoutineOp routineOp,
MLIRContext *ctx,
175 if (routineOp.getGangDimValue() ||
176 routineOp.getGangDimValue(DeviceType::Nvidia)) {
177 int64_t gangDimValue = routineOp.getGangDimValue(DeviceType::Nvidia)
178 ? *routineOp.getGangDimValue(DeviceType::Nvidia)
179 : *routineOp.getGangDimValue();
181 return policy.
gangDim(ctx, gangLevel);
183 if (routineOp.hasGang() || routineOp.hasGang(DeviceType::Nvidia))
184 return policy.
gangDim(ctx, ParLevel::gang_dim1);
185 if (routineOp.hasWorker() || routineOp.hasWorker(DeviceType::Nvidia))
187 if (routineOp.hasVector() || routineOp.hasVector(DeviceType::Nvidia))
189 return policy.
seqDim(ctx);
193static RoutineOp getRoutineOpForAccRoutineFunction(FunctionOpInterface funcOp,
196 SpecializedRoutineAttr attr = funcOp->getAttrOfType<SpecializedRoutineAttr>(
198 return symTab.
lookup<RoutineOp>(attr.getRoutine().getLeafReference());
200 RoutineInfoAttr routineInfo =
202 if (!routineInfo || routineInfo.getAccRoutines().empty())
204 return symTab.
lookup<RoutineOp>(
205 routineInfo.getAccRoutines().front().getLeafReference());
209static GPUParallelDimAttr
210getSpecializedRoutineDim(FunctionOpInterface funcOp,
212 SpecializedRoutineAttr specAttr =
213 funcOp->getAttrOfType<SpecializedRoutineAttr>(
215 assert(specAttr &&
"expected specialized routine attribute");
216 return policy.
map(funcOp->getContext(), specAttr.getLevel().getValue());
220static GPUParallelDimAttr
221getAccRoutineCallParDim(CallOpInterface callOp,
223 std::optional<CallInterfaceCallable> callee = callOp.getCallableForCallee();
226 SymbolRefAttr calleeSymbolRef = dyn_cast<SymbolRefAttr>(*callee);
227 if (!calleeSymbolRef)
229 ModuleOp moduleOp = callOp->getParentOfType<ModuleOp>();
234 FunctionOpInterface funcOp =
235 symTab.
lookup<FunctionOpInterface>(calleeSymbolRef.getLeafReference());
240 return getSpecializedRoutineDim(funcOp, policy);
241 if (RoutineOp routineOp = getRoutineOpForAccRoutineFunction(funcOp, symTab))
242 return getAccRoutineParDim(routineOp, funcOp.getContext(), policy);
249 ComputeRegionOp computeRegion = op->
getParentOfType<ComputeRegionOp>();
250 assert(computeRegion &&
"missing enclosing acc.compute_region");
253 bool isInnermostParallelParent =
true;
255 bool hasNonSeqParDim =
false;
256 if (GPUParallelDimsAttr parDimsAttr =
getParDimsAttr(parentLoop)) {
257 for (GPUParallelDimAttr parDim : parDimsAttr.getArray()) {
260 hasNonSeqParDim =
true;
271 (hasNonSeqParDim || isInnermostParallelParent))
272 for (GPUParallelDimAttr parDim : computeRegion.getLaunchParDims())
274 isInnermostParallelParent =
false;
275 parentLoop = parentLoop->getParentOfType<scf::ParallelOp>();
278 if (GPUParallelDimsAttr parDimsAttr =
getParDimsAttr(computeRegion))
279 for (GPUParallelDimAttr parDim : parDimsAttr.getArray())
285static Value stripIndexCastsFromValue(
Value x) {
289 while (arith::IndexCastOp castOp = dyn_cast<arith::IndexCastOp>(op)) {
298static FailureOr<int64_t> extractIntConst(
Value x,
299 bool stripIndexCasts =
false) {
301 x = stripIndexCastsFromValue(x);
305 assert(constOp.getType().getIntOrFloatBitWidth() <= 64);
306 return constOp.value();
309 return constOp.value();
316 x = stripIndexCastsFromValue(x);
317 FailureOr<int64_t> conX = extractIntConst(x);
324static bool getPassThroughResults(
Operation *userOp,
Value trackedOperand,
326 if (ViewLikeOpInterface viewLikeOp = dyn_cast<ViewLikeOpInterface>(userOp)) {
327 if (viewLikeOp.getViewSource() == trackedOperand) {
328 passThroughResults.push_back(viewLikeOp.getViewDest());
337 if (acc::PartialEntityAccessOpInterface partialAccess =
338 dyn_cast<acc::PartialEntityAccessOpInterface>(userOp)) {
339 if (partialAccess.getBaseEntity() == trackedOperand) {
351 if (ViewLikeOpInterface viewLike = dyn_cast<ViewLikeOpInterface>(op)) {
352 if (isa<MemRefType>(viewLike.getViewSource().getType()) ||
353 isa<MemRefType>(viewLike.getViewDest().getType())) {
354 v = viewLike.getViewSource();
366 if (value.
getType() == resultType)
368 if (PointerLikeType ptrLike = dyn_cast<PointerLikeType>(value.
getType())) {
369 if (
Value casted = ptrLike.genCast(builder, loc, value, resultType))
372 if (PointerLikeType ptrLike = dyn_cast<PointerLikeType>(resultType)) {
373 if (
Value casted = ptrLike.genCast(builder, loc, value, resultType))
376 emitError(loc) <<
"unsupported pointer-like type cast from "
377 << value.
getType() <<
" to " << resultType;
383static acc::PrivateLocalOp getPrivateLocalForMemref(
Value memref);
386static GPUParallelDimsAttr
387getPrivateParDims(acc::PrivateLocalOp privateLocal,
388 acc::ComputeRegionOp computeRegion);
392static bool storageHasThreadX(acc::PrivateLocalOp privateLocal,
393 acc::ComputeRegionOp computeRegion) {
394 GPUParallelDimsAttr dims = getPrivateParDims(privateLocal, computeRegion);
395 return !dims || llvm::any_of(dims.getArray(), [](GPUParallelDimAttr d) {
396 return d.isThreadX();
409 if (GPUParallelDimsAttr parDimsAttr = privatize.getParDimsAttr())
410 return llvm::any_of(parDimsAttr.getArray(),
411 [](GPUParallelDimAttr d) { return d.isThreadX(); });
417 gpu::BarrierOp::create(builder, loc);
422 gpu::BarrierOp::create(builder, loc,
ArrayAttr{},
424 gpu::BarrierScope::Subgroup);
428class ACCCGToGPULowering {
430 explicit ACCCGToGPULowering(acc::ComputeRegionOp computeRegion,
433 const ACCCGToGPUOptions &
options)
434 : rewriter(rewriter), computeRegion(computeRegion),
437 options.maxWorkgroupSharedMemory,
443 gpu::LaunchOp getLaunch()
const {
return launch; }
445 bool hasFailed =
false;
446 bool insideAccumulateGridStride =
false;
447 Value reductionSharedBuf;
456 void processParallelOp(scf::ParallelOp parallelOp);
458 template <
typename LoopOp>
459 void processSeqLoop(LoopOp loopOp);
461 void processPredicateRegion(acc::PredicateRegionOp interOp);
464 processPrivateLocal(acc::PrivateLocalOp privateLocal,
465 std::optional<int64_t> sharedMemCopies = std::nullopt);
467 Value processPrivatize(acc::PrivatizeOp privatize);
469 void processExecuteRegion(scf::ExecuteRegionOp op);
471 void processAccumulateOp(acc::ReductionAccumulateOp op);
473 void processAccumulateArrayOp(acc::ReductionAccumulateArrayOp op);
475 void processReductionOp(acc::ReductionInitOp op);
477 void processReductionCombineOp(acc::ReductionCombineOp op);
479 void processCombineRegionOp(acc::ReductionCombineRegionOp op);
483 void processGenericOpWithRegions(
Operation *op);
490 arith::AtomicRMWKind kind);
493 FailureOr<arith::AtomicRMWKind> getReductionKind(acc::ReductionOperator redOp,
498 std::pair<SmallVector<mlir::acc::GPUParallelDimAttr>,
509 std::optional<int64_t>
510 isEligibleForSharedMemory(acc::PrivateLocalOp privateLocal,
514 bool tryAllocateSharedMemory(
int64_t bytes);
520 bool canUseStackAlloca(MemRefType baseTy,
Location loc,
521 int64_t maxThreadPrivateStack)
const;
524 void createBarrier(
Location loc, mlir::acc::GPUParallelDimsAttr parDimsAttr);
530 void createPerRowBarrier(
Location loc);
534 void createBarrierAfterSeqLoop(
Operation *loopOp);
537 void flushDeferredBarriersBefore(
Operation *beforeOp);
540 bool mayWriteSharedMemory(
Operation *loopOp);
543 PrivateMemScope getPrivateMemScope(acc::PrivatizeOp privatizeOp);
546 PrivateMemScope getPrivateScopeForMemref(
Value memref);
549 acc::PrivatizeOp getPrivatizeForMemref(
Value memref);
553 PrivateMemScope needsPreStoreReuseBarrier(acc::PredicateRegionOp interOp);
557 arith::AtomicRMWKind kind,
558 mlir::acc::GPUParallelDimsAttr parDimsAttr,
560 bool isPerThreadPrivateTarget =
false);
563 void postprocessAccumulateOp(acc::ReductionAccumulateOp op);
566 void postprocessLoopReduction(scf::ParallelOp parLoop);
574 ids[gpu::Processor::BlockX] = gpu::BlockIdOp::create(
575 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::x);
576 ids[gpu::Processor::BlockY] = gpu::BlockIdOp::create(
577 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::y);
578 ids[gpu::Processor::BlockZ] = gpu::BlockIdOp::create(
579 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::z);
580 ids[gpu::Processor::ThreadX] = gpu::ThreadIdOp::create(
581 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::x);
582 ids[gpu::Processor::ThreadY] = gpu::ThreadIdOp::create(
583 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::y);
584 ids[gpu::Processor::ThreadZ] = gpu::ThreadIdOp::create(
585 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::z);
586 dims[gpu::Processor::BlockX] = gpu::GridDimOp::create(
587 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::x);
588 dims[gpu::Processor::BlockY] = gpu::GridDimOp::create(
589 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::y);
590 dims[gpu::Processor::BlockZ] = gpu::GridDimOp::create(
591 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::z);
592 dims[gpu::Processor::ThreadX] = gpu::BlockDimOp::create(
593 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::x);
594 dims[gpu::Processor::ThreadY] = gpu::BlockDimOp::create(
595 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::y);
596 dims[gpu::Processor::ThreadZ] = gpu::BlockDimOp::create(
597 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::z);
603 if (std::optional<BlockArgument> blockArg =
604 computeRegion.getBlockArg(outside)) {
607 return computeRegion.appendInputArg(outside);
611 void preparePrivatizeExtentInsOperands() {
612 computeRegion.walk([&](acc::PrivateLocalOp privateLocal) {
613 acc::PrivatizeOp privatizeOp =
615 if (privatizeOp->getParentOfType<acc::ComputeRegionOp>() == computeRegion)
617 for (
Value extent : privatizeOp.getDynamicSizes())
618 getOrAppendInsBlockArg(extent);
624 resolvePrivateLocalDynamicExtents(acc::PrivateLocalOp privateLocal) {
625 acc::PrivatizeOp privatizeOp =
getPrivatizeOp(privateLocal, computeRegion);
627 for (
Value extent : privatizeOp.getDynamicSizes()) {
628 if (std::optional<BlockArgument> blockArg =
629 computeRegion.getBlockArg(extent)) {
630 extents.push_back(mapping.lookupOrDefault(*blockArg));
633 extents.push_back(mapping.lookupOrDefault(extent));
639 acc::ComputeRegionOp computeRegion;
642 const ACCCGToGPUOptions &
options;
643 gpu::LaunchOp launch;
649 bool hasThreadYReduction =
false;
651 bool hasThreadLevelRoutineCall =
false;
653 bool hasThreadYBarrier =
false;
658 int64_t staticBlockDimX = 1024;
665 return gpu::ThreadIdOp::create(rewriter, loc, rewriter.
getIndexType(), dim);
669 return gpu::BlockDimOp::create(rewriter, loc, rewriter.
getIndexType(), dim);
673 Value getGPUThreadIdFor(gpu::Processor proc) {
678 Value getGPUSizeFor(gpu::Processor proc) {
679 return getGPUSize(proc, getLaunch(), dimensionMap);
684 Type elementType)
const {
685 ModuleOp module = computeRegion->getParentOfType<ModuleOp>();
686 if (std::optional<acc::TypeSizeAndAlignment> sizeAndAlignment =
688 return sizeAndAlignment->first.getFixedValue();
691 llvm::raw_string_ostream os(msg);
692 os <<
"element size computation for unsupported type: " << elementType;
697bool ACCCGToGPULowering::canUseStackAlloca(
698 MemRefType baseTy,
Location loc,
int64_t maxThreadPrivateStack)
const {
699 for (
int64_t dim : baseTy.getShape())
700 if (dim == ShapedType::kDynamic)
702 int64_t elementSize = getElementSizeInBytes(loc, baseTy.getElementType());
704 for (
int64_t dim : baseTy.getShape()) {
705 if (numElements > maxThreadPrivateStack / std::max<int64_t>(dim, 1))
709 return elementSize * numElements < maxThreadPrivateStack;
717static bool reductionHasBlockContext(acc::ReductionAccumulateArrayOp accArr) {
718 auto hasBlock = [](mlir::acc::GPUParallelDimsAttr parDims) {
719 return parDims && llvm::any_of(parDims.getArray(),
720 [](
auto pd) { return pd.isAnyBlock(); });
722 if (hasBlock(accArr.getParDimsAttr()))
724 for (scf::ParallelOp loop = accArr->getParentOfType<scf::ParallelOp>(); loop;
725 loop = loop->getParentOfType<scf::ParallelOp>()) {
735static acc::ReductionAccumulateArrayOp perThreadArrayReductionAccum(
Value v) {
738 while (!worklist.empty()) {
739 Value cur = worklist.pop_back_val();
740 if (!seen.insert(cur).second)
743 if (acc::ReductionAccumulateArrayOp accArr =
744 dyn_cast<acc::ReductionAccumulateArrayOp>(user)) {
745 bool hasThread =
false;
746 for (
auto pd : accArr.getParDims().getArray())
747 hasThread |= pd.isAnyThread();
748 if (hasThread && reductionHasBlockContext(accArr))
753 if (getPassThroughResults(user, cur, through))
754 worklist.append(through.begin(), through.end());
755 else if (isa<ViewLikeOpInterface>(user))
756 worklist.append(user->result_begin(), user->result_end());
767 arith::AtomicRMWKind kind) {
768 assert(baseTy.getRank() > 0 && baseTy.hasStaticShape() &&
769 "per-thread array reduction accumulator must be static ranked");
775 auto buildLoopNest = [&](
auto &&self,
unsigned dim) ->
void {
776 if (dim == baseTy.getRank()) {
777 memref::StoreOp::create(
b, loc, ident, alloca,
indices);
782 auto forOp = scf::ForOp::create(
b, loc, lb,
ub, step);
784 b.setInsertionPoint(forOp.getBody()->getTerminator());
785 indices.push_back(forOp.getInductionVar());
789 buildLoopNest(buildLoopNest, 0);
792std::optional<int64_t>
793ACCCGToGPULowering::isEligibleForSharedMemory(acc::PrivateLocalOp privateLocal,
798 if (perThreadArrayReductionAccum(privateLocal.getResult()) &&
799 storageHasThreadX(privateLocal, computeRegion))
801 ModuleOp module = computeRegion->getParentOfType<ModuleOp>();
803 privateLocal, computeRegion, module, defaultPolicy, &accSupport);
804 if (failed(isCandidate)) {
808 if (!isCandidate.value())
810 std::optional<int64_t> upperBound =
812 module, defaultPolicy);
813 assert(upperBound &&
"candidate private_local must have an upper bound");
815 getElementSizeInBytes(privateLocal.getLoc(), baseTy.getElementType());
817 for (
int64_t dim : baseTy.getShape())
819 return *upperBound / (elementSize * numElements);
822bool ACCCGToGPULowering::tryAllocateSharedMemory(
int64_t bytes) {
826FailureOr<arith::AtomicRMWKind>
827ACCCGToGPULowering::getReductionKind(acc::ReductionOperator redOp,
Type type,
829 if (std::optional<arith::AtomicRMWKind> kind =
834 llvm::raw_string_ostream os(msg);
835 os <<
"reduction operator (" << redOp <<
") for type " << type;
840LogicalResult ACCCGToGPULowering::rewrite() {
845 computeRegion->walk([&](acc::ReductionAccumulateOp op) ->
WalkResult {
846 for (
auto parDim : op.getParDimsAttr().getArray()) {
847 if (parDim.isThreadY()) {
848 hasThreadYReduction =
true;
859 computeRegion->walk([&](CallOpInterface callOp) ->
WalkResult {
860 if (mlir::acc::GPUParallelDimAttr parDim =
861 getAccRoutineCallParDim(callOp, defaultPolicy)) {
862 if (parDim.isThreadX() || parDim.isThreadY()) {
863 hasThreadLevelRoutineCall =
true;
870 Location loc = computeRegion->getLoc();
873 auto launchArgument = [&](gpu::Processor processor) ->
Value {
874 mlir::acc::GPUParallelDimAttr parDim = mlir::acc::GPUParallelDimAttr::get(
875 computeRegion->getContext(), processor);
876 std::optional<Value> maybeLaunchArg =
877 computeRegion.getKnownLaunchArg(parDim);
878 LLVM_DEBUG(llvm::dbgs() <<
"ACCCGToGPU: launch-arg: "
879 <<
" parDim: " << parDim <<
" gpu: " << processor
881 << maybeLaunchArg.value_or(constantOne) <<
"\n");
885 maybeLaunchArg.value_or(constantOne));
887 LLVM_DEBUG(llvm::dbgs() <<
"ACCCGToGPU: creating gpu launch op: \n");
891 auto mapLaunchArguments = [&](gpu::Processor processor,
Value launchArg) {
892 mlir::acc::GPUParallelDimAttr parDim = mlir::acc::GPUParallelDimAttr::get(
893 computeRegion->getContext(), processor);
894 std::optional<Value> kernelArg = computeRegion.getLaunchArg(parDim);
896 mapping.
map(computeRegion.gpuParWidth(processor), launchArg);
899 llvm::StringRef blockDimXName =
"blockDim.x";
900 llvm::StringRef blockDimYName =
"blockDim.y";
901 std::string deviceLabel = getDeviceRemarkQualifier(
options.deviceType);
903 if (!computeRegion->getParentOfType<gpu::GPUFuncOp>()) {
904 Value blockDimX = launchArgument(gpu::Processor::ThreadX);
907 staticBlockDimX = bdxVal.getSExtValue();
908 Value blockDimY = launchArgument(gpu::Processor::ThreadY);
909 Value blockDimZ = launchArgument(gpu::Processor::ThreadZ);
910 Value gridDimX = launchArgument(gpu::Processor::BlockX);
911 Value gridDimY = launchArgument(gpu::Processor::BlockY);
912 Value gridDimZ = launchArgument(gpu::Processor::BlockZ);
918 auto getName = [&](
Value val) -> std::string {
920 return name.empty() ?
"(*)" : name;
922 bool isEffectivelySerial =
923 sameEffectiveValue(blockDimX, 1) &&
924 sameEffectiveValue(blockDimY, 1) &&
925 sameEffectiveValue(blockDimZ, 1) && sameEffectiveValue(gridDimX, 1) &&
926 sameEffectiveValue(gridDimY, 1) && sameEffectiveValue(gridDimZ, 1);
927 return (llvm::Twine(
"Generating ") +
928 llvm::Twine(isEffectivelySerial ?
"serial " :
"") + deviceLabel +
929 " code with gridDim=" + getName(gridDimX) +
"x" +
930 getName(gridDimY) +
"x" + getName(gridDimZ) +
931 " blockDim=" + getName(blockDimX) +
"x" + getName(blockDimY) +
932 "x" + getName(blockDimZ))
937 if (
mlir::Value streamValue = computeRegion.getStream()) {
938 LLVM_DEBUG(llvm::dbgs()
939 <<
"\nDEBUG: Creating async gpu.launch with stream: "
940 << streamValue <<
"\n");
941 launch = gpu::LaunchOp::create(
942 rewriter, loc, gridDimX, gridDimY, gridDimZ, blockDimX, blockDimY,
948 launch.getAsyncDependenciesMutable().append(streamValue);
950 LLVM_DEBUG(llvm::dbgs()
951 <<
"\nDEBUG: No stream, creating sync gpu.launch\n");
952 launch = gpu::LaunchOp::create(rewriter, loc, gridDimX, gridDimY,
953 gridDimZ, blockDimX, blockDimY, blockDimZ);
958 if (
auto kernelFuncName = computeRegion.getKernelFuncNameAttr())
959 launch.setFunctionAttr(kernelFuncName);
960 if (
auto kernelModuleName = computeRegion.getKernelModuleNameAttr())
961 launch.setModuleAttr(kernelModuleName);
964 gpu::TerminatorOp::create(rewriter, loc);
966 mapLaunchArguments(gpu::Processor::BlockX,
967 gpu::GridDimOp::create(rewriter, loc,
970 mapLaunchArguments(gpu::Processor::BlockY,
971 gpu::GridDimOp::create(rewriter, loc,
974 mapLaunchArguments(gpu::Processor::BlockZ,
975 gpu::GridDimOp::create(rewriter, loc,
978 mapLaunchArguments(gpu::Processor::ThreadX,
979 gpu::BlockDimOp::create(rewriter, loc,
982 mapLaunchArguments(gpu::Processor::ThreadY,
983 gpu::BlockDimOp::create(rewriter, loc,
986 mapLaunchArguments(gpu::Processor::ThreadZ,
987 gpu::BlockDimOp::create(rewriter, loc,
997 mapLaunchArguments(gpu::Processor::BlockX,
998 dimensionMap[gpu::Processor::BlockX]);
999 mapLaunchArguments(gpu::Processor::BlockY,
1000 dimensionMap[gpu::Processor::BlockY]);
1001 mapLaunchArguments(gpu::Processor::BlockZ,
1002 dimensionMap[gpu::Processor::BlockZ]);
1003 mapLaunchArguments(gpu::Processor::ThreadX,
1004 dimensionMap[gpu::Processor::ThreadX]);
1005 mapLaunchArguments(gpu::Processor::ThreadY,
1006 dimensionMap[gpu::Processor::ThreadY]);
1007 mapLaunchArguments(gpu::Processor::ThreadZ,
1008 dimensionMap[gpu::Processor::ThreadZ]);
1013 preparePrivatizeExtentInsOperands();
1014 Block *body = computeRegion.getBody();
1015 unsigned numLaunchArgs = computeRegion.getLaunchArgs().size();
1016 ValueRange inputArgs = computeRegion.getInputArgs();
1020 assert(computeRegion.getRegion().hasOneBlock() &&
1021 "compute region only supports one block region for now");
1023 for (
auto &op : computeRegion.getRegion().getBlocks().front().getOperations())
1026 for (
auto &parLoop : loopReductions)
1027 postprocessLoopReduction(parLoop);
1031 if (!pendingCombineReloads.empty() && launch) {
1033 for (
auto &[slot, loadOp] : pendingCombineReloads) {
1035 reductionAccumValue.find(slot);
1036 if (it == reductionAccumValue.end())
1038 if (!domInfo.
dominates(it->second, loadOp.getOperation()))
1046 const int64_t subgroupAlignMask = subgroupSize - 1;
1052 bool isShuffleEnabled =
false;
1053 bool alignThreadXReduction =
1057 launch.walk([&](gpu::AllReduceOp allReduce) ->
WalkResult {
1060 for (
auto parDim : parDims) {
1061 if (parDim.isThreadY() ||
1062 (alignThreadXReduction && parDim.isThreadX())) {
1064 isShuffleEnabled =
true;
1071 if (!isShuffleEnabled) {
1072 launch.walk([&](func::CallOp callOp) ->
WalkResult {
1073 if (gpu::GPUFuncOp callee =
1074 callOp->getParentOfType<ModuleOp>()
1075 .lookupSymbol<gpu::GPUFuncOp>(callOp.getCallee())) {
1076 callee.walk([&](gpu::AllReduceOp allReduce) ->
WalkResult {
1079 for (
auto parDim : parDims) {
1080 if (parDim.isThreadY() ||
1081 (alignThreadXReduction && parDim.isThreadX())) {
1082 isShuffleEnabled =
true;
1094 if (isShuffleEnabled || hasThreadYBarrier) {
1097 Value curBlockDimX = launch.getBlockSizeX();
1098 Value curBlockDimY = launch.getBlockSizeY();
1099 Value curBlockDimZ = launch.getBlockSizeZ();
1103 auto getName = [&](
Value val) -> std::string {
1105 return name.empty() ?
"(*)" : name;
1107 std::string blockDimXValStr = getName(curBlockDimX);
1108 std::string blockDimYValStr = getName(curBlockDimY);
1109 llvm::StringRef kind =
1110 isShuffleEnabled ?
"Shuffle reduction" :
"ThreadY barrier";
1111 return (llvm::Twine(kind) +
1112 " is generated while adjusting the number of threads into "
1114 llvm::Twine(subgroupSize) +
".\n\t" + blockDimXName +
": `" +
1115 blockDimXValStr +
"` to `((" + blockDimXValStr +
" + " +
1116 llvm::Twine(subgroupAlignMask) +
") / " +
1117 llvm::Twine(subgroupSize) +
") * " + llvm::Twine(subgroupSize) +
1118 "`\n" +
"\t" + blockDimYName +
": `" + blockDimYValStr +
1119 "` to `max(1, (new-" + blockDimXName +
" * " + blockDimYValStr +
1120 ") / new-" + blockDimXName +
")`")
1133 bool skipAlign =
false;
1134 if (constBlockDimX && constBlockDimY && constBlockDimZ &&
1135 *constBlockDimX > 1 && *constBlockDimX < subgroupSize &&
1136 *constBlockDimY == 1 && *constBlockDimZ == 1) {
1145 Value newBlockDimX, newBlockDimY, newBlockDimZ;
1146 if (constBlockDimX && constBlockDimY && constBlockDimZ) {
1147 int64_t bdx = *constBlockDimX;
1148 int64_t bdy = *constBlockDimY;
1149 int64_t bdz = *constBlockDimZ;
1151 ((bdx + subgroupAlignMask) / subgroupSize) * subgroupSize;
1152 int64_t numXYThreads = bdx * bdy;
1153 int64_t numThreads = numXYThreads * bdz;
1154 int64_t newBdy = std::max<int64_t>(1, numXYThreads / alignedBdx);
1156 std::max<int64_t>(1, numThreads / (alignedBdx * newBdy));
1163 Value numXYThreads =
1164 arith::MulIOp::create(rewriter, loc, curBlockDimX, curBlockDimY);
1166 arith::MulIOp::create(rewriter, loc, numXYThreads, curBlockDimZ);
1170 Value cstSubgroupSize =
1173 arith::AddIOp::create(rewriter, loc, curBlockDimX, cstMask);
1174 Value subgroupsRequired =
1175 arith::DivUIOp::create(rewriter, loc, padded, cstSubgroupSize);
1176 newBlockDimX = arith::MulIOp::create(rewriter, loc, subgroupsRequired,
1180 arith::DivUIOp::create(rewriter, loc, numXYThreads, newBlockDimX);
1182 newBlockDimY = arith::MaxUIOp::create(rewriter, loc, cst1, quotient);
1184 Value newNumXYThreads =
1185 arith::MulIOp::create(rewriter, loc, newBlockDimX, newBlockDimY);
1187 arith::DivUIOp::create(rewriter, loc, numThreads, newNumXYThreads);
1188 newBlockDimZ = arith::MaxUIOp::create(rewriter, loc, cst1, quotient);
1192 launch.getBlockSizeXMutable().assign(newBlockDimX);
1193 launch.getBlockSizeYMutable().assign(newBlockDimY);
1194 launch.getBlockSizeZMutable().assign(newBlockDimZ);
1202 if (!sharedMemPrivateVarNames.empty()) {
1204 return (llvm::Twine(
"GPU shared memory used for ") +
1205 llvm::join(sharedMemPrivateVarNames,
","))
1210 rewriter.
eraseOp(computeRegion);
1226static bool isRedundantChainAccumulate(acc::ReductionAccumulateOp op) {
1228 memref::LoadOp loadOp = op.getValue().getDefiningOp<memref::LoadOp>();
1229 if (!loadOp || loadOp.getMemRef() !=
memref)
1232 acc::ReductionCombineOp combineOp = dyn_cast<acc::ReductionCombineOp>(user);
1233 if (!combineOp || combineOp.getDestMemref() !=
memref)
1237 if (llvm::any_of(parDims, [](mlir::acc::GPUParallelDimAttr d) {
1238 return d.isAnyBlock();
1246static GPUParallelDimsAttr
1247getPrivateParDims(acc::PrivateLocalOp privateLocal,
1248 acc::ComputeRegionOp computeRegion) {
1251 if (acc::PrivatizeOp privatize =
getPrivatizeOp(privateLocal, computeRegion))
1252 return privatize.getParDimsAttr();
1257static bool isThreadYPrivate(acc::PrivateLocalOp privateLocal,
bool allowBlock,
1258 acc::ComputeRegionOp computeRegion) {
1261 GPUParallelDimsAttr parDims = getPrivateParDims(privateLocal, computeRegion);
1264 return llvm::any_of(parDims.getArray(),
1265 [](
auto dim) { return dim.isThreadY(); }) &&
1266 llvm::all_of(parDims.getArray(), [=](
auto dim) {
1267 return dim.isThreadY() || (allowBlock && dim.isAnyBlock());
1271struct ThreadYBroadeningInfo {
1272 bool hasActiveWorkerCombine =
false;
1273 bool hasExplicitInactiveCombine =
false;
1274 bool hasBroadeningConflict =
false;
1277 void merge(
const ThreadYBroadeningInfo &other) {
1278 hasActiveWorkerCombine |= other.hasActiveWorkerCombine;
1279 hasExplicitInactiveCombine |= other.hasExplicitInactiveCombine;
1280 hasBroadeningConflict |= other.hasBroadeningConflict;
1282 diagnosticOp = other.diagnosticOp;
1287static bool hasUnsafeEffectsWhenBroadening(
Operation *op) {
1288 if (
auto effectOp = dyn_cast<MemoryEffectOpInterface>(op)) {
1290 effectOp.getEffects(effects);
1291 return llvm::any_of(effects, [](
const auto &effect) {
1292 return !isa<MemoryEffects::Read>(effect.getEffect());
1300static bool isFedByInnerBlockCombine(acc::PrivateLocalOp accumulator,
1305 if (user == selfCombine)
1307 auto combineOp = dyn_cast<acc::ReductionCombineOp>(user);
1309 unwrapMemRefConversion(combineOp.getDestMemref()).getDefiningOp() !=
1310 accumulator.getOperation())
1314 if (llvm::any_of(parDims, [](mlir::acc::GPUParallelDimAttr d) {
1315 return d.isAnyBlock();
1323static void classifyThreadYCombine(ThreadYBroadeningInfo &info,
1326 acc::ComputeRegionOp computeRegion) {
1327 bool hasThreadY = llvm::any_of(
1328 parDims, [](GPUParallelDimAttr parDim) {
return parDim.isThreadY(); });
1329 bool hasBlock = llvm::any_of(
1330 parDims, [](GPUParallelDimAttr parDim) {
return parDim.isAnyBlock(); });
1331 acc::PrivateLocalOp srcPrivate =
1332 unwrapMemRefConversion(src).getDefiningOp<acc::PrivateLocalOp>();
1333 acc::PrivateLocalOp destPrivate =
1334 unwrapMemRefConversion(dest).getDefiningOp<acc::PrivateLocalOp>();
1335 bool hasPrivateDest = isa<acc::ReductionCombineOp>(combineOp) && srcPrivate &&
1339 if (hasThreadY && hasBlock &&
1340 isThreadYPrivate(srcPrivate, hasPrivateDest, computeRegion)) {
1341 info.hasActiveWorkerCombine =
true;
1348 if (hasThreadY && hasBlock &&
1349 isThreadYPrivate(srcPrivate,
true, computeRegion) &&
1350 isFedByInnerBlockCombine(srcPrivate, combineOp)) {
1351 info.hasActiveWorkerCombine =
true;
1355 info.hasExplicitInactiveCombine =
true;
1356 if (!info.diagnosticOp)
1357 info.diagnosticOp = combineOp;
1363static ThreadYBroadeningInfo
1364analyzeThreadYBroadening(
Block &predicateBlock,
1365 acc::ComputeRegionOp computeRegion) {
1366 ThreadYBroadeningInfo info;
1367 for (
Operation &nestedOp : predicateBlock) {
1368 if (acc::PredicateRegionOp nestedPredicate =
1369 dyn_cast<acc::PredicateRegionOp>(nestedOp)) {
1370 ThreadYBroadeningInfo nestedInfo = analyzeThreadYBroadening(
1371 nestedPredicate.getRegion().front(), computeRegion);
1372 info.hasActiveWorkerCombine |= nestedInfo.hasActiveWorkerCombine;
1375 if (acc::ReductionCombineOp combineOp =
1376 dyn_cast<acc::ReductionCombineOp>(nestedOp)) {
1377 classifyThreadYCombine(
1378 info, combineOp, combineOp.getSrcMemref(), combineOp.getDestMemref(),
1382 if (acc::ReductionCombineRegionOp combineRegionOp =
1383 dyn_cast<acc::ReductionCombineRegionOp>(nestedOp)) {
1384 classifyThreadYCombine(info, combineRegionOp, combineRegionOp.getSrcVar(),
1385 combineRegionOp.getDestVar(),
1390 if (nestedOp.getNumRegions() != 0) {
1391 if (hasUnsafeEffectsWhenBroadening(&nestedOp)) {
1392 info.hasBroadeningConflict =
true;
1393 if (!info.diagnosticOp)
1394 info.diagnosticOp = &nestedOp;
1396 for (
Region ®ion : nestedOp.getRegions())
1397 for (
Block &nestedBlock : region)
1398 info.merge(analyzeThreadYBroadening(nestedBlock, computeRegion));
1401 if (hasUnsafeEffectsWhenBroadening(&nestedOp)) {
1402 info.hasBroadeningConflict =
true;
1403 if (!info.diagnosticOp)
1404 info.diagnosticOp = &nestedOp;
1410std::pair<SmallVector<mlir::acc::GPUParallelDimAttr>,
1412ACCCGToGPULowering::computeActiveAndInactiveParDims(
Operation *op,
1416 getAncestorParDims(op);
1421 bool noStructuralAncestorParDims =
1422 llvm::none_of(ancestorParDims, [](
auto pd) {
return !pd.isSeq(); });
1424 mlir::acc::GPUParallelDimAttr routineParDim;
1426 FunctionOpInterface funcOp =
1427 computeRegion->getParentOfType<FunctionOpInterface>();
1428 routineParDim = getSpecializedRoutineDim(funcOp, defaultPolicy);
1429 if (routineParDim.isThreadX()) {
1431 mlir::acc::GPUParallelDimAttr::threadYDim(ctx));
1434 mlir::acc::GPUParallelDimAttr::blockXDim(ctx));
1438 if (acc::PrivateLocalOp privateLocalOp = dyn_cast<acc::PrivateLocalOp>(op)) {
1440 if (acc::ReductionAccumulateOp accumulateOp =
1441 dyn_cast<acc::ReductionAccumulateOp>(user)) {
1442 if (accumulateOp.getMemref() == privateLocalOp.getResult()) {
1443 for (mlir::acc::GPUParallelDimAttr parDim :
1444 accumulateOp.getParDims().getArray()) {
1454 if (acc::ReductionCombineOp combineOp =
1455 dyn_cast<acc::ReductionCombineOp>(user)) {
1456 if (combineOp.getSrcMemref() == privateLocalOp.
getResult()) {
1457 for (mlir::acc::GPUParallelDimAttr parDim :
1463 if (
auto combineRegionOp =
1464 dyn_cast<acc::ReductionCombineRegionOp>(user)) {
1465 if (combineRegionOp.getSrcVar() == privateLocalOp.getResult()) {
1466 for (mlir::acc::GPUParallelDimAttr parDim :
1478 acc::PrivateType privTy =
1479 cast<acc::PrivateType>(privateLocalOp.getPrivatized().getType());
1481 privTy.getBaseTy(), computeRegion->getParentOfType<ModuleOp>());
1482 if (!baseTy.hasStaticShape()) {
1483 GPUParallelDimsAttr ownParDims =
1484 getPrivateParDims(privateLocalOp, computeRegion);
1486 for (GPUParallelDimAttr parDim : ownParDims.getArray())
1491 bool hasBlock =
false;
1492 for (mlir::acc::GPUParallelDimAttr parDim : ancestorParDims)
1493 if (parDim.isAnyBlock())
1496 mlir::acc::GPUParallelDimAttr lowestParDim =
1497 mlir::acc::GPUParallelDimAttr::threadXDim(ctx);
1499 ThreadYBroadeningInfo threadYInfo =
1500 analyzeThreadYBroadening(*block, computeRegion);
1502 auto applyCombineParDims =
1504 for (mlir::acc::GPUParallelDimAttr parDim : combineParDims)
1513 if (
auto privateLocalOp = getPrivateLocalForMemref(
target)) {
1514 GPUParallelDimsAttr parDimsAttr =
1515 getPrivateParDims(privateLocalOp, computeRegion);
1517 for (
auto parDim : parDimsAttr.getArray())
1521 if (
auto memEffects = dyn_cast<MemoryEffectOpInterface>(op)) {
1523 memEffects.getEffects(effects);
1525 if (isa<MemoryEffects::Write>(effect.getEffect()) &&
1527 addPrivateStoreParDims(effect.getValue());
1532 if (CallOpInterface callOp = dyn_cast<CallOpInterface>(op)) {
1533 if (mlir::acc::GPUParallelDimAttr parDim =
1534 getAccRoutineCallParDim(callOp, defaultPolicy)) {
1535 if (parDim.isBlockZ())
1536 lowestParDim = parDim;
1538 lowestParDim = parDim.getOneHigher();
1544 if (acc::ReductionCombineOp reductionCombineOp =
1545 dyn_cast<acc::ReductionCombineOp>(op)) {
1546 if (failed(applyCombineParDims(
1550 if (acc::ReductionCombineRegionOp combineRegionOp =
1551 dyn_cast<acc::ReductionCombineRegionOp>(op)) {
1552 if (failed(applyCombineParDims(
1559 if (acc::ReductionAccumulateArrayOp accArrayOp =
1560 dyn_cast<acc::ReductionAccumulateArrayOp>(op)) {
1561 for (mlir::acc::GPUParallelDimAttr parDim :
1562 accArrayOp.getParDims().getArray()) {
1568 mlir::acc::GPUParallelDimAttr threadY =
1569 mlir::acc::GPUParallelDimAttr::threadYDim(ctx);
1570 bool baselineThreadYActive = llvm::is_contained(ancestorParDims, threadY);
1571 if (threadYInfo.hasActiveWorkerCombine && !baselineThreadYActive) {
1572 if (threadYInfo.hasExplicitInactiveCombine ||
1573 threadYInfo.hasBroadeningConflict) {
1575 threadYInfo.diagnosticOp ? threadYInfo.diagnosticOp : op;
1578 "operations in the same predicate region require incompatible "
1579 "ThreadY predication");
1589 if (routineParDim) {
1590 for (mlir::acc::GPUParallelDimAttr parDim = routineParDim;
1591 parDim.getOrder() >= lowestParDim.getOrder();
1592 parDim = parDim.getOneLower()) {
1596 launchParDims = computeRegion.getLaunchParDims();
1601 for (mlir::acc::GPUParallelDimAttr launchParDim : launchParDims) {
1602 if (launchParDim.getOrder() < lowestParDim.getOrder())
1604 if (llvm::find(ancestorParDims, launchParDim) != ancestorParDims.end() ||
1605 (launchParDim.isAnyBlock() &&
1606 (noStructuralAncestorParDims || hasBlock))) {
1607 activeParDims.push_back(launchParDim);
1609 inactiveParDims.push_back(launchParDim);
1613 return std::pair{activeParDims, inactiveParDims};
1616Value ACCCGToGPULowering::emitPredicate(
1619 for (mlir::acc::GPUParallelDimAttr inactiveParDim : inactiveParDims) {
1620 Value threadId = getGPUThreadIdFor(inactiveParDim.getProcessor());
1622 Value zero = arith::ConstantOp::create(rewriter, loc, zeroAttr);
1623 Value cmp = arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::eq,
1626 predicate = arith::AndIOp::create(rewriter, loc, cmp, predicate);
1633void ACCCGToGPULowering::createBarrier(
1634 Location loc, mlir::acc::GPUParallelDimsAttr parDimsAttr) {
1635 bool hasAnyBlock =
false, hasThreadY =
false, hasThreadX =
false;
1636 for (
auto parDim : parDimsAttr.getArray()) {
1637 if (parDim.isAnyBlock())
1639 if (parDim.isThreadY())
1641 if (parDim.isThreadX())
1645 if (hasAnyBlock || hasThreadY)
1646 emitGPUBarrierWorkgroup(rewriter, loc);
1647 else if (hasThreadX)
1648 createPerRowBarrier(loc);
1651void ACCCGToGPULowering::createPerRowBarrier(
Location loc) {
1652 hasThreadYBarrier =
true;
1654 if (staticBlockDimX <=
options.subgroupSize) {
1655 emitGPUBarrierSubgroup(rewriter, loc);
1659 if (
options.deviceType != mlir::acc::DeviceType::Nvidia) {
1662 "per-row barrier to support worker parallelism on non-NVIDIA device");
1677 Value blockDimX = gpu::BlockDimOp::create(
1678 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::x);
1679 Value blockDimY = gpu::BlockDimOp::create(
1680 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::y);
1682 Value isSingleWorker = arith::CmpIOp::create(
1683 rewriter, loc, arith::CmpIPredicate::eq, blockDimY, cst1);
1685 auto outerIf = scf::IfOp::create(rewriter, loc, isSingleWorker,
1690 emitGPUBarrierWorkgroup(rewriter, loc);
1694 Value cstSubgroupSize =
1696 Value isSubgroupSized = arith::CmpIOp::create(
1697 rewriter, loc, arith::CmpIPredicate::ule, blockDimX, cstSubgroupSize);
1699 auto innerIf = scf::IfOp::create(rewriter, loc, isSubgroupSized,
1705 emitGPUBarrierSubgroup(rewriter, loc);
1712 Value threadYId = gpu::ThreadIdOp::create(
1713 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::y);
1714 Value barrierId = arith::AddIOp::create(rewriter, loc, threadYId, cst1);
1717 arith::IndexCastOp::create(rewriter, loc, i32Ty, barrierId);
1718 Value numberOfThreads32 =
1719 arith::IndexCastOp::create(rewriter, loc, i32Ty, blockDimX);
1723 assert(
options.deviceType == mlir::acc::DeviceType::Nvidia);
1724 NVVM::BarrierOp::create(rewriter, loc, barrierId32, numberOfThreads32);
1733static bool hasSubsequentLoopSibling(
Operation *loopOp) {
1734 for (
Operation *next = loopOp->getNextNode(); next;
1735 next = next->getNextNode()) {
1736 if (isa<scf::ParallelOp, scf::ForOp>(next))
1738 bool nested =
false;
1740 if (isa<scf::ParallelOp, scf::ForOp>(op)) {
1753static LoopLikeOpInterface findFirstSequentialLoop(
Operation *op) {
1754 auto isAllSequentialParDims = [](scf::ParallelOp par) ->
bool {
1756 if (!pd || pd.getArray().empty())
1758 return llvm::all_of(pd.getArray(), [](mlir::acc::GPUParallelDimAttr d) {
1765 if (isa<scf::ForOp>(p))
1766 return cast<LoopLikeOpInterface>(p);
1767 if (scf::ParallelOp parOp = dyn_cast<scf::ParallelOp>(p)) {
1768 if (isAllSequentialParDims(parOp))
1769 return cast<LoopLikeOpInterface>(p);
1785static bool isLoopBodyClosureOp(
Operation *op) {
1786 return isa<scf::ReduceOp, scf::YieldOp, acc::YieldOp>(op);
1791static bool isDeferredBarrierFlushPoint(
Operation *op) {
1792 if (isLoopBodyClosureOp(op))
1796 if (isa<scf::ForOp>(op))
1798 if (scf::ParallelOp parallelOp = dyn_cast<scf::ParallelOp>(op)) {
1800 if (mlir::acc::GPUParallelDimsAttr parDims =
1802 if (parDims.getArray().size() == 1 &&
1803 parDims.getArray().front().isSeq()) {
1815static bool hasTrailingSideEffectSiblings(
Operation *loopOp) {
1816 for (
Operation *next = loopOp->getNextNode(); next;
1817 next = next->getNextNode()) {
1818 return !isLoopBodyClosureOp(next);
1842void ACCCGToGPULowering::createBarrierAfterSeqLoop(
Operation *loopOp) {
1851 if (mayWriteSharedMemory(loopOp) && hasSubsequentLoopSibling(loopOp))
1852 emitGPUBarrierWorkgroup(rewriter, loopOp->
getLoc());
1856 bool parentIsSeq =
false;
1857 if (mlir::acc::GPUParallelDimsAttr wsParDims =
1859 if (wsParDims.getArray().size() == 1 &&
1860 wsParDims.getArray().front().isSeq()) {
1870 bool hasThreadSubLoop =
false;
1872 if (innerPar.getOperation() == loopOp)
1874 if (mlir::acc::GPUParallelDimsAttr dims =
1876 for (
auto d : dims.getArray()) {
1877 if (d.isThreadX() || d.isThreadY()) {
1878 hasThreadSubLoop =
true;
1885 if (!hasThreadSubLoop)
1887 scf::ParallelOp threadLoop = wsLoop->getParentOfType<scf::ParallelOp>();
1890 scf::ParallelOp blockLoop = threadLoop->getParentOfType<scf::ParallelOp>();
1893 mlir::acc::GPUParallelDimsAttr parDimsAttr =
1895 if (parDimsAttr.hasOnlyBlockLevel())
1896 createBarrier(loopOp->
getLoc(), parDimsAttr);
1902 scf::ParallelOp seqLoop = wsLoop->getParentOfType<scf::ParallelOp>();
1911 if (mayWriteSharedMemory(loopOp) && hasSubsequentLoopSibling(wsLoop))
1912 emitGPUBarrierWorkgroup(rewriter, loopOp->
getLoc());
1915 if (scf::ParallelOp outerParLoop =
1916 seqLoop->getParentOfType<scf::ParallelOp>()) {
1917 mlir::acc::GPUParallelDimsAttr parDimsAttr =
1919 if (parDimsAttr.hasOnlyBlockLevel()) {
1920 createBarrier(loopOp->
getLoc(), parDimsAttr);
1921 }
else if (parDimsAttr.hasOnlyThreadYLevel()) {
1922 createPerRowBarrier(loopOp->
getLoc());
1923 }
else if (parDimsAttr && parDimsAttr.isSeq()) {
1929 gangLoop; gangLoop = gangLoop->getParentOfType<scf::ParallelOp>()) {
1930 mlir::acc::GPUParallelDimsAttr gangDims =
1934 if (gangDims.hasOnlyBlockLevel()) {
1935 createBarrier(loopOp->
getLoc(), gangDims);
1938 if (!gangDims.isSeq())
1952 mlir::acc::GPUParallelDimsAttr parDimsAttr =
1954 if (parDimsAttr && parDimsAttr.hasOnlyBlockLevel() &&
1955 mayWriteSharedMemory(loopOp)) {
1956 createBarrier(loopOp->
getLoc(), parDimsAttr);
1960bool ACCCGToGPULowering::mayWriteSharedMemory(
Operation *loopOp) {
1962 loopOp->
walk([&](memref::StoreOp storeOp) {
1967 while (!worklist.empty()) {
1968 Value v = worklist.pop_back_val();
1969 if (!seen.insert(v).second)
1974 if (acc::PrivateLocalOp privateLocal =
1975 dyn_cast<acc::PrivateLocalOp>(def)) {
1976 acc::PrivatizeOp privatizeOp =
1984 if (mlir::acc::GPUParallelDimsAttr parDims =
1985 privatizeOp.getParDimsAttr()) {
1986 bool hasBlock =
false, hasThread =
false;
1987 for (mlir::acc::GPUParallelDimAttr d : parDims.getArray()) {
1990 if (d.isThreadX() || d.isThreadY())
1993 if (hasBlock && !hasThread) {
2008ACCCGToGPULowering::getPrivateMemScope(acc::PrivatizeOp privatizeOp) {
2009 bool hasBlock =
false;
2010 bool hasThreadX =
false;
2011 bool hasThreadY =
false;
2012 if (mlir::acc::GPUParallelDimsAttr parDims = privatizeOp.getParDimsAttr()) {
2013 for (mlir::acc::GPUParallelDimAttr d : parDims.getArray()) {
2022 for (mlir::acc::GPUParallelDimAttr d : computeRegion.getLaunchParDims())
2026 return PrivateMemScope::Gang;
2027 return PrivateMemScope::Thread;
2030 return PrivateMemScope::Thread;
2031 if (hasBlock && hasThreadY)
2032 return PrivateMemScope::Worker;
2034 return PrivateMemScope::Gang;
2035 return PrivateMemScope::Thread;
2039static acc::PrivateLocalOp getPrivateLocalForMemref(
Value memref) {
2042 while (!worklist.empty()) {
2043 Value v = worklist.pop_back_val();
2044 if (!seen.insert(v).second)
2049 if (acc::PrivateLocalOp privateLocal = dyn_cast<acc::PrivateLocalOp>(def))
2050 return privateLocal;
2056PrivateMemScope ACCCGToGPULowering::getPrivateScopeForMemref(
Value memref) {
2057 if (
auto privateLocal = getPrivateLocalForMemref(
memref))
2058 return getPrivateMemScope(
getPrivatizeOp(privateLocal, computeRegion));
2059 return PrivateMemScope::None;
2062acc::PrivatizeOp ACCCGToGPULowering::getPrivatizeForMemref(
Value memref) {
2063 if (
auto privateLocal = getPrivateLocalForMemref(
memref))
2065 return acc::PrivatizeOp();
2069ACCCGToGPULowering::needsPreStoreReuseBarrier(acc::PredicateRegionOp interOp) {
2073 LoopLikeOpInterface seqLoopOp = findFirstSequentialLoop(interOp);
2075 return PrivateMemScope::None;
2079 PrivateMemScope storeScope = PrivateMemScope::None;
2081 interOp.getRegion().walk([&](memref::StoreOp storeOp) {
2082 PrivateMemScope scope = getPrivateScopeForMemref(storeOp.getMemref());
2083 if (scope != PrivateMemScope::Gang && scope != PrivateMemScope::Worker)
2085 if (
auto privatize = getPrivatizeForMemref(storeOp.getMemref()))
2086 storePrivatizes.insert(privatize.getOperation());
2087 if (storeScope == PrivateMemScope::None)
2091 if (storeScope == PrivateMemScope::None || storePrivatizes.empty())
2092 return PrivateMemScope::None;
2095 bool hasParallelPrivateUse =
false;
2096 seqLoopOp.getOperation()->walk([&](
Operation *op) {
2098 if (interOp->isAncestor(op))
2102 if (memref::LoadOp loadOp = dyn_cast<memref::LoadOp>(op))
2103 memref = loadOp.getMemref();
2104 else if (memref::StoreOp storeOp = dyn_cast<memref::StoreOp>(op))
2105 memref = storeOp.getMemref();
2109 PrivateMemScope scope = getPrivateScopeForMemref(
memref);
2110 if (scope != storeScope)
2113 acc::PrivatizeOp usePrivatize = getPrivatizeForMemref(
memref);
2114 if (!usePrivatize || !storePrivatizes.contains(usePrivatize.getOperation()))
2117 bool insideNestedParallel =
false;
2120 if (scf::ParallelOp par = dyn_cast<scf::ParallelOp>(p)) {
2121 if (mlir::acc::GPUParallelDimsAttr pd =
2123 if (llvm::any_of(pd.getArray(), [](mlir::acc::GPUParallelDimAttr d) {
2126 insideNestedParallel =
true;
2132 if (!insideNestedParallel)
2135 hasParallelPrivateUse =
true;
2139 if (!hasParallelPrivateUse)
2140 return PrivateMemScope::None;
2145void ACCCGToGPULowering::processPredicateRegion(
2146 acc::PredicateRegionOp interOp) {
2147 LLVM_DEBUG(llvm::dbgs() <<
"processing predicate region: ";
2148 interOp->print(llvm::dbgs()); llvm::dbgs() <<
"\n");
2151 std::pair<SmallVector<mlir::acc::GPUParallelDimAttr>,
2153 parDimsPair = computeActiveAndInactiveParDims(
2154 interOp, &interOp.getRegion().front());
2164 if (hasThreadYReduction) {
2166 mlir::acc::GPUParallelDimAttr threadXParDim =
2167 mlir::acc::GPUParallelDimAttr::threadXDim(ctx);
2168 bool hasThreadXInActive =
2169 llvm::any_of(parDimsPair.first, [](mlir::acc::GPUParallelDimAttr pd) {
2170 return pd.isThreadX();
2172 bool hasThreadXInInactive =
2173 llvm::any_of(parDimsPair.second, [](mlir::acc::GPUParallelDimAttr pd) {
2174 return pd.isThreadX();
2180 bool regionHasThreadLevelRoutineCall =
false;
2181 if (hasThreadLevelRoutineCall) {
2182 interOp.getRegion().walk([&](CallOpInterface callOp) {
2183 if (mlir::acc::GPUParallelDimAttr parDim =
2184 getAccRoutineCallParDim(callOp, defaultPolicy)) {
2185 if (parDim.isThreadX() || parDim.isThreadY()) {
2186 regionHasThreadLevelRoutineCall =
true;
2194 if (!hasThreadXInActive && !hasThreadXInInactive &&
2195 !regionHasThreadLevelRoutineCall) {
2196 parDimsPair.second.push_back(threadXParDim);
2200 if (
Value predicate = emitPredicate(loc, parDimsPair.second)) {
2201 LLVM_DEBUG(llvm::dbgs() <<
"predicate: " << predicate <<
"\n");
2202 bool isInsideThreadXLoop =
false;
2203 bool isInsideThreadYLoop =
false;
2204 for (
auto parDim : parDimsPair.first) {
2205 if (parDim.isThreadX())
2206 isInsideThreadXLoop =
true;
2207 if (parDim.isThreadY())
2208 isInsideThreadYLoop =
true;
2214 auto emitReconvergenceBarrier = [&]() {
2215 if (isInsideThreadXLoop) {
2217 }
else if (isInsideThreadYLoop) {
2222 bool predicatesThreadX = llvm::any_of(
2224 [](mlir::acc::GPUParallelDimAttr pd) { return pd.isThreadX(); });
2225 if (predicatesThreadX) {
2226 createBarrier(loc, mlir::acc::GPUParallelDimsAttr::get(
2227 interOp->getContext(), parDimsPair.second));
2231 }
else if (!parDimsPair.first.empty()) {
2233 createBarrier(loc, mlir::acc::GPUParallelDimsAttr::get(
2234 interOp->getContext(), parDimsPair.first));
2237 createBarrier(loc, mlir::acc::GPUParallelDimsAttr::get(
2238 interOp->getContext(), parDimsPair.second));
2250 PrivateMemScope scope = needsPreStoreReuseBarrier(interOp);
2251 if (scope == PrivateMemScope::Gang)
2252 emitGPUBarrierWorkgroup(rewriter, loc);
2253 else if (scope == PrivateMemScope::Worker)
2254 createPerRowBarrier(loc);
2256 auto ifOp = scf::IfOp::create(rewriter, loc, predicate,
2258 Region &thenRegion = ifOp.getThenRegion();
2262 for (
auto &bodyOp : interOp.getRegion().front().getOperations()) {
2266 if (memref::StoreOp storeOp = dyn_cast<memref::StoreOp>(&bodyOp)) {
2267 std::optional<arith::AtomicRMWKind> blockReduceKind;
2268 bool failedReductionKind =
false;
2269 Value storeVal = storeOp.getValueToStore();
2276 Block *epilogueBlock = interOp->getBlock();
2277 auto findBlockAccLoad =
2279 Value val) -> std::optional<arith::AtomicRMWKind> {
2281 if (!def || def->
getBlock() != epilogueBlock)
2282 return std::nullopt;
2283 if (memref::LoadOp loadOp = dyn_cast<memref::LoadOp>(def)) {
2284 for (
auto *user : loadOp.getMemRef().getUsers()) {
2285 if (acc::ReductionAccumulateOp accOp =
2286 dyn_cast<acc::ReductionAccumulateOp>(user)) {
2287 if (llvm::any_of(accOp.getParDims().getArray(),
2288 [](mlir::acc::GPUParallelDimAttr pd) {
2289 return pd.isAnyBlock();
2291 FailureOr<arith::AtomicRMWKind> kind = getReductionKind(
2292 accOp.getReductionOperator(),
2293 accOp.getValue().getType(), accOp.getLoc());
2295 failedReductionKind =
true;
2296 return std::nullopt;
2302 return std::nullopt;
2305 if (
auto kind = self(self, operand))
2307 return std::nullopt;
2309 blockReduceKind = findBlockAccLoad(findBlockAccLoad, storeVal);
2311 if (failedReductionKind)
2313 if (blockReduceKind) {
2316 bool threadIsActive = llvm::any_of(
2317 parDimsPair.first, [](mlir::acc::GPUParallelDimAttr pd) {
2318 return !pd.isAnyBlock();
2320 if (!threadIsActive &&
2321 !isa_and_nonnull<memref::AllocaOp>(
2322 unwrapMemRefConversion(
memref).getDefiningOp())) {
2328 MemRefType memrefTy = cast<MemRefType>(
memref.getType());
2331 for (
Value idx : storeOp.getIndices())
2335 Block &launchBody = launch.getBody().front();
2341 if (parent == launch.getOperation())
2343 if (isa<scf::ParallelOp>(parent))
2346 insertBefore = parOp.getOperation();
2374 materialize(operand);
2376 for (
auto [orig, clonedRes] :
2378 initMapping.
map(orig, clonedRes);
2380 return initMapping.
lookup(val);
2383 for (
auto &idx : initIndices)
2384 idx = materialize(idx);
2386 rewriter, loc, memrefTy.getElementType(), *blockReduceKind,
2388 Value blockId = gpu::BlockIdOp::create(
2389 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::x);
2390 Value threadId = gpu::ThreadIdOp::create(
2391 rewriter, loc, rewriter.
getIndexType(), gpu::Dimension::x);
2393 Value isBlock0 = arith::CmpIOp::create(
2394 rewriter, loc, arith::CmpIPredicate::eq, blockId, zero);
2395 Value isThread0 = arith::CmpIOp::create(
2396 rewriter, loc, arith::CmpIPredicate::eq, threadId, zero);
2397 Value isFirstThread =
2398 arith::AndIOp::create(rewriter, loc, isBlock0, isThread0);
2399 auto initIf = scf::IfOp::create(rewriter, loc, isFirstThread,
2402 initIf.getThenRegion().back().getTerminator());
2403 memref::StoreOp::create(rewriter, loc, identityVal, initMemref,
2406 gpu::BarrierOp::create(rewriter, loc);
2409 for (
Value idx : storeOp.getIndices())
2411 constructAtomicAccumulation(loc,
memref, atomicIndices, input,
2420 emitReconvergenceBarrier();
2423 for (
auto &bodyOp : interOp.getRegion().front().getOperations())
2446Value ACCCGToGPULowering::processPrivatize(acc::PrivatizeOp privatize) {
2447 LLVM_DEBUG(llvm::dbgs() <<
"processing privatize: ";
2448 privatize->print(llvm::dbgs()); llvm::dbgs() <<
"\n");
2449 Value tracked = privatize.getResult();
2450 if (acc::ComputeRegionOp insUser =
2451 dyn_cast<acc::ComputeRegionOp>(getOnlyUser(tracked))) {
2452 assert(privatize->hasOneUse() &&
2453 "expected acc.privatize op to have one use");
2454 tracked = insUser.getBody()->getArgument(
2455 privatize->use_begin()->getOperandNumber());
2457 Operation *privatizeUser = getOnlyUser(tracked);
2458 assert(privatizeUser &&
"expected PrivateLocalOp user for privatize");
2460 std::pair<SmallVector<mlir::acc::GPUParallelDimAttr>,
2462 parDimsPair = computeActiveAndInactiveParDims(privatizeUser,
nullptr);
2464 if (!privatize.getParDimsAttr()) {
2465 privatize.setParDimsAttr(mlir::acc::GPUParallelDimsAttr::get(
2469 Location loc = privatize->getLoc();
2470 acc::PrivateType privTy = cast<acc::PrivateType>(privatize.getType());
2471 ModuleOp module = computeRegion->getParentOfType<ModuleOp>();
2474 gpu::GPUFuncOp gpuFuncOp = computeRegion->getParentOfType<gpu::GPUFuncOp>();
2479 privatize->getParentOfType<acc::ComputeRegionOp>() != computeRegion) {
2480 return privatize.getResult();
2483 for (mlir::acc::GPUParallelDimAttr parDim : parDimsPair.first) {
2484 if (parDim.isThreadX() &&
2485 canUseStackAlloca(baseTy, loc,
options.maxThreadPrivateStack)) {
2486 auto alloca = memref::AllocaOp::create(rewriter, loc, baseTy);
2487 mapping.
map(privatize.getResult(), alloca.getResult());
2488 return alloca.getResult();
2493 return privatize.getResult();
2501 bool threadYIsActive =
2502 llvm::any_of(parDimsPair.first, [](mlir::acc::GPUParallelDimAttr parDim) {
2503 return parDim.isThreadY();
2516 bool needsWorkgroupBarrier =
false;
2518 FunctionOpInterface funcOp =
2519 computeRegion->getParentOfType<FunctionOpInterface>();
2520 mlir::acc::GPUParallelDimAttr routineParDim =
2521 getSpecializedRoutineDim(funcOp, defaultPolicy);
2522 if (routineParDim.isThreadX()) {
2525 threadYIsActive =
true;
2526 }
else if (routineParDim.isThreadY()) {
2529 needsWorkgroupBarrier =
true;
2530 }
else if (routineParDim.isAnyBlock()) {
2531 needsWorkgroupBarrier =
true;
2536 for (
auto dynamicSize : privatize.getDynamicSizes()) {
2538 mappedDynamicSizes.push_back(mappedDynamicSize);
2541 computeRegion.isEffectivelySerial()) {
2542 if (mappedDynamicSizes.empty()) {
2545 memref::AllocaOp::create(rewriter, privatize->getLoc(), baseTy);
2546 mapping.
map(privatize.getResult(), alloca.getResult());
2547 return alloca.getResult();
2550 auto alloc = memref::AllocOp::create(rewriter, privatize->getLoc(), baseTy,
2551 mappedDynamicSizes);
2555 Block &parentBlock = *alloc->getBlock();
2558 memref::DeallocOp::create(rewriter, privatize->getLoc(), alloc);
2562 mapping.
map(privatize.getResult(), alloc.getResult());
2563 return alloc.getResult();
2569 for (
auto parDim : parDimsPair.second) {
2571 if (threadYIsActive && parDim.isThreadY())
2573 predicateDims.push_back(parDim);
2575 Value predicate = emitPredicate(loc, predicateDims);
2577 predicate = arith::ConstantOp::create(
2580 auto ifOp = scf::IfOp::create(rewriter, loc, predicate,
2582 Region &thenRegion = ifOp.getThenRegion();
2585 auto mem = memref::AllocOp::create(rewriter, privatize->getLoc(), baseTy,
2586 mappedDynamicSizes);
2588 gpu::AddressSpaceAttr sharedMemoryAddressSpace = gpu::AddressSpaceAttr::get(
2589 computeRegion->getContext(), gpu::GPUDialect::getWorkgroupAddressSpace());
2593 constexpr int64_t kMaxThreadY = 32;
2594 MemRefType sharedMemTy =
2596 ? MemRefType::get({kMaxThreadY}, baseTy, MemRefLayoutAttrInterface{},
2597 sharedMemoryAddressSpace)
2598 : MemRefType::get({}, baseTy, MemRefLayoutAttrInterface{},
2599 sharedMemoryAddressSpace);
2602 bool reuseBroadcast = !gpuFuncOp.isKernel();
2605 reuseBroadcast ? privatizeBroadcastCache.find(sharedMemTy)
2606 : privatizeBroadcastCache.end();
2607 if (reuseBroadcast && cachedSlot != privatizeBroadcastCache.end()) {
2608 alloca = cachedSlot->second;
2611 mlir::acc::GPUParallelDimAttr dim =
2612 needsWorkgroupBarrier
2613 ? mlir::acc::GPUParallelDimAttr::threadYDim(rewriter.
getContext())
2614 : mlir::acc::GPUParallelDimAttr::threadXDim(rewriter.
getContext());
2616 loc, mlir::acc::GPUParallelDimsAttr::get(rewriter.
getContext(), {dim}));
2618 alloca = gpuFuncOp.addWorkgroupAttribution(sharedMemTy,
2623 unsigned index = gpuFuncOp.getNumWorkgroupAttributions() - 1;
2624 gpuFuncOp.setWorkgroupAttributionAttr(
index,
2625 LLVM::LLVMDialect::getAlignAttrName(),
2628 privatizeBroadcastCache[sharedMemTy] = alloca;
2631 if (threadYIsActive) {
2632 Value threadYId = getThreadId(loc, gpu::Dimension::y);
2633 memref::StoreOp::create(rewriter, privatize->getLoc(), mem, alloca,
2636 memref::StoreOp::create(rewriter, privatize->getLoc(), mem, alloca);
2642 if (needsWorkgroupBarrier) {
2643 mlir::acc::GPUParallelDimsAttr threadYDimsAttr =
2644 mlir::acc::GPUParallelDimsAttr::get(
2646 {mlir::acc::GPUParallelDimAttr::threadYDim(rewriter.getContext())});
2647 createBarrier(loc, threadYDimsAttr);
2650 mlir::acc::GPUParallelDimsAttr threadXDimsAttr =
2651 mlir::acc::GPUParallelDimsAttr::get(
2653 {mlir::acc::GPUParallelDimAttr::threadXDim(rewriter.getContext())});
2654 createBarrier(loc, threadXDimsAttr);
2658 if (threadYIsActive) {
2659 Value threadYId = getThreadId(loc, gpu::Dimension::y);
2660 load = memref::LoadOp::create(rewriter, privatize->getLoc(), baseTy, alloca,
2664 memref::LoadOp::create(rewriter, privatize->getLoc(), baseTy, alloca);
2667 mapping.
map(privatize.getResult(),
load);
2671 if (!privatize->getParentOfType<acc::ComputeRegionOp>())
2675 if (needsWorkgroupBarrier) {
2676 mlir::acc::GPUParallelDimsAttr workerDimsAttr =
2677 mlir::acc::GPUParallelDimsAttr::get(
2679 {mlir::acc::GPUParallelDimAttr::threadYDim(rewriter.getContext())});
2680 createBarrier(loc, workerDimsAttr);
2682 mlir::acc::GPUParallelDimsAttr vectorDimsAttr =
2683 mlir::acc::GPUParallelDimsAttr::get(
2685 {mlir::acc::GPUParallelDimAttr::threadXDim(rewriter.getContext())});
2686 createBarrier(loc, vectorDimsAttr);
2688 auto ifOp2 = scf::IfOp::create(rewriter, loc, predicate,
2690 Region &thenRegion2 = ifOp2.getThenRegion();
2693 memref::DeallocOp::create(rewriter, privatize->getLoc(),
load);
2704void ACCCGToGPULowering::processPrivateLocal(
2705 acc::PrivateLocalOp privateLocal, std::optional<int64_t> sharedMemCopies) {
2706 LLVM_DEBUG(llvm::dbgs() <<
"processing private local: ";
2707 privateLocal->print(llvm::dbgs()); llvm::dbgs() <<
"\n");
2708 Location loc = privateLocal.getLoc();
2709 acc::PrivateType privTy =
2710 cast<acc::PrivateType>(privateLocal.getPrivatized().getType());
2711 ModuleOp module = computeRegion->getParentOfType<ModuleOp>();
2713 MemRefType byteMemrefTy =
2714 MemRefType::get({ShapedType::kDynamic}, rewriter.
getI8Type());
2716 acc::PrivatizeOp privatizeOp =
getPrivatizeOp(privateLocal, computeRegion);
2718 if (privatizeOp->getParentOfType<acc::ComputeRegionOp>() == computeRegion) {
2721 Value result = castPointerLikeTypeIfNeeded(rewriter, loc, inputMem,
2723 mapping.
map(privateLocal.getResult(),
result);
2731 acc::ReductionAccumulateArrayOp arrayAccum =
2732 perThreadArrayReductionAccum(privateLocal.getResult());
2734 (arrayAccum && storageHasThreadX(privateLocal, computeRegion))) &&
2735 canUseStackAlloca(baseTy, loc,
options.maxThreadPrivateStack)) {
2736 Value alloca = memref::AllocaOp::create(rewriter, loc, baseTy);
2738 FailureOr<arith::AtomicRMWKind> kind = getReductionKind(
2739 arrayAccum.getReductionOperator(), baseTy.getElementType(), loc);
2742 initPerThreadArrayAccum(rewriter, loc, alloca, baseTy, *kind);
2744 Value mem = castPointerLikeTypeIfNeeded(rewriter, loc, alloca,
2746 mapping.
map(privateLocal.getResult(), mem);
2752 std::optional<int64_t>
copies =
2753 sharedMemCopies ? sharedMemCopies
2754 : isEligibleForSharedMemory(privateLocal, baseTy);
2757 int64_t elementSize = getElementSizeInBytes(loc, baseTy.getElementType());
2759 for (
int64_t dim : baseTy.getShape())
2761 int64_t upperBound = elementSize * numElements * numCopies;
2763 if (tryAllocateSharedMemory(upperBound)) {
2764 std::string varName =
2766 sharedMemPrivateVarNames.push_back(varName.empty() ?
"(*)" : varName);
2768 gpu::AddressSpaceAttr workgroupAS = gpu::AddressSpaceAttr::get(
2769 computeRegion->getContext(),
2770 gpu::GPUDialect::getWorkgroupAddressSpace());
2771 MemRefType sharedMemTy =
2772 MemRefType::get(baseTy.getShape(), baseTy.getElementType(),
2773 MemRefLayoutAttrInterface{}, workgroupAS);
2774 Value sharedMem = acc::GPUSharedMemoryOp::create(
2780 castPointerLikeTypeIfNeeded(rewriter, loc, sharedMem, baseTy);
2781 Value result = castPointerLikeTypeIfNeeded(rewriter, loc, mem,
2784 mapping.
map(privateLocal.getResult(),
result);
2791 inputMem = processPrivatize(privatizeOp);
2795 assert(inputMem &&
"expected input mem to be mapped");
2796 Value result = castPointerLikeTypeIfNeeded(rewriter, loc, inputMem,
2798 mapping.
map(privateLocal.getResult(),
result);
2817 std::pair<SmallVector<mlir::acc::GPUParallelDimAttr>,
2819 parDimsPair = computeActiveAndInactiveParDims(privateLocal,
nullptr);
2820 acc::ReductionAccumulateArrayOp arrayAccum =
2821 perThreadArrayReductionAccum(privateLocal.getResult());
2822 for (mlir::acc::GPUParallelDimAttr parDim : parDimsPair.first) {
2823 if ((parDim.isThreadX() ||
2824 (arrayAccum && storageHasThreadX(privateLocal, computeRegion))) &&
2825 canUseStackAlloca(baseTy, loc,
options.maxThreadPrivateStack)) {
2826 Value alloca = memref::AllocaOp::create(rewriter, loc, baseTy);
2828 FailureOr<arith::AtomicRMWKind> kind = getReductionKind(
2829 arrayAccum.getReductionOperator(), baseTy.getElementType(), loc);
2832 initPerThreadArrayAccum(rewriter, loc, alloca, baseTy, *kind);
2834 Value mem = castPointerLikeTypeIfNeeded(rewriter, loc, alloca,
2836 mapping.
map(privateLocal.getResult(), mem);
2840 if (parDimsPair.first.empty()) {
2844 mlir::acc::GPUParallelDimAttr::blockXDim(privateLocal.getContext()));
2846 for (mlir::acc::GPUParallelDimAttr parDim : parDimsPair.first) {
2847 gpu::Processor gpuProc = parDim.getProcessor();
2848 Value gpuSize = getGPUSizeFor(gpuProc);
2849 viewDynSizes.push_back(gpuSize);
2850 viewShape.push_back(ShapedType::kDynamic);
2851 subviewOffset.push_back(getGPUThreadIdFor(gpuProc));
2856 resolvePrivateLocalDynamicExtents(privateLocal);
2858 unsigned dynIdx = 0;
2859 for (
auto innerDim : baseTy.getShape()) {
2861 viewShape.push_back(innerDim);
2862 subviewShape.push_back(innerDim);
2863 if (innerDim == ShapedType::kDynamic) {
2864 assert(dynIdx < innerDynSizes.size() &&
2865 "not enough dynamic sizes for inner dimensions");
2866 viewDynSizes.push_back(innerDynSizes[dynIdx]);
2867 subviewSizes.push_back(innerDynSizes[dynIdx]);
2870 subviewSizes.push_back(rewriter.
getIndexAttr(innerDim));
2876 for (
auto innerDimIt = baseTy.getShape().rbegin();
2877 innerDimIt != baseTy.getShape().rend(); ++innerDimIt) {
2878 int64_t innerDim = *innerDimIt;
2879 subviewStrides.insert(subviewStrides.begin(), stride);
2880 if (innerDim == ShapedType::kDynamic)
2881 stride = ShapedType::kDynamic;
2882 if (stride != ShapedType::kDynamic)
2887 castPointerLikeTypeIfNeeded(rewriter, loc, inputMem, byteMemrefTy);
2889 MemRefType viewType = MemRefType::get(viewShape, baseTy.getElementType());
2890 auto view = memref::ViewOp::create(rewriter, loc, viewType, memBuffer,
2891 c0.getResult(), viewDynSizes);
2894 StridedLayoutAttr stridedLayout = StridedLayoutAttr::get(
2895 computeRegion->getContext(), ShapedType::kDynamic, subviewStrides);
2896 MemRefType subviewType =
2897 MemRefType::get(subviewShape, baseTy.getElementType(), stridedLayout);
2899 Value subview = memref::SubViewOp::create(rewriter, loc, subviewType, view,
2900 subviewOffset, subviewSizes, ones);
2905 memref::ExtractStridedMetadataOp::create(rewriter, loc, subview);
2907 rewriter, loc, getElementSizeInBytes(loc, baseTy.getElementType()));
2909 arith::MulIOp::create(rewriter, loc, metadata.getOffset(), elementBytes);
2910 Value privateView = memref::ViewOp::create(rewriter, loc, baseTy, memBuffer,
2911 byteOffset, innerDynSizes);
2912 Value result = castPointerLikeTypeIfNeeded(rewriter, loc, privateView,
2914 mapping.
map(privateLocal.getResult(),
result);
2918template <
typename LoopOp>
2919void ACCCGToGPULowering::processSeqLoop(LoopOp loopOp) {
2923 LLVM_DEBUG(llvm::dbgs() <<
"processing seq loop: ";
2924 loopOp->print(llvm::dbgs()); llvm::dbgs() <<
"\n");
2926 ModuleOp module = computeRegion->getParentOfType<ModuleOp>();
2927 for (
auto &bodyOp : loopOp.getBody()->getOperations()) {
2928 if (acc::PrivateLocalOp privateLocal =
2929 dyn_cast<acc::PrivateLocalOp>(&bodyOp)) {
2930 acc::PrivateType privTy =
2931 cast<acc::PrivateType>(privateLocal.getPrivatized().getType());
2933 if (
auto copies = isEligibleForSharedMemory(privateLocal, baseTy)) {
2934 processPrivateLocal(privateLocal,
copies);
2935 preProcessedPrivateLocals.insert(privateLocal.getOperation());
2943 &newLoop.getRegion(), newLoop.getRegion().begin(),
2944 loopOp.getBody()->getArgumentTypes(),
2951 assert(blockArgs.size() &&
"expected block arguments for loop");
2952 mapping.
map(blockArgs, newLoop.getBody()->getArguments());
2954 for (
auto &bodyOp : loopOp.getBody()->getOperations()) {
2955 if (preProcessedPrivateLocals.contains(&bodyOp))
2960 mapping.
map(loopOp.getResults(), newLoop.getResults());
2965 if (hasTrailingSideEffectSiblings(loopOp.getOperation()))
2966 deferredBarrierSeqLoops.push_back(loopOp.getOperation());
2968 createBarrierAfterSeqLoop(loopOp.getOperation());
2971void ACCCGToGPULowering::flushDeferredBarriersBefore(
Operation *beforeOp) {
2974 for (
Operation *loopOp : deferredBarrierSeqLoops)
2976 toFlush.push_back(loopOp);
2977 if (toFlush.empty())
2982 createBarrierAfterSeqLoop(loopOp);
2983 deferredBarrierSeqLoops.erase(
2984 std::remove_if(deferredBarrierSeqLoops.begin(),
2985 deferredBarrierSeqLoops.end(),
2987 return loopOp->getBlock() == block &&
2988 loopOp->isBeforeInBlock(beforeOp);
2990 deferredBarrierSeqLoops.end());
2995void ACCCGToGPULowering::processParallelOp(scf::ParallelOp parallelOp) {
2996 LLVM_DEBUG(llvm::dbgs() <<
"processing par loop: ";
2997 parallelOp->print(llvm::dbgs()); llvm::dbgs() <<
"\n");
2999 "requires parallel dimensions attribute");
3000 mlir::acc::GPUParallelDimsAttr pDimsAttr =
3004 assert(pDimsAttr.getArray().size() == 1 &&
3005 "expected a single par dim in acc-cg-to-gpu");
3006 assert(parallelOp.getInductionVars().size() == 1 &&
3007 "expected a single induction variable in acc-cg-to-gpu");
3009 mlir::acc::GPUParallelDimAttr parDim = pDimsAttr.getArray().front();
3011 bool savedGridStrideFlag = insideAccumulateGridStride;
3012 Value savedReductionBuf = reductionSharedBuf;
3013 if (parDim.isThreadX()) {
3015 parallelOp.getBody()->walk([&](acc::ReductionAccumulateOp accOp) {
3016 bool hasBlockDim =
false;
3017 bool hasThreadDim =
false;
3018 for (
auto d : accOp.getParDims().getArray()) {
3021 if (d.isThreadX() || d.isThreadY())
3022 hasThreadDim =
true;
3024 if (hasThreadDim && !hasBlockDim) {
3031 insideAccumulateGridStride =
true;
3035 auto processLoopBody = [&]() {
3037 for (
auto &bodyOp : parallelOp.getBody()->getOperations()) {
3040 flushDeferredBarriersBefore(&bodyOp);
3048 if (parDim.isSeq()) {
3049 LLVM_DEBUG(llvm::dbgs() <<
"loop: parDim: " << parDim <<
" as gpu seq\n");
3055 bool needsAtomicReduction =
false;
3056 bool hasAccumulateSibling =
false;
3057 if (scf::ParallelOp parentPar =
3058 parallelOp->getParentOfType<scf::ParallelOp>()) {
3059 if (mlir::acc::GPUParallelDimsAttr parentDims =
3061 parentDims && llvm::any_of(parentDims.getArray(),
3062 [](
auto d) { return d.isThreadX(); })) {
3063 for (
auto &op : parentPar.getBody()->getOperations()) {
3064 if (acc::ReductionAccumulateOp
acc =
3065 dyn_cast<acc::ReductionAccumulateOp>(op)) {
3066 bool hasBlockDim =
false;
3067 bool hasThreadDim =
false;
3068 for (
auto d :
acc.getParDims().getArray()) {
3071 if (d.isThreadX() || d.isThreadY())
3072 hasThreadDim =
true;
3074 if (hasThreadDim && !hasBlockDim)
3075 hasAccumulateSibling =
true;
3080 if (insideAccumulateGridStride || hasAccumulateSibling) {
3081 for (
auto launchArg : computeRegion.getLaunchArgs()) {
3082 if (acc::ParWidthOp pw = launchArg.getDefiningOp<acc::ParWidthOp>()) {
3083 if (pw.getParDim().isThreadX()) {
3085 needsAtomicReduction = (*cval >=
options.subgroupSize);
3087 needsAtomicReduction =
true;
3093 if (needsAtomicReduction && !reductionSharedBuf) {
3095 parallelOp.getBody()->
walk([&](acc::ReductionAccumulateOp accOp) {
3096 Type t = accOp.getValue().getType();
3097 if (isa<FloatType, IntegerType>(t))
3102 needsAtomicReduction =
false;
3104 if (needsAtomicReduction && !reductionSharedBuf) {
3105 Location seqLoc = parallelOp->getLoc();
3106 gpu::AddressSpaceAttr workgroupAS = gpu::AddressSpaceAttr::get(
3107 computeRegion->getContext(),
3108 gpu::GPUDialect::getWorkgroupAddressSpace());
3110 parallelOp.getBody()->
walk([&](acc::ReductionAccumulateOp accOp) {
3111 Type t = accOp.getValue().getType();
3112 if (isa<FloatType, IntegerType>(t))
3116 assert(elemTy &&
"expected scalar reduction element type");
3118 MemRefType bufTy = MemRefType::get({
options.subgroupSize}, elemTy,
3120 reductionSharedBuf = acc::GPUSharedMemoryOp::create(
3124 Value tidY = getThreadId(seqLoc, gpu::Dimension::y);
3126 if (isa<FloatType>(elemTy)) {
3127 identity = arith::ConstantOp::create(
3128 rewriter, seqLoc, elemTy, rewriter.
getFloatAttr(elemTy, 0.0));
3132 memref::StoreOp::create(rewriter, seqLoc, identity, reductionSharedBuf,
3134 createPerRowBarrier(seqLoc);
3136 processSeqLoop(parallelOp);
3137 loopReductions.push_back(parallelOp);
3139 LLVM_DEBUG(llvm::dbgs()
3140 <<
"processing loop: parDim: " << parDim <<
" as gpu par\n");
3144 Value gpuThreadId = getGPUThreadIdFor(parDim.getProcessor());
3145 mapping.
map(parallelOp.getInductionVars()[0], gpuThreadId);
3152 llvm::for_each(parallelOp.getResults(), [&](
Value v) {
3153 Type valTy = v.getType();
3154 TypedAttr zeroAttr = rewriter.getZeroAttr(valTy);
3155 auto zero = arith::ConstantOp::create(rewriter, parallelOp->getLoc(),
3157 mapping.map(v, zero);
3159 loopReductions.push_back(parallelOp);
3161 insideAccumulateGridStride = savedGridStrideFlag;
3162 if (!insideAccumulateGridStride && !savedReductionBuf)
3163 reductionSharedBuf =
Value();
3167static gpu::AllReduceOperation
3168getAllReduceOperation(arith::AtomicRMWKind kind) {
3170 case arith::AtomicRMWKind::addf:
3171 case arith::AtomicRMWKind::addi:
3172 return gpu::AllReduceOperation::ADD;
3173 case arith::AtomicRMWKind::mulf:
3174 case arith::AtomicRMWKind::muli:
3175 return gpu::AllReduceOperation::MUL;
3176 case arith::AtomicRMWKind::minu:
3177 return gpu::AllReduceOperation::MINUI;
3178 case arith::AtomicRMWKind::mins:
3179 return gpu::AllReduceOperation::MINSI;
3180 case arith::AtomicRMWKind::minnumf:
3181 return gpu::AllReduceOperation::MINNUMF;
3182 case arith::AtomicRMWKind::maxu:
3183 return gpu::AllReduceOperation::MAXUI;
3184 case arith::AtomicRMWKind::maxs:
3185 return gpu::AllReduceOperation::MAXSI;
3186 case arith::AtomicRMWKind::maxnumf:
3187 return gpu::AllReduceOperation::MAXNUMF;
3188 case arith::AtomicRMWKind::ori:
3189 return gpu::AllReduceOperation::OR;
3190 case arith::AtomicRMWKind::andi:
3191 return gpu::AllReduceOperation::AND;
3192 case arith::AtomicRMWKind::xori:
3193 return gpu::AllReduceOperation::XOR;
3194 case arith::AtomicRMWKind::minimumf:
3195 return gpu::AllReduceOperation::MINIMUMF;
3196 case arith::AtomicRMWKind::maximumf:
3197 return gpu::AllReduceOperation::MAXIMUMF;
3198 case arith::AtomicRMWKind::assign:
3201 llvm_unreachable(
"unsupported atomic kind");
3204void ACCCGToGPULowering::constructAtomicAccumulation(
3206 arith::AtomicRMWKind kind) {
3207 assert(!
memref.getDefiningOp<memref::AllocaOp>() &&
3208 "cannot lower atomic accumulation on an stack variable");
3220 MemRefType memrefTy = cast<MemRefType>(
memref.getType());
3221 unsigned rank = memrefTy.getRank();
3222 assert(
indices.size() == rank &&
"expected one index per memref dimension");
3226 target = memref::SubViewOp::create(rewriter, loc,
memref, offsets, sizes,
3230 auto atomicUpdateOp =
3231 acc::AtomicUpdateOp::create(rewriter, loc,
target,
Value());
3232 Region ®ion = atomicUpdateOp->getRegion(0);
3236 Value reductionExpr =
3238 acc::YieldOp::create(rewriter, loc, reductionExpr);
3242void ACCCGToGPULowering::createGPUAllReduceOp(
3245 bool isPerThreadPrivateTarget) {
3246 gpu::AllReduceOperationAttr attr = gpu::AllReduceOperationAttr::get(
3247 computeRegion->getContext(), getAllReduceOperation(kind));
3248 auto allReduceOp = gpu::AllReduceOp::create(rewriter, loc, input, attr,
true);
3258 bool hasThreadX =
false;
3259 for (
auto parDim : parDimsAttr.getArray()) {
3260 if (parDim.isAnyBlock())
3262 if (parDim.isThreadX())
3264 if (computeRegion.getLaunchArg(parDim) ||
3266 inactiveParDims.push_back(parDim);
3272 inactiveParDims.push_back(mlir::acc::GPUParallelDimAttr::threadXDim(ctx));
3273 Value predicate = emitPredicate(loc, inactiveParDims);
3280 bool isPerThreadPrivate = isPerThreadPrivateTarget ||
3281 isa_and_nonnull<memref::AllocaOp>(
3282 unwrapMemRefConversion(
memref).getDefiningOp());
3285 if (predicate && !isPerThreadPrivate) {
3287 scf::IfOp::create(rewriter, loc, predicate,
false);
3288 Region &thenRegion = ifOp.getThenRegion();
3292 memref::StoreOp::create(rewriter, loc, allReduceOp,
memref,
indices);
3293 if (predicate && !isPerThreadPrivate)
3298 reductionAccumValue[
memref] = allReduceOp;
3301void ACCCGToGPULowering::postprocessAccumulateOp(
3302 acc::ReductionAccumulateOp op) {
3311 bool hasThreadDim =
false;
3313 for (
auto parDim : op.getParDims().getArray()) {
3314 if (!parDim.isAnyBlock()) {
3315 hasThreadDim =
true;
3316 threadParDims.push_back(parDim);
3320 std::optional<arith::AtomicRMWKind> kind;
3322 FailureOr<arith::AtomicRMWKind> kindOr = getReductionKind(
3323 op.getReductionOperator(), op.getValue().getType(), loc);
3329 if (hasThreadDim && reductionSharedBuf &&
3331 cast<MemRefType>(reductionSharedBuf.
getType()).getElementType()) {
3332 Value val = op.getValue();
3333 Value mem = op.getMemref();
3334 Value tidY = getThreadId(loc, gpu::Dimension::y);
3335 memref::AtomicRMWOp::create(rewriter, loc, *kind, val, reductionSharedBuf,
3337 createPerRowBarrier(loc);
3339 memref::LoadOp::create(rewriter, loc, reductionSharedBuf, tidY);
3340 memref::StoreOp::create(rewriter, loc,
result, mem);
3341 reductionAccumValue[mem] =
result;
3342 }
else if (hasThreadDim) {
3343 createGPUAllReduceOp(loc, op.getValue(), op.getMemref(), *kind,
3351 bool isPerThreadPrivate = isa_and_nonnull<memref::AllocaOp>(
3352 unwrapMemRefConversion(mem).getDefiningOp());
3353 if (!isPerThreadPrivate) {
3355 for (
auto parDim : computeRegion.getLaunchParDims())
3356 if (!parDim.isAnyBlock())
3357 predDims.push_back(parDim);
3358 if (predDims.empty()) {
3359 predDims.push_back(mlir::acc::GPUParallelDimAttr::threadXDim(
3360 computeRegion->getContext()));
3362 Value predicate = emitPredicate(loc, predDims);
3364 scf::IfOp::create(rewriter, loc, predicate,
false);
3366 memref::StoreOp::create(rewriter, loc, val, mem);
3369 memref::StoreOp::create(rewriter, loc, val, mem);
3377void ACCCGToGPULowering::postprocessLoopReduction(scf::ParallelOp parLoop) {
3378 if (parLoop.getNumReductions() == 0)
3381 for (
unsigned i = 0; i < parLoop.getNumResults(); ++i) {
3384 if (acc::ReductionAccumulateOp accumulateOp =
3385 dyn_cast<acc::ReductionAccumulateOp>(user)) {
3386 postprocessAccumulateOp(accumulateOp);
3392void ACCCGToGPULowering::processExecuteRegion(scf::ExecuteRegionOp op) {
3393 LLVM_DEBUG(llvm::dbgs() <<
"processing execute region op: ";
3394 op->print(llvm::dbgs()); llvm::dbgs() <<
"\n");
3396 auto types = op.getResultTypes();
3397 Region &oldRegion = op.getRegion();
3399 auto executeRegionOp = scf::ExecuteRegionOp::create(rewriter, loc, types);
3400 Region ®ion = executeRegionOp.getRegion();
3405 blockMap[&oldRegion.
front()] = ®ion.
front();
3409 for (
auto &oldBlock : llvm::drop_begin(oldRegion.
getBlocks())) {
3410 TypeRange argTypes = oldBlock.getArgumentTypes();
3411 size_t numArgs = argTypes.size();
3415 blockMap[&oldBlock] = newBlock;
3422 for (
auto [oldBlock, newBlock] :
3426 for (
auto &bodyOp : oldBlock.getOperations()) {
3434 Operation *oldTerminator = oldBlock.getTerminator();
3436 Operation *newTerminator = rewriter.
clone(*oldTerminator, mapping);
3441 Block *newDest = blockMap.lookup(oldDest);
3442 assert(newDest &&
"Successor block must be in blockMap");
3446 mapping.
map(op->getResults(), executeRegionOp->getResults());
3450void ACCCGToGPULowering::processAccumulateOp(acc::ReductionAccumulateOp op) {
3451 LLVM_DEBUG(llvm::dbgs() <<
"processing accumulate op: " << *op <<
"\n");
3452 Value accumulateValue = op.getValue();
3453 if (reductionSharedBuf &&
3455 cast<MemRefType>(reductionSharedBuf.
getType()).getElementType()) {
3457 FailureOr<arith::AtomicRMWKind> kind = getReductionKind(
3458 op.getReductionOperator(), accumulateValue.
getType(), loc);
3463 Value tidY = getThreadId(loc, gpu::Dimension::y);
3464 memref::AtomicRMWOp::create(rewriter, loc, *kind, mappedValue,
3466 createPerRowBarrier(loc);
3468 memref::LoadOp::create(rewriter, loc, reductionSharedBuf, tidY);
3469 memref::StoreOp::create(rewriter, loc,
result,
memref);
3476 }
else if (isRedundantChainAccumulate(op)) {
3482 LLVM_DEBUG(llvm::dbgs() <<
" skipped: redundant chain accumulate\n");
3483 gpu::BarrierOp::create(rewriter, op->getLoc());
3487 FailureOr<arith::AtomicRMWKind> kind = getReductionKind(
3488 op.getReductionOperator(), accumulateValue.
getType(), op.getLoc());
3491 createGPUAllReduceOp(op->getLoc(), mappedValue,
memref, *kind,
3502 if (!v || !visited.insert(v).second)
3504 if (llvm::is_contained(threadIds, v))
3506 if (
auto arg = dyn_cast<BlockArgument>(v)) {
3508 unsigned dim = arg.getArgNumber();
3509 if (
auto loop = dyn_cast<scf::ParallelOp>(owner)) {
3510 if (dim >= loop.getLowerBound().size())
3512 return isThreadVarying(loop.getLowerBound()[dim], threadIds, visited) ||
3513 isThreadVarying(loop.getStep()[dim], threadIds, visited);
3515 if (
auto loop = dyn_cast<scf::ForOp>(owner))
3517 (isThreadVarying(loop.getLowerBound(), threadIds, visited) ||
3518 isThreadVarying(loop.getStep(), threadIds, visited));
3524 if (isa<gpu::ThreadIdOp, gpu::LaneIdOp>(def))
3527 return isThreadVarying(o, threadIds, visited);
3534 if (
auto cast = dyn_cast<memref::MemorySpaceCastOp>(op)) {
3535 v = cast.getSource();
3538 if (
auto viewLike = dyn_cast<ViewLikeOpInterface>(op)) {
3539 if (isa<MemRefType>(viewLike.getViewSource().getType())) {
3540 v = viewLike.getViewSource();
3551static Value matchAccumulatorUpdate(memref::StoreOp store,
Value accum) {
3552 if (accumulatorRoot(store.getMemRef()) != accum)
3554 Operation *combine = store.getValueToStore().getDefiningOp();
3555 if (!combine || combine->getNumOperands() != 2)
3557 for (
unsigned i = 0; i != 2; ++i) {
3558 auto load = combine->getOperand(i).getDefiningOp<memref::LoadOp>();
3559 if (!
load || accumulatorRoot(
load.getMemRef()) != accum)
3561 if (!llvm::equal(
load.getIndices(), store.getIndices()))
3563 return combine->getOperand(1 - i);
3571static void atomicizeSharedAccumulatorUpdates(
Value accum,
3572 arith::AtomicRMWKind kind,
3579 while (!worklist.empty()) {
3580 Value cur = worklist.pop_back_val();
3581 if (!seen.insert(cur).second)
3584 if (
auto store = dyn_cast<memref::StoreOp>(user))
3585 stores.push_back(store);
3586 else if (isa<ViewLikeOpInterface, memref::MemorySpaceCastOp>(user))
3587 llvm::append_range(worklist, user->getResults());
3591 for (memref::StoreOp store : stores) {
3592 Value contribution = matchAccumulatorUpdate(store, accum);
3597 if (llvm::any_of(store.getIndices(), [&](
Value idx) {
3598 DenseSet<Value> visited;
3599 return isThreadVarying(idx, threadIds, visited);
3602 Operation *combine = store.getValueToStore().getDefiningOp();
3604 memref::AtomicRMWOp::create(rewriter, store.getLoc(), kind, contribution,
3605 store.getMemRef(), store.getIndices());
3607 if (combine && combine->use_empty())
3612void ACCCGToGPULowering::processAccumulateArrayOp(
3613 acc::ReductionAccumulateArrayOp op) {
3614 LLVM_DEBUG(llvm::dbgs() <<
"processing accumulate array op: " << *op <<
"\n");
3618 MemRefType memrefTy = dyn_cast<MemRefType>(
memref.getType());
3620 (
void)accSupport.
emitNYI(loc,
"reduction: non-MemRefTy accumulate array");
3623 FailureOr<arith::AtomicRMWKind> kindOr = getReductionKind(
3624 op.getReductionOperator(), memrefTy.getElementType(), loc);
3627 arith::AtomicRMWKind kind = *kindOr;
3632 .getDefiningOp<acc::DataBoundsOp>();
3633 assert(boundsOp &&
"expected acc.bounds defining op for array accumulate");
3634 auto eraseDeadBounds = [&] {
3635 if (boundsOp->use_empty())
3639 bool hasThreadDim =
false;
3640 bool hasBlockDim =
false;
3641 for (
auto pd : op.getParDims().getArray()) {
3642 hasThreadDim |= pd.isAnyThread();
3643 hasBlockDim |= pd.isAnyBlock();
3648 if (hasBlockDim && !hasThreadDim) {
3656 bool regionLaunchesBlocks = llvm::any_of(
3657 computeRegion.getLaunchParDims(),
3658 [](mlir::acc::GPUParallelDimAttr d) { return d.isAnyBlock(); });
3659 if (!reductionHasBlockContext(op) && regionLaunchesBlocks) {
3661 loc,
"reduction: thread-only array reduction accumulate");
3673 auto storageIsThreadXPrivate = [&](
Value v) ->
bool {
3674 acc::PrivateLocalOp privateLocal = getPrivateLocalForMemref(v);
3675 GPUParallelDimsAttr dims =
3676 privateLocal ? getPrivateParDims(privateLocal, computeRegion)
3677 : GPUParallelDimsAttr();
3679 if (
Operation *root = unwrapMemRefConversion(v).getDefiningOp())
3683 llvm::any_of(dims.getArray(), [](
auto d) { return d.isThreadX(); });
3686 bool isSharedStorage = isa_and_nonnull<memref::AllocOp>(rootOp) ||
3687 isa_and_nonnull<acc::GPUSharedMemoryOp>(rootOp);
3688 if (
auto addrSpace = dyn_cast_if_present<gpu::AddressSpaceAttr>(
3689 memrefTy.getMemorySpace())) {
3691 addrSpace.getValue() == gpu::GPUDialect::getWorkgroupAddressSpace();
3693 bool isPerThreadPrivate =
3694 !isSharedStorage && storageIsThreadXPrivate(op.getMemref()) &&
3695 (memrefTy.hasStaticShape()
3696 ? canUseStackAlloca(memrefTy, loc,
options.maxThreadPrivateStack)
3697 : llvm::any_of(op.getParDims().getArray(),
3698 [](mlir::acc::GPUParallelDimAttr d) {
3699 return d.isThreadX();
3701 if (!isPerThreadPrivate) {
3707 if (
Value xId = getGPUThreadIdFor(gpu::Processor::ThreadX))
3708 threadIds.push_back(xId);
3709 if (
Value yId = getGPUThreadIdFor(gpu::Processor::ThreadY))
3710 threadIds.push_back(yId);
3711 if (
Value zId = getGPUThreadIdFor(gpu::Processor::ThreadZ))
3712 threadIds.push_back(zId);
3713 atomicizeSharedAccumulatorUpdates(accumulatorRoot(
memref), kind, threadIds,
3723 return arith::IndexCastOp::create(rewriter, loc, rewriter.
getIndexType(),
3730 boundsOp.getLowerbound() ? toIndex(boundsOp.getLowerbound()) : zero;
3731 Value step = boundsOp.getStride() ? toIndex(boundsOp.getStride()) : one;
3736 if (boundsOp.getExtent()) {
3737 Value span = arith::MulIOp::create(rewriter, loc,
3738 toIndex(boundsOp.getExtent()), step);
3739 ub = arith::AddIOp::create(rewriter, loc, lb, span);
3741 assert(boundsOp.getUpperbound() &&
3742 "acc.bounds must specify an extent or upperbound");
3743 ub = arith::AddIOp::create(rewriter, loc, toIndex(boundsOp.getUpperbound()),
3748 auto forOp = scf::ForOp::create(rewriter, loc, lb,
ub, step);
3752 Value iv = forOp.getInductionVar();
3754 if (memrefTy.getRank() > 1) {
3755 indices.resize(memrefTy.getRank());
3756 Value linearIndex = iv;
3757 for (
int64_t dim = memrefTy.getRank() - 1; dim >= 0; --dim) {
3759 memrefTy.isDynamicDim(dim)
3760 ? memref::DimOp::create(rewriter, loc,
memref, dim).getResult()
3762 memrefTy.getDimSize(dim))
3765 arith::RemUIOp::create(rewriter, loc, linearIndex, dimSize);
3768 arith::DivUIOp::create(rewriter, loc, linearIndex, dimSize);
3772 createGPUAllReduceOp(loc, elem,
memref, kind, op.getParDims(),
indices,
3779void ACCCGToGPULowering::processReductionOp(acc::ReductionInitOp op) {
3782 if (acc::YieldOp yieldOp = dyn_cast<acc::YieldOp>(innerOp)) {
3783 op.getResult().replaceAllUsesWith(mapping.
lookup(yieldOp.getOperand(0)));
3786 if (innerOp->getNumRegions() > 0) {
3790 rewriter.
clone(*innerOp, mapping);
3795void ACCCGToGPULowering::processReductionCombineOp(acc::ReductionCombineOp op) {
3796 LLVM_DEBUG(llvm::dbgs() <<
"processing reduction combine op: ";
3797 op->print(llvm::dbgs()); llvm::dbgs() <<
"\n");
3799 MemRefType memrefType = dyn_cast<MemRefType>(op.getSrcMemref().getType());
3800 assert(memrefType &&
"expected memref type for reduction combine op");
3801 assert(memrefType.getRank() == 0 &&
3802 "expected scalar memref type for reduction combine op");
3803 Type elTy = memrefType.getElementType();
3804 FailureOr<arith::AtomicRMWKind> kindOr =
3805 getReductionKind(op.getReductionOperator(), elTy, loc);
3808 arith::AtomicRMWKind kind = *kindOr;
3819 bool destIsPerThreadPrivate = isa_and_nonnull<memref::AllocaOp>(
3820 unwrapMemRefConversion(destMemref).getDefiningOp());
3824 for (
auto parDim : parDims) {
3825 if (parDim.isAnyBlock() && !destIsPerThreadPrivate) {
3831 auto srcLoad = memref::LoadOp::create(rewriter, loc, srcMemref);
3832 pendingCombineReloads.push_back({srcMemref, srcLoad});
3833 constructAtomicAccumulation(loc, destMemref, {}, srcLoad,
3841 auto srcLoad = memref::LoadOp::create(rewriter, loc, srcMemref,
ValueRange{});
3843 memref::LoadOp::create(rewriter, loc, destMemref,
ValueRange{});
3845 memref::StoreOp::create(rewriter, loc, combine, destMemref,
ValueRange{});
3848void ACCCGToGPULowering::processCombineRegionOp(
3849 acc::ReductionCombineRegionOp op) {
3850 LLVM_DEBUG(llvm::dbgs() <<
"processing combine region op: ";
3851 op->print(llvm::dbgs()); llvm::dbgs() <<
"\n");
3855 bool destIsPerThreadPrivate = isa_and_nonnull<memref::AllocaOp>(
3860 for (
auto parDim : parDims) {
3861 if (parDim.isAnyBlock() && !destIsPerThreadPrivate) {
3866 if (acc::ReductionAccumulateOp accumulateOp =
3867 dyn_cast<acc::ReductionAccumulateOp>(user)) {
3868 Location loc = accumulateOp.getLoc();
3869 FailureOr<arith::AtomicRMWKind> kind =
3870 getReductionKind(accumulateOp.getReductionOperator(),
3871 accumulateOp.getValue().getType(), loc);
3876 auto reductionLoad = memref::LoadOp::create(rewriter, loc, srcMemref);
3877 pendingCombineReloads.push_back({srcMemref, reductionLoad});
3878 constructAtomicAccumulation(loc,
3880 {}, reductionLoad, *kind);
3888 MemRefType memrefTy = cast<MemRefType>(privateMemref.
getType());
3889 if (isa<ComplexType>(memrefTy.getElementType())) {
3891 Value reductionResult =
3892 memref::LoadOp::create(rewriter, loc, privateMemref);
3893 arith::AtomicRMWKind kind = arith::AtomicRMWKind::addf;
3894 op.getRegion().walk([&](
Operation *innerOp) {
3895 if (isa<complex::MulOp>(innerOp))
3896 kind = arith::AtomicRMWKind::mulf;
3898 constructAtomicAccumulation(loc,
3900 {}, reductionResult, kind);
3906 if (acc::YieldOp yieldOp = dyn_cast<acc::YieldOp>(innerOp))
3908 if (innerOp->getNumRegions() > 0) {
3912 rewriter.
clone(*innerOp, mapping);
3917void ACCCGToGPULowering::processGenericOp(
Operation *op) {
3920 LLVM_DEBUG(llvm::dbgs() <<
"processing generic op, cloning: ";
3921 op->
print(llvm::dbgs()); llvm::dbgs() <<
"\n");
3927void ACCCGToGPULowering::processGenericOpWithRegions(
Operation *op) {
3929 LLVM_DEBUG(llvm::dbgs() <<
"processing generic op with regions: ";
3930 op->
print(llvm::dbgs()); llvm::dbgs() <<
"\n");
3936 for (
auto [oldRegion, newRegion] :
3937 llvm::zip(op->
getRegions(), newOp->getRegions())) {
3939 for (
auto &oldBlock : oldRegion.getBlocks()) {
3940 TypeRange argTypes = oldBlock.getArgumentTypes();
3941 size_t numArgs = argTypes.size();
3944 rewriter.
createBlock(&newRegion, newRegion.end(), argTypes,
3951 for (
auto &innerOp : oldBlock.getOperations()) {
3954 processOp(&innerOp);
3965void ACCCGToGPULowering::processOp(
Operation *op) {
3966 if (isDeferredBarrierFlushPoint(op))
3967 flushDeferredBarriersBefore(op);
3971 scf::ParallelOp parallelOp = cast<scf::ParallelOp>(op);
3972 processParallelOp(parallelOp);
3973 }
else if (scf::ForOp seqLoop = dyn_cast<scf::ForOp>(op)) {
3974 processSeqLoop(seqLoop);
3975 }
else if (acc::PrivatizeOp privatize = dyn_cast<acc::PrivatizeOp>(op)) {
3976 processPrivatize(privatize);
3977 }
else if (acc::PrivateLocalOp privateLocal =
3978 dyn_cast<acc::PrivateLocalOp>(op)) {
3979 processPrivateLocal(privateLocal);
3980 }
else if (acc::PredicateRegionOp predicateRegionOp =
3981 dyn_cast<acc::PredicateRegionOp>(op)) {
3982 processPredicateRegion(predicateRegionOp);
3983 }
else if (acc::ReductionAccumulateOp accumulateOp =
3984 dyn_cast<acc::ReductionAccumulateOp>(op)) {
3985 processAccumulateOp(accumulateOp);
3986 }
else if (
auto accumulateArrayOp =
3987 dyn_cast<acc::ReductionAccumulateArrayOp>(op)) {
3988 processAccumulateArrayOp(accumulateArrayOp);
3989 }
else if (acc::ReductionInitOp reductionInitOp =
3990 dyn_cast<acc::ReductionInitOp>(op)) {
3991 processReductionOp(reductionInitOp);
3992 }
else if (acc::ReductionCombineOp reductionCombineOp =
3993 dyn_cast<acc::ReductionCombineOp>(op)) {
3994 processReductionCombineOp(reductionCombineOp);
3995 }
else if (
auto combineRegionOp =
3996 dyn_cast<acc::ReductionCombineRegionOp>(op)) {
3997 processCombineRegionOp(combineRegionOp);
3998 }
else if (acc::ReductionOp accReductionOp = dyn_cast<acc::ReductionOp>(op)) {
3999 mapping.
map(accReductionOp->getResult(0), accReductionOp.getVarPtr());
4002 LLVM_DEBUG(llvm::dbgs() <<
"skipping mapped op: " << *op <<
"\n");
4003 }
else if (isa<acc::YieldOp>(op)) {
4004 for (
auto [operand,
result] :
4008 }
else if (isa<scf::ExecuteRegionOp>(op)) {
4009 processExecuteRegion(cast<scf::ExecuteRegionOp>(op));
4011 isa<acc::OpenACCDialect>(op->
getDialect())) {
4012 processGenericOp(op);
4014 processGenericOpWithRegions(op);
4021 LogicalResult matchAndRewrite(acc::ParWidthOp op,
4023 if (
Value launchArg = op.getLaunchArg()) {
4034class ACCComputeRegionToGPUPattern
4037 ACCComputeRegionToGPUPattern(
MLIRContext *context,
4039 const ACCCGToGPUOptions &
options)
4043 LogicalResult matchAndRewrite(acc::ComputeRegionOp op,
4045 ACCCGToGPULowering kernelOpRewriter(op, rewriter, accSupport,
options);
4046 return kernelOpRewriter.rewrite();
4051 const ACCCGToGPUOptions &
options;
4054class ACCCGToGPU :
public acc::impl::ACCCGToGPUBase<ACCCGToGPU> {
4056 using acc::impl::ACCCGToGPUBase<ACCCGToGPU>::ACCCGToGPUBase;
4058 void runOnOperation()
override {
4059 FunctionOpInterface funcOp = getOperation();
4062 assert(deviceType != mlir::acc::DeviceType::Host &&
4063 deviceType != mlir::acc::DeviceType::Multicore &&
4064 "ACCCGToGPU only supports GPU device types");
4066 options.deviceType = deviceType;
4067 options.maxWorkgroupSharedMemory = maxWorkgroupSharedMemory;
4068 options.maxThreadPrivateStack = maxThreadPrivateStack;
4069 options.subgroupSize = subgroupSize;
4072 std::optional<std::reference_wrapper<acc::OpenACCSupport>> cachedAnalysis =
4073 getCachedParentAnalysis<acc::OpenACCSupport>(funcOp->getParentOp());
4075 ? cachedAnalysis->get()
4076 : getAnalysis<acc::OpenACCSupport>();
4079 patterns.
insert<ACCComputeRegionToGPUPattern>(context, accSupport,
options);
4080 patterns.
insert<RemoveParWidth>(context);
4082 target.markUnknownOpDynamicallyLegal([](
Operation *) {
return true; });
4083 target.addIllegalOp<acc::ComputeRegionOp, acc::ParWidthOp>();
4084 if (failed(applyPartialConversion(getOperation(),
target,
4085 std::move(patterns)))) {
4086 signalPassFailure();
static void createForAllDimensions(OpBuilder &builder, Location loc, SmallVectorImpl< Value > &values)
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing copies
Creates a buffer in the faster memory space for the specified memref region (memref has to be non-zer...
static llvm::ManagedStatic< PassManagerOptions > options
static void rewrite(DataFlowSolver &solver, MLIRContext *context, MutableArrayRef< Region > initialRegions)
Rewrite the given regions using the computing analysis.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
MutableArrayRef< BlockArgument > BlockArgListType
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
RetT walk(FnT &&callback)
Walk all nested operations, blocks (including this block) or regions, depending on the type of callba...
Operation * getTerminator()
Get the terminator operation of this block.
bool mightHaveTerminator()
Return "true" if this block might have a terminator.
BlockArgListType getArguments()
IntegerAttr getIndexAttr(int64_t value)
IntegerAttr getI32IntegerAttr(int32_t value)
IntegerAttr getIntegerAttr(Type type, int64_t value)
FloatAttr getFloatAttr(Type type, double value)
IntegerAttr getI64IntegerAttr(int64_t value)
TypedAttr getZeroAttr(Type type)
MLIRContext * getContext() const
A class for computing basic dominance information.
bool dominates(Operation *a, Operation *b) const
Return true if operation A dominates operation B, i.e.
This is a utility class for mapping one set of IR entities to another.
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
auto lookup(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
bool contains(T from) const
Checks to see if a mapping for 'from' exists.
auto lookupOrNull(T from) const
Lookup a mapped value within the map.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
This class represents a saved insertion point.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
InsertPoint saveInsertionPoint() const
Return a saved insertion point.
Block::iterator getInsertionPoint() const
Returns the current insertion point of the builder.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
void restoreInsertionPoint(InsertPoint ip)
Restore the insert point to a previously saved point.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Operation * cloneWithoutRegions(Operation &op, IRMapping &mapper)
Creates a deep copy of this operation but keep the operation regions empty.
This trait indicates that the memory effects of an operation includes the effects of operations neste...
This class provides the API for ops that are known to be terminators.
Operation is the basic unit of execution within MLIR.
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Value getOperand(unsigned idx)
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
unsigned getNumSuccessors()
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
result_iterator result_begin()
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
void print(raw_ostream &os, const OpPrintingFlags &flags={})
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
result_iterator result_end()
operand_range getOperands()
Returns an iterator on the underlying Value's.
void setSuccessor(Block *block, unsigned index)
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Block * getSuccessor(unsigned index)
user_range getUsers()
Returns a range of all users.
result_range getResults()
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
BlockListType & getBlocks()
RewritePatternSet & insert(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Operation * lookup(StringRef name) const
Look up a symbol with the specified name, returning null if no such name exists.
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
auto walk(WalkFns &&...walkFns)
Walk this type and all attibutes/types nested within using the provided walk functions.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
user_iterator user_begin() const
user_range getUsers() const
bool hasOneUse() const
Returns true if this value has exactly one use.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
static WalkResult interrupt()
ParDimAttrT seqDim(MLIRContext *ctx) const
virtual ParDimAttrT map(MLIRContext *ctx, ParLevel level) const =0
Map an OpenACC parallelism level to target dimension.
ParDimAttrT vectorDim(MLIRContext *ctx) const
ParDimAttrT workerDim(MLIRContext *ctx) const
ParDimAttrT gangDim(MLIRContext *ctx, ParLevel level) const
Convenience methods for specific parallelism levels.
Default policy that provides the standard GPU mapping: gang(dim:1) -> BlockX (gridDim....
remark::detail::InFlightRemark emitRemark(Operation *op, std::function< std::string()> messageFn, llvm::StringRef category="openacc")
Emit an OpenACC remark with lazy message generation.
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
std::string getVariableName(Value v)
Get the variable name for a given value.
std::optional< TypeSizeAndAlignment > getTypeSizeAndAlignment(Type ty, ModuleOp module)
Returns the size and ABI alignment in bytes for ty.
Tracks aligned byte consumption against a configurable shared memory cap.
bool tryAllocate(int64_t bytes, int64_t alignment=kDefaultAlignmentBytes)
Reserve bytes, rounding the current offset up to alignment first.
Specialization of arith.constant op that returns an integer of index type.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Specialization of arith.constant op that returns an integer value.
static ConstantIntOp create(OpBuilder &builder, Location location, int64_t value, unsigned width)
static ConcreteType get(MLIRContext *ctx, Args &&...args)
SideEffects::EffectInstance< Effect > EffectInstance
Value getGPUSize(gpu::Processor processor, gpu::LaunchOp launch, const llvm::DenseMap< gpu::Processor, Value > &dimensionOps)
Return the launch dimension for processor from launch, or from dimensionOps when launch is null.
ParLevel getGangParLevel(int64_t gangDimValue)
Convert a gang dimension value (1, 2, or 3) to the corresponding ParLevel.
GPUParallelDimsAttr getParDimsAttr(Operation *op)
Obtain the parallel dimensions carried by op, if any.
MemRefType getPrivateBaseMemRefType(Type baseTy, ModuleOp module)
Returns the ranked MemRef type used to allocate privatized storage.
SmallVector< GPUParallelDimAttr > getReductionCombineParDims(ReductionCombineOp op)
Returns the parallel dimensions that participate in op's combine step.
void insertParDim(llvm::SmallVector< GPUParallelDimAttr > &parDims, GPUParallelDimAttr parDim)
Insert parDim into parDims while preserving dimension ordering.
static constexpr StringLiteral getSpecializedRoutineAttrName()
bool hasParDimsAttr(Operation *op)
Return whether op carries parallel dimensions.
std::optional< arith::AtomicRMWKind > translateACCReductionOperator(ReductionOperator redOp, Type type)
Maps an acc reduction operator to the arith atomic RMW kind for type.
bool isSpecializedAccRoutine(mlir::Operation *op)
Used to check whether this is a specialized accelerator version of acc routine function.
static bool isInsideACCSpecializedRoutine(Operation *op)
Value createIdentityValue(OpBuilder &b, Location loc, Type type, arith::AtomicRMWKind kind, bool useOnlyFiniteValue=true)
Creates the identity (neutral) value for a reduction of type and kind.
FailureOr< bool > isPrivateLocalSharedMemoryCandidate(PrivateLocalOp privateLocal, ComputeRegionOp computeRegion, ModuleOp module, const ACCToGPUMappingPolicy &policy, OpenACCSupport *support=nullptr)
True when privateLocal may be placed in shared memory.
static constexpr StringLiteral getRoutineInfoAttrName()
int64_t sumExistingSharedMemoryBytes(Region ®ion)
Sum aligned static_upper_bound_bytes for all acc.gpu_shared_memory in region.
Value getGPUThreadId(gpu::Processor processor, gpu::LaunchOp launch, const llvm::DenseMap< gpu::Processor, Value > &indexOps)
Return the thread/block index for processor from launch, or from indexOps when launch is null.
PrivatizeOp getPrivatizeOp(PrivateLocalOp privateLocal, ComputeRegionOp computeRegion)
Resolve the acc.privatize operation associated with a private local.
bool hasGPUBlockRedundantAttr(Operation *op)
Return whether op is marked with the acc.gpu_block_redundant attribute, i.e.
Value generateReductionOp(OpBuilder &b, Location loc, Value lhs, Value rhs, arith::AtomicRMWKind kind)
Combines two reduction partial values using the operator for kind.
void removeParDim(llvm::SmallVector< GPUParallelDimAttr > &parDims, GPUParallelDimAttr parDim)
Remove parDim from parDims if present.
void setParDimsAttr(Operation *op, GPUParallelDimsAttr attr)
Set parallel dimensions on op.
std::optional< int64_t > getPrivateLocalSharedMemoryUpperBoundBytes(PrivateLocalOp privateLocal, ComputeRegionOp computeRegion, ModuleOp module, const ACCToGPUMappingPolicy &policy, OpenACCSupport *support=nullptr)
Upper-bound byte size for a shared-memory private_local candidate, or std::nullopt when not eligible ...
static bool isThreadXPrivatize(PrivatizeOp privatize)
ACCParMappingPolicy< mlir::acc::GPUParallelDimAttr > ACCToGPUMappingPolicy
Type alias for the GPU-specific mapping policy.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
Value getValueOrCreateCastToIndexLike(OpBuilder &b, Location loc, Type targetType, Value value)
Create a cast from an index-like value (index or integer) to another index-like value.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...