[compiler-rt] [MLIR] SPIRV Target Attribute (PR #69949)

Sang Ik Lee via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 30 14:19:46 PDT 2023


https://github.com/silee2 updated https://github.com/llvm/llvm-project/pull/69949

>From 1a4319cff8d95d5a6a6598f94162be28e56d68a8 Mon Sep 17 00:00:00 2001
From: "Lee, Sang Ik" <sang.ik.lee at intel.com>
Date: Mon, 23 Oct 2023 17:23:54 +0000
Subject: [PATCH 1/5] [MLIR] SPIRV Target Attribute

Create SPIRV Target Attribute to enable GPU compilation pipeline.
The Target Attribute is modeled after the existing spriv.target_env
Plan is to use this new attribute to enable GPU compilation pipeline
for OpenCL kernels only.
The changes do not impact Vulkan shaders using milr-vulkan-runner.
New GPU Dialect transform pass spirv-attach-target is implemented for
attaching attribute from CLI.
gpu-module-to-binary pass now works with GPU module that has SPIRV module
with OpenCL kernel functions inside.
---
 .../mlir/Dialect/GPU/Transforms/Passes.h      |   1 +
 .../mlir/Dialect/GPU/Transforms/Passes.td     |  42 +++++++
 .../mlir/Dialect/SPIRV/IR/SPIRVAttributes.h   |   6 +
 .../mlir/Dialect/SPIRV/IR/SPIRVAttributes.td  |  31 +++++
 mlir/include/mlir/InitAllDialects.h           |   2 +
 mlir/include/mlir/Target/SPIRV/Target.h       |  30 +++++
 mlir/lib/Dialect/GPU/CMakeLists.txt           |   2 +
 .../Dialect/GPU/Transforms/ModuleToBinary.cpp |   2 +
 .../GPU/Transforms/SPIRVAttachTarget.cpp      |  94 ++++++++++++++
 mlir/lib/Target/SPIRV/CMakeLists.txt          |  13 ++
 mlir/lib/Target/SPIRV/Target.cpp              | 115 ++++++++++++++++++
 .../Dialect/GPU/module-to-binary-spirv.mlir   |  13 ++
 .../Dialect/GPU/spirv-attach-targets.mlir     |   7 ++
 mlir/test/Dialect/SPIRV/IR/target.mlir        |  14 +++
 14 files changed, 372 insertions(+)
 create mode 100644 mlir/include/mlir/Target/SPIRV/Target.h
 create mode 100644 mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp
 create mode 100644 mlir/lib/Target/SPIRV/Target.cpp
 create mode 100644 mlir/test/Dialect/GPU/module-to-binary-spirv.mlir
 create mode 100644 mlir/test/Dialect/GPU/spirv-attach-targets.mlir
 create mode 100644 mlir/test/Dialect/SPIRV/IR/target.mlir

diff --git a/mlir/include/mlir/Dialect/GPU/Transforms/Passes.h b/mlir/include/mlir/Dialect/GPU/Transforms/Passes.h
index 2a891a7d24f809a..42fa46b0a57bdee 100644
--- a/mlir/include/mlir/Dialect/GPU/Transforms/Passes.h
+++ b/mlir/include/mlir/Dialect/GPU/Transforms/Passes.h
@@ -15,6 +15,7 @@
 
 #include "Utils.h"
 #include "mlir/Dialect/GPU/IR/GPUDialect.h"
+#include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h"
 #include "mlir/Pass/Pass.h"
 #include <optional>
 
diff --git a/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td b/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
index 3de8e18851369df..44e3e5b6226bfeb 100644
--- a/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
@@ -188,4 +188,46 @@ def GpuROCDLAttachTarget: Pass<"rocdl-attach-target", ""> {
   ];
 }
 
+def GpuSPIRVAttachTarget: Pass<"spirv-attach-target", ""> {
+  let summary = "Attaches an SPIRV target attribute to a GPU Module.";
+  let description = [{
+    This pass searches for all GPU Modules in the immediate regions and attaches
+    an SPIRV target if the module matches the name specified by the `module` argument.
+
+    Example:
+    ```
+    // File: in1.mlir:
+    gpu.module @nvvm_module_1 {...}
+    gpu.module @spirv_module_1 {...}
+    // mlir-opt --spirv-attach-target="module=spirv.* ver=v1.0 caps=Kernel" in1.mlir
+    gpu.module @nvvm_module_1 {...}
+    gpu.module @spirv_module_1 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>>] {...}
+    ```
+  }];
+  let options = [
+    Option<"moduleMatcher", "module", "std::string",
+           /*default=*/ [{""}],
+           "Regex used to identify the modules to attach the target to.">,
+    Option<"spirvVersion", "ver", "std::string",
+           /*default=*/ "\"v1.0\"",
+           "SPIRV Addressing Model.">,
+    ListOption<"spirvCapabilities", "caps", "std::string",
+           "List of required SPIRV Capabilities">,
+    ListOption<"spirvExtensions", "exts", "std::string",
+           "List of required SPIRV Extensions">,
+    Option<"clientApi", "client_api", "std::string",
+           /*default=*/ "\"Unknown\"",
+           "Client API">,
+    Option<"deviceVendor", "vendor", "std::string",
+           /*default=*/ "\"Unknown\"",
+           "Device Vendor">,
+    Option<"deviceType", "device_type", "std::string",
+           /*default=*/ "\"Unknown\"",
+           "Device Type">,
+    Option<"deviceId", "device_id", "uint32_t",
+           /*default=*/ "mlir::spirv::TargetEnvAttr::kUnknownDeviceID",
+           "Device ID">,
+  ];
+}
+
 #endif // MLIR_DIALECT_GPU_PASSES
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.h b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.h
index 1d304610a03a8dc..3b914dc4cc82f11 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.h
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.h
@@ -17,6 +17,12 @@
 #include "mlir/IR/BuiltinAttributes.h"
 #include "mlir/Support/LLVM.h"
 
+namespace mlir {
+namespace spirv {
+class VerCapExtAttr;
+}
+} // namespace mlir
+
 // Pull in TableGen'erated SPIR-V attribute definitions for target and ABI.
 #define GET_ATTRDEF_CLASSES
 #include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h.inc"
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.td
index f2c1ee5cfd56eab..e026f9dbfc27e30 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.td
@@ -166,4 +166,35 @@ def SPIRV_ResourceLimitsAttr : SPIRV_Attr<"ResourceLimits", "resource_limits"> {
   let assemblyFormat = "`<` struct(params) `>`";
 }
 
+//===----------------------------------------------------------------------===//
+// SPIRV target attribute.
+//===----------------------------------------------------------------------===//
+
+def SPIRV_TargetAttr : SPIRV_Attr<"SPIRVTarget", "target"> {
+  let description = [{
+    GPU target attribute for controlling compilation of SPIRV targets.
+  }];
+  let parameters = (ins
+    "mlir::spirv::VerCapExtAttr":$vce,
+    "mlir::spirv::ResourceLimitsAttr":$resource_limits,
+    DefaultValuedParameter<
+      "mlir::spirv::ClientAPI",
+      "mlir::spirv::ClientAPI::Unknown"
+    >:$client_api,
+    DefaultValuedParameter<
+      "mlir::spirv::Vendor",
+      "mlir::spirv::Vendor::Unknown"
+    >:$vendor_id,
+    DefaultValuedParameter<
+      "mlir::spirv::DeviceType",
+      "mlir::spirv::DeviceType::Unknown"
+    >:$device_type,
+    DefaultValuedParameter<
+      "uint32_t",
+      "mlir::spirv::TargetEnvAttr::kUnknownDeviceID"
+    >:$device_id
+  );
+  let assemblyFormat = "`<` struct(params) `>`";
+}
+
 #endif // MLIR_DIALECT_SPIRV_IR_TARGET_AND_ABI
diff --git a/mlir/include/mlir/InitAllDialects.h b/mlir/include/mlir/InitAllDialects.h
index 00f400aab5d50a0..919d6586f70a520 100644
--- a/mlir/include/mlir/InitAllDialects.h
+++ b/mlir/include/mlir/InitAllDialects.h
@@ -90,6 +90,7 @@
 #include "mlir/Interfaces/CastInterfaces.h"
 #include "mlir/Target/LLVM/NVVM/Target.h"
 #include "mlir/Target/LLVM/ROCDL/Target.h"
+#include "mlir/Target/SPIRV/Target.h"
 
 namespace mlir {
 
@@ -173,6 +174,7 @@ inline void registerAllDialects(DialectRegistry &registry) {
   vector::registerBufferizableOpInterfaceExternalModels(registry);
   NVVM::registerNVVMTargetInterfaceExternalModels(registry);
   ROCDL::registerROCDLTargetInterfaceExternalModels(registry);
+  spirv::registerSPIRVTargetInterfaceExternalModels(registry);
 }
 
 /// Append all the MLIR dialects to the registry contained in the given context.
diff --git a/mlir/include/mlir/Target/SPIRV/Target.h b/mlir/include/mlir/Target/SPIRV/Target.h
new file mode 100644
index 000000000000000..52154afbac66e57
--- /dev/null
+++ b/mlir/include/mlir/Target/SPIRV/Target.h
@@ -0,0 +1,30 @@
+//===- Target.h - MLIR SPIRV target registration ----------------*- C++ -*-===//
+//
+// 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 provides registration calls for attaching the SPIRV target interface.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_SPIRV_TARGET_H
+#define MLIR_TARGET_SPIRV_TARGET_H
+
+namespace mlir {
+class DialectRegistry;
+class MLIRContext;
+namespace spirv {
+/// Registers the `TargetAttrInterface` for the `#spirv.target` attribute in the
+/// given registry.
+void registerSPIRVTargetInterfaceExternalModels(DialectRegistry &registry);
+
+/// Registers the `TargetAttrInterface` for the `#spirv.target` attribute in the
+/// registry associated with the given context.
+void registerSPIRVTargetInterfaceExternalModels(MLIRContext &context);
+} // namespace spirv
+} // namespace mlir
+
+#endif // MLIR_TARGET_SPIRV_TARGET_H
diff --git a/mlir/lib/Dialect/GPU/CMakeLists.txt b/mlir/lib/Dialect/GPU/CMakeLists.txt
index 324d5c136672270..123767075e006b7 100644
--- a/mlir/lib/Dialect/GPU/CMakeLists.txt
+++ b/mlir/lib/Dialect/GPU/CMakeLists.txt
@@ -60,6 +60,7 @@ add_mlir_dialect_library(MLIRGPUTransforms
   Transforms/SerializeToCubin.cpp
   Transforms/SerializeToHsaco.cpp
   Transforms/ShuffleRewriter.cpp
+  Transforms/SPIRVAttachTarget.cpp
   Transforms/ROCDLAttachTarget.cpp
 
   ADDITIONAL_HEADER_DIRS
@@ -95,6 +96,7 @@ add_mlir_dialect_library(MLIRGPUTransforms
   MLIRPass
   MLIRSCFDialect
   MLIRSideEffectInterfaces
+  MLIRSPIRVTarget
   MLIRSupport
   MLIRROCDLTarget
   MLIRTransformUtils
diff --git a/mlir/lib/Dialect/GPU/Transforms/ModuleToBinary.cpp b/mlir/lib/Dialect/GPU/Transforms/ModuleToBinary.cpp
index 2bf89f8c57903e5..e81992ca8e9a331 100644
--- a/mlir/lib/Dialect/GPU/Transforms/ModuleToBinary.cpp
+++ b/mlir/lib/Dialect/GPU/Transforms/ModuleToBinary.cpp
@@ -18,6 +18,7 @@
 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
 #include "mlir/Dialect/LLVMIR/NVVMDialect.h"
 #include "mlir/Dialect/LLVMIR/ROCDLDialect.h"
+#include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
 #include "mlir/IR/BuiltinOps.h"
 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
 
@@ -53,6 +54,7 @@ void GpuModuleToBinaryPass::getDependentDialects(
 #if MLIR_ROCM_CONVERSIONS_ENABLED == 1
   registry.insert<ROCDL::ROCDLDialect>();
 #endif
+  registry.insert<spirv::SPIRVDialect>();
 }
 
 void GpuModuleToBinaryPass::runOnOperation() {
diff --git a/mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp b/mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp
new file mode 100644
index 000000000000000..7366c0eefd05a9d
--- /dev/null
+++ b/mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp
@@ -0,0 +1,94 @@
+//===- SPIRVAttachTarget.cpp - Attach an SPIRV target ---------------------===//
+//
+// 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 file implements the `GpuSPIRVAttachTarget` pass, attaching
+// `#spirv.target` attributes to GPU modules.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/GPU/Transforms/Passes.h"
+
+#include "mlir/Dialect/GPU/IR/GPUDialect.h"
+#include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h"
+#include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
+#include "mlir/Dialect/SPIRV/IR/TargetAndABI.h"
+#include "mlir/IR/Builders.h"
+#include "mlir/Pass/Pass.h"
+#include "mlir/Target/SPIRV/Target.h"
+#include "llvm/Support/Regex.h"
+
+namespace mlir {
+#define GEN_PASS_DEF_GPUSPIRVATTACHTARGET
+#include "mlir/Dialect/GPU/Transforms/Passes.h.inc"
+} // namespace mlir
+
+using namespace mlir;
+using namespace mlir::spirv;
+
+namespace {
+struct SPIRVAttachTarget
+    : public impl::GpuSPIRVAttachTargetBase<SPIRVAttachTarget> {
+  using Base::Base;
+
+  void runOnOperation() override;
+
+  void getDependentDialects(DialectRegistry &registry) const override {
+    registry.insert<spirv::SPIRVDialect>();
+  }
+};
+} // namespace
+
+void SPIRVAttachTarget::runOnOperation() {
+  OpBuilder builder(&getContext());
+  if (!symbolizeVersion(spirvVersion))
+    return signalPassFailure();
+  if (!symbolizeClientAPI(clientApi))
+    return signalPassFailure();
+  if (!symbolizeVendor(deviceVendor))
+    return signalPassFailure();
+  if (!symbolizeDeviceType(deviceType))
+    return signalPassFailure();
+
+  Version version = symbolizeVersion(spirvVersion).value();
+  SmallVector<Capability, 4> capabilities;
+  SmallVector<Extension, 8> extensions;
+  for (auto cap : spirvCapabilities) {
+    if (symbolizeCapability(cap))
+      capabilities.push_back(symbolizeCapability(cap).value());
+  }
+  ArrayRef<Capability> caps(capabilities);
+  for (auto ext : spirvExtensions) {
+    if (symbolizeCapability(ext))
+      extensions.push_back(symbolizeExtension(ext).value());
+  }
+  ArrayRef<Extension> exts(extensions);
+  VerCapExtAttr vce = VerCapExtAttr::get(version, caps, exts, &getContext());
+  auto target = builder.getAttr<SPIRVTargetAttr>(
+      vce, getDefaultResourceLimits(&getContext()),
+      symbolizeClientAPI(clientApi).value(),
+      symbolizeVendor(deviceVendor).value(),
+      symbolizeDeviceType(deviceType).value(), deviceId);
+  llvm::Regex matcher(moduleMatcher);
+  for (Region &region : getOperation()->getRegions())
+    for (Block &block : region.getBlocks())
+      for (auto module : block.getOps<gpu::GPUModuleOp>()) {
+        // Check if the name of the module matches.
+        if (!moduleMatcher.empty() && !matcher.match(module.getName()))
+          continue;
+        // Create the target array.
+        SmallVector<Attribute> targets;
+        if (std::optional<ArrayAttr> attrs = module.getTargets())
+          targets.append(attrs->getValue().begin(), attrs->getValue().end());
+        targets.push_back(target);
+        // Remove any duplicate targets.
+        targets.erase(std::unique(targets.begin(), targets.end()),
+                      targets.end());
+        // Update the target attribute array.
+        module.setTargetsAttr(builder.getArrayAttr(targets));
+      }
+}
diff --git a/mlir/lib/Target/SPIRV/CMakeLists.txt b/mlir/lib/Target/SPIRV/CMakeLists.txt
index 97bb64a74c41c70..f7a7d6e9378dc6d 100644
--- a/mlir/lib/Target/SPIRV/CMakeLists.txt
+++ b/mlir/lib/Target/SPIRV/CMakeLists.txt
@@ -4,6 +4,7 @@ add_subdirectory(Serialization)
 set(LLVM_OPTIONAL_SOURCES
   SPIRVBinaryUtils.cpp
   TranslateRegistration.cpp
+  Target.cpp
   )
 
 add_mlir_translation_library(MLIRSPIRVBinaryUtils
@@ -26,3 +27,15 @@ add_mlir_translation_library(MLIRSPIRVTranslateRegistration
   MLIRSupport
   MLIRTranslateLib
   )
+
+add_mlir_dialect_library(MLIRSPIRVTarget
+  Target.cpp
+
+  LINK_LIBS PUBLIC
+  MLIRIR
+  MLIRSPIRVDialect
+  MLIRSPIRVSerialization
+  MLIRSPIRVDeserialization
+  MLIRSupport
+  MLIRTranslateLib
+  )
diff --git a/mlir/lib/Target/SPIRV/Target.cpp b/mlir/lib/Target/SPIRV/Target.cpp
new file mode 100644
index 000000000000000..e704c696fc5da4a
--- /dev/null
+++ b/mlir/lib/Target/SPIRV/Target.cpp
@@ -0,0 +1,115 @@
+//===- Target.cpp - MLIR SPIRV target compilation ---------------*- C++ -*-===//
+//
+// 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 files defines SPIRV target related functions including registration
+// calls for the `#spirv.target` compilation attribute.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Target/SPIRV/Target.h"
+
+#include "mlir/Dialect/GPU/IR/GPUDialect.h"
+#include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h"
+#include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
+#include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
+#include "mlir/Target/LLVMIR/Dialect/GPU/GPUToLLVMIRTranslation.h"
+#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
+#include "mlir/Target/LLVMIR/Export.h"
+#include "mlir/Target/SPIRV/Serialization.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include <cstdlib>
+
+using namespace mlir;
+using namespace mlir::spirv;
+
+namespace {
+// Implementation of the `TargetAttrInterface` model.
+class SPIRVTargetAttrImpl
+    : public gpu::TargetAttrInterface::FallbackModel<SPIRVTargetAttrImpl> {
+public:
+  std::optional<SmallVector<char, 0>>
+  serializeToObject(Attribute attribute, Operation *module,
+                    const gpu::TargetOptions &options) const;
+
+  Attribute createObject(Attribute attribute,
+                         const SmallVector<char, 0> &object,
+                         const gpu::TargetOptions &options) const;
+};
+} // namespace
+
+// Register the SPIRV dialect, the SPIRV translation & the target interface.
+void mlir::spirv::registerSPIRVTargetInterfaceExternalModels(
+    DialectRegistry &registry) {
+  registry.addExtension(+[](MLIRContext *ctx, spirv::SPIRVDialect *dialect) {
+    spirv::SPIRVTargetAttr::attachInterface<SPIRVTargetAttrImpl>(*ctx);
+  });
+}
+
+void mlir::spirv::registerSPIRVTargetInterfaceExternalModels(
+    MLIRContext &context) {
+  DialectRegistry registry;
+  registerSPIRVTargetInterfaceExternalModels(registry);
+  context.appendDialectRegistry(registry);
+}
+
+// Reuse from existing serializer
+std::optional<SmallVector<char, 0>> SPIRVTargetAttrImpl::serializeToObject(
+    Attribute attribute, Operation *module,
+    const gpu::TargetOptions &options) const {
+  assert(module && "The module must be non null.");
+  if (!module)
+    return std::nullopt;
+  if (!mlir::isa<gpu::GPUModuleOp>(module)) {
+    module->emitError("Module must be a GPU module.");
+    return std::nullopt;
+  }
+  auto gpuMod = dyn_cast<gpu::GPUModuleOp>(module);
+  auto spvMods = gpuMod.getOps<spirv::ModuleOp>();
+  auto spvMod = *spvMods.begin();
+  llvm::SmallVector<uint32_t, 0> spvBinary;
+
+  spvBinary.clear();
+  // serialize the spv module to spv binary
+  if (mlir::failed(spirv::serialize(spvMod, spvBinary))) {
+    spvMod.emitError() << "Failed to serialize SPIR-V module";
+    return std::nullopt;
+  }
+
+  SmallVector<char, 0> spvData;
+  const char *data = reinterpret_cast<const char *>(spvBinary.data());
+  for (uint32_t i = 0; i < spvBinary.size() * sizeof(uint32_t); i++) {
+    spvData.push_back(*(data + i));
+  }
+
+  spvMod.erase();
+  return spvData;
+}
+
+// Prepare Attribute for gpu.binary with serialized kernel object
+Attribute
+SPIRVTargetAttrImpl::createObject(Attribute attribute,
+                                  const SmallVector<char, 0> &object,
+                                  const gpu::TargetOptions &options) const {
+  auto target = cast<SPIRVTargetAttr>(attribute);
+  gpu::CompilationTarget format = options.getCompilationTarget();
+  DictionaryAttr objectProps;
+  Builder builder(attribute.getContext());
+  return builder.getAttr<gpu::ObjectAttr>(
+      attribute, format,
+      builder.getStringAttr(StringRef(object.data(), object.size())),
+      objectProps);
+}
diff --git a/mlir/test/Dialect/GPU/module-to-binary-spirv.mlir b/mlir/test/Dialect/GPU/module-to-binary-spirv.mlir
new file mode 100644
index 000000000000000..e6cc1ad4edc622b
--- /dev/null
+++ b/mlir/test/Dialect/GPU/module-to-binary-spirv.mlir
@@ -0,0 +1,13 @@
+// RUN: mlir-opt %s --gpu-module-to-binary | FileCheck %s
+
+module attributes {gpu.container_module} {
+  // CHECK-LABEL:gpu.binary @kernel_module
+  // CHECK:[#gpu.object<#spirv.target<vce = #spirv.vce<v1.0, [Int64, Int16, Kernel, Addresses], []>, resource_limits = <>>, "{{.*}}">]
+  gpu.module @kernel_module [#spirv.target<vce = #spirv.vce<v1.0, [Int64, Int16, Kernel, Addresses], []>, resource_limits = <>>] {
+    spirv.module @__spv__kernel_module Physical64 OpenCL requires #spirv.vce<v1.0, [Int64, Int16, Kernel, Addresses], []> attributes {spirv.target_env = #spirv.target_env<#spirv.vce<v1.0, [Addresses, Float16Buffer, Int64, Int16, Int8, Kernel, Linkage, Vector16, GenericPointer, Groups, Float16, Float64, AtomicFloat32AddEXT, ExpectAssumeKHR], [SPV_EXT_shader_atomic_float_add, SPV_KHR_expect_assume, SPV_AMD_shader_ballot]>, api=OpenCL, #spirv.resource_limits<>>} {
+      spirv.func @test_kernel(%arg0: !spirv.ptr<!spirv.array<200 x i16>, CrossWorkgroup>, %arg1: !spirv.ptr<!spirv.array<200 x i16>, CrossWorkgroup>, %arg2: !spirv.ptr<!spirv.array<200 x i16>, CrossWorkgroup>) "None" attributes {workgroup_attributions = 0 : i64} {
+        spirv.Return
+      }
+    }
+  }
+}
diff --git a/mlir/test/Dialect/GPU/spirv-attach-targets.mlir b/mlir/test/Dialect/GPU/spirv-attach-targets.mlir
new file mode 100644
index 000000000000000..f9ab3c88d4a69da
--- /dev/null
+++ b/mlir/test/Dialect/GPU/spirv-attach-targets.mlir
@@ -0,0 +1,7 @@
+// RUN: mlir-opt %s --spirv-attach-target='module=spirv.* ver=v1.0 caps=Kernel' | FileCheck %s
+
+module attributes {gpu.container_module} {
+// CHECK: @spirv_module_1 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>>]
+gpu.module @spirv_module_1 {
+}
+}
diff --git a/mlir/test/Dialect/SPIRV/IR/target.mlir b/mlir/test/Dialect/SPIRV/IR/target.mlir
new file mode 100644
index 000000000000000..89b9344f0498126
--- /dev/null
+++ b/mlir/test/Dialect/SPIRV/IR/target.mlir
@@ -0,0 +1,14 @@
+// RUN: mlir-opt %s -split-input-file -verify-diagnostics | FileCheck %s
+
+// CHECK: @module_1 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>>]
+gpu.module @module_1 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>>] {
+}
+
+// CHECK: @module_2 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>, client_api = OpenCL>]
+gpu.module @module_2 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>, client_api = OpenCL>] {
+}
+
+// CHECK: @module_3 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>, client_api = OpenCL, vendor_id = Intel, device_type = IntegratedGPU>]
+gpu.module @module_3 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>, client_api = OpenCL, vendor_id = Intel, device_type = IntegratedGPU>] {
+}
+

>From 54adf4099c12eee7be39faae95309e530b16e8de Mon Sep 17 00:00:00 2001
From: "Lee, Sang Ik" <sang.ik.lee at intel.com>
Date: Mon, 23 Oct 2023 17:40:44 +0000
Subject: [PATCH 2/5] Register spirv target attribute.

---
 mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp b/mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp
index a51d77dda78bf2f..ad42f77fc536cd3 100644
--- a/mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp
@@ -14,6 +14,7 @@
 
 #include "SPIRVParsingUtils.h"
 
+#include "mlir/Dialect/GPU/IR/CompilationInterfaces.h"
 #include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
 #include "mlir/Dialect/SPIRV/IR/SPIRVTypes.h"
 #include "mlir/Dialect/SPIRV/IR/TargetAndABI.h"
@@ -133,6 +134,7 @@ void SPIRVDialect::initialize() {
 
   // Allow unknown operations because SPIR-V is extensible.
   allowUnknownOperations();
+  declarePromisedInterface<SPIRVTargetAttr, gpu::TargetAttrInterface>();
 }
 
 std::string SPIRVDialect::getAttributeName(Decoration decoration) {

>From 08b1d2d538922413081ad049119bf38d7a5d7b68 Mon Sep 17 00:00:00 2001
From: "Lee, Sang Ik" <sang.ik.lee at intel.com>
Date: Thu, 26 Oct 2023 18:34:33 +0000
Subject: [PATCH 3/5] Address reviewer comments.

---
 mlir/lib/Target/SPIRV/Target.cpp | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/mlir/lib/Target/SPIRV/Target.cpp b/mlir/lib/Target/SPIRV/Target.cpp
index e704c696fc5da4a..a7367479ec74b8e 100644
--- a/mlir/lib/Target/SPIRV/Target.cpp
+++ b/mlir/lib/Target/SPIRV/Target.cpp
@@ -32,6 +32,7 @@
 #include "llvm/Support/TargetSelect.h"
 
 #include <cstdlib>
+#include <cstring>
 
 using namespace mlir;
 using namespace mlir::spirv;
@@ -79,6 +80,10 @@ std::optional<SmallVector<char, 0>> SPIRVTargetAttrImpl::serializeToObject(
   }
   auto gpuMod = dyn_cast<gpu::GPUModuleOp>(module);
   auto spvMods = gpuMod.getOps<spirv::ModuleOp>();
+  // Empty spirv::ModuleOp
+  if (spvMods.empty()) {
+    return std::nullopt;
+  }
   auto spvMod = *spvMods.begin();
   llvm::SmallVector<uint32_t, 0> spvBinary;
 
@@ -89,11 +94,8 @@ std::optional<SmallVector<char, 0>> SPIRVTargetAttrImpl::serializeToObject(
     return std::nullopt;
   }
 
-  SmallVector<char, 0> spvData;
-  const char *data = reinterpret_cast<const char *>(spvBinary.data());
-  for (uint32_t i = 0; i < spvBinary.size() * sizeof(uint32_t); i++) {
-    spvData.push_back(*(data + i));
-  }
+  SmallVector<char, 0> spvData(spvBinary.size() * sizeof(uint32_t), 0);
+  std::memcpy(spvData.data(), spvBinary.data(), spvData.size());
 
   spvMod.erase();
   return spvData;

>From 7f0f1191ae91470988acb4da955574396e9a41e4 Mon Sep 17 00:00:00 2001
From: "Lee, Sang Ik" <sang.ik.lee at intel.com>
Date: Mon, 30 Oct 2023 20:20:22 +0000
Subject: [PATCH 4/5] Use existing TargetEnvAttr instead of introducing a new
 Attribute.

---
 .../mlir/Dialect/GPU/Transforms/Passes.td     |  2 +-
 .../mlir/Dialect/SPIRV/IR/SPIRVAttributes.td  | 31 -------------------
 .../GPU/Transforms/SPIRVAttachTarget.cpp      |  2 +-
 mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp    |  2 +-
 mlir/lib/Target/SPIRV/Target.cpp              |  4 +--
 .../Dialect/GPU/module-to-binary-spirv.mlir   |  4 +--
 .../Dialect/GPU/spirv-attach-targets.mlir     |  2 +-
 mlir/test/Dialect/SPIRV/IR/target.mlir        | 12 +++----
 8 files changed, 14 insertions(+), 45 deletions(-)

diff --git a/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td b/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
index 44e3e5b6226bfeb..ea75502387ae251 100644
--- a/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
@@ -201,7 +201,7 @@ def GpuSPIRVAttachTarget: Pass<"spirv-attach-target", ""> {
     gpu.module @spirv_module_1 {...}
     // mlir-opt --spirv-attach-target="module=spirv.* ver=v1.0 caps=Kernel" in1.mlir
     gpu.module @nvvm_module_1 {...}
-    gpu.module @spirv_module_1 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>>] {...}
+    gpu.module @spirv_module_1 [#spirv.target<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>] {...}
     ```
   }];
   let options = [
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.td
index e026f9dbfc27e30..f2c1ee5cfd56eab 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAttributes.td
@@ -166,35 +166,4 @@ def SPIRV_ResourceLimitsAttr : SPIRV_Attr<"ResourceLimits", "resource_limits"> {
   let assemblyFormat = "`<` struct(params) `>`";
 }
 
-//===----------------------------------------------------------------------===//
-// SPIRV target attribute.
-//===----------------------------------------------------------------------===//
-
-def SPIRV_TargetAttr : SPIRV_Attr<"SPIRVTarget", "target"> {
-  let description = [{
-    GPU target attribute for controlling compilation of SPIRV targets.
-  }];
-  let parameters = (ins
-    "mlir::spirv::VerCapExtAttr":$vce,
-    "mlir::spirv::ResourceLimitsAttr":$resource_limits,
-    DefaultValuedParameter<
-      "mlir::spirv::ClientAPI",
-      "mlir::spirv::ClientAPI::Unknown"
-    >:$client_api,
-    DefaultValuedParameter<
-      "mlir::spirv::Vendor",
-      "mlir::spirv::Vendor::Unknown"
-    >:$vendor_id,
-    DefaultValuedParameter<
-      "mlir::spirv::DeviceType",
-      "mlir::spirv::DeviceType::Unknown"
-    >:$device_type,
-    DefaultValuedParameter<
-      "uint32_t",
-      "mlir::spirv::TargetEnvAttr::kUnknownDeviceID"
-    >:$device_id
-  );
-  let assemblyFormat = "`<` struct(params) `>`";
-}
-
 #endif // MLIR_DIALECT_SPIRV_IR_TARGET_AND_ABI
diff --git a/mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp b/mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp
index 7366c0eefd05a9d..7986210d9fe6292 100644
--- a/mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp
+++ b/mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp
@@ -68,7 +68,7 @@ void SPIRVAttachTarget::runOnOperation() {
   }
   ArrayRef<Extension> exts(extensions);
   VerCapExtAttr vce = VerCapExtAttr::get(version, caps, exts, &getContext());
-  auto target = builder.getAttr<SPIRVTargetAttr>(
+  auto target = TargetEnvAttr::get(
       vce, getDefaultResourceLimits(&getContext()),
       symbolizeClientAPI(clientApi).value(),
       symbolizeVendor(deviceVendor).value(),
diff --git a/mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp b/mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp
index ad42f77fc536cd3..2de849dc4465e37 100644
--- a/mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/SPIRVDialect.cpp
@@ -134,7 +134,7 @@ void SPIRVDialect::initialize() {
 
   // Allow unknown operations because SPIR-V is extensible.
   allowUnknownOperations();
-  declarePromisedInterface<SPIRVTargetAttr, gpu::TargetAttrInterface>();
+  declarePromisedInterface<TargetEnvAttr, gpu::TargetAttrInterface>();
 }
 
 std::string SPIRVDialect::getAttributeName(Decoration decoration) {
diff --git a/mlir/lib/Target/SPIRV/Target.cpp b/mlir/lib/Target/SPIRV/Target.cpp
index a7367479ec74b8e..879822fa9d60bce 100644
--- a/mlir/lib/Target/SPIRV/Target.cpp
+++ b/mlir/lib/Target/SPIRV/Target.cpp
@@ -56,7 +56,7 @@ class SPIRVTargetAttrImpl
 void mlir::spirv::registerSPIRVTargetInterfaceExternalModels(
     DialectRegistry &registry) {
   registry.addExtension(+[](MLIRContext *ctx, spirv::SPIRVDialect *dialect) {
-    spirv::SPIRVTargetAttr::attachInterface<SPIRVTargetAttrImpl>(*ctx);
+    spirv::TargetEnvAttr::attachInterface<SPIRVTargetAttrImpl>(*ctx);
   });
 }
 
@@ -106,7 +106,7 @@ Attribute
 SPIRVTargetAttrImpl::createObject(Attribute attribute,
                                   const SmallVector<char, 0> &object,
                                   const gpu::TargetOptions &options) const {
-  auto target = cast<SPIRVTargetAttr>(attribute);
+  auto target = cast<TargetEnvAttr>(attribute);
   gpu::CompilationTarget format = options.getCompilationTarget();
   DictionaryAttr objectProps;
   Builder builder(attribute.getContext());
diff --git a/mlir/test/Dialect/GPU/module-to-binary-spirv.mlir b/mlir/test/Dialect/GPU/module-to-binary-spirv.mlir
index e6cc1ad4edc622b..d62f43927980174 100644
--- a/mlir/test/Dialect/GPU/module-to-binary-spirv.mlir
+++ b/mlir/test/Dialect/GPU/module-to-binary-spirv.mlir
@@ -2,8 +2,8 @@
 
 module attributes {gpu.container_module} {
   // CHECK-LABEL:gpu.binary @kernel_module
-  // CHECK:[#gpu.object<#spirv.target<vce = #spirv.vce<v1.0, [Int64, Int16, Kernel, Addresses], []>, resource_limits = <>>, "{{.*}}">]
-  gpu.module @kernel_module [#spirv.target<vce = #spirv.vce<v1.0, [Int64, Int16, Kernel, Addresses], []>, resource_limits = <>>] {
+  // CHECK:[#gpu.object<#spirv.target_env<#spirv.vce<v1.0, [Int64, Int16, Kernel, Addresses], []>, #spirv.resource_limits<>>, "{{.*}}">]
+  gpu.module @kernel_module [#spirv.target_env<#spirv.vce<v1.0, [Int64, Int16, Kernel, Addresses], []>, #spirv.resource_limits<>>] {
     spirv.module @__spv__kernel_module Physical64 OpenCL requires #spirv.vce<v1.0, [Int64, Int16, Kernel, Addresses], []> attributes {spirv.target_env = #spirv.target_env<#spirv.vce<v1.0, [Addresses, Float16Buffer, Int64, Int16, Int8, Kernel, Linkage, Vector16, GenericPointer, Groups, Float16, Float64, AtomicFloat32AddEXT, ExpectAssumeKHR], [SPV_EXT_shader_atomic_float_add, SPV_KHR_expect_assume, SPV_AMD_shader_ballot]>, api=OpenCL, #spirv.resource_limits<>>} {
       spirv.func @test_kernel(%arg0: !spirv.ptr<!spirv.array<200 x i16>, CrossWorkgroup>, %arg1: !spirv.ptr<!spirv.array<200 x i16>, CrossWorkgroup>, %arg2: !spirv.ptr<!spirv.array<200 x i16>, CrossWorkgroup>) "None" attributes {workgroup_attributions = 0 : i64} {
         spirv.Return
diff --git a/mlir/test/Dialect/GPU/spirv-attach-targets.mlir b/mlir/test/Dialect/GPU/spirv-attach-targets.mlir
index f9ab3c88d4a69da..83f325b69ea7c82 100644
--- a/mlir/test/Dialect/GPU/spirv-attach-targets.mlir
+++ b/mlir/test/Dialect/GPU/spirv-attach-targets.mlir
@@ -1,7 +1,7 @@
 // RUN: mlir-opt %s --spirv-attach-target='module=spirv.* ver=v1.0 caps=Kernel' | FileCheck %s
 
 module attributes {gpu.container_module} {
-// CHECK: @spirv_module_1 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>>]
+// CHECK: @spirv_module_1 [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>]
 gpu.module @spirv_module_1 {
 }
 }
diff --git a/mlir/test/Dialect/SPIRV/IR/target.mlir b/mlir/test/Dialect/SPIRV/IR/target.mlir
index 89b9344f0498126..6c60fe79f20bb95 100644
--- a/mlir/test/Dialect/SPIRV/IR/target.mlir
+++ b/mlir/test/Dialect/SPIRV/IR/target.mlir
@@ -1,14 +1,14 @@
 // RUN: mlir-opt %s -split-input-file -verify-diagnostics | FileCheck %s
 
-// CHECK: @module_1 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>>]
-gpu.module @module_1 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>>] {
+// CHECK: @module_1 [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>]
+gpu.module @module_1 [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>] {
 }
 
-// CHECK: @module_2 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>, client_api = OpenCL>]
-gpu.module @module_2 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>, client_api = OpenCL>] {
+// CHECK: @module_2 [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, api=OpenCL, #spirv.resource_limits<>>]
+gpu.module @module_2 [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, api=OpenCL, #spirv.resource_limits<>>] {
 }
 
-// CHECK: @module_3 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>, client_api = OpenCL, vendor_id = Intel, device_type = IntegratedGPU>]
-gpu.module @module_3 [#spirv.target<vce = #spirv.vce<v1.0, [Kernel], []>, resource_limits = <>, client_api = OpenCL, vendor_id = Intel, device_type = IntegratedGPU>] {
+// CHECK: @module_3 [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, api=OpenCL, Intel:IntegratedGPU, #spirv.resource_limits<>>]
+gpu.module @module_3 [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, api=OpenCL, Intel:IntegratedGPU, #spirv.resource_limits<>>] {
 }
 

>From 6a9e26809785efd431359577dcd7dbef10e2781a Mon Sep 17 00:00:00 2001
From: "Lee, Sang Ik" <sang.ik.lee at intel.com>
Date: Mon, 30 Oct 2023 21:02:25 +0000
Subject: [PATCH 5/5] Address reviewer comments.

---
 .../mlir/Dialect/GPU/Transforms/Passes.td     | 14 ++--
 mlir/include/mlir/Target/SPIRV/Target.h       | 12 ++--
 .../GPU/Transforms/SPIRVAttachTarget.cpp      | 69 ++++++++++---------
 mlir/lib/Target/SPIRV/Target.cpp              | 27 ++++----
 .../Dialect/GPU/spirv-attach-targets.mlir     | 14 +++-
 5 files changed, 73 insertions(+), 63 deletions(-)

diff --git a/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td b/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
index ea75502387ae251..b22d26d49dbdb0e 100644
--- a/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
@@ -189,17 +189,19 @@ def GpuROCDLAttachTarget: Pass<"rocdl-attach-target", ""> {
 }
 
 def GpuSPIRVAttachTarget: Pass<"spirv-attach-target", ""> {
-  let summary = "Attaches an SPIRV target attribute to a GPU Module.";
+  let summary = "Attaches an SPIR-V target attribute to a GPU Module.";
   let description = [{
     This pass searches for all GPU Modules in the immediate regions and attaches
-    an SPIRV target if the module matches the name specified by the `module` argument.
+    an SPIR-V target if the module matches the name specified by the `module` argument.
 
     Example:
     ```
-    // File: in1.mlir:
+    // Given the following file: in1.mlir:
     gpu.module @nvvm_module_1 {...}
     gpu.module @spirv_module_1 {...}
+    // With
     // mlir-opt --spirv-attach-target="module=spirv.* ver=v1.0 caps=Kernel" in1.mlir
+    // it will generate,
     gpu.module @nvvm_module_1 {...}
     gpu.module @spirv_module_1 [#spirv.target<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>] {...}
     ```
@@ -210,11 +212,11 @@ def GpuSPIRVAttachTarget: Pass<"spirv-attach-target", ""> {
            "Regex used to identify the modules to attach the target to.">,
     Option<"spirvVersion", "ver", "std::string",
            /*default=*/ "\"v1.0\"",
-           "SPIRV Addressing Model.">,
+           "SPIR-V Version.">,
     ListOption<"spirvCapabilities", "caps", "std::string",
-           "List of required SPIRV Capabilities">,
+           "List of supported SPIR-V Capabilities">,
     ListOption<"spirvExtensions", "exts", "std::string",
-           "List of required SPIRV Extensions">,
+           "List of supported SPIR-V Extensions">,
     Option<"clientApi", "client_api", "std::string",
            /*default=*/ "\"Unknown\"",
            "Client API">,
diff --git a/mlir/include/mlir/Target/SPIRV/Target.h b/mlir/include/mlir/Target/SPIRV/Target.h
index 52154afbac66e57..fea6471ced7c2fc 100644
--- a/mlir/include/mlir/Target/SPIRV/Target.h
+++ b/mlir/include/mlir/Target/SPIRV/Target.h
@@ -1,4 +1,4 @@
-//===- Target.h - MLIR SPIRV target registration ----------------*- C++ -*-===//
+//===- Target.h - MLIR SPIR-V target registration ---------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This provides registration calls for attaching the SPIRV target interface.
+// This provides registration calls for attaching the SPIR-V target interface.
 //
 //===----------------------------------------------------------------------===//
 
@@ -17,12 +17,12 @@ namespace mlir {
 class DialectRegistry;
 class MLIRContext;
 namespace spirv {
-/// Registers the `TargetAttrInterface` for the `#spirv.target` attribute in the
-/// given registry.
+/// Registers the `TargetAttrInterface` for the `#spirv.target_env` attribute in
+/// the given registry.
 void registerSPIRVTargetInterfaceExternalModels(DialectRegistry &registry);
 
-/// Registers the `TargetAttrInterface` for the `#spirv.target` attribute in the
-/// registry associated with the given context.
+/// Registers the `TargetAttrInterface` for the `#spirv.target_env` attribute in
+/// the registry associated with the given context.
 void registerSPIRVTargetInterfaceExternalModels(MLIRContext &context);
 } // namespace spirv
 } // namespace mlir
diff --git a/mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp b/mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp
index 7986210d9fe6292..eece62b9c6cb9c8 100644
--- a/mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp
+++ b/mlir/lib/Dialect/GPU/Transforms/SPIRVAttachTarget.cpp
@@ -1,4 +1,4 @@
-//===- SPIRVAttachTarget.cpp - Attach an SPIRV target ---------------------===//
+//===- SPIRVAttachTarget.cpp - Attach an SPIR-V target --------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file implements the `GpuSPIRVAttachTarget` pass, attaching
-// `#spirv.target` attributes to GPU modules.
+// This file implements the `GPUSPIRVAttachTarget` pass, attaching
+// `#spirv.target_env` attributes to GPU modules.
 //
 //===----------------------------------------------------------------------===//
 
@@ -45,50 +45,51 @@ struct SPIRVAttachTarget
 
 void SPIRVAttachTarget::runOnOperation() {
   OpBuilder builder(&getContext());
-  if (!symbolizeVersion(spirvVersion))
+  auto versionSymbol = symbolizeVersion(spirvVersion);
+  if (!versionSymbol)
     return signalPassFailure();
-  if (!symbolizeClientAPI(clientApi))
+  auto apiSymbol = symbolizeClientAPI(clientApi);
+  if (!apiSymbol)
     return signalPassFailure();
-  if (!symbolizeVendor(deviceVendor))
+  auto vendorSymbol = symbolizeVendor(deviceVendor);
+  if (!vendorSymbol)
     return signalPassFailure();
-  if (!symbolizeDeviceType(deviceType))
+  auto deviceTypeSymbol = symbolizeDeviceType(deviceType);
+  if (!deviceTypeSymbol)
     return signalPassFailure();
 
-  Version version = symbolizeVersion(spirvVersion).value();
+  Version version = versionSymbol.value();
   SmallVector<Capability, 4> capabilities;
   SmallVector<Extension, 8> extensions;
   for (auto cap : spirvCapabilities) {
-    if (symbolizeCapability(cap))
-      capabilities.push_back(symbolizeCapability(cap).value());
+    auto capSymbol = symbolizeCapability(cap);
+    if (capSymbol)
+      capabilities.push_back(capSymbol.value());
   }
   ArrayRef<Capability> caps(capabilities);
   for (auto ext : spirvExtensions) {
-    if (symbolizeCapability(ext))
-      extensions.push_back(symbolizeExtension(ext).value());
+    auto extSymbol = symbolizeExtension(ext);
+    if (extSymbol)
+      extensions.push_back(extSymbol.value());
   }
   ArrayRef<Extension> exts(extensions);
   VerCapExtAttr vce = VerCapExtAttr::get(version, caps, exts, &getContext());
-  auto target = TargetEnvAttr::get(
-      vce, getDefaultResourceLimits(&getContext()),
-      symbolizeClientAPI(clientApi).value(),
-      symbolizeVendor(deviceVendor).value(),
-      symbolizeDeviceType(deviceType).value(), deviceId);
+  auto target = TargetEnvAttr::get(vce, getDefaultResourceLimits(&getContext()),
+                                   apiSymbol.value(), vendorSymbol.value(),
+                                   deviceTypeSymbol.value(), deviceId);
   llvm::Regex matcher(moduleMatcher);
-  for (Region &region : getOperation()->getRegions())
-    for (Block &block : region.getBlocks())
-      for (auto module : block.getOps<gpu::GPUModuleOp>()) {
-        // Check if the name of the module matches.
-        if (!moduleMatcher.empty() && !matcher.match(module.getName()))
-          continue;
-        // Create the target array.
-        SmallVector<Attribute> targets;
-        if (std::optional<ArrayAttr> attrs = module.getTargets())
-          targets.append(attrs->getValue().begin(), attrs->getValue().end());
-        targets.push_back(target);
-        // Remove any duplicate targets.
-        targets.erase(std::unique(targets.begin(), targets.end()),
-                      targets.end());
-        // Update the target attribute array.
-        module.setTargetsAttr(builder.getArrayAttr(targets));
-      }
+  getOperation()->walk([&](gpu::GPUModuleOp gpuModule) {
+    // Check if the name of the module matches.
+    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(std::unique(targets.begin(), targets.end()), targets.end());
+    // Update the target attribute array.
+    gpuModule.setTargetsAttr(builder.getArrayAttr(targets));
+  });
 }
diff --git a/mlir/lib/Target/SPIRV/Target.cpp b/mlir/lib/Target/SPIRV/Target.cpp
index 879822fa9d60bce..1b6bfbac38b6193 100644
--- a/mlir/lib/Target/SPIRV/Target.cpp
+++ b/mlir/lib/Target/SPIRV/Target.cpp
@@ -1,4 +1,4 @@
-//===- Target.cpp - MLIR SPIRV target compilation ---------------*- C++ -*-===//
+//===- Target.cpp - MLIR SPIR-V target compilation --------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This files defines SPIRV target related functions including registration
-// calls for the `#spirv.target` compilation attribute.
+// This files defines SPIR-V target related functions including registration
+// calls for the `#spirv.target_env` compilation attribute.
 //
 //===----------------------------------------------------------------------===//
 
@@ -38,7 +38,7 @@ using namespace mlir;
 using namespace mlir::spirv;
 
 namespace {
-// Implementation of the `TargetAttrInterface` model.
+// SPIR-V implementation of the gpu:TargetAttrInterface.
 class SPIRVTargetAttrImpl
     : public gpu::TargetAttrInterface::FallbackModel<SPIRVTargetAttrImpl> {
 public:
@@ -52,7 +52,7 @@ class SPIRVTargetAttrImpl
 };
 } // namespace
 
-// Register the SPIRV dialect, the SPIRV translation & the target interface.
+// Register the SPIR-V dialect, the SPIR-V translation & the target interface.
 void mlir::spirv::registerSPIRVTargetInterfaceExternalModels(
     DialectRegistry &registry) {
   registry.addExtension(+[](MLIRContext *ctx, spirv::SPIRVDialect *dialect) {
@@ -71,26 +71,24 @@ void mlir::spirv::registerSPIRVTargetInterfaceExternalModels(
 std::optional<SmallVector<char, 0>> SPIRVTargetAttrImpl::serializeToObject(
     Attribute attribute, Operation *module,
     const gpu::TargetOptions &options) const {
-  assert(module && "The module must be non null.");
   if (!module)
     return std::nullopt;
-  if (!mlir::isa<gpu::GPUModuleOp>(module)) {
-    module->emitError("Module must be a GPU module.");
+  auto gpuMod = dyn_cast<gpu::GPUModuleOp>(module);
+  if (!gpuMod) {
+    module->emitError("expected to be a gpu.module op");
     return std::nullopt;
   }
-  auto gpuMod = dyn_cast<gpu::GPUModuleOp>(module);
   auto spvMods = gpuMod.getOps<spirv::ModuleOp>();
-  // Empty spirv::ModuleOp
-  if (spvMods.empty()) {
+  if (spvMods.empty())
     return std::nullopt;
-  }
+
   auto spvMod = *spvMods.begin();
   llvm::SmallVector<uint32_t, 0> spvBinary;
 
   spvBinary.clear();
-  // serialize the spv module to spv binary
+  // Serialize the spirv.module op to SPIR-V blob.
   if (mlir::failed(spirv::serialize(spvMod, spvBinary))) {
-    spvMod.emitError() << "Failed to serialize SPIR-V module";
+    spvMod.emitError() << "failed to serialize SPIR-V module";
     return std::nullopt;
   }
 
@@ -106,7 +104,6 @@ Attribute
 SPIRVTargetAttrImpl::createObject(Attribute attribute,
                                   const SmallVector<char, 0> &object,
                                   const gpu::TargetOptions &options) const {
-  auto target = cast<TargetEnvAttr>(attribute);
   gpu::CompilationTarget format = options.getCompilationTarget();
   DictionaryAttr objectProps;
   Builder builder(attribute.getContext());
diff --git a/mlir/test/Dialect/GPU/spirv-attach-targets.mlir b/mlir/test/Dialect/GPU/spirv-attach-targets.mlir
index 83f325b69ea7c82..2ab748834e49fac 100644
--- a/mlir/test/Dialect/GPU/spirv-attach-targets.mlir
+++ b/mlir/test/Dialect/GPU/spirv-attach-targets.mlir
@@ -1,7 +1,17 @@
 // RUN: mlir-opt %s --spirv-attach-target='module=spirv.* ver=v1.0 caps=Kernel' | FileCheck %s
+// RUN: mlir-opt %s --spirv-attach-target='module=spirv_warm.* ver=v1.0 caps=Kernel' | FileCheck %s --check-prefix=CHECK_WARM
 
 module attributes {gpu.container_module} {
-// CHECK: @spirv_module_1 [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>]
-gpu.module @spirv_module_1 {
+//      CHECK: @spirv_hot_module [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>]
+// CHECK_WARM: @spirv_hot_module {
+gpu.module @spirv_hot_module {
+}
+//      CHECK: @spirv_warm_module [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>]
+// CHECK_WARM: @spirv_warm_module [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>]
+gpu.module @spirv_warm_module {
+}
+//      CHECK: @spirv_cold_module [#spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>]
+// CHECK_WARM: @spirv_cold_module {
+gpu.module @spirv_cold_module {
 }
 }



More information about the llvm-commits mailing list