CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
230 lines
8.5 KiB
C++
230 lines
8.5 KiB
C++
//===- ACCLoopTiling.cpp - Tile ACC Loops ---------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This pass implements the OpenACC loop tiling transformation for acc.loop
|
|
// operations that have the tile clause (OpenACC 3.4 spec, section 2.9.8).
|
|
//
|
|
// Overview:
|
|
// ---------
|
|
// The tile clause specifies that the iterations of the associated loops should
|
|
// be divided into tiles (rectangular blocks). This pass transforms a single
|
|
// or nested acc.loop with tile clauses into a structure of "tile loops"
|
|
// (iterating over tiles) containing "element loops" (iterating within tiles).
|
|
//
|
|
// For example, tiling a 2-level nested loop with tile(T1, T2) produces:
|
|
//
|
|
// // Before tiling:
|
|
// acc.loop tile(T1, T2) control(%i, %j) = (lb1, lb2) to (ub1, ub2) step (s1,
|
|
// s2)
|
|
//
|
|
// // After tiling:
|
|
// acc.loop control(%i) = (lb1) to (ub1) step (s1*T1) { // tile loop 1
|
|
// acc.loop control(%j) = (lb2) to (ub2) step (s2*T2) { // tile loop 2
|
|
// acc.loop control(%ii) = (%i) to (min(ub1, %i+s1*T1)) step (s1) { //
|
|
// element 1
|
|
// acc.loop control(%jj) = (%j) to (min(ub2, %j+s2*T2)) step (s2) { //
|
|
// element 2
|
|
// // loop body using %ii, %jj
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// Gang/worker/vector attributes are distributed as follows:
|
|
// - gang: applied to tile loops
|
|
// - vector: applied to element loops
|
|
// - worker: removed from inner loops
|
|
//
|
|
// Unknown Tile Sizes:
|
|
// -------------------
|
|
// The OpenACC tile(*) syntax indicates an implementation-defined tile size.
|
|
// In the IR, this is represented as -1. The pass resolves these to the
|
|
// default tile size (configurable via pass option).
|
|
//
|
|
// Requirements:
|
|
// -------------
|
|
// 1. The pass uses the OpenACCSupport analysis for remark and NYI (not yet
|
|
// implemented) emission. Custom implementations can be registered via
|
|
// setImplementation() to provide pipeline-specific handling.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
|
#include "mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h"
|
|
#include "mlir/Dialect/OpenACC/OpenACC.h"
|
|
#include "mlir/Dialect/OpenACC/OpenACCUtilsTiling.h"
|
|
#include "mlir/Dialect/OpenACC/Transforms/Passes.h"
|
|
#include "mlir/IR/BuiltinAttributes.h"
|
|
#include "mlir/IR/PatternMatch.h"
|
|
#include "mlir/Support/LLVM.h"
|
|
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
namespace mlir {
|
|
namespace acc {
|
|
#define GEN_PASS_DEF_ACCLOOPTILING
|
|
#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
|
|
} // namespace acc
|
|
} // namespace mlir
|
|
|
|
#define DEBUG_TYPE "acc-loop-tile"
|
|
|
|
namespace {
|
|
using namespace mlir;
|
|
|
|
struct ACCLoopTilingImpl : public OpRewritePattern<acc::LoopOp> {
|
|
ACCLoopTilingImpl(MLIRContext *context, int32_t defaultTileSize,
|
|
acc::OpenACCSupport &accSupport)
|
|
: OpRewritePattern<acc::LoopOp>(context),
|
|
defaultTileSize(defaultTileSize), accSupport(accSupport) {}
|
|
|
|
// Check that tile size types are not narrower than IV types.
|
|
// We only check when both types are IntegerType. For IndexType, the width
|
|
// is target-dependent and the casting utility will handle it correctly.
|
|
LogicalResult checkTileSizeTypes(acc::LoopOp loop,
|
|
ArrayRef<Value> tileSizes) const {
|
|
auto ivTypes = loop.getBody().getArgumentTypes();
|
|
for (size_t i = 0; i < tileSizes.size() && i < ivTypes.size(); ++i) {
|
|
Type tileType = tileSizes[i].getType();
|
|
Type ivType = ivTypes[i];
|
|
|
|
// Skip unknown tile sizes (will be created with correct type)
|
|
auto constVal = getConstantIntValue(tileSizes[i]);
|
|
if (constVal && *constVal < 0)
|
|
continue;
|
|
|
|
// Only compare when both are integer types (not index)
|
|
auto tileIntType = dyn_cast<IntegerType>(tileType);
|
|
auto ivIntType = dyn_cast<IntegerType>(ivType);
|
|
if (tileIntType && ivIntType) {
|
|
if (tileIntType.getWidth() > ivIntType.getWidth()) {
|
|
accSupport.emitNYI(loop.getLoc(),
|
|
"tile size type (i" +
|
|
std::to_string(tileIntType.getWidth()) +
|
|
") is wider than loop IV type (i" +
|
|
std::to_string(ivIntType.getWidth()) + ")");
|
|
return failure();
|
|
}
|
|
}
|
|
}
|
|
return success();
|
|
}
|
|
|
|
void emitTilingRemarks(acc::LoopOp loop, ArrayRef<Value> tileSizes) const {
|
|
// Emit remarks for loop tiling
|
|
accSupport.emitRemark(
|
|
loop,
|
|
[&]() {
|
|
auto getTileSizeStr = [&](Value v) -> std::string {
|
|
std::string name = accSupport.getVariableName(v);
|
|
// Use "*" for unknown tile sizes (represented as -1 or empty)
|
|
if (name.empty() || name == "-1")
|
|
return "*";
|
|
return name;
|
|
};
|
|
SmallVector<std::string> tileStrs;
|
|
for (Value v : tileSizes)
|
|
tileStrs.push_back(getTileSizeStr(v));
|
|
return "Tiling " + std::to_string(tileSizes.size()) +
|
|
"-level loop nest with tile(" + llvm::join(tileStrs, ",") +
|
|
")";
|
|
},
|
|
DEBUG_TYPE);
|
|
|
|
// Emit remarks for unknown tile sizes that will be resolved to default
|
|
// TODO: Need to base the default tile size on some heuristics.
|
|
for (Value tileSize : tileSizes) {
|
|
std::optional<int64_t> val = getConstantIntValue(tileSize);
|
|
if (val && *val < 0) {
|
|
accSupport.emitRemark(
|
|
loop,
|
|
[&]() {
|
|
return "Picking default tile size " +
|
|
std::to_string(defaultTileSize) +
|
|
" for unknown tile size '*'";
|
|
},
|
|
DEBUG_TYPE);
|
|
}
|
|
}
|
|
}
|
|
|
|
LogicalResult matchAndRewrite(acc::LoopOp origLoop,
|
|
PatternRewriter &rewriter) const override {
|
|
|
|
if (origLoop.getTileValues().empty())
|
|
return failure();
|
|
|
|
SmallVector<Value> tileSizes(origLoop.getTileValues().begin(),
|
|
origLoop.getTileValues().end());
|
|
unsigned tileCount = tileSizes.size();
|
|
unsigned collapseCount = origLoop.getCollapseValue().value_or(1);
|
|
|
|
// Sanity check tile size types
|
|
if (failed(checkTileSizeTypes(origLoop, tileSizes)))
|
|
return failure();
|
|
|
|
// Emit remarks for loop tiling. This is emitted before the original loop
|
|
// is modified. However, it assumes that tiling will not fail.
|
|
emitTilingRemarks(origLoop, tileSizes);
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "\nBefore tiling:\n" << *origLoop << "\n");
|
|
|
|
// Clear tile operands from origLoop
|
|
rewriter.startOpModification(origLoop);
|
|
origLoop.getTileOperandsMutable().clear();
|
|
origLoop.removeTileOperandsSegmentsAttr();
|
|
origLoop.removeTileOperandsDeviceTypeAttr();
|
|
rewriter.finalizeOpModification(origLoop);
|
|
|
|
SmallVector<acc::LoopOp> loopsToTile;
|
|
if (collapseCount < tileCount) {
|
|
// Uncollapse tile loops before tiling if necessary
|
|
loopsToTile =
|
|
acc::uncollapseLoops(origLoop, tileCount, collapseCount, rewriter);
|
|
rewriter.replaceOp(origLoop, loopsToTile[0]);
|
|
LLVM_DEBUG(llvm::dbgs() << "\nAfter uncollapsing:\n"
|
|
<< *loopsToTile[0] << "\n");
|
|
} else {
|
|
loopsToTile.push_back(origLoop);
|
|
}
|
|
|
|
// loopsToTile is a vector of perfectly nested loops. The outermost loop
|
|
// may have multiple IVs but inner loops can only have one IV.
|
|
// The utility handles unknown tile sizes (*) by using `defaultTileSize`.
|
|
acc::tileACCLoops(loopsToTile, tileSizes, defaultTileSize, rewriter);
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "\nAfter tiling:\n " << *loopsToTile[0] << "\n");
|
|
return success();
|
|
}
|
|
|
|
private:
|
|
int32_t defaultTileSize;
|
|
acc::OpenACCSupport &accSupport;
|
|
};
|
|
|
|
class ACCLoopTiling : public acc::impl::ACCLoopTilingBase<ACCLoopTiling> {
|
|
public:
|
|
using ACCLoopTilingBase<ACCLoopTiling>::ACCLoopTilingBase;
|
|
|
|
void runOnOperation() override {
|
|
func::FuncOp funcOp = getOperation();
|
|
MLIRContext *context = funcOp.getContext();
|
|
acc::OpenACCSupport &accSupport = getAnalysis<acc::OpenACCSupport>();
|
|
|
|
RewritePatternSet patterns(context);
|
|
patterns.insert<ACCLoopTilingImpl>(context, defaultTileSize, accSupport);
|
|
GreedyRewriteConfig grc;
|
|
grc.setUseTopDownTraversal(true);
|
|
grc.setMaxIterations(1);
|
|
(void)applyPatternsGreedily(funcOp, std::move(patterns), grc);
|
|
}
|
|
};
|
|
|
|
} // namespace
|