Skip to content

[MLIR][GPU] Add xevm-attach-target transform pass. #147372

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,38 @@ def GpuSPIRVAttachTarget: Pass<"spirv-attach-target", ""> {
];
}

def GpuXeVMAttachTarget : Pass<"xevm-attach-target", "mlir::gpu::GPUModuleOp"> {
let summary = "Attaches a XeVM target attribute to a GPU Module.";
let description = [{
This pass searches for all GPU Modules in the immediate regions and attaches
a XeVM target if the module matches the name specified by the `module` argument.

Example:
```
// File: in.mlir:
gpu.module @nvvm_module_1 {...}
gpu.module @rocdl_module_2 {...}
gpu.module @xevm_module_3 {...}
// mlir-opt --xevm-attach-target="module=xevm.* chip=pvc" in.mlir
gpu.module @nvvm_module_1 {...}
gpu.module @rocdl_module_2 {...}
gpu.module @xevm_module_3 [#xevm.target<chip = "pvc">] {...}
```
}];
let options =
[Option<"moduleMatcher", "module", "std::string",
/*default=*/[{""}],
"Regex used to identify the modules to attach the target to.">,
Option<"triple", "triple", "std::string",
/*default=*/"\"spirv64-unknown-unknown\"", "Target triple.">,
Option<"chip", "chip", "std::string",
/*default=*/"\"bmg\"", "Target chip.">,
Option<"optLevel", "O", "unsigned",
/*default=*/"2", "Optimization level.">,
ListOption<"linkLibs", "l", "std::string",
"Extra bitcode libraries paths to link to.">,
Option<"cmdOptions", "cmd-options", "std::string",
/*default=*/[{""}],
"Command line options passed to downstream compiler">];
}
#endif // MLIR_DIALECT_GPU_PASSES
2 changes: 2 additions & 0 deletions mlir/lib/Dialect/GPU/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ add_mlir_dialect_library(MLIRGPUTransforms
Transforms/ShuffleRewriter.cpp
Transforms/SubgroupIdRewriter.cpp
Transforms/SubgroupReduceLowering.cpp
Transforms/XeVMAttachTarget.cpp

OBJECT

Expand Down Expand Up @@ -78,6 +79,7 @@ add_mlir_dialect_library(MLIRGPUTransforms
MLIRSupport
MLIRTransformUtils
MLIRVectorDialect
MLIRXeVMDialect
)

add_subdirectory(TransformOps)
Expand Down
88 changes: 88 additions & 0 deletions mlir/lib/Dialect/GPU/Transforms/XeVMAttachTarget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//===-- XeVMAttachTarget.cpp - DESC -----------------------------*- C++ -*-===//
//
// This file is licensed 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 file implements the `GpuXeVMAttachTarget` pass, attaching `#xevm.target`
// attributes to GPU modules.
//
//===----------------------------------------------------------------------===//

#include "mlir/Dialect/GPU/Transforms/Passes.h"

#include "mlir/Dialect/GPU/IR/GPUDialect.h"
#include "mlir/Dialect/LLVMIR/XeVMDialect.h"
#include "mlir/IR/Builders.h"
#include "mlir/Pass/Pass.h"
#include "llvm/Support/Regex.h"

namespace mlir {
#define GEN_PASS_DEF_GPUXEVMATTACHTARGET
#include "mlir/Dialect/GPU/Transforms/Passes.h.inc"
} // namespace mlir

using namespace mlir;
using namespace mlir::xevm;

namespace {
struct XeVMAttachTarget
: public mlir::impl::GpuXeVMAttachTargetBase<XeVMAttachTarget> {
using Base::Base;

DictionaryAttr getFlags(OpBuilder &builder) const;

void runOnOperation() override;

void getDependentDialects(DialectRegistry &registry) const override {
registry.insert<mlir::xevm::XeVMDialect>();
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe no need to use explicit namespaces because they already imported?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed mlir::

}
};
} // namespace

DictionaryAttr XeVMAttachTarget::getFlags(OpBuilder &builder) const {
SmallVector<NamedAttribute, 3> flags;
// Tokenize and set the optional command line options.
if (!cmdOptions.empty()) {
auto options = gpu::TargetOptions::tokenizeCmdOptions(cmdOptions);
Copy link
Contributor

Choose a reason for hiding this comment

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

please mention the type and avoid using auto because output type is not obvious in the context (not a cast).

Refer to LLVM coding manuals for auto usage.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replaced auto with actual type std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>>

if (!options.second.empty()) {
llvm::SmallVector<mlir::Attribute> xevmOptionAttrs;
for (const char *opt : options.second) {
xevmOptionAttrs.emplace_back(
mlir::StringAttr::get(builder.getContext(), StringRef(opt)));
}
flags.push_back(builder.getNamedAttr(
"cmd-options",
mlir::ArrayAttr::get(builder.getContext(), xevmOptionAttrs)));
}
}

if (!flags.empty())
return builder.getDictionaryAttr(flags);
return nullptr;
}

void XeVMAttachTarget::runOnOperation() {
OpBuilder builder(&getContext());
ArrayRef<std::string> libs(linkLibs);
SmallVector<StringRef> filesToLink(libs);
auto target = builder.getAttr<mlir::xevm::XeVMTargetAttr>(
optLevel, triple, chip, getFlags(builder),
filesToLink.empty() ? nullptr : builder.getStrArrayAttr(filesToLink));
llvm::Regex matcher(moduleMatcher);
Copy link
Member

Choose a reason for hiding this comment

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

If the name is empty, do you just attach to all GPU modules?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't get your question.
filesToLink is files to link, like external spir-v library files. Name would be empty in most cases.
Behavior is the same as nvvm-attach-target.

Copy link
Contributor

Choose a reason for hiding this comment

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

If the name is empty, do you just attach to all GPU modules?

I think so.
When moduleMatcher is empty, nvvm info is attached to all GPU module.

Copy link
Contributor

Choose a reason for hiding this comment

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

Now after updates, this should be the behavior here as well.

// Check if the name of the module matches.
auto gpuModule = cast<gpu::GPUModuleOp>(getOperation());
Copy link
Member

Choose a reason for hiding this comment

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

Both NV and ROC similar methods iterate through regions and basic blocks, but your definition above specified mlir::gpu::GPUModuleOp. Is that because it's assumed that this will only run on the GPU modules (and thus needs no search)? Or are we just not covering the general case in the tests?

Copy link
Contributor

Choose a reason for hiding this comment

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

Both NV and ROC also assume that the attachment logic will only be applied to GPU modules:

for (auto module : block.getOps<gpu::GPUModuleOp>()) {

as indicated by their pass description itself:

This pass searches for all GPU Modules in the immediate regions and attaches

This PR decided to rely on the MLIR pass infrastructure for traversal.

If you think we should follow nv/roc, please consider responding to the comment below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated logic to match nvvm, rocdl and other attach-target passes.

if (!moduleMatcher.empty() && !matcher.match(gpuModule.getName()))
return;
// Create the target array.
SmallVector<Attribute> targets;
if (std::optional<ArrayAttr> attrs = gpuModule.getTargets())
targets.append(attrs->getValue().begin(), attrs->getValue().end());
targets.push_back(target);
// Remove any duplicate targets.
targets.erase(llvm::unique(targets), targets.end());
// Update the target attribute array.
gpuModule.setTargetsAttr(builder.getArrayAttr(targets));
}
12 changes: 8 additions & 4 deletions mlir/test/Dialect/LLVMIR/attach-targets.mlir
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: mlir-opt %s --nvvm-attach-target='module=nvvm.* O=3 chip=sm_90' --rocdl-attach-target='module=rocdl.* O=3 chip=gfx90a' | FileCheck %s
// RUN: mlir-opt %s --nvvm-attach-target='module=options.* O=1 chip=sm_70 fast=true ftz=true' --rocdl-attach-target='module=options.* l=file1.bc,file2.bc wave64=false finite-only=true' | FileCheck %s --check-prefix=CHECK_OPTS
// RUN: mlir-opt %s --nvvm-attach-target='module=nvvm.* O=3 chip=sm_90' --rocdl-attach-target='module=rocdl.* O=3 chip=gfx90a' --xevm-attach-target='module=xevm.* O=3 chip=pvc' | FileCheck %s
// RUN: mlir-opt %s --nvvm-attach-target='module=options.* O=1 chip=sm_70 fast=true ftz=true' --rocdl-attach-target='module=options.* l=file1.bc,file2.bc wave64=false finite-only=true' --xevm-attach-target='module=options.* O=1 chip=pvc' | FileCheck %s --check-prefix=CHECK_OPTS

module attributes {gpu.container_module} {
// Verify the target is appended.
Expand All @@ -18,12 +18,16 @@ gpu.module @nvvm_module_3 [#nvvm.target<O = 3, chip = "sm_90">] {
// CHECK: @rocdl_module [#rocdl.target<O = 3, chip = "gfx90a">] {
gpu.module @rocdl_module {
}
// Verify that other targets are not added as they fail to match the regex, but XeVM does get appended.
// CHECK: @xevm_module [#xevm.target<O = 3, chip = "pvc">] {
gpu.module @xevm_module {
}
// Check the options were added.
// CHECK_OPTS: @options_module_1 [#nvvm.target<O = 1, chip = "sm_70", flags = {fast, ftz}>, #rocdl.target<flags = {finite_only, no_wave64}, link = ["file1.bc", "file2.bc"]>] {
// CHECK_OPTS: @options_module_1 [#nvvm.target<O = 1, chip = "sm_70", flags = {fast, ftz}>, #rocdl.target<flags = {finite_only, no_wave64}, link = ["file1.bc", "file2.bc"]>, #xevm.target<O = 1, chip = "pvc">] {
gpu.module @options_module_1 {
}
// Check the options were added and that the first target was preserved.
// CHECK_OPTS: @options_module_2 [#nvvm.target<O = 3, chip = "sm_90">, #nvvm.target<O = 1, chip = "sm_70", flags = {fast, ftz}>, #rocdl.target<flags = {finite_only, no_wave64}, link = ["file1.bc", "file2.bc"]>] {
// CHECK_OPTS: @options_module_2 [#nvvm.target<O = 3, chip = "sm_90">, #nvvm.target<O = 1, chip = "sm_70", flags = {fast, ftz}>, #rocdl.target<flags = {finite_only, no_wave64}, link = ["file1.bc", "file2.bc"]>, #xevm.target<O = 1, chip = "pvc">] {
gpu.module @options_module_2 [#nvvm.target<O = 3, chip = "sm_90">] {
}
}
1 change: 1 addition & 0 deletions mlir/test/lib/Dialect/GPU/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ set(LIBS
MLIRTranslateLib
MLIRVectorDialect
MLIRVectorToLLVMPass
MLIRXeVMDialect
)

add_mlir_library(MLIRGPUTestPasses
Expand Down